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.

408 lines
11 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 <Ticker.h>
  7. // -----------------------------------------------------------------------------
  8. // DEFINITIONS
  9. // -----------------------------------------------------------------------------
  10. #define RF_MESSAGE_SIZE 9
  11. #define RF_CODE_START 0xAA
  12. #define RF_CODE_ACK 0xA0
  13. #define RF_CODE_LEARN 0xA1
  14. #define RF_CODE_LEARN_KO 0xA2
  15. #define RF_CODE_LEARN_OK 0xA3
  16. #define RF_CODE_RFIN 0xA4
  17. #define RF_CODE_RFOUT 0xA5
  18. #define RF_CODE_STOP 0x55
  19. // -----------------------------------------------------------------------------
  20. // GLOBALS TO THE MODULE
  21. // -----------------------------------------------------------------------------
  22. unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
  23. unsigned char _uartpos = 0;
  24. unsigned char _learnId = 0;
  25. bool _learnStatus = true;
  26. bool _rfbin = false;
  27. byte _message_to_send[RF_MESSAGE_SIZE] = {0};
  28. byte _message_count = 0;
  29. Ticker _rfbTicker;
  30. // -----------------------------------------------------------------------------
  31. // PRIVATES
  32. // -----------------------------------------------------------------------------
  33. void _rfbWebSocketOnSend(JsonObject& root) {
  34. root["rfbVisible"] = 1;
  35. root["rfbCount"] = relayCount();
  36. JsonArray& rfb = root.createNestedArray("rfb");
  37. for (byte id=0; id<relayCount(); id++) {
  38. for (byte status=0; status<2; status++) {
  39. JsonObject& node = rfb.createNestedObject();
  40. node["id"] = id;
  41. node["status"] = status;
  42. node["data"] = rfbRetrieve(id, status == 1);
  43. }
  44. }
  45. }
  46. void _rfbWebSocketOnAction(const char * action, JsonObject& data) {
  47. if (strcmp(action, "rfblearn") == 0) rfbLearn(data["id"], data["status"]);
  48. if (strcmp(action, "rfbforget") == 0) rfbForget(data["id"], data["status"]);
  49. if (strcmp(action, "rfbsend") == 0) rfbStore(data["id"], data["status"], data["data"].as<const char*>());
  50. }
  51. void _rfbAck() {
  52. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
  53. Serial.println();
  54. Serial.write(RF_CODE_START);
  55. Serial.write(RF_CODE_ACK);
  56. Serial.write(RF_CODE_STOP);
  57. Serial.flush();
  58. Serial.println();
  59. }
  60. void _rfbLearn() {
  61. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
  62. Serial.println();
  63. Serial.write(RF_CODE_START);
  64. Serial.write(RF_CODE_LEARN);
  65. Serial.write(RF_CODE_STOP);
  66. Serial.flush();
  67. Serial.println();
  68. #if WEB_SUPPORT
  69. char buffer[100];
  70. snprintf_P(buffer, sizeof(buffer), PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
  71. wsSend(buffer);
  72. #endif
  73. }
  74. void _rfbSend(byte * message) {
  75. Serial.println();
  76. Serial.write(RF_CODE_START);
  77. Serial.write(RF_CODE_RFOUT);
  78. for (unsigned char j=0; j<RF_MESSAGE_SIZE; j++) {
  79. Serial.write(message[j]);
  80. }
  81. Serial.write(RF_CODE_STOP);
  82. Serial.flush();
  83. Serial.println();
  84. }
  85. void _rfbSend() {
  86. if (_message_count) {
  87. _rfbSend(_message_to_send);
  88. if (--_message_count > 0) _rfbTicker.once_ms(RF_SEND_DELAY, _rfbSend);
  89. }
  90. }
  91. void _rfbSend(byte * message, int times) {
  92. char buffer[RF_MESSAGE_SIZE];
  93. _rfbToChar(message, buffer);
  94. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending MESSAGE '%s' %d time(s)\n"), buffer, times);
  95. _message_count = times;
  96. memcpy(_message_to_send, message, RF_MESSAGE_SIZE);
  97. _rfbSend();
  98. }
  99. bool _rfbMatch(char * code, unsigned char& relayID, unsigned char& value) {
  100. if (strlen(code) != 18) return false;
  101. bool found = false;
  102. String compareto = String(&code[12]);
  103. compareto.toUpperCase();
  104. DEBUG_MSG_P(PSTR("[RFBRIDGE] Trying to match code %s\n"), compareto.c_str());
  105. for (unsigned char i=0; i<relayCount(); i++) {
  106. String code_on = rfbRetrieve(i, true);
  107. if (code_on.length() && code_on.endsWith(compareto)) {
  108. DEBUG_MSG_P(PSTR("[RFBRIDGE] Match ON code for relay %d\n"), i);
  109. value = 1;
  110. found = true;
  111. }
  112. String code_off = rfbRetrieve(i, false);
  113. if (code_off.length() && code_off.endsWith(compareto)) {
  114. DEBUG_MSG_P(PSTR("[RFBRIDGE] Match OFF code for relay %d\n"), i);
  115. if (found) value = 2;
  116. found = true;
  117. }
  118. if (found) {
  119. relayID = i;
  120. return true;
  121. }
  122. }
  123. return false;
  124. }
  125. void _rfbDecode() {
  126. static unsigned long last = 0;
  127. if (millis() - last < RF_RECEIVE_DELAY) return;
  128. last = millis();
  129. byte action = _uartbuf[0];
  130. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  131. DEBUG_MSG_P(PSTR("[RFBRIDGE] Action 0x%02X\n"), action);
  132. if (action == RF_CODE_LEARN_KO) {
  133. _rfbAck();
  134. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n"));
  135. #if WEB_SUPPORT
  136. wsSend_P(PSTR("{\"action\": \"rfbTimeout\"}"));
  137. #endif
  138. }
  139. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  140. #if MQTT_SUPPORT
  141. _rfbToChar(&_uartbuf[1], buffer);
  142. mqttSend(MQTT_TOPIC_RFIN, buffer);
  143. #endif
  144. _rfbAck();
  145. }
  146. if (action == RF_CODE_LEARN_OK) {
  147. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn success\n"));
  148. rfbStore(_learnId, _learnStatus, buffer);
  149. // Websocket update
  150. #if WEB_SUPPORT
  151. char wsb[100];
  152. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), _learnId, _learnStatus ? 1 : 0, buffer);
  153. wsSend(wsb);
  154. #endif
  155. }
  156. if (action == RF_CODE_RFIN) {
  157. DEBUG_MSG_P(PSTR("[RFBRIDGE] Forward message '%s'\n"), buffer);
  158. // Look for the code
  159. unsigned char id;
  160. unsigned char status = 0;
  161. if (_rfbMatch(buffer, id, status)) {
  162. _rfbin = true;
  163. if (status == 2) {
  164. relayToggle(id);
  165. } else {
  166. relayStatus(id, status == 1);
  167. }
  168. }
  169. }
  170. }
  171. void _rfbReceive() {
  172. static bool receiving = false;
  173. while (Serial.available()) {
  174. yield();
  175. byte c = Serial.read();
  176. //DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
  177. if (receiving) {
  178. if (c == RF_CODE_STOP) {
  179. _rfbDecode();
  180. receiving = false;
  181. } else {
  182. _uartbuf[_uartpos++] = c;
  183. }
  184. } else if (c == RF_CODE_START) {
  185. _uartpos = 0;
  186. receiving = true;
  187. }
  188. }
  189. }
  190. bool _rfbCompare(const char * code1, const char * code2) {
  191. return strcmp(&code1[12], &code2[12]) == 0;
  192. }
  193. bool _rfbSameOnOff(unsigned char id) {
  194. return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
  195. }
  196. /*
  197. From an hexa char array ("A220EE...") to a byte array (half the size)
  198. */
  199. bool _rfbToArray(const char * in, byte * out) {
  200. if (strlen(in) != RF_MESSAGE_SIZE * 2) return false;
  201. char tmp[3] = {0};
  202. for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
  203. memcpy(tmp, &in[p*2], 2);
  204. out[p] = strtol(tmp, NULL, 16);
  205. }
  206. return true;
  207. }
  208. /*
  209. From a byte array to an hexa char array ("A220EE...", double the size)
  210. */
  211. bool _rfbToChar(byte * in, char * out) {
  212. for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
  213. sprintf_P(&out[p*2], PSTR("%02X"), in[p]);
  214. }
  215. return true;
  216. }
  217. #if MQTT_SUPPORT
  218. void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
  219. if (type == MQTT_CONNECT_EVENT) {
  220. char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
  221. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RFLEARN);
  222. mqttSubscribe(buffer);
  223. mqttSubscribe(MQTT_TOPIC_RFOUT);
  224. }
  225. if (type == MQTT_MESSAGE_EVENT) {
  226. // Match topic
  227. String t = mqttSubtopic((char *) topic);
  228. // Check if should go into learn mode
  229. if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
  230. _learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
  231. if (_learnId >= relayCount()) {
  232. DEBUG_MSG_P(PSTR("[RFBRIDGE] Wrong learnID (%d)\n"), _learnId);
  233. return;
  234. }
  235. _learnStatus = (char)payload[0] != '0';
  236. _rfbLearn();
  237. }
  238. if (t.equals(MQTT_TOPIC_RFOUT)) {
  239. // The payload may be a code in HEX format ([0-9A-Z]{18}) or
  240. // the code comma the number of times to transmit it.
  241. byte message[RF_MESSAGE_SIZE];
  242. char * tok = strtok((char *) payload, ",");
  243. // Check if a switch is linked to that message
  244. unsigned char id;
  245. unsigned char status = 0;
  246. if (_rfbMatch(tok, id, status)) {
  247. if (status == 2) {
  248. relayToggle(id);
  249. } else {
  250. relayStatus(id, status == 1);
  251. }
  252. return;
  253. }
  254. if (_rfbToArray(tok, message)) {
  255. tok = strtok(NULL, ",");
  256. byte times = (tok != NULL) ? atoi(tok) : 1;
  257. _rfbSend(message, times);
  258. }
  259. }
  260. }
  261. }
  262. #endif
  263. // -----------------------------------------------------------------------------
  264. // PUBLIC
  265. // -----------------------------------------------------------------------------
  266. void rfbStore(unsigned char id, bool status, const char * code) {
  267. DEBUG_MSG_P(PSTR("[RFBRIDGE] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  268. char key[8] = {0};
  269. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  270. setSetting(key, code);
  271. }
  272. String rfbRetrieve(unsigned char id, bool status) {
  273. char key[8] = {0};
  274. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  275. return getSetting(key);
  276. }
  277. void rfbStatus(unsigned char id, bool status) {
  278. String value = rfbRetrieve(id, status);
  279. if (value.length() > 0) {
  280. bool same = _rfbSameOnOff(id);
  281. byte message[RF_MESSAGE_SIZE];
  282. _rfbToArray(value.c_str(), message);
  283. unsigned char times = RF_SEND_TIMES;
  284. if (same) times = _rfbin ? 0 : 1;
  285. _rfbSend(message, times);
  286. }
  287. }
  288. void rfbLearn(unsigned char id, bool status) {
  289. _learnId = id;
  290. _learnStatus = status;
  291. _rfbLearn();
  292. }
  293. void rfbForget(unsigned char id, bool status) {
  294. char key[8] = {0};
  295. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  296. delSetting(key);
  297. // Websocket update
  298. #if WEB_SUPPORT
  299. char wsb[100];
  300. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  301. wsSend(wsb);
  302. #endif
  303. }
  304. // -----------------------------------------------------------------------------
  305. // SETUP & LOOP
  306. // -----------------------------------------------------------------------------
  307. void rfbSetup() {
  308. #if MQTT_SUPPORT
  309. mqttRegister(_rfbMqttCallback);
  310. #endif
  311. #if WEB_SUPPORT
  312. wsOnSendRegister(_rfbWebSocketOnSend);
  313. wsOnActionRegister(_rfbWebSocketOnAction);
  314. #endif
  315. }
  316. void rfbLoop() {
  317. _rfbReceive();
  318. }
  319. #endif