Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

500 lines
14 KiB

  1. /*
  2. ITEAD RF BRIDGE MODULE
  3. Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #ifdef ITEAD_SONOFF_RFBRIDGE
  6. #include <vector>
  7. #include <Ticker.h>
  8. // -----------------------------------------------------------------------------
  9. // DEFINITIONS
  10. // -----------------------------------------------------------------------------
  11. #define RF_MESSAGE_SIZE 9
  12. #ifndef RF_RAW_SUPPORT
  13. # define RF_MAX_MESSAGE_SIZE RF_MESSAGE_SIZE
  14. #else
  15. # undef RF_MAX_MESSAGE_SIZE
  16. # define RF_MAX_MESSAGE_SIZE 16 // (112+4)
  17. #endif
  18. #define RF_CODE_START 0xAA
  19. #define RF_CODE_ACK 0xA0
  20. #define RF_CODE_LEARN 0xA1
  21. #define RF_CODE_LEARN_KO 0xA2
  22. #define RF_CODE_LEARN_OK 0xA3
  23. #define RF_CODE_RFIN 0xA4
  24. #define RF_CODE_RFOUT 0xA5
  25. #define RF_CODE_SNIFFING_ON 0xA6
  26. #define RF_CODE_SNIFFING_OFF 0xA7
  27. #define RF_CODE_RFOUT_NEW 0xA8
  28. #define RF_CODE_LEARN_NEW 0xA9
  29. #define RF_CODE_LEARN_KO_NEW 0xAA
  30. #define RF_CODE_LEARN_OK_NEW 0xAB
  31. #define RF_CODE_RFOUT_BUCKET 0xB0
  32. #define RF_CODE_STOP 0x55
  33. // -----------------------------------------------------------------------------
  34. // GLOBALS TO THE MODULE
  35. // -----------------------------------------------------------------------------
  36. unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
  37. unsigned char _uartpos = 0;
  38. unsigned char _learnId = 0;
  39. bool _learnStatus = true;
  40. bool _rfbin = false;
  41. typedef struct {
  42. byte code[RF_MAX_MESSAGE_SIZE];
  43. byte times;
  44. #ifdef RF_RAW_SUPPORT
  45. byte length;
  46. #endif
  47. } rfb_message_t;
  48. std::vector<rfb_message_t> _rfb_message_queue;
  49. Ticker _rfbTicker;
  50. // -----------------------------------------------------------------------------
  51. // PRIVATES
  52. // -----------------------------------------------------------------------------
  53. void _rfbWebSocketOnSend(JsonObject& root) {
  54. root["rfbVisible"] = 1;
  55. root["rfbCount"] = relayCount();
  56. JsonArray& rfb = root.createNestedArray("rfb");
  57. for (byte id=0; id<relayCount(); id++) {
  58. for (byte status=0; status<2; status++) {
  59. JsonObject& node = rfb.createNestedObject();
  60. node["id"] = id;
  61. node["status"] = status;
  62. node["data"] = rfbRetrieve(id, status == 1);
  63. }
  64. }
  65. }
  66. void _rfbWebSocketOnAction(const char * action, JsonObject& data) {
  67. if (strcmp(action, "rfblearn") == 0) rfbLearn(data["id"], data["status"]);
  68. if (strcmp(action, "rfbforget") == 0) rfbForget(data["id"], data["status"]);
  69. if (strcmp(action, "rfbsend") == 0) rfbStore(data["id"], data["status"], data["data"].as<const char*>());
  70. }
  71. void _rfbAck() {
  72. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
  73. Serial.println();
  74. Serial.write(RF_CODE_START);
  75. Serial.write(RF_CODE_ACK);
  76. Serial.write(RF_CODE_STOP);
  77. Serial.flush();
  78. Serial.println();
  79. }
  80. void _rfbLearn() {
  81. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
  82. Serial.println();
  83. Serial.write(RF_CODE_START);
  84. Serial.write(RF_CODE_LEARN);
  85. Serial.write(RF_CODE_STOP);
  86. Serial.flush();
  87. Serial.println();
  88. #if WEB_SUPPORT
  89. char buffer[100];
  90. snprintf_P(buffer, sizeof(buffer), PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
  91. wsSend(buffer);
  92. #endif
  93. }
  94. void _rfbSendRaw(const byte *message, const unsigned char n = RF_MESSAGE_SIZE) {
  95. for (unsigned char j=0; j<n; j++) {
  96. Serial.write(message[j]);
  97. }
  98. }
  99. void _rfbSend(byte * message) {
  100. Serial.println();
  101. Serial.write(RF_CODE_START);
  102. Serial.write(RF_CODE_RFOUT);
  103. _rfbSendRaw(message);
  104. Serial.write(RF_CODE_STOP);
  105. Serial.flush();
  106. Serial.println();
  107. }
  108. void _rfbSend() {
  109. // Check if there is something in the queue
  110. if (_rfb_message_queue.size() == 0) return;
  111. // Pop the first element
  112. rfb_message_t message = _rfb_message_queue.front();
  113. _rfb_message_queue.erase(_rfb_message_queue.begin());
  114. // Send the message
  115. #ifdef RF_RAW_SUPPORT
  116. bool sendRaw = message.times < 0;
  117. int length = sendRaw ? -message.times : message.times;
  118. if (sendRaw) {
  119. _rfbSendRaw(message.code, message.length);
  120. Serial.flush();
  121. }
  122. else
  123. #endif
  124. _rfbSend(message.code);
  125. // If it should be further sent, push it to the stack again
  126. if (message.times > 1) {
  127. message.times = message.times - 1;
  128. _rfb_message_queue.push_back(message);
  129. }
  130. #ifdef RF_RAW_SUPPORT
  131. else if (message.times < -1) {
  132. message.times = message.times + 1;
  133. _rfb_message_queue.push_back(message);
  134. }
  135. #endif
  136. // if there are still messages in the queue...
  137. if (_rfb_message_queue.size() > 0) {
  138. _rfbTicker.once_ms(RF_SEND_DELAY, _rfbSend);
  139. }
  140. }
  141. void _rfbSend(byte * code, int times) {
  142. char buffer[RF_MESSAGE_SIZE];
  143. _rfbToChar(code, buffer);
  144. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending MESSAGE '%s' %d time(s)\n"), buffer, times);
  145. rfb_message_t message;
  146. memcpy(message.code, code, RF_MESSAGE_SIZE);
  147. message.times = times;
  148. #ifdef RF_RAW_SUPPORT
  149. message.length = RF_MESSAGE_SIZE;
  150. #endif
  151. _rfb_message_queue.push_back(message);
  152. _rfbSend();
  153. }
  154. #ifdef RF_RAW_SUPPORT
  155. void _rfbSendRawRepeated(byte *code, int length, int times) {
  156. char buffer[RF_MESSAGE_SIZE*2];
  157. _rfbToChar(code, buffer);
  158. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending raw MESSAGE '%s' %d time(s)\n"), buffer, times);
  159. rfb_message_t message;
  160. memcpy(message.code, code, length);
  161. message.times = -times;
  162. message.length = length;
  163. _rfb_message_queue.push_back(message);
  164. _rfbSend();
  165. }
  166. #endif
  167. bool _rfbMatch(char * code, unsigned char& relayID, unsigned char& value) {
  168. if (strlen(code) != 18) return false;
  169. bool found = false;
  170. String compareto = String(&code[12]);
  171. compareto.toUpperCase();
  172. DEBUG_MSG_P(PSTR("[RFBRIDGE] Trying to match code %s\n"), compareto.c_str());
  173. for (unsigned char i=0; i<relayCount(); i++) {
  174. String code_on = rfbRetrieve(i, true);
  175. if (code_on.length() && code_on.endsWith(compareto)) {
  176. DEBUG_MSG_P(PSTR("[RFBRIDGE] Match ON code for relay %d\n"), i);
  177. value = 1;
  178. found = true;
  179. }
  180. String code_off = rfbRetrieve(i, false);
  181. if (code_off.length() && code_off.endsWith(compareto)) {
  182. DEBUG_MSG_P(PSTR("[RFBRIDGE] Match OFF code for relay %d\n"), i);
  183. if (found) value = 2;
  184. found = true;
  185. }
  186. if (found) {
  187. relayID = i;
  188. return true;
  189. }
  190. }
  191. return false;
  192. }
  193. void _rfbDecode() {
  194. static unsigned long last = 0;
  195. if (millis() - last < RF_RECEIVE_DELAY) return;
  196. last = millis();
  197. byte action = _uartbuf[0];
  198. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  199. DEBUG_MSG_P(PSTR("[RFBRIDGE] Action 0x%02X\n"), action);
  200. if (action == RF_CODE_LEARN_KO) {
  201. _rfbAck();
  202. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n"));
  203. #if WEB_SUPPORT
  204. wsSend_P(PSTR("{\"action\": \"rfbTimeout\"}"));
  205. #endif
  206. }
  207. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  208. #if MQTT_SUPPORT
  209. _rfbToChar(&_uartbuf[1], buffer);
  210. mqttSend(MQTT_TOPIC_RFIN, buffer);
  211. #endif
  212. _rfbAck();
  213. }
  214. if (action == RF_CODE_LEARN_OK) {
  215. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn success\n"));
  216. rfbStore(_learnId, _learnStatus, buffer);
  217. // Websocket update
  218. #if WEB_SUPPORT
  219. char wsb[100];
  220. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), _learnId, _learnStatus ? 1 : 0, buffer);
  221. wsSend(wsb);
  222. #endif
  223. }
  224. if (action == RF_CODE_RFIN) {
  225. DEBUG_MSG_P(PSTR("[RFBRIDGE] Forward message '%s'\n"), buffer);
  226. // Look for the code
  227. unsigned char id;
  228. unsigned char status = 0;
  229. if (_rfbMatch(buffer, id, status)) {
  230. _rfbin = true;
  231. if (status == 2) {
  232. relayToggle(id);
  233. } else {
  234. relayStatus(id, status == 1);
  235. }
  236. }
  237. }
  238. }
  239. void _rfbReceive() {
  240. static bool receiving = false;
  241. while (Serial.available()) {
  242. yield();
  243. byte c = Serial.read();
  244. //DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
  245. if (receiving) {
  246. if (c == RF_CODE_STOP) {
  247. _rfbDecode();
  248. receiving = false;
  249. } else {
  250. _uartbuf[_uartpos++] = c;
  251. }
  252. } else if (c == RF_CODE_START) {
  253. _uartpos = 0;
  254. receiving = true;
  255. }
  256. }
  257. }
  258. bool _rfbCompare(const char * code1, const char * code2) {
  259. return strcmp(&code1[12], &code2[12]) == 0;
  260. }
  261. bool _rfbSameOnOff(unsigned char id) {
  262. return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
  263. }
  264. /*
  265. From an hexa char array ("A220EE...") to a byte array (half the size)
  266. */
  267. int _rfbToArray(const char * in, byte * out, int length = RF_MESSAGE_SIZE * 2) {
  268. int n = strlen(in);
  269. if (n > RF_MAX_MESSAGE_SIZE*2 || (length > 0 && n != length)) return 0;
  270. char tmp[3] = {0,0,0};
  271. n /= 2;
  272. for (unsigned char p = 0; p<n; p++) {
  273. memcpy(tmp, &in[p*2], 2);
  274. out[p] = strtol(tmp, NULL, 16);
  275. }
  276. return n;
  277. }
  278. /*
  279. From a byte array to an hexa char array ("A220EE...", double the size)
  280. */
  281. bool _rfbToChar(byte * in, char * out) {
  282. for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
  283. sprintf_P(&out[p*2], PSTR("%02X"), in[p]);
  284. }
  285. return true;
  286. }
  287. #if MQTT_SUPPORT
  288. void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
  289. if (type == MQTT_CONNECT_EVENT) {
  290. char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
  291. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RFLEARN);
  292. mqttSubscribe(buffer);
  293. mqttSubscribe(MQTT_TOPIC_RFOUT);
  294. }
  295. if (type == MQTT_MESSAGE_EVENT) {
  296. // Match topic
  297. String t = mqttSubtopic((char *) topic);
  298. // Check if should go into learn mode
  299. if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
  300. _learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
  301. if (_learnId >= relayCount()) {
  302. DEBUG_MSG_P(PSTR("[RFBRIDGE] Wrong learnID (%d)\n"), _learnId);
  303. return;
  304. }
  305. _learnStatus = (char)payload[0] != '0';
  306. _rfbLearn();
  307. }
  308. #ifdef RF_RAW_SUPPORT
  309. bool isRFOut = t.equals(MQTT_TOPIC_RFOUT);
  310. bool isRFRaw = !isRFOut && t.equals(MQTT_TOPIC_RFRAW);
  311. if (isRFOut || isRFRaw) {
  312. #else
  313. if (t.equals(MQTT_TOPIC_RFOUT)) {
  314. #endif
  315. // The payload may be a code in HEX format ([0-9A-Z]{18}) or
  316. // the code comma the number of times to transmit it.
  317. byte message[RF_MAX_MESSAGE_SIZE];
  318. char * tok = strtok((char *) payload, ",");
  319. // Check if a switch is linked to that message
  320. unsigned char id;
  321. unsigned char status = 0;
  322. if (_rfbMatch(tok, id, status)) {
  323. if (status == 2) {
  324. relayToggle(id);
  325. } else {
  326. relayStatus(id, status == 1);
  327. }
  328. return;
  329. }
  330. const char *tok2 = strtok(NULL, ",");
  331. byte times = (t != NULL) ? atoi(tok2) : 1;
  332. #ifdef RF_RAW_SUPPORT
  333. int len = _rfbToArray(tok, message, 0);
  334. if (len > 0 && (isRFRaw || len != RF_MESSAGE_SIZE)) {
  335. _rfbSendRawRepeated(message, len, times);
  336. } else {
  337. #else
  338. if (_rfbToArray(tok, message)) {
  339. #endif
  340. _rfbSend(message, times);
  341. }
  342. }
  343. }
  344. }
  345. #endif
  346. // -----------------------------------------------------------------------------
  347. // PUBLIC
  348. // -----------------------------------------------------------------------------
  349. void rfbStore(unsigned char id, bool status, const char * code) {
  350. DEBUG_MSG_P(PSTR("[RFBRIDGE] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  351. char key[8] = {0};
  352. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  353. setSetting(key, code);
  354. }
  355. String rfbRetrieve(unsigned char id, bool status) {
  356. char key[8] = {0};
  357. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  358. return getSetting(key);
  359. }
  360. void rfbStatus(unsigned char id, bool status) {
  361. String value = rfbRetrieve(id, status);
  362. if (value.length() > 0) {
  363. bool same = _rfbSameOnOff(id);
  364. byte message[RF_MESSAGE_SIZE];
  365. _rfbToArray(value.c_str(), message);
  366. unsigned char times = RF_SEND_TIMES;
  367. if (same) times = _rfbin ? 0 : 1;
  368. _rfbSend(message, times);
  369. }
  370. }
  371. void rfbLearn(unsigned char id, bool status) {
  372. _learnId = id;
  373. _learnStatus = status;
  374. _rfbLearn();
  375. }
  376. void rfbForget(unsigned char id, bool status) {
  377. char key[8] = {0};
  378. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  379. delSetting(key);
  380. // Websocket update
  381. #if WEB_SUPPORT
  382. char wsb[100];
  383. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  384. wsSend(wsb);
  385. #endif
  386. }
  387. // -----------------------------------------------------------------------------
  388. // SETUP & LOOP
  389. // -----------------------------------------------------------------------------
  390. void rfbSetup() {
  391. #if MQTT_SUPPORT
  392. mqttRegister(_rfbMqttCallback);
  393. #endif
  394. #if WEB_SUPPORT
  395. wsOnSendRegister(_rfbWebSocketOnSend);
  396. wsOnActionRegister(_rfbWebSocketOnAction);
  397. #endif
  398. }
  399. void rfbLoop() {
  400. _rfbReceive();
  401. }
  402. #endif