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.

492 lines
14 KiB

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