#include "common.h" #include "bus/usart.h" #include "bus/mosfet.h" #include "bus/pwm.h" #include "bus/i2c.h" #include // 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; }