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.

307 lines
9.4 KiB

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