gt_wt_03.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. #include "gt_wt_03.h"
  2. #define TAG "WSProtocolGT_WT03"
  3. /*
  4. * Help
  5. * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_03.c
  6. *
  7. *
  8. * Globaltronics GT-WT-03 sensor on 433.92MHz.
  9. * The 01-set sensor has 60 ms packet gap with 10 repeats.
  10. * The 02-set sensor has no packet gap with 23 repeats.
  11. * Example:
  12. * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes ]
  13. * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes Batt-Changed ]
  14. * {41} 17 cf fe fa ea 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-No Batt-Changed ]
  15. * {41} 01 cf 6f 11 b2 80 [ S2 C2 23,8 C 74.8 F 48% Bat-LOW Manual-No ]
  16. * {41} 01 c8 d0 2b 76 80 [ S2 C3 -4,4 C 24.1 F 55% Bat-Good Manual-No Batt-Changed ]
  17. * Format string:
  18. * ID:8h HUM:8d B:b M:b C:2d TEMP:12d CHK:8h 1x
  19. * Data layout:
  20. * TYP IIIIIIII HHHHHHHH BMCCTTTT TTTTTTTT XXXXXXXX
  21. * - I: Random Device Code: changes with battery reset
  22. * - H: Humidity: 8 Bit 00-99, Display LL=10%, Display HH=110% (Range 20-95%)
  23. * - B: Battery: 0=OK 1=LOW
  24. * - M: Manual Send Button Pressed: 0=not pressed, 1=pressed
  25. * - C: Channel: 00=CH1, 01=CH2, 10=CH3
  26. * - T: Temperature: 12 Bit 2's complement, scaled by 10, range-50.0 C (-50.1 shown as Lo) to +70.0 C (+70.1 C is shown as Hi)
  27. * - X: Checksum, xor shifting key per byte
  28. * Humidity:
  29. * - the working range is 20-95 %
  30. * - if "LL" in display view it sends 10 %
  31. * - if "HH" in display view it sends 110%
  32. * Checksum:
  33. * Per byte xor the key for each 1-bit, shift per bit. Key list per bit, starting at MSB:
  34. * - 0x00 [07]
  35. * - 0x80 [06]
  36. * - 0x40 [05]
  37. * - 0x20 [04]
  38. * - 0x10 [03]
  39. * - 0x88 [02]
  40. * - 0xc4 [01]
  41. * - 0x62 [00]
  42. * Note: this can also be seen as lower byte of a Galois/Fibonacci LFSR-16, gen 0x00, init 0x3100 (or 0x62 if reversed) resetting at every byte.
  43. * Battery voltages:
  44. * - U=<2,65V +- ~5% Battery indicator
  45. * - U=>2.10C +- 5% plausible readings
  46. * - U=2,00V +- ~5% Temperature offset -5°C Humidity offset unknown
  47. * - U=<1,95V +- ~5% does not initialize anymore
  48. * - U=1,90V +- 5% temperature offset -15°C
  49. * - U=1,80V +- 5% Display is showing refresh pattern
  50. * - U=1.75V +- ~5% TX causes cut out
  51. *
  52. */
  53. static const SubGhzBlockConst ws_protocol_gt_wt_03_const = {
  54. .te_short = 285,
  55. .te_long = 570,
  56. .te_delta = 120,
  57. .min_count_bit_for_found = 41,
  58. };
  59. struct WSProtocolDecoderGT_WT03 {
  60. SubGhzProtocolDecoderBase base;
  61. SubGhzBlockDecoder decoder;
  62. WSBlockGeneric generic;
  63. uint16_t header_count;
  64. };
  65. struct WSProtocolEncoderGT_WT03 {
  66. SubGhzProtocolEncoderBase base;
  67. SubGhzProtocolBlockEncoder encoder;
  68. WSBlockGeneric generic;
  69. };
  70. typedef enum {
  71. GT_WT03DecoderStepReset = 0,
  72. GT_WT03DecoderStepCheckPreambule,
  73. GT_WT03DecoderStepSaveDuration,
  74. GT_WT03DecoderStepCheckDuration,
  75. } GT_WT03DecoderStep;
  76. const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder = {
  77. .alloc = ws_protocol_decoder_gt_wt_03_alloc,
  78. .free = ws_protocol_decoder_gt_wt_03_free,
  79. .feed = ws_protocol_decoder_gt_wt_03_feed,
  80. .reset = ws_protocol_decoder_gt_wt_03_reset,
  81. .get_hash_data = ws_protocol_decoder_gt_wt_03_get_hash_data,
  82. .serialize = ws_protocol_decoder_gt_wt_03_serialize,
  83. .deserialize = ws_protocol_decoder_gt_wt_03_deserialize,
  84. .get_string = ws_protocol_decoder_gt_wt_03_get_string,
  85. };
  86. const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder = {
  87. .alloc = NULL,
  88. .free = NULL,
  89. .deserialize = NULL,
  90. .stop = NULL,
  91. .yield = NULL,
  92. };
  93. const SubGhzProtocol ws_protocol_gt_wt_03 = {
  94. .name = WS_PROTOCOL_GT_WT_03_NAME,
  95. .type = SubGhzProtocolWeatherStation,
  96. .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
  97. SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
  98. .decoder = &ws_protocol_gt_wt_03_decoder,
  99. .encoder = &ws_protocol_gt_wt_03_encoder,
  100. };
  101. void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment) {
  102. UNUSED(environment);
  103. WSProtocolDecoderGT_WT03* instance = malloc(sizeof(WSProtocolDecoderGT_WT03));
  104. instance->base.protocol = &ws_protocol_gt_wt_03;
  105. instance->generic.protocol_name = instance->base.protocol->name;
  106. return instance;
  107. }
  108. void ws_protocol_decoder_gt_wt_03_free(void* context) {
  109. furi_assert(context);
  110. WSProtocolDecoderGT_WT03* instance = context;
  111. free(instance);
  112. }
  113. void ws_protocol_decoder_gt_wt_03_reset(void* context) {
  114. furi_assert(context);
  115. WSProtocolDecoderGT_WT03* instance = context;
  116. instance->decoder.parser_step = GT_WT03DecoderStepReset;
  117. }
  118. static bool ws_protocol_gt_wt_03_check_crc(WSProtocolDecoderGT_WT03* instance) {
  119. uint8_t msg[] = {
  120. instance->decoder.decode_data >> 33,
  121. instance->decoder.decode_data >> 25,
  122. instance->decoder.decode_data >> 17,
  123. instance->decoder.decode_data >> 9};
  124. uint8_t sum = 0;
  125. for(unsigned k = 0; k < sizeof(msg); ++k) {
  126. uint8_t data = msg[k];
  127. uint16_t key = 0x3100;
  128. for(int i = 7; i >= 0; --i) {
  129. // XOR key into sum if data bit is set
  130. if((data >> i) & 1) sum ^= key & 0xff;
  131. // roll the key right
  132. key = (key >> 1);
  133. }
  134. }
  135. return ((sum ^ (uint8_t)((instance->decoder.decode_data >> 1) & 0xFF)) == 0x2D);
  136. }
  137. /**
  138. * Analysis of received data
  139. * @param instance Pointer to a WSBlockGeneric* instance
  140. */
  141. static void ws_protocol_gt_wt_03_remote_controller(WSBlockGeneric* instance) {
  142. instance->id = instance->data >> 33;
  143. instance->humidity = (instance->data >> 25) & 0xFF;
  144. if(instance->humidity <= 10) { // actually the sensors sends 10 below working range of 20%
  145. instance->humidity = 0;
  146. } else if(instance->humidity > 95) { // actually the sensors sends 110 above working range of 90%
  147. instance->humidity = 100;
  148. }
  149. instance->battery_low = (instance->data >> 24) & 1;
  150. instance->btn = (instance->data >> 23) & 1;
  151. instance->channel = ((instance->data >> 21) & 0x03) + 1;
  152. if(!((instance->data >> 20) & 1)) {
  153. instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f;
  154. } else {
  155. instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f;
  156. }
  157. }
  158. void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration) {
  159. furi_assert(context);
  160. WSProtocolDecoderGT_WT03* instance = context;
  161. switch(instance->decoder.parser_step) {
  162. case GT_WT03DecoderStepReset:
  163. if((level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) <
  164. ws_protocol_gt_wt_03_const.te_delta * 2)) {
  165. instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule;
  166. instance->decoder.te_last = duration;
  167. instance->header_count = 0;
  168. }
  169. break;
  170. case GT_WT03DecoderStepCheckPreambule:
  171. if(level) {
  172. instance->decoder.te_last = duration;
  173. } else {
  174. if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) <
  175. ws_protocol_gt_wt_03_const.te_delta * 2) &&
  176. (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) <
  177. ws_protocol_gt_wt_03_const.te_delta * 2)) {
  178. //Found preambule
  179. instance->header_count++;
  180. } else if(instance->header_count == 4) {
  181. if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) <
  182. ws_protocol_gt_wt_03_const.te_delta) &&
  183. (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) <
  184. ws_protocol_gt_wt_03_const.te_delta)) {
  185. instance->decoder.decode_data = 0;
  186. instance->decoder.decode_count_bit = 0;
  187. subghz_protocol_blocks_add_bit(&instance->decoder, 0);
  188. instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration;
  189. } else if(
  190. (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) <
  191. ws_protocol_gt_wt_03_const.te_delta) &&
  192. (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) <
  193. ws_protocol_gt_wt_03_const.te_delta)) {
  194. instance->decoder.decode_data = 0;
  195. instance->decoder.decode_count_bit = 0;
  196. subghz_protocol_blocks_add_bit(&instance->decoder, 1);
  197. instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration;
  198. } else {
  199. instance->decoder.parser_step = GT_WT03DecoderStepReset;
  200. }
  201. } else {
  202. instance->decoder.parser_step = GT_WT03DecoderStepReset;
  203. }
  204. }
  205. break;
  206. case GT_WT03DecoderStepSaveDuration:
  207. if(level) {
  208. instance->decoder.te_last = duration;
  209. instance->decoder.parser_step = GT_WT03DecoderStepCheckDuration;
  210. } else {
  211. instance->decoder.parser_step = GT_WT03DecoderStepReset;
  212. }
  213. break;
  214. case GT_WT03DecoderStepCheckDuration:
  215. if(!level) {
  216. if(((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) <
  217. ws_protocol_gt_wt_03_const.te_delta * 2) &&
  218. (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) <
  219. ws_protocol_gt_wt_03_const.te_delta * 2))) {
  220. if((instance->decoder.decode_count_bit ==
  221. ws_protocol_gt_wt_03_const.min_count_bit_for_found) &&
  222. ws_protocol_gt_wt_03_check_crc(instance)) {
  223. instance->generic.data = instance->decoder.decode_data;
  224. instance->generic.data_count_bit = instance->decoder.decode_count_bit;
  225. ws_protocol_gt_wt_03_remote_controller(&instance->generic);
  226. if(instance->base.callback)
  227. instance->base.callback(&instance->base, instance->base.context);
  228. }
  229. instance->decoder.decode_data = 0;
  230. instance->decoder.decode_count_bit = 0;
  231. instance->header_count = 1;
  232. instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule;
  233. break;
  234. } else if(
  235. (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) <
  236. ws_protocol_gt_wt_03_const.te_delta) &&
  237. (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) <
  238. ws_protocol_gt_wt_03_const.te_delta)) {
  239. subghz_protocol_blocks_add_bit(&instance->decoder, 0);
  240. instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration;
  241. } else if(
  242. (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) <
  243. ws_protocol_gt_wt_03_const.te_delta) &&
  244. (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) <
  245. ws_protocol_gt_wt_03_const.te_delta)) {
  246. subghz_protocol_blocks_add_bit(&instance->decoder, 1);
  247. instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration;
  248. } else {
  249. instance->decoder.parser_step = GT_WT03DecoderStepReset;
  250. }
  251. } else {
  252. instance->decoder.parser_step = GT_WT03DecoderStepReset;
  253. }
  254. break;
  255. }
  256. }
  257. uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) {
  258. furi_assert(context);
  259. WSProtocolDecoderGT_WT03* instance = context;
  260. return subghz_protocol_blocks_get_hash_data(
  261. &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
  262. }
  263. SubGhzProtocolStatus ws_protocol_decoder_gt_wt_03_serialize(
  264. void* context,
  265. FlipperFormat* flipper_format,
  266. SubGhzRadioPreset* preset) {
  267. furi_assert(context);
  268. WSProtocolDecoderGT_WT03* instance = context;
  269. return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
  270. }
  271. SubGhzProtocolStatus
  272. ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) {
  273. furi_assert(context);
  274. WSProtocolDecoderGT_WT03* instance = context;
  275. return ws_block_generic_deserialize_check_count_bit(
  276. &instance->generic, flipper_format, ws_protocol_gt_wt_03_const.min_count_bit_for_found);
  277. }
  278. void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) {
  279. furi_assert(context);
  280. WSProtocolDecoderGT_WT03* instance = context;
  281. furi_string_printf(
  282. output,
  283. "%s %dbit\r\n"
  284. "Key:0x%lX%08lX\r\n"
  285. "Sn:0x%lX Ch:%d Bat:%d\r\n"
  286. "Temp:%3.1f C Hum:%d%%",
  287. instance->generic.protocol_name,
  288. instance->generic.data_count_bit,
  289. (uint32_t)(instance->generic.data >> 32),
  290. (uint32_t)(instance->generic.data),
  291. instance->generic.id,
  292. instance->generic.channel,
  293. instance->generic.battery_low,
  294. (double)instance->generic.temp,
  295. instance->generic.humidity);
  296. }