xsns_05_ds18x20.ino 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. /*
  2. xsns_05_ds18x20.ino - DS18x20 temperature sensor support for Sonoff-Tasmota
  3. Copyright (C) 2018 Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_DS18x20
  16. /*********************************************************************************************\
  17. * DS18B20 - Temperature - Multiple sensors
  18. \*********************************************************************************************/
  19. #define XSNS_05 5
  20. //#define USE_DS18x20_RECONFIGURE // When sensor is lost keep retrying or re-configure
  21. #define DS18S20_CHIPID 0x10 // +/-0.5C 9-bit
  22. #define DS1822_CHIPID 0x22 // +/-2C 12-bit
  23. #define DS18B20_CHIPID 0x28 // +/-0.5C 12-bit
  24. #define MAX31850_CHIPID 0x3B // +/-0.25C 14-bit
  25. #define W1_SKIP_ROM 0xCC
  26. #define W1_CONVERT_TEMP 0x44
  27. #define W1_WRITE_EEPROM 0x48
  28. #define W1_WRITE_SCRATCHPAD 0x4E
  29. #define W1_READ_SCRATCHPAD 0xBE
  30. #define DS18X20_MAX_SENSORS 8
  31. const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850";
  32. uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID };
  33. struct DS18X20STRUCT {
  34. uint8_t address[8];
  35. uint8_t index;
  36. uint8_t valid;
  37. float temperature;
  38. } ds18x20_sensor[DS18X20_MAX_SENSORS];
  39. uint8_t ds18x20_sensors = 0;
  40. uint8_t ds18x20_pin = 0;
  41. char ds18x20_types[12];
  42. #ifdef W1_PARASITE_POWER
  43. uint8_t ds18x20_sensor_curr = 0;
  44. unsigned long w1_power_until = 0;
  45. #endif
  46. /*********************************************************************************************\
  47. * Embedded tuned OneWire library
  48. \*********************************************************************************************/
  49. #define W1_MATCH_ROM 0x55
  50. #define W1_SEARCH_ROM 0xF0
  51. uint8_t onewire_last_discrepancy = 0;
  52. uint8_t onewire_last_family_discrepancy = 0;
  53. bool onewire_last_device_flag = false;
  54. unsigned char onewire_rom_id[8] = { 0 };
  55. uint8_t OneWireReset(void)
  56. {
  57. uint8_t retries = 125;
  58. //noInterrupts();
  59. pinMode(ds18x20_pin, INPUT);
  60. do {
  61. if (--retries == 0) {
  62. return 0;
  63. }
  64. delayMicroseconds(2);
  65. } while (!digitalRead(ds18x20_pin));
  66. pinMode(ds18x20_pin, OUTPUT);
  67. digitalWrite(ds18x20_pin, LOW);
  68. delayMicroseconds(480);
  69. pinMode(ds18x20_pin, INPUT);
  70. delayMicroseconds(70);
  71. uint8_t r = !digitalRead(ds18x20_pin);
  72. //interrupts();
  73. delayMicroseconds(410);
  74. return r;
  75. }
  76. void OneWireWriteBit(uint8_t v)
  77. {
  78. static const uint8_t delay_low[2] = { 65, 10 };
  79. static const uint8_t delay_high[2] = { 5, 55 };
  80. v &= 1;
  81. //noInterrupts();
  82. digitalWrite(ds18x20_pin, LOW);
  83. pinMode(ds18x20_pin, OUTPUT);
  84. delayMicroseconds(delay_low[v]);
  85. digitalWrite(ds18x20_pin, HIGH);
  86. //interrupts();
  87. delayMicroseconds(delay_high[v]);
  88. }
  89. uint8_t OneWireReadBit(void)
  90. {
  91. //noInterrupts();
  92. pinMode(ds18x20_pin, OUTPUT);
  93. digitalWrite(ds18x20_pin, LOW);
  94. delayMicroseconds(3);
  95. pinMode(ds18x20_pin, INPUT);
  96. delayMicroseconds(10);
  97. uint8_t r = digitalRead(ds18x20_pin);
  98. //interrupts();
  99. delayMicroseconds(53);
  100. return r;
  101. }
  102. void OneWireWrite(uint8_t v)
  103. {
  104. for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) {
  105. OneWireWriteBit((bit_mask & v) ? 1 : 0);
  106. }
  107. }
  108. uint8_t OneWireRead(void)
  109. {
  110. uint8_t r = 0;
  111. for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) {
  112. if (OneWireReadBit()) {
  113. r |= bit_mask;
  114. }
  115. }
  116. return r;
  117. }
  118. void OneWireSelect(const uint8_t rom[8])
  119. {
  120. OneWireWrite(W1_MATCH_ROM);
  121. for (uint8_t i = 0; i < 8; i++) {
  122. OneWireWrite(rom[i]);
  123. }
  124. }
  125. void OneWireResetSearch(void)
  126. {
  127. onewire_last_discrepancy = 0;
  128. onewire_last_device_flag = false;
  129. onewire_last_family_discrepancy = 0;
  130. for (uint8_t i = 0; i < 8; i++) {
  131. onewire_rom_id[i] = 0;
  132. }
  133. }
  134. uint8_t OneWireSearch(uint8_t *newAddr)
  135. {
  136. uint8_t id_bit_number = 1;
  137. uint8_t last_zero = 0;
  138. uint8_t rom_byte_number = 0;
  139. uint8_t search_result = 0;
  140. uint8_t id_bit;
  141. uint8_t cmp_id_bit;
  142. unsigned char rom_byte_mask = 1;
  143. unsigned char search_direction;
  144. if (!onewire_last_device_flag) {
  145. if (!OneWireReset()) {
  146. onewire_last_discrepancy = 0;
  147. onewire_last_device_flag = false;
  148. onewire_last_family_discrepancy = 0;
  149. return false;
  150. }
  151. OneWireWrite(W1_SEARCH_ROM);
  152. do {
  153. id_bit = OneWireReadBit();
  154. cmp_id_bit = OneWireReadBit();
  155. if ((id_bit == 1) && (cmp_id_bit == 1)) {
  156. break;
  157. } else {
  158. if (id_bit != cmp_id_bit) {
  159. search_direction = id_bit;
  160. } else {
  161. if (id_bit_number < onewire_last_discrepancy) {
  162. search_direction = ((onewire_rom_id[rom_byte_number] & rom_byte_mask) > 0);
  163. } else {
  164. search_direction = (id_bit_number == onewire_last_discrepancy);
  165. }
  166. if (search_direction == 0) {
  167. last_zero = id_bit_number;
  168. if (last_zero < 9) {
  169. onewire_last_family_discrepancy = last_zero;
  170. }
  171. }
  172. }
  173. if (search_direction == 1) {
  174. onewire_rom_id[rom_byte_number] |= rom_byte_mask;
  175. } else {
  176. onewire_rom_id[rom_byte_number] &= ~rom_byte_mask;
  177. }
  178. OneWireWriteBit(search_direction);
  179. id_bit_number++;
  180. rom_byte_mask <<= 1;
  181. if (rom_byte_mask == 0) {
  182. rom_byte_number++;
  183. rom_byte_mask = 1;
  184. }
  185. }
  186. } while (rom_byte_number < 8);
  187. if (!(id_bit_number < 65)) {
  188. onewire_last_discrepancy = last_zero;
  189. if (onewire_last_discrepancy == 0) {
  190. onewire_last_device_flag = true;
  191. }
  192. search_result = true;
  193. }
  194. }
  195. if (!search_result || !onewire_rom_id[0]) {
  196. onewire_last_discrepancy = 0;
  197. onewire_last_device_flag = false;
  198. onewire_last_family_discrepancy = 0;
  199. search_result = false;
  200. }
  201. for (uint8_t i = 0; i < 8; i++) {
  202. newAddr[i] = onewire_rom_id[i];
  203. }
  204. return search_result;
  205. }
  206. boolean OneWireCrc8(uint8_t *addr)
  207. {
  208. uint8_t crc = 0;
  209. uint8_t len = 8;
  210. while (len--) {
  211. uint8_t inbyte = *addr++; // from 0 to 7
  212. for (uint8_t i = 8; i; i--) {
  213. uint8_t mix = (crc ^ inbyte) & 0x01;
  214. crc >>= 1;
  215. if (mix) {
  216. crc ^= 0x8C;
  217. }
  218. inbyte >>= 1;
  219. }
  220. }
  221. return (crc == *addr); // addr 8
  222. }
  223. /********************************************************************************************/
  224. void Ds18x20Init(void)
  225. {
  226. uint64_t ids[DS18X20_MAX_SENSORS];
  227. ds18x20_pin = pin[GPIO_DSB];
  228. OneWireResetSearch();
  229. for (ds18x20_sensors = 0; ds18x20_sensors < DS18X20_MAX_SENSORS; ds18x20_sensors) {
  230. if (!OneWireSearch(ds18x20_sensor[ds18x20_sensors].address)) {
  231. break;
  232. }
  233. if (OneWireCrc8(ds18x20_sensor[ds18x20_sensors].address) &&
  234. ((ds18x20_sensor[ds18x20_sensors].address[0] == DS18S20_CHIPID) ||
  235. (ds18x20_sensor[ds18x20_sensors].address[0] == DS1822_CHIPID) ||
  236. (ds18x20_sensor[ds18x20_sensors].address[0] == DS18B20_CHIPID) ||
  237. (ds18x20_sensor[ds18x20_sensors].address[0] == MAX31850_CHIPID))) {
  238. ds18x20_sensor[ds18x20_sensors].index = ds18x20_sensors;
  239. ids[ds18x20_sensors] = ds18x20_sensor[ds18x20_sensors].address[0]; // Chip id
  240. for (uint8_t j = 6; j > 0; j--) {
  241. ids[ds18x20_sensors] = ids[ds18x20_sensors] << 8 | ds18x20_sensor[ds18x20_sensors].address[j];
  242. }
  243. ds18x20_sensors++;
  244. }
  245. }
  246. for (uint8_t i = 0; i < ds18x20_sensors; i++) {
  247. for (uint8_t j = i + 1; j < ds18x20_sensors; j++) {
  248. if (ids[ds18x20_sensor[i].index] > ids[ds18x20_sensor[j].index]) { // Sort ascending
  249. std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index);
  250. }
  251. }
  252. }
  253. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors);
  254. AddLog(LOG_LEVEL_DEBUG);
  255. }
  256. void Ds18x20Convert(void)
  257. {
  258. OneWireReset();
  259. #ifdef W1_PARASITE_POWER
  260. // With parasite power address one sensor at a time
  261. if (++ds18x20_sensor_curr >= ds18x20_sensors)
  262. ds18x20_sensor_curr = 0;
  263. OneWireSelect(ds18x20_sensor[ds18x20_sensor_curr].address);
  264. #else
  265. OneWireWrite(W1_SKIP_ROM); // Address all Sensors on Bus
  266. #endif
  267. OneWireWrite(W1_CONVERT_TEMP); // start conversion, no parasite power on at the end
  268. // delay(750); // 750ms should be enough for 12bit conv
  269. }
  270. bool Ds18x20Read(uint8_t sensor)
  271. {
  272. uint8_t data[9];
  273. int8_t sign = 1;
  274. uint16_t temp12 = 0;
  275. int16_t temp14 = 0;
  276. float temp9 = 0.0;
  277. uint8_t index = ds18x20_sensor[sensor].index;
  278. if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; }
  279. for (uint8_t retry = 0; retry < 3; retry++) {
  280. OneWireReset();
  281. OneWireSelect(ds18x20_sensor[index].address);
  282. OneWireWrite(W1_READ_SCRATCHPAD);
  283. for (uint8_t i = 0; i < 9; i++) {
  284. data[i] = OneWireRead();
  285. }
  286. if (OneWireCrc8(data)) {
  287. switch(ds18x20_sensor[index].address[0]) {
  288. case DS18S20_CHIPID:
  289. if (data[1] > 0x80) {
  290. data[0] = (~data[0]) +1;
  291. sign = -1; // App-Note fix possible sign error
  292. }
  293. if (data[0] & 1) {
  294. temp9 = ((data[0] >> 1) + 0.5) * sign;
  295. } else {
  296. temp9 = (data[0] >> 1) * sign;
  297. }
  298. ds18x20_sensor[index].temperature = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0));
  299. ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
  300. return true;
  301. case DS1822_CHIPID:
  302. case DS18B20_CHIPID:
  303. if (data[4] != 0x7F) {
  304. data[4] = 0x7F; // Set resolution to 12-bit
  305. OneWireReset();
  306. OneWireSelect(ds18x20_sensor[index].address);
  307. OneWireWrite(W1_WRITE_SCRATCHPAD);
  308. OneWireWrite(data[2]); // Th Register
  309. OneWireWrite(data[3]); // Tl Register
  310. OneWireWrite(data[4]); // Configuration Register
  311. OneWireSelect(ds18x20_sensor[index].address);
  312. OneWireWrite(W1_WRITE_EEPROM); // Save scratchpad to EEPROM
  313. #ifdef W1_PARASITE_POWER
  314. w1_power_until = millis() + 10; // 10ms specified duration for EEPROM write
  315. #endif
  316. }
  317. temp12 = (data[1] << 8) + data[0];
  318. if (temp12 > 2047) {
  319. temp12 = (~temp12) +1;
  320. sign = -1;
  321. }
  322. ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16
  323. ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
  324. return true;
  325. case MAX31850_CHIPID:
  326. temp14 = (data[1] << 8) + (data[0] & 0xFC);
  327. ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); // Divide by 16
  328. ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
  329. return true;
  330. }
  331. }
  332. }
  333. AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR));
  334. return false;
  335. }
  336. void Ds18x20Name(uint8_t sensor)
  337. {
  338. uint8_t index = sizeof(ds18x20_chipids);
  339. while (index) {
  340. if (ds18x20_sensor[ds18x20_sensor[sensor].index].address[0] == ds18x20_chipids[index]) {
  341. break;
  342. }
  343. index--;
  344. }
  345. GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types);
  346. if (ds18x20_sensors > 1) {
  347. snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s-%d"), ds18x20_types, sensor +1);
  348. }
  349. }
  350. /********************************************************************************************/
  351. void Ds18x20EverySecond(void)
  352. {
  353. #ifdef W1_PARASITE_POWER
  354. // skip access if there is still an eeprom write ongoing
  355. unsigned long now = millis();
  356. if (now < w1_power_until)
  357. return;
  358. #endif
  359. if (uptime & 1
  360. #ifdef W1_PARASITE_POWER
  361. // if more than 1 sensor and only parasite power: convert every cycle
  362. || ds18x20_sensors >= 2
  363. #endif
  364. ) {
  365. // 2mS
  366. Ds18x20Convert(); // Start conversion, takes up to one second
  367. } else {
  368. for (uint8_t i = 0; i < ds18x20_sensors; i++) {
  369. // 12mS per device
  370. if (!Ds18x20Read(i)) { // Read temperature
  371. Ds18x20Name(i);
  372. AddLogMissed(ds18x20_types, ds18x20_sensor[ds18x20_sensor[i].index].valid);
  373. #ifdef USE_DS18x20_RECONFIGURE
  374. if (!ds18x20_sensor[ds18x20_sensor[i].index].valid) {
  375. memset(&ds18x20_sensor, 0, sizeof(ds18x20_sensor));
  376. Ds18x20Init(); // Re-configure
  377. }
  378. #endif // USE_DS18x20_RECONFIGURE
  379. }
  380. }
  381. }
  382. }
  383. void Ds18x20Show(boolean json)
  384. {
  385. for (uint8_t i = 0; i < ds18x20_sensors; i++) {
  386. uint8_t index = ds18x20_sensor[i].index;
  387. if (ds18x20_sensor[index].valid) { // Check for valid temperature
  388. char temperature[33];
  389. dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature);
  390. Ds18x20Name(i);
  391. if (json) {
  392. if (1 == ds18x20_sensors) {
  393. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), mqtt_data, ds18x20_types, temperature);
  394. } else {
  395. char address[17];
  396. for (byte j = 0; j < 6; j++) {
  397. sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); // Skip sensor type and crc
  398. }
  399. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), mqtt_data, ds18x20_types, address, temperature);
  400. }
  401. #ifdef USE_DOMOTICZ
  402. if ((0 == tele_period) && (0 == i)) {
  403. DomoticzSensor(DZ_TEMP, temperature);
  404. }
  405. #endif // USE_DOMOTICZ
  406. #ifdef USE_KNX
  407. if ((0 == tele_period) && (0 == i)) {
  408. KnxSensor(KNX_TEMPERATURE, ds18x20_sensor[index].temperature);
  409. }
  410. #endif // USE_KNX
  411. #ifdef USE_WEBSERVER
  412. } else {
  413. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, ds18x20_types, temperature, TempUnit());
  414. #endif // USE_WEBSERVER
  415. }
  416. }
  417. }
  418. }
  419. /*********************************************************************************************\
  420. * Interface
  421. \*********************************************************************************************/
  422. boolean Xsns05(byte function)
  423. {
  424. boolean result = false;
  425. if (pin[GPIO_DSB] < 99) {
  426. switch (function) {
  427. case FUNC_INIT:
  428. Ds18x20Init();
  429. break;
  430. case FUNC_EVERY_SECOND:
  431. Ds18x20EverySecond();
  432. break;
  433. case FUNC_JSON_APPEND:
  434. Ds18x20Show(1);
  435. break;
  436. #ifdef USE_WEBSERVER
  437. case FUNC_WEB_APPEND:
  438. Ds18x20Show(0);
  439. break;
  440. #endif // USE_WEBSERVER
  441. }
  442. }
  443. return result;
  444. }
  445. #endif // USE_DS18x20