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.

417 lines
14 KiB

7 years ago
7 years ago
7 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. if (_previous != GPIO_NONE) gpioReleaseLock(_previous);
  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) gpioReleaseLock(_previous);
  79. _previous = GPIO_NONE;
  80. if (!gpioGetLock(_gpio)) {
  81. _error = SENSOR_ERROR_GPIO_USED;
  82. return;
  83. }
  84. // OneWire
  85. if (_wire) delete _wire;
  86. _wire = new OneWire(_gpio);
  87. // Search devices
  88. loadDevices();
  89. // If no devices found check again pulling up the line
  90. if (_count == 0) {
  91. pinMode(_gpio, INPUT_PULLUP);
  92. loadDevices();
  93. }
  94. // Check connection
  95. if (_count == 0) {
  96. gpioReleaseLock(_gpio);
  97. } else {
  98. _previous = _gpio;
  99. }
  100. _ready = true;
  101. _dirty = false;
  102. }
  103. // Loop-like method, call it in your main loop
  104. void tick() {
  105. static unsigned long last = 0;
  106. if (millis() - last < DALLAS_READ_INTERVAL) return;
  107. last = millis();
  108. // Every second we either start a conversion or read the scratchpad
  109. static bool conversion = true;
  110. if (conversion) {
  111. // Start conversion
  112. _wire->reset();
  113. _wire->skip();
  114. _wire->write(DS_CMD_START_CONVERSION, DS_PARASITE);
  115. } else {
  116. // Read scratchpads
  117. for (unsigned char index=0; index<_devices.size(); index++) {
  118. if (_devices[index].address[0] == DS_CHIP_DS2406) {
  119. uint8_t data[DS2406_STATE_BUF_LEN];
  120. // Read scratchpad
  121. if (_wire->reset() == 0) {
  122. // Force a CRC check error
  123. _devices[index].data[0] = _devices[index].data[0] + 1;
  124. return;
  125. }
  126. _wire->select(_devices[index].address);
  127. data[0] = DS2406_CHANNEL_ACCESS;
  128. data[1] = DS2406_CHANNEL_CONTROL_BYTE;
  129. data[2] = 0xFF;
  130. _wire->write_bytes(data,3);
  131. // 3 cmd bytes, 1 channel info byte, 1 0x00, 2 CRC16
  132. for(int i = 3; i<DS2406_STATE_BUF_LEN; i++) {
  133. data[i] = _wire->read();
  134. }
  135. // Read scratchpad
  136. if (_wire->reset() == 0) {
  137. // Force a CRC check error
  138. _devices[index].data[0] = _devices[index].data[0] + 1;
  139. return;
  140. }
  141. memcpy(_devices[index].data, data, DS2406_STATE_BUF_LEN);
  142. } else {
  143. // Read scratchpad
  144. if (_wire->reset() == 0) {
  145. // Force a CRC check error
  146. _devices[index].data[0] = _devices[index].data[0] + 1;
  147. return;
  148. }
  149. _wire->select(_devices[index].address);
  150. _wire->write(DS_CMD_READ_SCRATCHPAD);
  151. uint8_t data[DS_DATA_SIZE];
  152. for (unsigned char i = 0; i < DS_DATA_SIZE; i++) {
  153. data[i] = _wire->read();
  154. }
  155. if (_wire->reset() != 1) {
  156. // Force a CRC check error
  157. _devices[index].data[0] = _devices[index].data[0] + 1;
  158. return;
  159. }
  160. memcpy(_devices[index].data, data, DS_DATA_SIZE);
  161. }
  162. }
  163. }
  164. conversion = !conversion;
  165. }
  166. // Descriptive name of the sensor
  167. String description() {
  168. char buffer[20];
  169. snprintf(buffer, sizeof(buffer), "Dallas @ GPIO%d", _gpio);
  170. return String(buffer);
  171. }
  172. // Address of the device
  173. String address(unsigned char index) {
  174. char buffer[20] = {0};
  175. if (index < _count) {
  176. uint8_t * address = _devices[index].address;
  177. snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X%02X%02X",
  178. address[0], address[1], address[2], address[3],
  179. address[4], address[5], address[6], address[7]
  180. );
  181. }
  182. return String(buffer);
  183. }
  184. // Descriptive name of the slot # index
  185. String slot(unsigned char index) {
  186. if (index < _count) {
  187. char buffer[40];
  188. uint8_t * address = _devices[index].address;
  189. snprintf(buffer, sizeof(buffer), "%s (%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%d",
  190. chipAsString(index).c_str(),
  191. address[0], address[1], address[2], address[3],
  192. address[4], address[5], address[6], address[7],
  193. _gpio
  194. );
  195. return String(buffer);
  196. }
  197. return String();
  198. }
  199. // Type for slot # index
  200. unsigned char type(unsigned char index) {
  201. if (index < _count) {
  202. if (chip(index) == DS_CHIP_DS2406) {
  203. return MAGNITUDE_DIGITAL;
  204. } else {
  205. return MAGNITUDE_TEMPERATURE;
  206. }
  207. }
  208. return MAGNITUDE_NONE;
  209. }
  210. // Number of decimals for a magnitude (or -1 for default)
  211. signed char decimals(sensor::Unit unit) {
  212. switch (unit) {
  213. // Smallest increment is 0.0625 C, so 2 decimals
  214. case sensor::Unit::Celcius:
  215. return 2;
  216. // In case we have DS2406, there is no decimal places
  217. default:
  218. return 0;
  219. }
  220. }
  221. // Pre-read hook (usually to populate registers with up-to-date data)
  222. void pre() {
  223. _error = SENSOR_ERROR_OK;
  224. }
  225. // Current value for slot # index
  226. double value(unsigned char index) {
  227. if (index >= _count) return 0;
  228. uint8_t * data = _devices[index].data;
  229. if (chip(index) == DS_CHIP_DS2406) {
  230. if (!OneWire::check_crc16(data, 5, &data[5])) {
  231. _error = SENSOR_ERROR_CRC;
  232. return 0;
  233. }
  234. // 3 cmd bytes, 1 channel info byte, 1 0x00, 2 CRC16
  235. // CHANNEL INFO BYTE
  236. // Bit 7 : Supply Indication 0 = no supply
  237. // Bit 6 : Number of Channels 0 = channel A only
  238. // Bit 5 : PIO-B Activity Latch
  239. // Bit 4 : PIO-A Activity Latch
  240. // Bit 3 : PIO B Sensed Level
  241. // Bit 2 : PIO A Sensed Level
  242. // Bit 1 : PIO-B Channel Flip-Flop Q
  243. // Bit 0 : PIO-A Channel Flip-Flop Q
  244. return (data[3] & 0x04) != 0;
  245. }
  246. if (OneWire::crc8(data, DS_DATA_SIZE-1) != data[DS_DATA_SIZE-1]) {
  247. _error = SENSOR_ERROR_CRC;
  248. return 0;
  249. }
  250. // Registers
  251. // byte 0: temperature LSB
  252. // byte 1: temperature MSB
  253. // byte 2: high alarm temp
  254. // byte 3: low alarm temp
  255. // byte 4: DS18S20: store for crc
  256. // DS18B20 & DS1822: configuration register
  257. // byte 5: internal use & crc
  258. // byte 6: DS18S20: COUNT_REMAIN
  259. // DS18B20 & DS1822: store for crc
  260. // byte 7: DS18S20: COUNT_PER_C
  261. // DS18B20 & DS1822: store for crc
  262. // byte 8: SCRATCHPAD_CRC
  263. int16_t raw = (data[1] << 8) | data[0];
  264. if (chip(index) == DS_CHIP_DS18S20) {
  265. raw = raw << 3; // 9 bit resolution default
  266. if (data[7] == 0x10) {
  267. raw = (raw & 0xFFF0) + 12 - data[6]; // "count remain" gives full 12 bit resolution
  268. }
  269. } else {
  270. byte cfg = (data[4] & 0x60);
  271. if (cfg == 0x00) raw = raw & ~7; // 9 bit res, 93.75 ms
  272. else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
  273. else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
  274. // 12 bit res, 750 ms
  275. }
  276. double value = (float) raw / 16.0;
  277. if (value == DS_DISCONNECTED) {
  278. _error = SENSOR_ERROR_CRC;
  279. return 0;
  280. }
  281. return value;
  282. }
  283. protected:
  284. // ---------------------------------------------------------------------
  285. // Protected
  286. // ---------------------------------------------------------------------
  287. bool validateID(unsigned char id) {
  288. return (id == DS_CHIP_DS18S20) || (id == DS_CHIP_DS18B20) || (id == DS_CHIP_DS1822) || (id == DS_CHIP_DS1825) || (id == DS_CHIP_DS2406) ;
  289. }
  290. unsigned char chip(unsigned char index) {
  291. if (index < _count) return _devices[index].address[0];
  292. return 0;
  293. }
  294. String chipAsString(unsigned char index) {
  295. unsigned char chip_id = chip(index);
  296. if (chip_id == DS_CHIP_DS18S20) return String("DS18S20");
  297. if (chip_id == DS_CHIP_DS18B20) return String("DS18B20");
  298. if (chip_id == DS_CHIP_DS1822) return String("DS1822");
  299. if (chip_id == DS_CHIP_DS1825) return String("DS1825");
  300. if (chip_id == DS_CHIP_DS2406) return String("DS2406");
  301. return String("Unknown");
  302. }
  303. void loadDevices() {
  304. uint8_t address[8];
  305. _wire->reset();
  306. _wire->reset_search();
  307. while (_wire->search(address)) {
  308. // Check CRC
  309. if (_wire->crc8(address, 7) == address[7]) {
  310. // Check ID
  311. if (validateID(address[0])) {
  312. ds_device_t device;
  313. memcpy(device.address, address, 8);
  314. _devices.push_back(device);
  315. }
  316. }
  317. }
  318. _count = _devices.size();
  319. }
  320. typedef struct {
  321. uint8_t address[8];
  322. uint8_t data[DS_DATA_SIZE];
  323. } ds_device_t;
  324. std::vector<ds_device_t> _devices;
  325. unsigned char _gpio = GPIO_NONE;
  326. unsigned char _previous = GPIO_NONE;
  327. OneWire * _wire = NULL;
  328. };
  329. #endif // SENSOR_SUPPORT && DALLAS_SUPPORT