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.

524 lines
14 KiB

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