xdrv_19_ps16dz_dimmer.ino 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. xdrv_19_ps16dz_dimmer.ino - PS_16_DZ dimmer support for Sonoff-Tasmota
  3. Copyright (C) 2018 Joel Stein and Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_PS_16_DZ
  16. #define XDRV_19 19
  17. #define PS16DZ_BUFFER_SIZE 80
  18. #define PS16DZ_TYPE_ACK 0
  19. #define PS16DZ_TYPE_PWR 1
  20. #define PS16DZ_TYPE_DIM 2
  21. #include <TasmotaSerial.h>
  22. TasmotaSerial *PS16DZSerial = nullptr;
  23. boolean ps16dz_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction
  24. //uint64_t ps16dz_seq = 0;
  25. char *ps16dz_tx_buffer = NULL; // Serial transmit buffer
  26. char *ps16dz_rx_buffer = NULL; // Serial receive buffer
  27. int ps16dz_byte_counter = 0;
  28. /*********************************************************************************************\
  29. * Internal Functions
  30. \*********************************************************************************************/
  31. void printTimestamp(void)
  32. {
  33. snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR( "%s%d%03d"), ps16dz_tx_buffer, LocalTime(), millis()%1000);
  34. }
  35. void PS16DZSendCommand(char type = 0, uint8_t value = 0)
  36. {
  37. switch(type){
  38. case PS16DZ_TYPE_ACK:
  39. snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR( "AT+SEND=ok"));
  40. break;
  41. case PS16DZ_TYPE_PWR:
  42. case PS16DZ_TYPE_DIM:
  43. snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR( "AT+UPDATE=\"sequence\":\""));
  44. printTimestamp();
  45. if ( type == PS16DZ_TYPE_PWR) {
  46. snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR( "%s\",\"switch\":\"%s\""), ps16dz_tx_buffer, value?"on":"off");
  47. }
  48. else if ( type == PS16DZ_TYPE_DIM) {
  49. snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR( "%s\",\"bright\":%d"), ps16dz_tx_buffer, round(value * (100. / 255.)));
  50. }
  51. break;
  52. }
  53. snprintf_P(log_data, sizeof(log_data), PSTR( "PSZ: Send serial command: %s"), ps16dz_tx_buffer );
  54. AddLog(LOG_LEVEL_DEBUG);
  55. PS16DZSerial->print(ps16dz_tx_buffer);
  56. PS16DZSerial->write(0x1B);
  57. PS16DZSerial->flush();
  58. }
  59. boolean PS16DZSetPower(void)
  60. {
  61. boolean status = false;
  62. uint8_t rpower = XdrvMailbox.index;
  63. int16_t source = XdrvMailbox.payload;
  64. if (source != SRC_SWITCH && PS16DZSerial) { // ignore to prevent loop from pushing state from faceplate interaction
  65. PS16DZSendCommand(PS16DZ_TYPE_PWR, rpower);
  66. status = true;
  67. }
  68. return status;
  69. }
  70. boolean PS16DZSetChannels(void)
  71. {
  72. PS16DZSerialDuty(((uint8_t*)XdrvMailbox.data)[0]);
  73. return true;
  74. }
  75. void PS16DZSerialDuty(uint8_t duty)
  76. {
  77. if (duty > 0 && !ps16dz_ignore_dim && PS16DZSerial) {
  78. if (duty < 25) {
  79. duty = 25; // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself
  80. }
  81. PS16DZSendCommand(PS16DZ_TYPE_DIM, duty);
  82. } else {
  83. ps16dz_ignore_dim = false; // reset flag
  84. snprintf_P(log_data, sizeof(log_data), PSTR( "PSZ: Send Dim Level skipped due to 0 or already set. Value=%d"), duty);
  85. AddLog(LOG_LEVEL_DEBUG);
  86. }
  87. }
  88. void PS16DZResetWifi(void)
  89. {
  90. if (!Settings.flag.button_restrict) {
  91. char scmnd[20];
  92. snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2);
  93. ExecuteCommand(scmnd, SRC_BUTTON);
  94. }
  95. }
  96. /*********************************************************************************************\
  97. * API Functions
  98. \*********************************************************************************************/
  99. boolean PS16DZModuleSelected(void)
  100. {
  101. light_type = LT_SERIAL1;
  102. return true;
  103. }
  104. void PS16DZInit(void)
  105. {
  106. ps16dz_tx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE));
  107. if (ps16dz_tx_buffer != NULL) {
  108. ps16dz_rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE));
  109. if (ps16dz_rx_buffer != NULL) {
  110. PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2);
  111. if (PS16DZSerial->begin(19200)) {
  112. if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); }
  113. }
  114. }
  115. }
  116. }
  117. void PS16DZSerialInput(void)
  118. {
  119. char scmnd[20];
  120. while (PS16DZSerial->available()) {
  121. yield();
  122. byte serial_in_byte = PS16DZSerial->read();
  123. if (serial_in_byte != 0x1B){
  124. if (ps16dz_byte_counter >= PS16DZ_BUFFER_SIZE - 1) {
  125. memset(ps16dz_rx_buffer, 0, PS16DZ_BUFFER_SIZE);
  126. ps16dz_byte_counter = 0;
  127. }
  128. if (ps16dz_byte_counter || (!ps16dz_byte_counter && serial_in_byte == 'A')) {
  129. ps16dz_rx_buffer[ps16dz_byte_counter++] = serial_in_byte;
  130. }
  131. }
  132. else {
  133. ps16dz_rx_buffer[ps16dz_byte_counter++] = 0x00;
  134. snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: command received: %s"), ps16dz_rx_buffer);
  135. AddLog(LOG_LEVEL_DEBUG);
  136. if(!strncmp(ps16dz_rx_buffer+3, "UPDATE", 6) || !strncmp(ps16dz_rx_buffer+3, "RESULT", 6)) {
  137. char *end_str;
  138. char *string = ps16dz_rx_buffer+10;
  139. char* token = strtok_r(string, ",", &end_str);
  140. while (token != NULL) {
  141. char* end_token;
  142. char* token2 = strtok_r(token, ":", &end_token);
  143. char* token3 = strtok_r(NULL, ":", &end_token);
  144. if(!strncmp(token2, "\"switch\"", 8)){
  145. boolean ps16dz_power = !strncmp(token3, "\"on\"", 4)?true:false;
  146. snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: power received: %s"), token3);
  147. AddLog(LOG_LEVEL_DEBUG);
  148. if((power || Settings.light_dimmer > 0) && (power !=ps16dz_power)) {
  149. ExecuteCommandPower(1, ps16dz_power, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
  150. }
  151. }
  152. else if(!strncmp(token2, "\"bright\"", 8)){
  153. uint8_t ps16dz_bright = atoi(token3);
  154. snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: brightness received: %d"), ps16dz_bright);
  155. AddLog(LOG_LEVEL_DEBUG);
  156. if(power && ps16dz_bright > 0 && ps16dz_bright != Settings.light_dimmer) {
  157. snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), ps16dz_bright );
  158. snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: Send CMND_DIMMER_STR=%s"), scmnd );
  159. AddLog(LOG_LEVEL_DEBUG);
  160. ps16dz_ignore_dim = true;
  161. ExecuteCommand(scmnd, SRC_SWITCH);
  162. }
  163. }
  164. else if(!strncmp(token2, "\"sequence\"", 10)){
  165. //ps16dz_seq = strtoull(token3+1, NULL, 10);
  166. snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: sequence received: %s"), token3);
  167. AddLog(LOG_LEVEL_DEBUG);
  168. }
  169. token = strtok_r(NULL, ",", &end_str);
  170. }
  171. }
  172. else if(!strncmp(ps16dz_rx_buffer+3, "SETTING", 7)) {
  173. snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: Reset"));
  174. AddLog(LOG_LEVEL_DEBUG);
  175. PS16DZResetWifi();
  176. }
  177. memset(ps16dz_rx_buffer, 0, PS16DZ_BUFFER_SIZE);
  178. ps16dz_byte_counter = 0;
  179. PS16DZSendCommand();
  180. }
  181. }
  182. }
  183. /*********************************************************************************************\
  184. * Interface
  185. \*********************************************************************************************/
  186. boolean Xdrv19(byte function)
  187. {
  188. boolean result = false;
  189. if (PS_16_DZ == Settings.module) {
  190. switch (function) {
  191. case FUNC_MODULE_INIT:
  192. result = PS16DZModuleSelected();
  193. break;
  194. case FUNC_INIT:
  195. PS16DZInit();
  196. break;
  197. case FUNC_LOOP:
  198. if (PS16DZSerial) { PS16DZSerialInput(); }
  199. break;
  200. case FUNC_SET_DEVICE_POWER:
  201. result = PS16DZSetPower();
  202. break;
  203. case FUNC_SET_CHANNELS:
  204. result = PS16DZSetChannels();
  205. break;
  206. }
  207. }
  208. return result;
  209. }
  210. #endif // USE_PS_16_DZ