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.

315 lines
9.8 KiB

6 years ago
6 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 "BaseSensor.h"
  10. #include <vector>
  11. #include <OneWire.h>
  12. #define DS_CHIP_DS18S20 0x10
  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. class DallasSensor : public BaseSensor {
  22. public:
  23. // ---------------------------------------------------------------------
  24. // Public
  25. // ---------------------------------------------------------------------
  26. DallasSensor(): BaseSensor() {
  27. _sensor_id = SENSOR_DALLAS_ID;
  28. }
  29. ~DallasSensor() {
  30. if (_wire) delete _wire;
  31. if (_previous != GPIO_NONE) gpioReleaseLock(_previous);
  32. }
  33. // ---------------------------------------------------------------------
  34. void setGPIO(unsigned char gpio) {
  35. if (_gpio == gpio) return;
  36. _gpio = gpio;
  37. _dirty = true;
  38. }
  39. // ---------------------------------------------------------------------
  40. unsigned char getGPIO() {
  41. return _gpio;
  42. }
  43. // ---------------------------------------------------------------------
  44. // Sensor API
  45. // ---------------------------------------------------------------------
  46. // Initialization method, must be idempotent
  47. void begin() {
  48. if (!_dirty) return;
  49. // Manage GPIO lock
  50. if (_previous != GPIO_NONE) gpioReleaseLock(_previous);
  51. _previous = GPIO_NONE;
  52. if (!gpioGetLock(_gpio)) {
  53. _error = SENSOR_ERROR_GPIO_USED;
  54. return;
  55. }
  56. // OneWire
  57. if (_wire) delete _wire;
  58. _wire = new OneWire(_gpio);
  59. // Search devices
  60. loadDevices();
  61. // If no devices found check again pulling up the line
  62. if (_count == 0) {
  63. pinMode(_gpio, INPUT_PULLUP);
  64. loadDevices();
  65. }
  66. // Check connection
  67. if (_count == 0) {
  68. gpioReleaseLock(_gpio);
  69. } else {
  70. _previous = _gpio;
  71. }
  72. _ready = true;
  73. _dirty = false;
  74. }
  75. // Loop-like method, call it in your main loop
  76. void tick() {
  77. static unsigned long last = 0;
  78. if (millis() - last < DALLAS_READ_INTERVAL) return;
  79. last = millis();
  80. // Every second we either start a conversion or read the scratchpad
  81. static bool conversion = true;
  82. if (conversion) {
  83. // Start conversion
  84. _wire->reset();
  85. _wire->skip();
  86. _wire->write(DS_CMD_START_CONVERSION, DS_PARASITE);
  87. } else {
  88. // Read scratchpads
  89. for (unsigned char index=0; index<_devices.size(); index++) {
  90. // Read scratchpad
  91. if (_wire->reset() == 0) {
  92. // Force a CRC check error
  93. _devices[index].data[0] = _devices[index].data[0] + 1;
  94. return;
  95. }
  96. _wire->select(_devices[index].address);
  97. _wire->write(DS_CMD_READ_SCRATCHPAD);
  98. uint8_t data[DS_DATA_SIZE];
  99. for (unsigned char i = 0; i < DS_DATA_SIZE; i++) {
  100. data[i] = _wire->read();
  101. }
  102. if (_wire->reset() != 1) {
  103. // Force a CRC check error
  104. _devices[index].data[0] = _devices[index].data[0] + 1;
  105. return;
  106. }
  107. memcpy(_devices[index].data, data, DS_DATA_SIZE);
  108. }
  109. }
  110. conversion = !conversion;
  111. }
  112. // Descriptive name of the sensor
  113. String description() {
  114. char buffer[20];
  115. snprintf(buffer, sizeof(buffer), "Dallas @ GPIO%d", _gpio);
  116. return String(buffer);
  117. }
  118. // Address of the device
  119. String address(unsigned char index) {
  120. char buffer[20] = {0};
  121. if (index < _count) {
  122. uint8_t * address = _devices[index].address;
  123. snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X%02X%02X",
  124. address[0], address[1], address[2], address[3],
  125. address[4], address[5], address[6], address[7]
  126. );
  127. }
  128. return String(buffer);
  129. }
  130. // Descriptive name of the slot # index
  131. String slot(unsigned char index) {
  132. if (index < _count) {
  133. char buffer[40];
  134. uint8_t * address = _devices[index].address;
  135. snprintf(buffer, sizeof(buffer), "%s (%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%d",
  136. chipAsString(index).c_str(),
  137. address[0], address[1], address[2], address[3],
  138. address[4], address[5], address[6], address[7],
  139. _gpio
  140. );
  141. return String(buffer);
  142. }
  143. return String();
  144. }
  145. // Type for slot # index
  146. unsigned char type(unsigned char index) {
  147. if (index < _count) return MAGNITUDE_TEMPERATURE;
  148. return MAGNITUDE_NONE;
  149. }
  150. // Number of decimals for a magnitude (or -1 for default)
  151. signed char decimals(unsigned char type) {
  152. return 2; // smallest increment is 0.0625 C, so 2 decimals
  153. }
  154. // Pre-read hook (usually to populate registers with up-to-date data)
  155. void pre() {
  156. _error = SENSOR_ERROR_OK;
  157. }
  158. // Current value for slot # index
  159. double value(unsigned char index) {
  160. if (index >= _count) return 0;
  161. uint8_t * data = _devices[index].data;
  162. if (OneWire::crc8(data, DS_DATA_SIZE-1) != data[DS_DATA_SIZE-1]) {
  163. _error = SENSOR_ERROR_CRC;
  164. return 0;
  165. }
  166. // Registers
  167. // byte 0: temperature LSB
  168. // byte 1: temperature MSB
  169. // byte 2: high alarm temp
  170. // byte 3: low alarm temp
  171. // byte 4: DS18S20: store for crc
  172. // DS18B20 & DS1822: configuration register
  173. // byte 5: internal use & crc
  174. // byte 6: DS18S20: COUNT_REMAIN
  175. // DS18B20 & DS1822: store for crc
  176. // byte 7: DS18S20: COUNT_PER_C
  177. // DS18B20 & DS1822: store for crc
  178. // byte 8: SCRATCHPAD_CRC
  179. int16_t raw = (data[1] << 8) | data[0];
  180. if (chip(index) == DS_CHIP_DS18S20) {
  181. raw = raw << 3; // 9 bit resolution default
  182. if (data[7] == 0x10) {
  183. raw = (raw & 0xFFF0) + 12 - data[6]; // "count remain" gives full 12 bit resolution
  184. }
  185. } else {
  186. byte cfg = (data[4] & 0x60);
  187. if (cfg == 0x00) raw = raw & ~7; // 9 bit res, 93.75 ms
  188. else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
  189. else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
  190. // 12 bit res, 750 ms
  191. }
  192. double value = (float) raw / 16.0;
  193. if (value == DS_DISCONNECTED) {
  194. _error = SENSOR_ERROR_CRC;
  195. return 0;
  196. }
  197. return value;
  198. }
  199. protected:
  200. // ---------------------------------------------------------------------
  201. // Protected
  202. // ---------------------------------------------------------------------
  203. bool validateID(unsigned char id) {
  204. return (id == DS_CHIP_DS18S20) || (id == DS_CHIP_DS18B20) || (id == DS_CHIP_DS1822) || (id == DS_CHIP_DS1825);
  205. }
  206. unsigned char chip(unsigned char index) {
  207. if (index < _count) return _devices[index].address[0];
  208. return 0;
  209. }
  210. String chipAsString(unsigned char index) {
  211. unsigned char chip_id = chip(index);
  212. if (chip_id == DS_CHIP_DS18S20) return String("DS18S20");
  213. if (chip_id == DS_CHIP_DS18B20) return String("DS18B20");
  214. if (chip_id == DS_CHIP_DS1822) return String("DS1822");
  215. if (chip_id == DS_CHIP_DS1825) return String("DS1825");
  216. return String("Unknown");
  217. }
  218. void loadDevices() {
  219. uint8_t address[8];
  220. _wire->reset();
  221. _wire->reset_search();
  222. while (_wire->search(address)) {
  223. // Check CRC
  224. if (_wire->crc8(address, 7) == address[7]) {
  225. // Check ID
  226. if (validateID(address[0])) {
  227. ds_device_t device;
  228. memcpy(device.address, address, 8);
  229. _devices.push_back(device);
  230. }
  231. }
  232. }
  233. _count = _devices.size();
  234. }
  235. typedef struct {
  236. uint8_t address[8];
  237. uint8_t data[DS_DATA_SIZE];
  238. } ds_device_t;
  239. std::vector<ds_device_t> _devices;
  240. unsigned char _gpio = GPIO_NONE;
  241. unsigned char _previous = GPIO_NONE;
  242. OneWire * _wire = NULL;
  243. };
  244. #endif // SENSOR_SUPPORT && DALLAS_SUPPORT