168 lines
3.9 KiB
C
168 lines
3.9 KiB
C
#include "common.h"
|
|
#include "bus/usart.h"
|
|
#include "bus/mosfet.h"
|
|
#include "bus/pwm.h"
|
|
#include "bus/i2c.h"
|
|
|
|
#include <avr/interrupt.h>
|
|
|
|
// TODO: Implement primary state machine for update loop.
|
|
// TODO: Migrate to ATMega 1284P-PU for 2nd 16-bit timer.
|
|
// TODO: Keep persistent TEMP and DEWP targets in EEPROM.
|
|
// TODO: Check thermistor conversion results /w thermometer.
|
|
// TODO: Implement optional CRC8 sensor measurement check.
|
|
// TODO: Use 18.432MHz quarz crystal, burn required fuses.
|
|
// TODO: Write an improved command parser (low priority).
|
|
// TODO: Proper error handling and recovery (after testing).
|
|
|
|
enum state_e
|
|
{
|
|
S_IDLE,
|
|
S_HEAT_UP,
|
|
S_COOL_DOWN,
|
|
S_DEHUMIDIFY
|
|
};
|
|
|
|
static enum state_e state;
|
|
static float temp, temp_target;
|
|
static float dewp, dewp_target;
|
|
|
|
static int Init(void)
|
|
{
|
|
state = S_IDLE;
|
|
|
|
// MOSFETS control things like the heating element
|
|
// so they are the highest priority to initialize
|
|
// to a default state via MOS_Init().
|
|
|
|
MOS_Init();
|
|
|
|
// The serial interface is required for output
|
|
// functions like Info() and Error() and it uses
|
|
// IRQs, so we need to initialize it as soon as
|
|
// possible and make sure to enable interrupts.
|
|
|
|
USART_Init();
|
|
sei();
|
|
|
|
Info("Initializing...");
|
|
|
|
// The watchdog timer is clocked from a separate
|
|
// on-chip oscillator which runs at 1 MHz. Eight
|
|
// different clock cycle periods can be selected
|
|
// to determine the reset period. If the reset
|
|
// period expires, the chip resets and executes
|
|
// from the reset vector.
|
|
|
|
// The update loop must call WDT_Reset to reset
|
|
// the timer, kind of like a dead man's switch
|
|
// allowing us to detect infinite loops and any
|
|
// other error that halts execution.
|
|
|
|
if (WDT_HasTriggered())
|
|
Info("Unexpected system reset.");
|
|
|
|
WDT_Enable();
|
|
WDT_SetTimeoutFlag(WDT2000); // 2 seconds
|
|
|
|
// There is a possiblity to use interrupt signals
|
|
// for I2C communication but only as one large
|
|
// branching routine for the whole I2C system.
|
|
// The blocking approach used right now is fine.
|
|
|
|
I2C_Init();
|
|
PWM_Init();
|
|
|
|
MOS_Enable(MOS03); // Lights
|
|
MOS_Enable(MOS01); // Peltier
|
|
// MOS_Disable(MOS02); // Heating
|
|
|
|
// Only FAN01 and FAN02 are receiving the correct
|
|
// frequency (25 KHz) right now. The 16-bit timer on
|
|
// the ATMega32A has two outputs so it would require
|
|
// software PWM to have a variable frequency on PD7.
|
|
|
|
// A simple implementation will take up around 30-50
|
|
// percent of CPU time. Faster approaches are quite
|
|
// complicated so it might be worth it to switch to
|
|
// something like an ATmega328PB.
|
|
|
|
PWM_SetValue(FAN01, 20); // Fan Peltier Hot side
|
|
PWM_SetValue(FAN02, 50); // Fan Peltier Cold Side
|
|
// PWM_SetValue(FAN03, 20); // Fan Heating
|
|
|
|
// The I2C_SetChannel command changes the channel
|
|
// setting of the PCA9546 I2C multiplexer. Any
|
|
// command after it will be sent to the device
|
|
// listening on that channel.
|
|
|
|
I2C_SetChannel(AHT01);
|
|
I2C_AHT20_Init();
|
|
|
|
I2C_SetChannel(AHT02);
|
|
I2C_AHT20_Init();
|
|
|
|
I2C_SetChannel(AHT03);
|
|
I2C_AHT20_Init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void Update(void)
|
|
{
|
|
float t[3], rh[3];
|
|
float adct[3];
|
|
|
|
Info("Reading sensor values...");
|
|
|
|
I2C_SetChannel(AHT01);
|
|
I2C_AHT20_Read(&t[0], &rh[0]);
|
|
|
|
I2C_SetChannel(AHT02);
|
|
I2C_AHT20_Read(&t[1], &rh[1]);
|
|
|
|
I2C_SetChannel(AHT03);
|
|
I2C_AHT20_Read(&t[2], &rh[2]);
|
|
|
|
adct[0] = I2C_ADS1115_ReadThermistor(ADS01);
|
|
adct[1] = I2C_ADS1115_ReadThermistor(ADS02);
|
|
adct[2] = I2C_ADS1115_ReadThermistor(ADS03);
|
|
|
|
Info("T1=%.2fC, RH1=%.2f%%, ADCT1=%.2fC", t[0], rh[0], adct[0]);
|
|
Info("T2=%.2fC, RH2=%.2f%%, ADCT2=%.2fC", t[1], rh[1], adct[1]);
|
|
Info("T3=%.2fC, RH3=%.2f%%, ADCT3=%.2fC", t[2], rh[2], adct[2]);
|
|
|
|
// TODO: Implement state machine
|
|
// TODO: Handle serial commands
|
|
|
|
switch (state) {
|
|
case S_IDLE:
|
|
break;
|
|
case S_HEAT_UP:
|
|
break;
|
|
case S_COOL_DOWN:
|
|
break;
|
|
case S_DEHUMIDIFY:
|
|
break;
|
|
}
|
|
|
|
UNUSED(temp);
|
|
UNUSED(dewp);
|
|
UNUSED(temp_target);
|
|
UNUSED(dewp_target);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
Init();
|
|
|
|
for (;;) {
|
|
WDT_Reset();
|
|
Update();
|
|
WDT_Reset();
|
|
Sleep(1000);
|
|
}
|
|
|
|
return 0;
|
|
}
|