xsns_08_htu21.ino 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*
  2. xsns_08_htu21.ino - HTU21 temperature and humidity sensor support for Sonoff-Tasmota
  3. Copyright (C) 2018 Heiko Krupp and 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_I2C
  16. #ifdef USE_HTU
  17. /*********************************************************************************************\
  18. * HTU21 - Temperature and Humidy
  19. *
  20. * Source: Heiko Krupp
  21. *
  22. * I2C Address: 0x40
  23. \*********************************************************************************************/
  24. #define XSNS_08 8
  25. #define HTU21_ADDR 0x40
  26. #define SI7013_CHIPID 0x0D
  27. #define SI7020_CHIPID 0x14
  28. #define SI7021_CHIPID 0x15
  29. #define HTU21_CHIPID 0x32
  30. #define HTU21_READTEMP 0xE3
  31. #define HTU21_READHUM 0xE5
  32. #define HTU21_WRITEREG 0xE6
  33. #define HTU21_READREG 0xE7
  34. #define HTU21_RESET 0xFE
  35. #define HTU21_HEATER_WRITE 0x51
  36. #define HTU21_HEATER_READ 0x11
  37. #define HTU21_SERIAL2_READ1 0xFC /* Read 3rd two Serial bytes */
  38. #define HTU21_SERIAL2_READ2 0xC9 /* Read 4th two Serial bytes */
  39. #define HTU21_HEATER_ON 0x04
  40. #define HTU21_HEATER_OFF 0xFB
  41. #define HTU21_RES_RH12_T14 0x00 // Default
  42. #define HTU21_RES_RH8_T12 0x01
  43. #define HTU21_RES_RH10_T13 0x80
  44. #define HTU21_RES_RH11_T11 0x81
  45. #define HTU21_CRC8_POLYNOM 0x13100
  46. const char kHtuTypes[] PROGMEM = "HTU21|SI7013|SI7020|SI7021|T/RH?";
  47. uint8_t htu_address;
  48. uint8_t htu_type = 0;
  49. uint8_t htu_delay_temp;
  50. uint8_t htu_delay_humidity = 50;
  51. uint8_t htu_valid = 0;
  52. float htu_temperature = 0;
  53. float htu_humidity = 0;
  54. char htu_types[7];
  55. uint8_t HtuCheckCrc8(uint16_t data)
  56. {
  57. for (uint8_t bit = 0; bit < 16; bit++) {
  58. if (data & 0x8000) {
  59. data = (data << 1) ^ HTU21_CRC8_POLYNOM;
  60. } else {
  61. data <<= 1;
  62. }
  63. }
  64. return data >>= 8;
  65. }
  66. uint8_t HtuReadDeviceId(void)
  67. {
  68. uint16_t deviceID = 0;
  69. uint8_t checksum = 0;
  70. Wire.beginTransmission(HTU21_ADDR);
  71. Wire.write(HTU21_SERIAL2_READ1);
  72. Wire.write(HTU21_SERIAL2_READ2);
  73. Wire.endTransmission();
  74. Wire.requestFrom(HTU21_ADDR, 3);
  75. deviceID = Wire.read() << 8;
  76. deviceID |= Wire.read();
  77. checksum = Wire.read();
  78. if (HtuCheckCrc8(deviceID) == checksum) {
  79. deviceID = deviceID >> 8;
  80. } else {
  81. deviceID = 0;
  82. }
  83. return (uint8_t)deviceID;
  84. }
  85. void HtuSetResolution(uint8_t resolution)
  86. {
  87. uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG);
  88. current &= 0x7E; // Replace current resolution bits with 0
  89. current |= resolution; // Add new resolution bits to register
  90. I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current);
  91. }
  92. void HtuReset(void)
  93. {
  94. Wire.beginTransmission(HTU21_ADDR);
  95. Wire.write(HTU21_RESET);
  96. Wire.endTransmission();
  97. delay(15); // Reset takes 15ms
  98. }
  99. void HtuHeater(uint8_t heater)
  100. {
  101. uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG);
  102. switch(heater)
  103. {
  104. case HTU21_HEATER_ON : current |= heater;
  105. break;
  106. case HTU21_HEATER_OFF : current &= heater;
  107. break;
  108. default : current &= heater;
  109. break;
  110. }
  111. I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current);
  112. }
  113. void HtuInit(void)
  114. {
  115. HtuReset();
  116. HtuHeater(HTU21_HEATER_OFF);
  117. HtuSetResolution(HTU21_RES_RH12_T14);
  118. }
  119. boolean HtuRead(void)
  120. {
  121. uint8_t checksum = 0;
  122. uint16_t sensorval = 0;
  123. if (htu_valid) { htu_valid--; }
  124. Wire.beginTransmission(HTU21_ADDR);
  125. Wire.write(HTU21_READTEMP);
  126. if (Wire.endTransmission() != 0) { return false; } // In case of error
  127. delay(htu_delay_temp); // Sensor time at max resolution
  128. Wire.requestFrom(HTU21_ADDR, 3);
  129. if (3 == Wire.available()) {
  130. sensorval = Wire.read() << 8; // MSB
  131. sensorval |= Wire.read(); // LSB
  132. checksum = Wire.read();
  133. }
  134. if (HtuCheckCrc8(sensorval) != checksum) { return false; } // Checksum mismatch
  135. htu_temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85);
  136. Wire.beginTransmission(HTU21_ADDR);
  137. Wire.write(HTU21_READHUM);
  138. if (Wire.endTransmission() != 0) { return false; } // In case of error
  139. delay(htu_delay_humidity); // Sensor time at max resolution
  140. Wire.requestFrom(HTU21_ADDR, 3);
  141. if (3 <= Wire.available()) {
  142. sensorval = Wire.read() << 8; // MSB
  143. sensorval |= Wire.read(); // LSB
  144. checksum = Wire.read();
  145. }
  146. if (HtuCheckCrc8(sensorval) != checksum) { return false; } // Checksum mismatch
  147. sensorval ^= 0x02; // clear status bits
  148. htu_humidity = 0.001907 * (float)sensorval - 6;
  149. if (htu_humidity > 100) { htu_humidity = 100.0; }
  150. if (htu_humidity < 0) { htu_humidity = 0.01; }
  151. if ((0.00 == htu_humidity) && (0.00 == htu_temperature)) {
  152. htu_humidity = 0.0;
  153. }
  154. if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) {
  155. htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity;
  156. }
  157. SetGlobalValues(htu_temperature, htu_humidity);
  158. htu_valid = SENSOR_MAX_MISS;
  159. return true;
  160. }
  161. /********************************************************************************************/
  162. void HtuDetect(void)
  163. {
  164. if (htu_type) { return; }
  165. htu_address = HTU21_ADDR;
  166. htu_type = HtuReadDeviceId();
  167. if (htu_type) {
  168. uint8_t index = 0;
  169. HtuInit();
  170. switch (htu_type) {
  171. case HTU21_CHIPID:
  172. htu_delay_temp = 50;
  173. htu_delay_humidity = 16;
  174. break;
  175. case SI7021_CHIPID:
  176. index++; // 3
  177. case SI7020_CHIPID:
  178. index++; // 2
  179. case SI7013_CHIPID:
  180. index++; // 1
  181. htu_delay_temp = 12;
  182. htu_delay_humidity = 23;
  183. break;
  184. default:
  185. index = 4;
  186. htu_delay_temp = 50;
  187. htu_delay_humidity = 23;
  188. }
  189. GetTextIndexed(htu_types, sizeof(htu_types), index, kHtuTypes);
  190. snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, htu_types, htu_address);
  191. AddLog(LOG_LEVEL_DEBUG);
  192. }
  193. }
  194. void HtuEverySecond(void)
  195. {
  196. if (92 == (uptime %100)) {
  197. // 1mS
  198. HtuDetect();
  199. }
  200. else if (uptime &1) {
  201. // HTU21: 68mS, SI70xx: 37mS
  202. if (htu_type) {
  203. if (!HtuRead()) {
  204. AddLogMissed(htu_types, htu_valid);
  205. // if (!htu_valid) { htu_type = 0; }
  206. }
  207. }
  208. }
  209. }
  210. void HtuShow(boolean json)
  211. {
  212. if (htu_valid) {
  213. char temperature[33];
  214. dtostrfd(htu_temperature, Settings.flag2.temperature_resolution, temperature);
  215. char humidity[33];
  216. dtostrfd(htu_humidity, Settings.flag2.humidity_resolution, humidity);
  217. if (json) {
  218. snprintf_P(mqtt_data, sizeof(mqtt_data), JSON_SNS_TEMPHUM, mqtt_data, htu_types, temperature, humidity);
  219. #ifdef USE_DOMOTICZ
  220. if (0 == tele_period) {
  221. DomoticzTempHumSensor(temperature, humidity);
  222. }
  223. #endif // USE_DOMOTICZ
  224. #ifdef USE_KNX
  225. if (0 == tele_period) {
  226. KnxSensor(KNX_TEMPERATURE, htu_temperature);
  227. KnxSensor(KNX_HUMIDITY, htu_humidity);
  228. }
  229. #endif // USE_KNX
  230. #ifdef USE_WEBSERVER
  231. } else {
  232. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, htu_types, temperature, TempUnit());
  233. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, htu_types, humidity);
  234. #endif // USE_WEBSERVER
  235. }
  236. }
  237. }
  238. /*********************************************************************************************\
  239. * Interface
  240. \*********************************************************************************************/
  241. boolean Xsns08(byte function)
  242. {
  243. boolean result = false;
  244. if (i2c_flg) {
  245. switch (function) {
  246. case FUNC_INIT:
  247. HtuDetect();
  248. break;
  249. case FUNC_EVERY_SECOND:
  250. HtuEverySecond();
  251. break;
  252. case FUNC_JSON_APPEND:
  253. HtuShow(1);
  254. break;
  255. #ifdef USE_WEBSERVER
  256. case FUNC_WEB_APPEND:
  257. HtuShow(0);
  258. break;
  259. #endif // USE_WEBSERVER
  260. }
  261. }
  262. return result;
  263. }
  264. #endif // USE_HTU
  265. #endif // USE_I2C