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.

198 lines
7.2 KiB

  1. // -----------------------------------------------------------------------------
  2. // AM2320 Humidity & Temperature sensor over I2C
  3. // Copyright (C) 2018 by Mustafa Tufan
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && AM2320_SUPPORT
  6. #pragma once
  7. #undef I2C_SUPPORT
  8. #define I2C_SUPPORT 1 // Explicitly request I2C support.
  9. #include "Arduino.h"
  10. #include "I2CSensor.h"
  11. // https://akizukidenshi.com/download/ds/aosong/AM2320.pdf
  12. #define AM2320_I2C_READ_REGISTER_DATA 0x03 // Read one or more data registers
  13. #define AM2320_I2C_WRITE_MULTIPLE_REGISTERS 0x10 // Multiple sets of binary data to write multiple registers
  14. /*
  15. Register | Address | Register | Address | Register | Address | Register | Address
  16. -----------------+---------+--------------------+---------+-------------------------+---------+-----------+--------
  17. High humidity | 0x00 | Model High | 0x08 | Users register a high | 0x10 | Retention | 0x18
  18. Low humidity | 0x01 | Model Low | 0x09 | Users register a low | 0x11 | Retention | 0x19
  19. High temperature | 0x02 | The version number | 0x0A | Users register 2 high | 0x12 | Retention | 0x1A
  20. Low temperature | 0x03 | Device ID(24-31)Bit| 0x0B | Users register 2 low | 0x13 | Retention | 0x1B
  21. Retention | 0x04 | Device ID(24-31)Bit| 0x0C | Retention | 0x14 | Retention | 0x1C
  22. Retention | 0x05 | Device ID(24-31)Bit| 0x0D | Retention | 0x15 | Retention | 0x1D
  23. Retention | 0x06 | Device ID(24-31)Bit| 0x0E | Retention | 0x16 | Retention | 0x1E
  24. Retention | 0x07 | Status Register | 0x0F | Retention | 0x17 | Retention | 0x1F
  25. */
  26. class AM2320Sensor : public I2CSensor {
  27. public:
  28. // ---------------------------------------------------------------------
  29. // Public
  30. // ---------------------------------------------------------------------
  31. AM2320Sensor(): I2CSensor() {
  32. _count = 2;
  33. _sensor_id = SENSOR_AM2320_ID;
  34. }
  35. // ---------------------------------------------------------------------
  36. // Sensor API
  37. // ---------------------------------------------------------------------
  38. // Initialization method, must be idempotent
  39. void begin() {
  40. if (!_dirty) return;
  41. // I2C auto-discover
  42. unsigned char addresses[] = {0x23, 0x5C, 0xB8};
  43. _address = _begin_i2c(_address, sizeof(addresses), addresses);
  44. if (_address == 0) return;
  45. _ready = true;
  46. _dirty = false;
  47. }
  48. // Descriptive name of the sensor
  49. String description() {
  50. char buffer[25];
  51. snprintf(buffer, sizeof(buffer), "AM2320 @ I2C (0x%02X)", _address);
  52. return String(buffer);
  53. }
  54. // Descriptive name of the slot # index
  55. String slot(unsigned char index) {
  56. return description();
  57. };
  58. // Type for slot # index
  59. unsigned char type(unsigned char index) {
  60. if (index == 0) return MAGNITUDE_TEMPERATURE;
  61. if (index == 1) return MAGNITUDE_HUMIDITY;
  62. return MAGNITUDE_NONE;
  63. }
  64. // Pre-read hook (usually to populate registers with up-to-date data)
  65. void pre() {
  66. _error = SENSOR_ERROR_OK;
  67. _read();
  68. }
  69. // Current value for slot # index
  70. double value(unsigned char index) {
  71. if (index == 0) return _temperature;
  72. if (index == 1) return _humidity;
  73. return 0;
  74. }
  75. protected:
  76. // ---------------------------------------------------------------------
  77. // Protected
  78. // ---------------------------------------------------------------------
  79. /*
  80. // Get device model, version, device_id
  81. void _init() {
  82. i2c_wakeup(address);
  83. delayMicroseconds(800);
  84. unsigned char _buffer[11];
  85. // 0x08 = read address
  86. // 7 = number of bytes to read
  87. if (i2c_write_uint8(_address, AM2320_I2C_READ_REGISTER_DATA, 0x08, 7) != I2C_TRANS_SUCCESS) {
  88. _error = SENSOR_ERROR_TIMEOUT;
  89. return false;
  90. }
  91. uint16_t model = (_buffer[2] << 8) | _buffer[3];
  92. uint8_t version = _buffer[4];
  93. uint32_t device_id = _buffer[8] << 24 | _buffer[7] << 16 | _buffer[6] << 8 | _buffer[5];
  94. }
  95. */
  96. void _read() {
  97. i2c_wakeup(address);
  98. // waiting time of at least 800 μs, the maximum 3000 μs
  99. delayMicroseconds(800); // just to be on safe side
  100. // 0x00 = read address
  101. // 4 = number of bytes to read
  102. if (i2c_write_uint8(_address, AM2320_I2C_READ_REGISTER_DATA, 0x00, 4) != I2C_TRANS_SUCCESS) {
  103. _error = SENSOR_ERROR_TIMEOUT;
  104. return;
  105. }
  106. unsigned char _buffer[8];
  107. // waiting time of at least 800 μs, the maximum 3000 μs
  108. delayMicroseconds(800 + ((3000-800)/2) );
  109. i2c_read_buffer(_address, _buffer, 8);
  110. // Humidity : 01F4 = (1×256)+(F×16)+4 = 500 => humidity = 500÷10 = 50.0 %
  111. // 0339 = (3×256)+(3×16)+9 = 825 => humidity = 825÷10 = 82.5 %
  112. // Temperature: 00FA = (F×16)+A = 250 => temperature = 250÷10 = 25.0 C
  113. // 0115 = (1×256)+(1×16)+5 = 277 => temperature = 277÷10 = 27.7 C
  114. // Temperature resolution is 16Bit, temperature highest bit (Bit 15) is equal to 1 indicates a negative temperature
  115. // _buffer 0 = function code
  116. // _buffer 1 = number of bytes
  117. // _buffer 2-3 = high/low humidity
  118. // _buffer 4-5 = high/low temperature
  119. // _buffer 6-7 = CRC low/high
  120. unsigned int responseCRC = 0;
  121. responseCRC = ((responseCRC | _buffer[7]) << 8 | _buffer[6]);
  122. if (responseCRC == _CRC16(_buffer)) {
  123. int foo = (_buffer[2] << 8) | _buffer[3];
  124. _humidity = foo / 10.0;
  125. foo = ((_buffer[4] & 0x7F) << 8) | _buffer[5]; // clean bit 15 and merge
  126. _temperature = foo / 10.0;
  127. if (_buffer[4] & 0x80) { // is bit 15 == 1
  128. _temperature = _temperature * -1; // negative temperature
  129. }
  130. _error = SENSOR_ERROR_OK;
  131. } else {
  132. _error = SENSOR_ERROR_CRC;
  133. return;
  134. }
  135. }
  136. unsigned int _CRC16(unsigned char buffer[]) {
  137. unsigned int crc16 = 0xFFFF;
  138. for (unsigned int i = 0; i < 6; i++) {
  139. crc16 ^= buffer[i];
  140. for (unsigned int b = 8; b != 0; b--) {
  141. if (crc16 & 0x01) { // is lsb set
  142. crc16 >>= 1;
  143. crc16 ^= 0xA001;
  144. } else {
  145. crc16 >>= 1;
  146. }
  147. }
  148. }
  149. return crc16;
  150. }
  151. double _temperature = 0;
  152. double _humidity = 0;
  153. };
  154. #endif // SENSOR_SUPPORT && AM2320_SUPPORT