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.

390 lines
11 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
6 years ago
7 years ago
4 years ago
  1. /*
  2. BUTTON MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if BUTTON_SUPPORT
  6. #include <memory>
  7. #include <vector>
  8. #include "system.h"
  9. #include "relay.h"
  10. #include "light.h"
  11. #include "button.h"
  12. #include "button_config.h"
  13. #include "debounce.h"
  14. // -----------------------------------------------------------------------------
  15. button_event_delays_t::button_event_delays_t() :
  16. debounce(BUTTON_DEBOUNCE_DELAY),
  17. dblclick(BUTTON_DBLCLICK_DELAY),
  18. lngclick(BUTTON_LNGCLICK_DELAY),
  19. lnglngclick(BUTTON_LNGLNGCLICK_DELAY)
  20. {}
  21. button_event_delays_t::button_event_delays_t(unsigned long debounce, unsigned long dblclick, unsigned long lngclick, unsigned long lnglngclick) :
  22. debounce(debounce),
  23. dblclick(dblclick),
  24. lngclick(lngclick),
  25. lnglngclick(lnglngclick)
  26. {}
  27. button_t::button_t(std::shared_ptr<DebounceEvent::PinBase> pin, int mode, unsigned long actions, unsigned char relayID, button_event_delays_t delays) :
  28. event_handler(new DebounceEvent::DebounceEvent(pin, mode, delays.debounce, delays.dblclick)),
  29. event_delays(delays),
  30. actions(actions),
  31. relayID(relayID)
  32. {}
  33. bool button_t::state() {
  34. return event_handler->pressed();
  35. }
  36. std::vector<button_t> _buttons;
  37. // -----------------------------------------------------------------------------
  38. constexpr const uint8_t _buttonMapReleased(uint8_t count, uint8_t length, unsigned long lngclick_delay, unsigned long lnglngclick_delay) {
  39. return (
  40. (1 == count) ? (
  41. (length > lnglngclick_delay) ? BUTTON_EVENT_LNGLNGCLICK :
  42. (length > lngclick_delay) ? BUTTON_EVENT_LNGCLICK : BUTTON_EVENT_CLICK
  43. ) :
  44. (2 == count) ? BUTTON_EVENT_DBLCLICK :
  45. (3 == count) ? BUTTON_EVENT_TRIPLECLICK :
  46. BUTTON_EVENT_NONE
  47. );
  48. }
  49. const uint8_t _buttonMapEvent(button_t& button, DebounceEvent::Types::event_t event) {
  50. using namespace DebounceEvent;
  51. switch (event) {
  52. case Types::EventPressed:
  53. return BUTTON_EVENT_PRESSED;
  54. case Types::EventChanged:
  55. return BUTTON_EVENT_CLICK;
  56. case Types::EventReleased: {
  57. return _buttonMapReleased(
  58. button.event_handler->getEventCount(),
  59. button.event_handler->getEventLength(),
  60. button.event_delays.lngclick,
  61. button.event_delays.lnglngclick
  62. );
  63. }
  64. case Types::EventNone:
  65. default:
  66. return BUTTON_EVENT_NONE;
  67. }
  68. }
  69. unsigned char buttonCount() {
  70. return _buttons.size();
  71. }
  72. #if MQTT_SUPPORT
  73. void buttonMQTT(unsigned char id, uint8_t event) {
  74. char payload[4] = {0};
  75. itoa(event, payload, 10);
  76. mqttSend(MQTT_TOPIC_BUTTON, id, payload, false, false); // 1st bool = force, 2nd = retain
  77. }
  78. #endif
  79. #if WEB_SUPPORT
  80. void _buttonWebSocketOnVisible(JsonObject& root) {
  81. if (buttonCount() > 0) {
  82. root["btnVisible"] = 1;
  83. }
  84. }
  85. bool _buttonWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  86. return (strncmp(key, "btn", 3) == 0);
  87. }
  88. #endif
  89. bool buttonState(unsigned char id) {
  90. if (id >= _buttons.size()) return false;
  91. return _buttons[id].state();
  92. }
  93. unsigned char buttonAction(unsigned char id, unsigned char event) {
  94. if (id >= _buttons.size()) return BUTTON_MODE_NONE;
  95. return _buttonDecodeEventAction(_buttons[id].actions, event);
  96. }
  97. void buttonEvent(unsigned char id, unsigned char event) {
  98. DEBUG_MSG_P(PSTR("[BUTTON] Button #%u event %u\n"), id, event);
  99. if (event == 0) return;
  100. auto& button = _buttons[id];
  101. unsigned char action = _buttonDecodeEventAction(button.actions, event);
  102. #if MQTT_SUPPORT
  103. if (action != BUTTON_MODE_NONE || BUTTON_MQTT_SEND_ALL_EVENTS) {
  104. buttonMQTT(id, event);
  105. }
  106. #endif
  107. if (BUTTON_MODE_TOGGLE == action) {
  108. relayToggle(button.relayID);
  109. }
  110. if (BUTTON_MODE_ON == action) {
  111. relayStatus(button.relayID, true);
  112. }
  113. if (BUTTON_MODE_OFF == action) {
  114. relayStatus(button.relayID, false);
  115. }
  116. if (BUTTON_MODE_AP == action) {
  117. if (wifiState() & WIFI_STATE_AP) {
  118. wifiStartSTA();
  119. } else {
  120. wifiStartAP();
  121. }
  122. }
  123. if (BUTTON_MODE_RESET == action) {
  124. deferredReset(100, CUSTOM_RESET_HARDWARE);
  125. }
  126. if (BUTTON_MODE_FACTORY == action) {
  127. DEBUG_MSG_P(PSTR("\n\nFACTORY RESET\n\n"));
  128. resetSettings();
  129. deferredReset(100, CUSTOM_RESET_FACTORY);
  130. }
  131. #if defined(JUSTWIFI_ENABLE_WPS)
  132. if (BUTTON_MODE_WPS == action) {
  133. wifiStartWPS();
  134. }
  135. #endif // defined(JUSTWIFI_ENABLE_WPS)
  136. #if defined(JUSTWIFI_ENABLE_SMARTCONFIG)
  137. if (BUTTON_MODE_SMART_CONFIG == action) {
  138. wifiStartSmartConfig();
  139. }
  140. #endif // defined(JUSTWIFI_ENABLE_SMARTCONFIG)
  141. #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  142. if (BUTTON_MODE_DIM_UP == action) {
  143. lightBrightnessStep(1);
  144. lightUpdate(true, true);
  145. }
  146. if (BUTTON_MODE_DIM_DOWN == action) {
  147. lightBrightnessStep(-1);
  148. lightUpdate(true, true);
  149. }
  150. #endif // LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
  151. }
  152. struct DummyPin : virtual public DebounceEvent::PinBase {
  153. DummyPin(unsigned char pin) : DebounceEvent::PinBase(pin) {}
  154. void digitalWrite(int8_t val) {}
  155. void pinMode(int8_t mode) {}
  156. int digitalRead() { return 0; }
  157. };
  158. unsigned char buttonAdd(unsigned char pin, unsigned char mode, unsigned long actions, unsigned char relayID) {
  159. const unsigned char index = _buttons.size();
  160. button_event_delays_t delays {
  161. getSetting({"btnDebDelay", index}, _buttonDebounceDelay(index)),
  162. getSetting({"btnDblCDelay", index}, _buttonDoubleClickDelay(index)),
  163. getSetting({"btnLngCDelay", index}, _buttonLongClickDelay(index)),
  164. getSetting({"btnLngLngCDelay", index}, _buttonLongLongClickDelay(index))
  165. };
  166. _buttons.emplace_back(std::make_shared<DummyPin>(GPIO_NONE), BUTTON_PUSHBUTTON, actions, relayID, delays);
  167. return _buttons.size() - 1;
  168. }
  169. void buttonSetup() {
  170. // Special hardware cases
  171. #if defined(ITEAD_SONOFF_DUAL)
  172. _buttons.reserve(3);
  173. buttonAdd(GPIO_NONE, BUTTON_PUSHBUTTON, 0, _buttonRelay(0));
  174. buttonAdd(GPIO_NONE, BUTTON_PUSHBUTTON, 0, _buttonRelay(1));
  175. buttonAdd(GPIO_NONE, BUTTON_PUSHBUTTON, 0, _buttonRelay(2));
  176. #elif defined(FOXEL_LIGHTFOX_DUAL)
  177. _buttons.reserve(4);
  178. const auto actions = _buttonConstructActions(
  179. BUTTON_MODE_NONE, BUTTON_MODE_TOGGLE, BUTTON_MODE_NONE,
  180. BUTTON_MODE_NONE, BUTTON_MODE_NONE, BUTTON_MODE_NONE
  181. );
  182. for (unsigned char id = 0; id < 4; ++id) {
  183. buttonAdd(
  184. GPIO_NONE, BUTTON_PUSHBUTTON,
  185. actions, getSetting({"btnRelay", id}, _buttonRelay(id))
  186. );
  187. }
  188. // Generic GPIO input handlers
  189. #else
  190. size_t buttons = 0;
  191. #if BUTTON1_PIN != GPIO_NONE
  192. ++buttons;
  193. #endif
  194. #if BUTTON2_PIN != GPIO_NONE
  195. ++buttons;
  196. #endif
  197. #if BUTTON3_PIN != GPIO_NONE
  198. ++buttons;
  199. #endif
  200. #if BUTTON4_PIN != GPIO_NONE
  201. ++buttons;
  202. #endif
  203. #if BUTTON5_PIN != GPIO_NONE
  204. ++buttons;
  205. #endif
  206. #if BUTTON6_PIN != GPIO_NONE
  207. ++buttons;
  208. #endif
  209. #if BUTTON7_PIN != GPIO_NONE
  210. ++buttons;
  211. #endif
  212. #if BUTTON8_PIN != GPIO_NONE
  213. ++buttons;
  214. #endif
  215. _buttons.reserve(buttons);
  216. for (unsigned char index = 0; index < buttons; ++index) {
  217. const auto pin = getSetting({"btnGPIO", index}, _buttonPin(index));
  218. if (!gpioValid(pin)) {
  219. break;
  220. }
  221. button_event_delays_t delays {
  222. getSetting({"btnDebDelay", index}, _buttonDebounceDelay(index)),
  223. getSetting({"btnDblCDelay", index}, _buttonDoubleClickDelay(index)),
  224. getSetting({"btnLngCDelay", index}, _buttonLongClickDelay(index)),
  225. getSetting({"btnLngLngCDelay", index}, _buttonLongLongClickDelay(index))
  226. };
  227. _buttons.emplace_back(
  228. std::make_shared<DebounceEvent::DigitalPin>(pin),
  229. getSetting({"btnMode", index}, _buttonMode(index)),
  230. getSetting({"btnActions", index}, _buttonConstructActions(index)),
  231. getSetting({"btnRelay", index}, _buttonRelay(index)),
  232. delays
  233. );
  234. }
  235. #endif
  236. DEBUG_MSG_P(PSTR("[BUTTON] Number of buttons: %u\n"), _buttons.size());
  237. // Websocket Callbacks
  238. #if WEB_SUPPORT
  239. wsRegister()
  240. .onVisible(_buttonWebSocketOnVisible)
  241. .onKeyCheck(_buttonWebSocketOnKeyCheck);
  242. #endif
  243. // Register loop
  244. espurnaRegisterLoop(buttonLoop);
  245. }
  246. void buttonLoop() {
  247. #if defined(ITEAD_SONOFF_DUAL)
  248. if (Serial.available() >= 4) {
  249. if (Serial.read() == 0xA0) {
  250. if (Serial.read() == 0x04) {
  251. unsigned char value = Serial.read();
  252. if (Serial.read() == 0xA1) {
  253. // RELAYs and BUTTONs are synchonized in the SIL F330
  254. // The on-board BUTTON2 should toggle RELAY0 value
  255. // Since we are not passing back RELAY2 value
  256. // (in the relayStatus method) it will only be present
  257. // here if it has actually been pressed
  258. if ((value & 4) == 4) {
  259. buttonEvent(2, BUTTON_EVENT_CLICK);
  260. return;
  261. }
  262. // Otherwise check if any of the other two BUTTONs
  263. // (in the header) has been pressed, but we should
  264. // ensure that we only toggle one of them to avoid
  265. // the synchronization going mad
  266. // This loop is generic for any PSB-04 module
  267. for (unsigned int i=0; i<relayCount(); i++) {
  268. bool status = (value & (1 << i)) > 0;
  269. // Check if the status for that relay has changed
  270. if (relayStatus(i) != status) {
  271. buttonEvent(i, BUTTON_EVENT_CLICK);
  272. break;
  273. }
  274. }
  275. }
  276. }
  277. }
  278. }
  279. #elif defined(FOXEL_LIGHTFOX_DUAL)
  280. if (Serial.available() >= 4) {
  281. if (Serial.read() == 0xA0) {
  282. if (Serial.read() == 0x04) {
  283. unsigned char value = Serial.read();
  284. if (Serial.read() == 0xA1) {
  285. DEBUG_MSG_P(PSTR("[BUTTON] [LIGHTFOX] Received buttons mask: %d\n"), value);
  286. for (unsigned int i=0; i<_buttons.size(); i++) {
  287. bool clicked = (value & (1 << i)) > 0;
  288. if (clicked) {
  289. buttonEvent(i, BUTTON_EVENT_CLICK);
  290. }
  291. }
  292. }
  293. }
  294. }
  295. }
  296. #else
  297. for (size_t id = 0; id < _buttons.size(); ++id) {
  298. auto& button = _buttons[id];
  299. if (auto event = button.event_handler->loop()) {
  300. buttonEvent(id, _buttonMapEvent(button, event));
  301. }
  302. }
  303. #endif
  304. }
  305. #endif // BUTTON_SUPPORT