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.

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