194 lines
7.1 KiB
C
194 lines
7.1 KiB
C
|
// -----------------------------------------------------------------------------
|
|||
|
// AM2320 Humidity & Temperature sensor over I2C
|
|||
|
// Copyright (C) 2018 by Mustafa Tufan
|
|||
|
// -----------------------------------------------------------------------------
|
|||
|
|
|||
|
#if SENSOR_SUPPORT && AM2320_SUPPORT
|
|||
|
|
|||
|
#pragma once
|
|||
|
|
|||
|
#include "Arduino.h"
|
|||
|
#include "I2CSensor.h"
|
|||
|
|
|||
|
// https://akizukidenshi.com/download/ds/aosong/AM2320.pdf
|
|||
|
#define AM2320_I2C_READ_REGISTER_DATA 0x03 // Read one or more data registers
|
|||
|
#define AM2320_I2C_WRITE_MULTIPLE_REGISTERS 0x10 // Multiple sets of binary data to write multiple registers
|
|||
|
/*
|
|||
|
Register | Address | Register | Address | Register | Address | Register | Address
|
|||
|
-----------------+---------+--------------------+---------+-------------------------+---------+-----------+--------
|
|||
|
High humidity | 0x00 | Model High | 0x08 | Users register a high | 0x10 | Retention | 0x18
|
|||
|
Low humidity | 0x01 | Model Low | 0x09 | Users register a low | 0x11 | Retention | 0x19
|
|||
|
High temperature | 0x02 | The version number | 0x0A | Users register 2 high | 0x12 | Retention | 0x1A
|
|||
|
Low temperature | 0x03 | Device ID(24-31)Bit| 0x0B | Users register 2 low | 0x13 | Retention | 0x1B
|
|||
|
Retention | 0x04 | Device ID(24-31)Bit| 0x0C | Retention | 0x14 | Retention | 0x1C
|
|||
|
Retention | 0x05 | Device ID(24-31)Bit| 0x0D | Retention | 0x15 | Retention | 0x1D
|
|||
|
Retention | 0x06 | Device ID(24-31)Bit| 0x0E | Retention | 0x16 | Retention | 0x1E
|
|||
|
Retention | 0x07 | Status Register | 0x0F | Retention | 0x17 | Retention | 0x1F
|
|||
|
*/
|
|||
|
|
|||
|
class AM2320Sensor : public I2CSensor {
|
|||
|
|
|||
|
public:
|
|||
|
|
|||
|
// ---------------------------------------------------------------------
|
|||
|
// Public
|
|||
|
// ---------------------------------------------------------------------
|
|||
|
|
|||
|
AM2320Sensor(): I2CSensor() {
|
|||
|
_count = 2;
|
|||
|
_sensor_id = SENSOR_AM2320_ID;
|
|||
|
}
|
|||
|
|
|||
|
// ---------------------------------------------------------------------
|
|||
|
// Sensor API
|
|||
|
// ---------------------------------------------------------------------
|
|||
|
|
|||
|
// Initialization method, must be idempotent
|
|||
|
void begin() {
|
|||
|
if (!_dirty) return;
|
|||
|
|
|||
|
// I2C auto-discover
|
|||
|
unsigned char addresses[] = {0x23, 0x5C, 0xB8};
|
|||
|
_address = _begin_i2c(_address, sizeof(addresses), addresses);
|
|||
|
if (_address == 0) return;
|
|||
|
|
|||
|
_ready = true;
|
|||
|
_dirty = false;
|
|||
|
}
|
|||
|
|
|||
|
// Descriptive name of the sensor
|
|||
|
String description() {
|
|||
|
char buffer[25];
|
|||
|
snprintf(buffer, sizeof(buffer), "AM2320 @ I2C (0x%02X)", _address);
|
|||
|
return String(buffer);
|
|||
|
}
|
|||
|
|
|||
|
// Descriptive name of the slot # index
|
|||
|
String slot(unsigned char index) {
|
|||
|
return description();
|
|||
|
};
|
|||
|
|
|||
|
// Type for slot # index
|
|||
|
unsigned char type(unsigned char index) {
|
|||
|
if (index == 0) return MAGNITUDE_TEMPERATURE;
|
|||
|
if (index == 1) return MAGNITUDE_HUMIDITY;
|
|||
|
return MAGNITUDE_NONE;
|
|||
|
}
|
|||
|
|
|||
|
// Pre-read hook (usually to populate registers with up-to-date data)
|
|||
|
void pre() {
|
|||
|
_error = SENSOR_ERROR_OK;
|
|||
|
_read();
|
|||
|
}
|
|||
|
|
|||
|
// Current value for slot # index
|
|||
|
double value(unsigned char index) {
|
|||
|
if (index == 0) return _temperature;
|
|||
|
if (index == 1) return _humidity;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
protected:
|
|||
|
|
|||
|
// ---------------------------------------------------------------------
|
|||
|
// Protected
|
|||
|
// ---------------------------------------------------------------------
|
|||
|
|
|||
|
/*
|
|||
|
// Get device model, version, device_id
|
|||
|
|
|||
|
void _init() {
|
|||
|
i2c_wakeup();
|
|||
|
delayMicroseconds(800);
|
|||
|
|
|||
|
unsigned char _buffer[11];
|
|||
|
|
|||
|
// 0x08 = read address
|
|||
|
// 7 = number of bytes to read
|
|||
|
if (i2c_write_uint8(_address, AM2320_I2C_READ_REGISTER_DATA, 0x08, 7) != I2C_TRANS_SUCCESS) {
|
|||
|
_error = SENSOR_ERROR_TIMEOUT;
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
uint16_t model = (_buffer[2] << 8) | _buffer[3];
|
|||
|
uint8_t version = _buffer[4];
|
|||
|
uint32_t device_id = _buffer[8] << 24 | _buffer[7] << 16 | _buffer[6] << 8 | _buffer[5];
|
|||
|
}
|
|||
|
*/
|
|||
|
|
|||
|
void _read() {
|
|||
|
i2c_wakeup();
|
|||
|
// waiting time of at least 800 μs, the maximum 3000 μs
|
|||
|
delayMicroseconds(800); // just to be on safe side
|
|||
|
|
|||
|
// 0x00 = read address
|
|||
|
// 4 = number of bytes to read
|
|||
|
if (i2c_write_uint8(_address, AM2320_I2C_READ_REGISTER_DATA, 0x00, 4) != I2C_TRANS_SUCCESS) {
|
|||
|
_error = SENSOR_ERROR_TIMEOUT;
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
unsigned char _buffer[8];
|
|||
|
|
|||
|
// waiting time of at least 800 μs, the maximum 3000 μs
|
|||
|
delayMicroseconds(800 + ((3000-800)/2) );
|
|||
|
i2c_read_buffer(_address, _buffer, 8);
|
|||
|
|
|||
|
// Humidity : 01F4 = (1×256)+(F×16)+4 = 500 => humidity = 500÷10 = 50.0 %
|
|||
|
// 0339 = (3×256)+(3×16)+9 = 825 => humidity = 825÷10 = 82.5 %
|
|||
|
// Temperature: 00FA = (F×16)+A = 250 => temperature = 250÷10 = 25.0 C
|
|||
|
// 0115 = (1×256)+(1×16)+5 = 277 => temperature = 277÷10 = 27.7 C
|
|||
|
// Temperature resolution is 16Bit, temperature highest bit (Bit 15) is equal to 1 indicates a negative temperature
|
|||
|
|
|||
|
// _buffer 0 = function code
|
|||
|
// _buffer 1 = number of bytes
|
|||
|
// _buffer 2-3 = high/low humidity
|
|||
|
// _buffer 4-5 = high/low temperature
|
|||
|
// _buffer 6-7 = CRC low/high
|
|||
|
|
|||
|
unsigned int responseCRC = 0;
|
|||
|
responseCRC = ((responseCRC | _buffer[7]) << 8 | _buffer[6]);
|
|||
|
|
|||
|
if (responseCRC == _CRC16(_buffer)) {
|
|||
|
int foo = (_buffer[2] << 8) | _buffer[3];
|
|||
|
_humidity = foo / 10.0;
|
|||
|
|
|||
|
foo = ((_buffer[4] & 0x7F) << 8) | _buffer[5]; // clean bit 15 and merge
|
|||
|
_temperature = foo / 10.0;
|
|||
|
|
|||
|
if (_buffer[4] & 0x80) { // is bit 15 == 1
|
|||
|
_temperature = _temperature * -1; // negative temperature
|
|||
|
}
|
|||
|
|
|||
|
_error = SENSOR_ERROR_OK;
|
|||
|
} else {
|
|||
|
_error = SENSOR_ERROR_CRC;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
unsigned int _CRC16(unsigned char buffer[]) {
|
|||
|
unsigned int crc16 = 0xFFFF;
|
|||
|
|
|||
|
for (unsigned int i = 0; i < 6; i++) {
|
|||
|
crc16 ^= buffer[i];
|
|||
|
|
|||
|
for (unsigned int b = 8; b != 0; b--) {
|
|||
|
if (crc16 & 0x01) { // is lsb set
|
|||
|
crc16 >>= 1;
|
|||
|
crc16 ^= 0xA001;
|
|||
|
} else {
|
|||
|
crc16 >>= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return crc16;
|
|||
|
}
|
|||
|
|
|||
|
double _temperature = 0;
|
|||
|
double _humidity = 0;
|
|||
|
};
|
|||
|
|
|||
|
#endif // SENSOR_SUPPORT && AM2320_SUPPORT
|