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.

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