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.

419 lines
14 KiB

6 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
6 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
6 years ago
  1. // -----------------------------------------------------------------------------
  2. // Dallas OneWire Sensor
  3. // Uses OneWire library
  4. // Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  5. // -----------------------------------------------------------------------------
  6. #if SENSOR_SUPPORT && DALLAS_SUPPORT
  7. #pragma once
  8. #include <OneWire.h>
  9. #include <vector>
  10. #include "BaseSensor.h"
  11. #define DS_CHIP_DS18S20 0x10
  12. #define DS_CHIP_DS2406 0x12
  13. #define DS_CHIP_DS1822 0x22
  14. #define DS_CHIP_DS18B20 0x28
  15. #define DS_CHIP_DS1825 0x3B
  16. #define DS_DATA_SIZE 9
  17. #define DS_PARASITE 1
  18. #define DS_DISCONNECTED -127
  19. #define DS_CMD_START_CONVERSION 0x44
  20. #define DS_CMD_READ_SCRATCHPAD 0xBE
  21. // ====== DS2406 specific constants =======
  22. #define DS2406_CHANNEL_ACCESS 0xF5;
  23. // CHANNEL CONTROL BYTE
  24. // 7 6 5 4 3 2 1 0
  25. // ALR IM TOG IC CHS1 CHS0 CRC1 CRC0
  26. // 0 1 0 0 0 1 0 1 0x45
  27. // CHS1 CHS0 Description
  28. // 0 0 (not allowed)
  29. // 0 1 channel A only
  30. // 1 0 channel B only
  31. // 1 1 both channels interleaved
  32. // TOG IM CHANNELS EFFECT
  33. // 0 0 one channel Write all bits to the selected channel
  34. // 0 1 one channel Read all bits from the selected channel
  35. // 1 0 one channel Write 8 bits, read 8 bits, write, read, etc. to/from the selected channel
  36. // 1 1 one channel Read 8 bits, write 8 bits, read, write, etc. from/to the selected channel
  37. // 0 0 two channels Repeat: four times (write A, write B)
  38. // 0 1 two channels Repeat: four times (read A, read B)
  39. // 1 0 two channels Four times: (write A, write B), four times: (readA, read B), write, read, etc.
  40. // 1 1 two channels Four times: (read A, read B), four times: (write A, write B), read, write, etc.
  41. // CRC1 CRC0 Description
  42. // 0 0 CRC disabled (no CRC at all)
  43. // 0 1 CRC after every byte
  44. // 1 0 CRC after 8 bytes
  45. // 1 1 CRC after 32 bytes
  46. #define DS2406_CHANNEL_CONTROL_BYTE 0x45;
  47. #define DS2406_STATE_BUF_LEN 7
  48. class DallasSensor : public BaseSensor {
  49. public:
  50. // ---------------------------------------------------------------------
  51. // Public
  52. // ---------------------------------------------------------------------
  53. DallasSensor() {
  54. _sensor_id = SENSOR_DALLAS_ID;
  55. }
  56. ~DallasSensor() {
  57. if (_wire) delete _wire;
  58. gpioUnlock(_gpio);
  59. }
  60. // ---------------------------------------------------------------------
  61. void setGPIO(unsigned char gpio) {
  62. if (_gpio == gpio) return;
  63. _gpio = gpio;
  64. _dirty = true;
  65. }
  66. // ---------------------------------------------------------------------
  67. unsigned char getGPIO() {
  68. return _gpio;
  69. }
  70. // ---------------------------------------------------------------------
  71. // Sensor API
  72. // ---------------------------------------------------------------------
  73. // Initialization method, must be idempotent
  74. void begin() {
  75. if (!_dirty) return;
  76. // Manage GPIO lock
  77. if (_previous != GPIO_NONE) {
  78. gpioUnlock(_previous);
  79. }
  80. _previous = GPIO_NONE;
  81. if (!gpioLock(_gpio)) {
  82. _error = SENSOR_ERROR_GPIO_USED;
  83. return;
  84. }
  85. // OneWire
  86. if (_wire) delete _wire;
  87. _wire = new OneWire(_gpio);
  88. // Search devices
  89. loadDevices();
  90. // If no devices found check again pulling up the line
  91. if (_count == 0) {
  92. pinMode(_gpio, INPUT_PULLUP);
  93. loadDevices();
  94. }
  95. // Check connection
  96. if (_count == 0) {
  97. gpioUnlock(_gpio);
  98. } else {
  99. _previous = _gpio;
  100. }
  101. _ready = true;
  102. _dirty = false;
  103. }
  104. // Loop-like method, call it in your main loop
  105. void tick() {
  106. static unsigned long last = 0;
  107. if (millis() - last < DALLAS_READ_INTERVAL) return;
  108. last = millis();
  109. // Every second we either start a conversion or read the scratchpad
  110. static bool conversion = true;
  111. if (conversion) {
  112. // Start conversion
  113. _wire->reset();
  114. _wire->skip();
  115. _wire->write(DS_CMD_START_CONVERSION, DS_PARASITE);
  116. } else {
  117. // Read scratchpads
  118. for (unsigned char index=0; index<_devices.size(); index++) {
  119. if (_devices[index].address[0] == DS_CHIP_DS2406) {
  120. uint8_t data[DS2406_STATE_BUF_LEN];
  121. // Read scratchpad
  122. if (_wire->reset() == 0) {
  123. // Force a CRC check error
  124. _devices[index].data[0] = _devices[index].data[0] + 1;
  125. return;
  126. }
  127. _wire->select(_devices[index].address);
  128. data[0] = DS2406_CHANNEL_ACCESS;
  129. data[1] = DS2406_CHANNEL_CONTROL_BYTE;
  130. data[2] = 0xFF;
  131. _wire->write_bytes(data,3);
  132. // 3 cmd bytes, 1 channel info byte, 1 0x00, 2 CRC16
  133. for(int i = 3; i<DS2406_STATE_BUF_LEN; i++) {
  134. data[i] = _wire->read();
  135. }
  136. // Read scratchpad
  137. if (_wire->reset() == 0) {
  138. // Force a CRC check error
  139. _devices[index].data[0] = _devices[index].data[0] + 1;
  140. return;
  141. }
  142. memcpy(_devices[index].data, data, DS2406_STATE_BUF_LEN);
  143. } else {
  144. // Read scratchpad
  145. if (_wire->reset() == 0) {
  146. // Force a CRC check error
  147. _devices[index].data[0] = _devices[index].data[0] + 1;
  148. return;
  149. }
  150. _wire->select(_devices[index].address);
  151. _wire->write(DS_CMD_READ_SCRATCHPAD);
  152. uint8_t data[DS_DATA_SIZE];
  153. for (unsigned char i = 0; i < DS_DATA_SIZE; i++) {
  154. data[i] = _wire->read();
  155. }
  156. if (_wire->reset() != 1) {
  157. // Force a CRC check error
  158. _devices[index].data[0] = _devices[index].data[0] + 1;
  159. return;
  160. }
  161. memcpy(_devices[index].data, data, DS_DATA_SIZE);
  162. }
  163. }
  164. }
  165. conversion = !conversion;
  166. }
  167. // Descriptive name of the sensor
  168. String description() {
  169. char buffer[20];
  170. snprintf(buffer, sizeof(buffer), "Dallas @ GPIO%d", _gpio);
  171. return String(buffer);
  172. }
  173. // Address of the device
  174. String address(unsigned char index) {
  175. char buffer[20] = {0};
  176. if (index < _count) {
  177. uint8_t * address = _devices[index].address;
  178. snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X%02X%02X",
  179. address[0], address[1], address[2], address[3],
  180. address[4], address[5], address[6], address[7]
  181. );
  182. }
  183. return String(buffer);
  184. }
  185. // Descriptive name of the slot # index
  186. String description(unsigned char index) {
  187. if (index < _count) {
  188. char buffer[40];
  189. uint8_t * address = _devices[index].address;
  190. snprintf(buffer, sizeof(buffer), "%s (%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%d",
  191. chipAsString(index).c_str(),
  192. address[0], address[1], address[2], address[3],
  193. address[4], address[5], address[6], address[7],
  194. _gpio
  195. );
  196. return String(buffer);
  197. }
  198. return String();
  199. }
  200. // Type for slot # index
  201. unsigned char type(unsigned char index) {
  202. if (index < _count) {
  203. if (chip(index) == DS_CHIP_DS2406) {
  204. return MAGNITUDE_DIGITAL;
  205. } else {
  206. return MAGNITUDE_TEMPERATURE;
  207. }
  208. }
  209. return MAGNITUDE_NONE;
  210. }
  211. // Number of decimals for a magnitude (or -1 for default)
  212. signed char decimals(sensor::Unit unit) {
  213. switch (unit) {
  214. // Smallest increment is 0.0625 C, so 2 decimals
  215. case sensor::Unit::Celcius:
  216. return 2;
  217. // In case we have DS2406, there is no decimal places
  218. default:
  219. return 0;
  220. }
  221. }
  222. // Pre-read hook (usually to populate registers with up-to-date data)
  223. void pre() {
  224. _error = SENSOR_ERROR_OK;
  225. }
  226. // Current value for slot # index
  227. double value(unsigned char index) {
  228. if (index >= _count) return 0;
  229. uint8_t * data = _devices[index].data;
  230. if (chip(index) == DS_CHIP_DS2406) {
  231. if (!OneWire::check_crc16(data, 5, &data[5])) {
  232. _error = SENSOR_ERROR_CRC;
  233. return 0;
  234. }
  235. // 3 cmd bytes, 1 channel info byte, 1 0x00, 2 CRC16
  236. // CHANNEL INFO BYTE
  237. // Bit 7 : Supply Indication 0 = no supply
  238. // Bit 6 : Number of Channels 0 = channel A only
  239. // Bit 5 : PIO-B Activity Latch
  240. // Bit 4 : PIO-A Activity Latch
  241. // Bit 3 : PIO B Sensed Level
  242. // Bit 2 : PIO A Sensed Level
  243. // Bit 1 : PIO-B Channel Flip-Flop Q
  244. // Bit 0 : PIO-A Channel Flip-Flop Q
  245. return (data[3] & 0x04) != 0;
  246. }
  247. if (OneWire::crc8(data, DS_DATA_SIZE-1) != data[DS_DATA_SIZE-1]) {
  248. _error = SENSOR_ERROR_CRC;
  249. return 0;
  250. }
  251. // Registers
  252. // byte 0: temperature LSB
  253. // byte 1: temperature MSB
  254. // byte 2: high alarm temp
  255. // byte 3: low alarm temp
  256. // byte 4: DS18S20: store for crc
  257. // DS18B20 & DS1822: configuration register
  258. // byte 5: internal use & crc
  259. // byte 6: DS18S20: COUNT_REMAIN
  260. // DS18B20 & DS1822: store for crc
  261. // byte 7: DS18S20: COUNT_PER_C
  262. // DS18B20 & DS1822: store for crc
  263. // byte 8: SCRATCHPAD_CRC
  264. int16_t raw = (data[1] << 8) | data[0];
  265. if (chip(index) == DS_CHIP_DS18S20) {
  266. raw = raw << 3; // 9 bit resolution default
  267. if (data[7] == 0x10) {
  268. raw = (raw & 0xFFF0) + 12 - data[6]; // "count remain" gives full 12 bit resolution
  269. }
  270. } else {
  271. byte cfg = (data[4] & 0x60);
  272. if (cfg == 0x00) raw = raw & ~7; // 9 bit res, 93.75 ms
  273. else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
  274. else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
  275. // 12 bit res, 750 ms
  276. }
  277. double value = (float) raw / 16.0;
  278. if (value == DS_DISCONNECTED) {
  279. _error = SENSOR_ERROR_CRC;
  280. return 0;
  281. }
  282. return value;
  283. }
  284. protected:
  285. // ---------------------------------------------------------------------
  286. // Protected
  287. // ---------------------------------------------------------------------
  288. bool validateID(unsigned char id) {
  289. return (id == DS_CHIP_DS18S20) || (id == DS_CHIP_DS18B20) || (id == DS_CHIP_DS1822) || (id == DS_CHIP_DS1825) || (id == DS_CHIP_DS2406) ;
  290. }
  291. unsigned char chip(unsigned char index) {
  292. if (index < _count) return _devices[index].address[0];
  293. return 0;
  294. }
  295. String chipAsString(unsigned char index) {
  296. unsigned char chip_id = chip(index);
  297. if (chip_id == DS_CHIP_DS18S20) return String("DS18S20");
  298. if (chip_id == DS_CHIP_DS18B20) return String("DS18B20");
  299. if (chip_id == DS_CHIP_DS1822) return String("DS1822");
  300. if (chip_id == DS_CHIP_DS1825) return String("DS1825");
  301. if (chip_id == DS_CHIP_DS2406) return String("DS2406");
  302. return String("Unknown");
  303. }
  304. void loadDevices() {
  305. uint8_t address[8];
  306. _wire->reset();
  307. _wire->reset_search();
  308. while (_wire->search(address)) {
  309. // Check CRC
  310. if (_wire->crc8(address, 7) == address[7]) {
  311. // Check ID
  312. if (validateID(address[0])) {
  313. ds_device_t device;
  314. memcpy(device.address, address, 8);
  315. _devices.push_back(device);
  316. }
  317. }
  318. }
  319. _count = _devices.size();
  320. }
  321. typedef struct {
  322. uint8_t address[8];
  323. uint8_t data[DS_DATA_SIZE];
  324. } ds_device_t;
  325. std::vector<ds_device_t> _devices;
  326. unsigned char _gpio = GPIO_NONE;
  327. unsigned char _previous = GPIO_NONE;
  328. OneWire * _wire = NULL;
  329. };
  330. #endif // SENSOR_SUPPORT && DALLAS_SUPPORT