Read and translate AHT20 sensor readings
This commit is contained in:
2
Makefile
2
Makefile
@@ -33,7 +33,7 @@ RMR := rm -rf
|
|||||||
CPPFLAGS := -DF_CPU=$(FREQ) -I$(SRCDIR)
|
CPPFLAGS := -DF_CPU=$(FREQ) -I$(SRCDIR)
|
||||||
CFLAGS := -mmcu=$(MCU) -Os -std=c99 -Wall -Wextra -Werror
|
CFLAGS := -mmcu=$(MCU) -Os -std=c99 -Wall -Wextra -Werror
|
||||||
OCFLAGS := -j .text -j .data -O ihex
|
OCFLAGS := -j .text -j .data -O ihex
|
||||||
LDFLAGS := -mmcu=$(MCU)
|
LDFLAGS := -mmcu=$(MCU) -Wl,-u,vfprintf -lprintf_flt
|
||||||
LDLIBS :=
|
LDLIBS :=
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|||||||
45
docs/README
45
docs/README
@@ -1,49 +1,10 @@
|
|||||||
## Serial Interface
|
## Supported Commands
|
||||||
|
|
||||||
Instead of polling for input we use an interrupt vector
|
|
||||||
to write data to a rx ring buffer when it's available.
|
|
||||||
|
|
||||||
We parse commands using a state machine so we can deal
|
|
||||||
with small chunks of characters at a time and spit out
|
|
||||||
the parsed command when the transmission has finished.
|
|
||||||
|
|
||||||
NOTE: System clock frequency should be a multiple of
|
|
||||||
1.8432MHz for perfect USART. This means we would need
|
|
||||||
an external crystal oscillator.
|
|
||||||
|
|
||||||
### Supported Commands
|
|
||||||
|
|
||||||
- RUN
|
- RUN
|
||||||
- STOP
|
- STOP
|
||||||
- TEMP <num>
|
- TEMP <float>
|
||||||
- DEWP <num>
|
- DEWP <float>
|
||||||
|
|
||||||
Decimal point for numbers is currently not supported.
|
Decimal point for numbers is currently not supported.
|
||||||
Make sure you have disabled hardware flow control on
|
Make sure you have disabled hardware flow control on
|
||||||
the client side.
|
the client side.
|
||||||
|
|
||||||
### Web Interface Example
|
|
||||||
|
|
||||||
There is an example https and websocket server (written
|
|
||||||
in Python) which serves a simple Javascript shell that
|
|
||||||
controls your box. Its main purpose is to give you a
|
|
||||||
demonstration on how you can write a custom bridge or
|
|
||||||
interface.
|
|
||||||
|
|
||||||
Check out the directory "/opt/webgui" under the project
|
|
||||||
root. You may need to run the following command to load
|
|
||||||
optional submodules if you can't see the webgui folder:
|
|
||||||
|
|
||||||
$ git submodule update --init
|
|
||||||
|
|
||||||
First you need to generate a self signed certificate to
|
|
||||||
ensure SSL encryption. Run the following command in the
|
|
||||||
webgui root directory:
|
|
||||||
|
|
||||||
$ ./configure
|
|
||||||
|
|
||||||
To manually start the server you can use the Makefile
|
|
||||||
(but it's recommended to run the server automatically
|
|
||||||
as a daemon when the host machine starts):
|
|
||||||
|
|
||||||
$ make run
|
|
||||||
|
|||||||
194
src/bus/twi.c
194
src/bus/twi.c
@@ -4,18 +4,23 @@
|
|||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define TW_START 0x08
|
#define TW_START 0x08
|
||||||
#define TW_REP_START 0x10
|
#define TW_REP_START 0x10
|
||||||
#define TW_MT_SLA_ACK 0x18
|
#define TW_MT_SLA_ACK 0x18
|
||||||
#define TW_MT_SLA_NACK 0x20
|
#define TW_MT_SLA_NACK 0x20
|
||||||
#define TW_MT_DATA_ACK 0x28
|
#define TW_MT_DATA_ACK 0x28
|
||||||
#define TW_MR_SLA_ACK 0x40
|
#define TW_MR_SLA_ACK 0x40
|
||||||
#define TW_MR_SLA_NACK 0x48
|
#define TW_MR_SLA_NACK 0x48
|
||||||
#define TW_MR_DATA_ACK 0x50
|
#define TW_MR_DATA_ACK 0x50
|
||||||
|
|
||||||
// XXX: Handle interrupts in TWI_vect ISR? This may not
|
// XXX: Handle interrupts in TWI_vect ISR? This may not
|
||||||
// actually be much better than the blocking approach
|
// actually be much better than the blocking approach
|
||||||
|
|
||||||
|
static void TWI_AHT20_Reset(void);
|
||||||
|
static bool TWI_AHT20_IsCalibrated(void);
|
||||||
|
static void TWI_AHT20_Calibrate(void);
|
||||||
|
static void TWI_AHT20_Trigger(void);
|
||||||
|
|
||||||
int TWI_Init(void)
|
int TWI_Init(void)
|
||||||
{
|
{
|
||||||
// Set SCL bit rate to 400kHz
|
// Set SCL bit rate to 400kHz
|
||||||
@@ -139,11 +144,6 @@ int TWI_SetChannel(int channel)
|
|||||||
{
|
{
|
||||||
unsigned char crb;
|
unsigned char crb;
|
||||||
|
|
||||||
// assert(channel >= 0 && channel <= 3);
|
|
||||||
|
|
||||||
// PCA9546 I2C Multiplexer
|
|
||||||
// =======================
|
|
||||||
|
|
||||||
Info("Switching I2C channel to %d...", channel);
|
Info("Switching I2C channel to %d...", channel);
|
||||||
|
|
||||||
// Excerpts taken from the PCA9546A datasheet:
|
// Excerpts taken from the PCA9546A datasheet:
|
||||||
@@ -187,3 +187,171 @@ int TWI_SetChannel(int channel)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TWI_AHT20_Init(void)
|
||||||
|
{
|
||||||
|
TWI_AHT20_Reset();
|
||||||
|
|
||||||
|
// Wait 40ms after power-on
|
||||||
|
|
||||||
|
Sleep(40);
|
||||||
|
|
||||||
|
Info("Initializing AHT20 sensor...");
|
||||||
|
|
||||||
|
if (!TWI_AHT20_IsCalibrated()) {
|
||||||
|
TWI_AHT20_Calibrate();
|
||||||
|
if (!TWI_AHT20_IsCalibrated()) {
|
||||||
|
Info("Error: Calibration failed.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TWI_AHT20_Read(float *temp, float *rhum)
|
||||||
|
{
|
||||||
|
unsigned char data[6], crc;
|
||||||
|
unsigned long hraw, traw;
|
||||||
|
|
||||||
|
TWI_AHT20_Trigger();
|
||||||
|
|
||||||
|
TWI_Start(0x38, 1);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
|
||||||
|
data[0] = TWI_Read_ACK();
|
||||||
|
data[1] = TWI_Read_ACK();
|
||||||
|
data[2] = TWI_Read_ACK();
|
||||||
|
data[3] = TWI_Read_ACK();
|
||||||
|
data[4] = TWI_Read_ACK();
|
||||||
|
data[5] = TWI_Read_ACK();
|
||||||
|
crc = TWI_Read_NACK();
|
||||||
|
TWI_Stop();
|
||||||
|
|
||||||
|
// After receiving six bytes, the next byte is the
|
||||||
|
// CRC check data, the user can read it as needed,
|
||||||
|
// if the receiving end needs CRC check, then send
|
||||||
|
// it after receiving the sixth byte ACK response,
|
||||||
|
// otherwise NACK is sent out, CRC initial value is
|
||||||
|
// 0xFF. The CRC8 check polynomial is:
|
||||||
|
// CRC[7:0]= 1 + (x^4) + (x^5) + (x^8)
|
||||||
|
|
||||||
|
// TODO: Implement CRC8 check
|
||||||
|
UNUSED(crc);
|
||||||
|
|
||||||
|
hraw = (unsigned long) data[1] << 12;
|
||||||
|
hraw |= (unsigned short) data[2] << 4;
|
||||||
|
hraw |= data[3] >> 4;
|
||||||
|
|
||||||
|
traw = (unsigned long) (data[3] & 0x0F) << 16;
|
||||||
|
traw |= (unsigned short) data[4] << 8;
|
||||||
|
traw |= data[5];
|
||||||
|
|
||||||
|
*rhum = (float) hraw / 1048576.0f * 100.0f;
|
||||||
|
*temp = (float) traw / 1048576.0f * 200.0f - 50.0f;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TWI_AHT20_Reset(void)
|
||||||
|
{
|
||||||
|
// The command 0xBA is used to restart the sensor
|
||||||
|
// system without turning the power off and on
|
||||||
|
// again. After receiving this command, the sensor
|
||||||
|
// system begins to re-initialize and restore the
|
||||||
|
// default setting state. The time required for
|
||||||
|
// soft reset does not exceed 20 ms.
|
||||||
|
|
||||||
|
TWI_Start(0x38, 0);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
|
||||||
|
TWI_Write(0xBA);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Stop();
|
||||||
|
|
||||||
|
Sleep(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TWI_AHT20_IsCalibrated(void)
|
||||||
|
{
|
||||||
|
unsigned char status;
|
||||||
|
|
||||||
|
// Before reading the temperature and humidity
|
||||||
|
// values, first check whether the calibration
|
||||||
|
// enable bit Bit [3] of the status word is 1 (you
|
||||||
|
// can get a byte of status word by sending 0x71).
|
||||||
|
|
||||||
|
TWI_Start(0x38, 0);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
|
||||||
|
TWI_Write(0x71);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Stop();
|
||||||
|
|
||||||
|
TWI_Start(0x38, 1);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
|
||||||
|
status = TWI_Read_NACK();
|
||||||
|
TWI_Stop();
|
||||||
|
|
||||||
|
// Note: The calibration status check only needs to
|
||||||
|
// be checked at power-on. No operation is required
|
||||||
|
// during the normal acquisition process.
|
||||||
|
|
||||||
|
return status & BIT(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TWI_AHT20_Calibrate(void)
|
||||||
|
{
|
||||||
|
// To calibrate you need to send 0xBE command (for
|
||||||
|
// initialization), this command parameter has two
|
||||||
|
// bytes, the first byte is 0x08, the second byte
|
||||||
|
// is 0x00, and then wait for 10ms.
|
||||||
|
|
||||||
|
TWI_Start(0x38, 0);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
|
||||||
|
TWI_Write(0xBE);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Write(0x08);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Write(0x00);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TWI_AHT20_Trigger(void)
|
||||||
|
{
|
||||||
|
unsigned char status;
|
||||||
|
|
||||||
|
// Send the 0xAC command directly (trigger
|
||||||
|
// measurement). The parameter of this command has
|
||||||
|
// two bytes, the first byte is 0x33 and the second
|
||||||
|
// byte is 0x00.
|
||||||
|
|
||||||
|
TWI_Start(0x38, 0);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
|
||||||
|
TWI_Write(0xAC);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Write(0x33);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Write(0x00);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
TWI_Stop();
|
||||||
|
|
||||||
|
// Wait for 80ms to wait for the measurement to be
|
||||||
|
// completed. If the read status word Bit [7] is 0,
|
||||||
|
// it indicates that the measurement is completed,
|
||||||
|
// and six bytes can be read in a row. Otherwise
|
||||||
|
// continue to wait.
|
||||||
|
|
||||||
|
TWI_Start(0x38, 1);
|
||||||
|
TWI_Wait_ACK();
|
||||||
|
|
||||||
|
do {
|
||||||
|
Sleep(80);
|
||||||
|
status = TWI_Read_ACK();
|
||||||
|
} while ((status & BIT(7)) == 1);
|
||||||
|
TWI_Stop();
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,4 +15,7 @@ unsigned char TWI_Read_NACK(void);
|
|||||||
int TWI_Wait_ACK(void);
|
int TWI_Wait_ACK(void);
|
||||||
int TWI_Stop(void);
|
int TWI_Stop(void);
|
||||||
|
|
||||||
|
int TWI_AHT20_Init(void);
|
||||||
|
int TWI_AHT20_Read(float *temp, float *rhum);
|
||||||
|
|
||||||
#endif // MAD_CORE_BUS_TWI_H
|
#endif // MAD_CORE_BUS_TWI_H
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define MAD_CORE_COMMON_H
|
#define MAD_CORE_COMMON_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|||||||
176
src/main.c
176
src/main.c
@@ -6,137 +6,14 @@
|
|||||||
|
|
||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
short TWI_ReadAHT20(void)
|
// TODO: Get readings from ADC ADS1115 over TWI.
|
||||||
{
|
// TODO: Implement optional sensor value check with CRC8.
|
||||||
unsigned char data, crc;
|
// TODO: Either implement software PWM for the FAN03 timer
|
||||||
|
// (which will be quite complicated) or pick a chip with
|
||||||
|
// more than two 16-bit PWM outputs like the ATmega328PB.
|
||||||
|
// https://www.mikrocontroller.net/articles/Soft-PWM
|
||||||
|
|
||||||
// After the transmission is initiated, the first
|
int Init(void)
|
||||||
// byte of the subsequent I2C transmission includes
|
|
||||||
// the 7-bit I2C device address 0x38 and a SDA
|
|
||||||
// direction bit.
|
|
||||||
|
|
||||||
// Soft reset device.
|
|
||||||
|
|
||||||
TWI_Start(0x38, 0);
|
|
||||||
TWI_Write(0xBA);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
TWI_Stop();
|
|
||||||
|
|
||||||
// Wait 40ms after power-on.
|
|
||||||
|
|
||||||
Sleep(40);
|
|
||||||
|
|
||||||
// Before reading the temperature and humidity
|
|
||||||
// values, first check whether the calibration
|
|
||||||
// enable bit Bit [3] of the status word is 1 (you
|
|
||||||
// can get a byte of status word by sending 0x71).
|
|
||||||
|
|
||||||
TWI_Start(0x38, 0);
|
|
||||||
TWI_Write(0x71);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
|
|
||||||
data = TWI_Read_NACK();
|
|
||||||
TWI_Stop();
|
|
||||||
|
|
||||||
Info("Received calibration status %02X.", data);
|
|
||||||
|
|
||||||
// If not 1, need to send 0xBE command (for
|
|
||||||
// initialization), this command parameter has two
|
|
||||||
// bytes, the first byte is 0x08, the second byte
|
|
||||||
// is 0x00, and then wait for 10ms.
|
|
||||||
|
|
||||||
if (data & ~BIT(3)) {
|
|
||||||
Info("Requesting calibration...");
|
|
||||||
|
|
||||||
TWI_Start(0x38, 0);
|
|
||||||
TWI_Write(0xBE);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
TWI_Write(0x08);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
TWI_Write(0x00);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
|
|
||||||
Sleep(10);
|
|
||||||
data = TWI_Read_NACK();
|
|
||||||
TWI_Stop();
|
|
||||||
|
|
||||||
if (data & ~BIT(3)) {
|
|
||||||
Info("Error: Calibration failed.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the 0xAC command directly (trigger
|
|
||||||
// measurement). The parameter of this command has
|
|
||||||
// two bytes, the first byte is 0x33 and the second
|
|
||||||
// byte is 0x00.
|
|
||||||
|
|
||||||
Info("Triggering measurement...");
|
|
||||||
|
|
||||||
TWI_Start(0x38, 0);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
TWI_Write(0xAC);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
TWI_Write(0x33);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
TWI_Write(0x00);
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
TWI_Stop();
|
|
||||||
|
|
||||||
// Wait for 80ms to wait for the measurement to be
|
|
||||||
// completed. If the read status word Bit [7] is 0,
|
|
||||||
// it indicates that the measurement is completed,
|
|
||||||
// and six bytes can be read in a row. Otherwise
|
|
||||||
// continue to wait.
|
|
||||||
|
|
||||||
Info("Reading measurement...");
|
|
||||||
|
|
||||||
TWI_Start(0x38, 1); // Read
|
|
||||||
TWI_Wait_ACK();
|
|
||||||
|
|
||||||
do {
|
|
||||||
Sleep(80);
|
|
||||||
data = TWI_Read_ACK();
|
|
||||||
} while (data & BIT(7));
|
|
||||||
|
|
||||||
data = TWI_Read_ACK();
|
|
||||||
Info("Received data byte %02X.", data);
|
|
||||||
data = TWI_Read_ACK();
|
|
||||||
Info("Received data byte %02X.", data);
|
|
||||||
data = TWI_Read_ACK();
|
|
||||||
Info("Received data byte %02X.", data);
|
|
||||||
data = TWI_Read_ACK();
|
|
||||||
Info("Received data byte %02X.", data);
|
|
||||||
data = TWI_Read_ACK();
|
|
||||||
Info("Received data byte %02X.", data);
|
|
||||||
data = TWI_Read_ACK();
|
|
||||||
Info("Received data byte %02X.", data);
|
|
||||||
|
|
||||||
// After receiving six bytes, the next byte is the
|
|
||||||
// CRC check data, the user can read it as needed,
|
|
||||||
// if the receiving end needs CRC check, then send
|
|
||||||
// it after receiving the sixth byte ACK response,
|
|
||||||
// otherwise NACK is sent out, CRC initial value is
|
|
||||||
// 0xFF. The CRC8 check polynomial is:
|
|
||||||
// CRC[7:0]= 1 + (x^4) + (x^5) + (x^8)
|
|
||||||
|
|
||||||
crc = TWI_Read_NACK();
|
|
||||||
Info("Received CRC8 byte %02X.", crc);
|
|
||||||
|
|
||||||
// Calculate the temperature and humidity values.
|
|
||||||
// Note: The calibration status check in the first
|
|
||||||
// step only needs to be checked at power-on. No
|
|
||||||
// operation is required during the normal
|
|
||||||
// acquisition process.
|
|
||||||
|
|
||||||
// TODO: Calculate values
|
|
||||||
|
|
||||||
TWI_Stop();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
{
|
||||||
USART_Init();
|
USART_Init();
|
||||||
sei();
|
sei();
|
||||||
@@ -154,22 +31,41 @@ int main(void)
|
|||||||
PWM_SetValue(FAN02, 50);
|
PWM_SetValue(FAN02, 50);
|
||||||
PWM_SetValue(FAN03, 20);
|
PWM_SetValue(FAN03, 20);
|
||||||
|
|
||||||
// TODO: Set FAN03 timer frequency
|
TWI_SetChannel(AHT01);
|
||||||
// TODO: Implement ADS1115 and AHT20
|
TWI_AHT20_Init();
|
||||||
|
|
||||||
// TEM01, TEM02, TEM03
|
TWI_SetChannel(AHT02);
|
||||||
// HUM01, HUM02, HUM03
|
TWI_AHT20_Init();
|
||||||
|
|
||||||
// TWI_GetValue(TEM03)
|
TWI_SetChannel(AHT03);
|
||||||
// TWI_GetValue(HUM03)
|
TWI_AHT20_Init();
|
||||||
|
|
||||||
TWI_SetChannel(AHT01); // I2C Mux
|
return 0;
|
||||||
TWI_ReadAHT20(); // TEMP and RH
|
}
|
||||||
|
|
||||||
Info("Running idle loop...");
|
void Update(void)
|
||||||
|
{
|
||||||
|
float temp, rhum;
|
||||||
|
|
||||||
|
TWI_SetChannel(AHT01);
|
||||||
|
TWI_AHT20_Read(&temp, &rhum);
|
||||||
|
Info("TEMP=%.2f, RHUM=%.2f", temp, rhum);
|
||||||
|
|
||||||
|
TWI_SetChannel(AHT02);
|
||||||
|
TWI_AHT20_Read(&temp, &rhum);
|
||||||
|
Info("TEMP=%.2f, RHUM=%.2f", temp, rhum);
|
||||||
|
|
||||||
|
TWI_SetChannel(AHT03);
|
||||||
|
TWI_AHT20_Read(&temp, &rhum);
|
||||||
|
Info("TEMP=%.2f, RHUM=%.2f", temp, rhum);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Info("PING");
|
Update();
|
||||||
Sleep(1000);
|
Sleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user