somfy.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // ***************************************************************************
  2. // Somfy module for ESP8266 with NodeMCU
  3. //
  4. // Written by Lukas Voborsky, @voborsky
  5. // based on https://github.com/Nickduino/Somfy_Remote
  6. // Somfy protocol description: https://pushstack.wordpress.com/somfy-rts-protocol/
  7. // and discussion: https://forum.arduino.cc/index.php?topic=208346.0
  8. //
  9. // MIT license, http://opensource.org/licenses/MIT
  10. // ***************************************************************************
  11. //#define NODE_DEBUG
  12. #include "os_type.h"
  13. #include "osapi.h"
  14. #include "sections.h"
  15. #include "module.h"
  16. #include "lauxlib.h"
  17. #include "lmem.h"
  18. #include "platform.h"
  19. #include "hw_timer.h"
  20. #include "user_interface.h"
  21. #define SYMBOL 640 // symbol width in microseconds
  22. #define SOMFY_UP 0x2
  23. #define SOMFY_STOP 0x1
  24. #define SOMFY_DOWN 0x4
  25. #define SOMFY_PROG 0x8
  26. #define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0))
  27. #define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1))
  28. static const os_param_t TIMER_OWNER = 0x736f6d66; // "somf"
  29. static task_handle_t done_taskid;
  30. static uint8_t pin;
  31. static uint8_t frame[7];
  32. static uint8_t sync;
  33. static uint8_t repeat;
  34. //static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // in us
  35. // the `delay` array of constants must be in RAM as it is accessed from the timer interrupt
  36. static const RAM_CONST_SECTION_ATTR uint32_t delay[10] = {US_TO_RTC_TIMER_TICKS(9415), US_TO_RTC_TIMER_TICKS(89565), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4550), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(30415)}; // in ticks (no need to recalculate)
  37. static uint8_t repeatindex;
  38. static uint8_t signalindex;
  39. static uint8_t subindex;
  40. static uint8_t bitcondition;
  41. int lua_done_ref; // callback when transmission is done
  42. void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) {
  43. // NODE_DBG("remote: %x\n", remote);
  44. // NODE_DBG("button: %x\n", button);
  45. // NODE_DBG("rolling code: %x\n", code);
  46. frame[0] = 0xA7; // Encryption key. Doesn't matter much
  47. frame[1] = button << 4; // Which button did you press? The 4 LSB will be the checksum
  48. frame[2] = code >> 8; // Rolling code (big endian)
  49. frame[3] = code; // Rolling code
  50. frame[4] = remote >> 16; // Remote address
  51. frame[5] = remote >> 8; // Remote address
  52. frame[6] = remote; // Remote address
  53. // frame[7] = 0x80;
  54. // frame[8] = 0x0;
  55. // frame[9] = 0x0;
  56. // NODE_DBG("Frame:\t\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
  57. // Checksum calculation: a XOR of all the nibbles
  58. uint8_t checksum = 0;
  59. for(uint8_t i = 0; i < 7; i++) {
  60. checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
  61. }
  62. checksum &= 0b1111; // We keep the last 4 bits only
  63. //Checksum integration
  64. frame[1] |= checksum; // If a XOR of all the nibbles is equal to 0, the blinds will consider the checksum ok.
  65. // NODE_DBG("With checksum:\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
  66. // Obfuscation: a XOR of all the uint8_ts
  67. for(uint8_t i = 1; i < 7; i++) {
  68. frame[i] ^= frame[i-1];
  69. }
  70. // NODE_DBG("Obfuscated:\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
  71. }
  72. static void somfy_transmissionDone (task_param_t arg)
  73. {
  74. lua_State *L = lua_getstate();
  75. lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref);
  76. luaL_unref (L, LUA_REGISTRYINDEX, lua_done_ref);
  77. lua_done_ref = LUA_NOREF;
  78. lua_call (L, 0, 0);
  79. }
  80. static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
  81. (void) p;
  82. // NODE_DBG("%d\t%d\n", signalindex, subindex);
  83. switch (signalindex) {
  84. case 0:
  85. subindex = 0;
  86. if(sync == 2) { // Only with the first frame.
  87. //Wake-up pulse & Silence
  88. DIRECT_WRITE_HIGH(pin);
  89. signalindex++;
  90. // delayMicroseconds(9415);
  91. break;
  92. } else {
  93. signalindex++; signalindex++; //no break means: go directly to step 3
  94. }
  95. case 1:
  96. //Wake-up pulse & Silence
  97. DIRECT_WRITE_LOW(pin);
  98. signalindex++;
  99. // delayMicroseconds(89565);
  100. break;
  101. case 2:
  102. signalindex++;
  103. // no break means go directly to step 3
  104. // a "useless" step to allow repeating the hardware sync w/o the silence after wake-up pulse
  105. case 3:
  106. // Hardware sync: two sync for the first frame, seven for the following ones.
  107. DIRECT_WRITE_HIGH(pin);
  108. signalindex++;
  109. // delayMicroseconds(4*SYMBOL);
  110. break;
  111. case 4:
  112. DIRECT_WRITE_LOW(pin);
  113. subindex++;
  114. if (subindex < sync) {signalindex--;} else {signalindex++;}
  115. // delayMicroseconds(4*SYMBOL);
  116. break;
  117. case 5:
  118. // Software sync
  119. DIRECT_WRITE_HIGH(pin);
  120. signalindex++;
  121. // delayMicroseconds(4550);
  122. break;
  123. case 6:
  124. DIRECT_WRITE_LOW(pin);
  125. signalindex++;
  126. subindex=0;
  127. // delayMicroseconds(SYMBOL);
  128. break;
  129. case 7:
  130. //Data: bits are sent one by one, starting with the MSB.
  131. bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1;
  132. if(bitcondition) {
  133. DIRECT_WRITE_LOW(pin);
  134. }
  135. else {
  136. DIRECT_WRITE_HIGH(pin);
  137. }
  138. signalindex++;
  139. // delayMicroseconds(SYMBOL);
  140. break;
  141. case 8:
  142. //Data: bits are sent one by one, starting with the MSB.
  143. if(bitcondition) {
  144. DIRECT_WRITE_HIGH(pin);
  145. }
  146. else {
  147. DIRECT_WRITE_LOW(pin);
  148. }
  149. if (subindex<56) {
  150. subindex++;
  151. signalindex--;
  152. }
  153. else {
  154. signalindex++;
  155. }
  156. // delayMicroseconds(SYMBOL);
  157. break;
  158. case 9:
  159. DIRECT_WRITE_LOW(pin);
  160. signalindex++;
  161. // delayMicroseconds(30415); // Inter-frame silence
  162. break;
  163. case 10:
  164. repeatindex++;
  165. if (repeatindex<repeat) {
  166. DIRECT_WRITE_HIGH(pin); //start repeat from step 3, but don't wait as after step 1
  167. signalindex=4; subindex=0; sync=7;
  168. } else {
  169. platform_hw_timer_close(TIMER_OWNER);
  170. if (lua_done_ref != LUA_NOREF) {
  171. task_post_low (done_taskid, (task_param_t)0);
  172. }
  173. }
  174. break;
  175. }
  176. if (signalindex<10) {
  177. platform_hw_timer_arm_ticks(TIMER_OWNER, delay[signalindex-1]);
  178. }
  179. }
  180. static int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rolling_code, num_repeat, callback
  181. if (!lua_isnumber(L, 4)) {
  182. return luaL_error(L, "wrong arg range");
  183. }
  184. pin = luaL_checkinteger(L, 1);
  185. uint64_t remote = luaL_checkinteger(L, 2);
  186. uint8_t cmd = luaL_checkinteger(L, 3);
  187. uint16_t code = luaL_checkinteger(L, 4);
  188. repeat=luaL_optint( L, 5, 2 );
  189. luaL_argcheck(L, platform_gpio_exists(pin), 1, "Invalid pin");
  190. luaL_unref(L, LUA_REGISTRYINDEX, lua_done_ref);
  191. if (!lua_isnoneornil(L, 6)) {
  192. lua_pushvalue(L, 6);
  193. lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  194. } else {
  195. lua_done_ref = LUA_NOREF;
  196. }
  197. MOD_CHECK_ID(gpio, pin);
  198. platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
  199. buildFrame(frame, remote, cmd, code);
  200. if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
  201. // Failed to init the timer
  202. luaL_error(L, "Unable to initialize timer");
  203. }
  204. platform_hw_timer_set_func(TIMER_OWNER, sendCommand, 0);
  205. sync=2;
  206. signalindex=0; repeatindex=0;
  207. sendCommand(0);
  208. return 0;
  209. }
  210. static const LUA_REG_TYPE somfy_map[] = {
  211. { LSTRKEY( "UP" ), LNUMVAL( SOMFY_UP ) },
  212. { LSTRKEY( "DOWN" ), LNUMVAL( SOMFY_DOWN ) },
  213. { LSTRKEY( "PROG" ), LNUMVAL( SOMFY_PROG ) },
  214. { LSTRKEY( "STOP" ), LNUMVAL( SOMFY_STOP ) },
  215. { LSTRKEY( "sendcommand" ), LFUNCVAL(somfy_lua_sendcommand)},
  216. { LNILKEY, LNILVAL}
  217. };
  218. int luaopen_somfy( lua_State *L ) {
  219. done_taskid = task_get_id((task_callback_t) somfy_transmissionDone);
  220. return 0;
  221. }
  222. NODEMCU_MODULE(SOMFY, "somfy", somfy_map, luaopen_somfy);