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.

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