diff --git a/F3:F303/MLX90640-allsky/Readme.md b/F3:F303/MLX90640-allsky/Readme.md index 44789c8..3cb7d9f 100644 --- a/F3:F303/MLX90640-allsky/Readme.md +++ b/F3:F303/MLX90640-allsky/Readme.md @@ -41,3 +41,15 @@ Us - send string 's' to other interface ``` To call this help just print '?', 'h' or 'H' in terminal. + + +### Sensors + +| N | ID | Cardinal direction | +|---|------|--------------------| +| 0 | 0x10 | Zenith | +| 1 | 0x11 | East | +| 2 | 0x12 | South | +| 3 | 0x13 | West | +| 4 | 0x14 | North | +--------------------------------- diff --git a/F3:F303/MLX90640-allsky/allsky.bin b/F3:F303/MLX90640-allsky/allsky.bin index a207654..880dd86 100755 Binary files a/F3:F303/MLX90640-allsky/allsky.bin and b/F3:F303/MLX90640-allsky/allsky.bin differ diff --git a/F3:F303/MLX90640-allsky/commproto.cpp b/F3:F303/MLX90640-allsky/commproto.cpp index b6d8f5e..32e7326 100644 --- a/F3:F303/MLX90640-allsky/commproto.cpp +++ b/F3:F303/MLX90640-allsky/commproto.cpp @@ -7,6 +7,7 @@ extern "C"{ #include "adc.h" #include "commproto.h" #include "hardware.h" +#include "heater.h" #include "i2c.h" #include "mlxproc.h" #include "strfunc.h" @@ -61,41 +62,50 @@ uint8_t cartoon = 0; // Command list #define COMMAND_TABLE \ - COMMAND(help, "show this help") \ + DELIM("MLX commands") \ + COMMAND(acqtime, "show nth image aquisition time") \ COMMAND(ascii, "draw nth image in ASCII (n=0..4)") \ COMMAND(binary, "get nth image as text array of floats") \ + COMMAND(cartoon, "toggle cartoon mode") \ COMMAND(listids, "list active sensors IDs") \ - COMMAND(tempmap, "show temperature map of nth image") \ - COMMAND(acqtime, "show nth image aquisition time") \ - COMMAND(bmereinit, "reinit BME280") \ - COMMAND(environ, "get environment parameters") \ - COMMAND(state, "get MLX state") \ - COMMAND(reset, "reset MCU") \ - COMMAND(time, "print current Tms") \ - COMMAND(iicaddr, "get/set I2C address (non-shifted)") \ + COMMAND(mlxaddr, "get/set I2C address of sensor n (n=0..4)") \ COMMAND(mlxcont, "continue MLX") \ - COMMAND(iicspeed, "get/set I2C speed (0..4)") \ + COMMAND(mlxdump, "dump parameters for sensor n") \ COMMAND(mlxpause, "pause MLX") \ COMMAND(mlxstop, "stop MLX") \ - COMMAND(adc, "get n'th ADC values") \ + COMMAND(state, "get MLX state") \ + COMMAND(tempmap, "show temperature map of nth image") \ + DELIM("Environment/heaters") \ + COMMAND(autoheater, "get/set automatic heater flag (0/1)") \ + COMMAND(bmereinit, "reinit BME280") \ + COMMAND(clearheater,"clear temperature holding") \ + COMMAND(environ, "get environment parameters") \ COMMAND(ntc, "get n'th NTC temperatures") \ - COMMAND(cartoon, "toggle cartoon mode") \ - COMMAND(mlxdump, "dump MLX parameters for sensor n") \ - COMMAND(mlxaddr, "get/set MLX address of sensor n") \ - COMMAND(readreg, "read I2C register: readreg reg [= nwords]") \ - COMMAND(writedata, "write I2C data: writedata = val1 val2 ...") \ - COMMAND(iicscan, "scan I2C bus") \ + COMMAND(pwm, "get/set PWM for channel n (0..100%), (n=0,1 - heaters)") \ + COMMAND(setheater, "set heater holding temperature, int degC") \ + DELIM("Other commands") \ + COMMAND(adc, "get n'th ADC values") \ + COMMAND(dac, "get/set DAC value (MCU heater)") \ + COMMAND(help, "show this help") \ COMMAND(mcutemp, "get MCU temperature") \ COMMAND(mcuvdd, "get MCU Vdd") \ - COMMAND(dac, "get/set DAC value") \ - COMMAND(pwm, "get/set PWM for channel n (0..100%)") \ - COMMAND(sendstr, "send string to other interface: sendstr = text") - + COMMAND(reset, "reset MCU") \ + COMMAND(sendstr, "send string to other interface: sendstr = text") \ + COMMAND(time, "print current Tms") \ + DELIM("Raw I2C commands (MLX process should be stopped or paused)") \ + COMMAND(hwaddr, "set hardware I2C address (non-shifted) ") \ + COMMAND(iicaddr, "get/set I2C address for raw operations") \ + COMMAND(iicscan, "scan I2C bus") \ + COMMAND(iicspeed, "get/set I2C speed (0..4)") \ + COMMAND(readreg, "read I2C register: readreg reg [= nwords]") \ + COMMAND(writedata, "write I2C data: writedata = val1 val2 ...") // Command prototypes +#define DELIM(text) #define COMMAND(name, desc) static errcodes_t cmd_ ## name(const char*, char*); COMMAND_TABLE #undef COMMAND +#undef DELIM // descrtiptions for `help` typedef struct { @@ -104,9 +114,11 @@ typedef struct { } CmdInfo; static const CmdInfo cmdInfo[] = { +#define DELIM(text) { NULL, text }, #define COMMAND(name, desc) { #name, desc }, COMMAND_TABLE #undef COMMAND +#undef DELIM }; // Text descriptions for error codes @@ -171,11 +183,44 @@ static bool argsvals(char *args, int32_t *parno, int32_t *parval){ /************* List of proto functions for each command *************/ +static errcodes_t cmd_autoheater(const char* cmd, char* args){ + int32_t flag; + if(argsvals(args, NULL, &flag)){ + if(flag == 0) AutoHeater = 0; + else AutoHeater = 1; + } + CMDEQ(); + printu(AutoHeater); N(); + return ERR_AMOUNT; +} + +static errcodes_t cmd_clearheater(const char*, char*){ + clearThold(); + return ERR_OK; +} + +static errcodes_t cmd_setheater(const char* cmd, char* args){ + int32_t Tset; + if(argsvals(args, NULL, &Tset)){ + if(setThold(Tset)) return ERR_OK; + else return ERR_CANTRUN; + } + float T; + CMDEQ(); + if(!getThold(&T)) SEND("none\n"); + else{ + printfl(T, 1); N(); + } + return ERR_AMOUNT; +} + static errcodes_t cmd_help(const char*, char*){ SEND(REPOURL); for(size_t i = 0; i < sizeof(cmdInfo)/sizeof(cmdInfo[0]); ++i){ - SEND(cmdInfo[i].name); - SEND(" - "); + if(cmdInfo[i].name){ + SEND(cmdInfo[i].name); + SEND(" - "); + }else SEND(" "); SEND(cmdInfo[i].desc); SEND("\n"); } @@ -300,17 +345,14 @@ static errcodes_t cmd_state(const char* cmd, char*){ /********** I2C commands **********/ -static errcodes_t cmd_iicaddr(const char* cmd, char* args){ +static errcodes_t cmd_hwaddr(const char*, char* args){ int32_t addr; if(argsvals(args, NULL, &addr)){ if(addr < 0 || addr > 0x7f) return ERR_BADVAL; - I2Caddress = (uint8_t)(addr << 1); mlx_sethwaddr(I2Caddress, addr); - return ERR_AMOUNT; + return ERR_OK; } - // getter - CMDEQ(); printuhex(I2Caddress >> 1); N(); - return ERR_AMOUNT; + return ERR_BADVAL; } static errcodes_t cmd_mlxcont(const char*, char*){ @@ -414,16 +456,20 @@ static errcodes_t cmd_mlxdump(const char*, char* args){ return ERR_AMOUNT; } +static errcodes_t cmd_iicaddr(const char* cmd, char* args){ + int32_t addr = -1; + if(argsvals(args, NULL, &addr)){ + if(addr < 0 || addr > 0x7f) return ERR_BADVAL; + I2Caddress = addr << 1; + } + CMDEQ(); printuhex(I2Caddress>>1); N(); + return ERR_AMOUNT; +} + static errcodes_t cmd_mlxaddr(const char* cmd, char* args){ int32_t sensno = -1; - if(!args || !*args) { // without args: show global address - //CMDEQ(); printuhex(I2Caddress>>1); N(); - //return ERR_AMOUNT; - return ERR_BADPAR; - } const char *setter = splitargs(args, &sensno); if(sensno < 0 || sensno >= N_SENSORS) return ERR_BADPAR; - if(setter){ // setter: set current address uint32_t a; const char *nxt = getnum(setter, &a); @@ -574,9 +620,11 @@ const char *parse_cmd(int (*sendfun)(const char*), char *buf) { uint32_t h = hash(command); errcodes_t ecode = ERR_AMOUNT; switch (h){ +#define DELIM(text) #define COMMAND(name, desc) case hash(#name): ecode = cmd_##name(command, args); break; COMMAND_TABLE #undef COMMAND +#undef DELIM default: SEND("Unknown command, try 'help'\n"); } diff --git a/F3:F303/MLX90640-allsky/hardware.h b/F3:F303/MLX90640-allsky/hardware.h index 3b92191..8172127 100644 --- a/F3:F303/MLX90640-allsky/hardware.h +++ b/F3:F303/MLX90640-allsky/hardware.h @@ -30,29 +30,54 @@ #define SPI_CS_0() pin_clear(GPIOB, 1<<9) // interval of environment measurements, ms -#define ENV_MEAS_PERIOD (10000) +#define ENV_MEAS_PERIOD (10000) + +// heater check period, ms +#define HTR_CHECK_PERIOD (10000) + +// temperature hysteresis (+-hyst from holding value) +#define HOLDT_HYSTERESIS (5.f) +// environment for auto-heater +// maximal humidity +#define HUMIDITY_MAX (90.f) +// delta over dew point +#define TDEW_OVER_DELTA (7.f) +// defrosting temperature (when there's very cold) +#define TEMP_DEFROST (5.f) +// PWM starting value (up to reaching holding T) +#define PWM_START_VAL (100) +// middle value +#define PWM_MID_VAL (50) +// PWM holding value (up to setTemp+Thyst) +#define PWM_HOLD_VAL (10) // External heater PWM: TIM3_CH1 or TIM16_CH1 // Max PWM CCR1 value (->1) #define PWM_CCR_MAX (100) +// amount of heaters +#define HTR_AMOUNT (2) // PWM channels (start from 0 - CH1) +#define PWM_CH_HTR(x) (x) // propto external T (the higher - the brighter) #define PWM_CH_TEXT (2) // propto Tsky - Text (the higher - the brighter) #define PWM_CH_TSKY (3) #define PWM_CH_MAX (3) +// amount of T channels +#define NTC_AMOUNT (4) + typedef struct{ float T; // temperature, degC float Tdew; // dew point, degC float P; // pressure, Pa float H; // humidity, percents float Tsky; // mean Tsky, degC - // TODO: add here values of NTC on ADC channels 1/2 uint32_t Tmeas; // time of measurement } bme280_t; extern volatile uint32_t Tms; +extern uint8_t AutoHeater; void hw_setup(); int bme_init(); diff --git a/F3:F303/MLX90640-allsky/heater.c b/F3:F303/MLX90640-allsky/heater.c new file mode 100644 index 0000000..ce0149c --- /dev/null +++ b/F3:F303/MLX90640-allsky/heater.c @@ -0,0 +1,134 @@ +/* + * This file is part of the ir-allsky project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adc.h" +#include "heater.h" +#include "hardware.h" + +#ifdef EBUG +#include "strfunc.h" +#include "usb_dev.h" +#endif + +// turn on/off automatic cover heater in case of high humidity +uint8_t AutoHeater = 0; +static float setTemp = 0.f; +// try to hold setTemp +static uint8_t holdTemp = 0; +// no NTC found - errflag +static uint8_t noNTCfound = 0; +// last time env/hold was checked +static uint32_t lastTcheck = 0; + +uint8_t getThold(float *T){ + if(T) *T = setTemp; + return holdTemp; +} + +// set Thold in degrC +int setThold(int Th){ + if(Th < -10 || Th > 50) return FALSE; + if(noNTCfound) return FALSE; + setTemp = (float) Th; + holdTemp = 1; + return TRUE; +} + +static void setHeaters(uint8_t PWMval){ + for(uint8_t i = 0; i < HTR_AMOUNT; ++i) + setPWM(i, PWMval); +} + +void clearThold(){ + holdTemp = 0; + setHeaters(0); +} + +// calculate maximal and mean T +// return amount of working NTCs +static int CalcT(float *maxval, float *meanval){ + float max = -100.f, sum = 0.f; + int N; + for(uint8_t i = 0; i < NTC_AMOUNT; ++i){ + float Tcur = getNTCtemp(i); + if(Tcur < -100.f) continue; // bad value: short cirquit or break + ++N; + if(Tcur > max) max = Tcur; + sum += Tcur; + } + if(N == 0) return 0; // oops: nothing found + if(meanval) *meanval = sum / N; + if(maxval) *maxval = max; + return N; +} + +void heater_process(){ + static uint8_t overheating = 0; // if ==1 wait until Tset to run again + if(!AutoHeater && !holdTemp) return; + if(Tms - lastTcheck < HTR_CHECK_PERIOD) return; + float Tmax, Tmean; + if(0 == CalcT(&Tmax, &Tmean)){ // turn off heater and return + setHeaters(0); + DBG("NTC not found!"); + noNTCfound = 1; + return; + } +#ifdef EBUG + USB_sendstr("Tmax="); USB_sendstr(float2str(Tmax, 1)); + USB_sendstr("\nTmean="); USB_sendstr(float2str(Tmean, 1)); + newline(); +#endif + noNTCfound = 0; + lastTcheck = Tms; + if(AutoHeater) do{ // check weather conditions and set/reset holdTemp/setTemp + bme280_t env; + if(!get_environment(&env)) break; // can't set holding temperature when dunno env + if(env.H > HUMIDITY_MAX){ + float Tset = env.T + TDEW_OVER_DELTA + 0.5f; + if(Tset < TEMP_DEFROST) Tset = TEMP_DEFROST; // defrost + setThold((int)Tset); + }else{ // all OK: turn off heaters and clear holding + setHeaters(0); + holdTemp = 0; + } + }while(0); + if(holdTemp){ // try to hold `setTemp` +-5 degC + float Tdiff = Tmean - setTemp; // >0 in case of overheating + uint8_t PWMset = 0; + if(Tdiff < -HOLDT_HYSTERESIS){ + PWMset = PWM_START_VAL; + overheating = 0; + }else if(!overheating){ + if(Tdiff > HOLDT_HYSTERESIS){ + overheating = 1; + }else if(Tdiff > 0){ + PWMset = PWM_HOLD_VAL; + }else PWMset = PWM_MID_VAL; + }else if(Tdiff < 0.f){ // need slight heating + PWMset = PWM_HOLD_VAL; + } + // check max for overheating + if(Tmax - setTemp > HOLDT_HYSTERESIS && PWMset > PWM_HOLD_VAL) PWMset = PWM_HOLD_VAL; +#ifdef EBUG + USB_sendstr("Tdiff="); USB_sendstr(float2str(Tdiff, 1)); + USB_sendstr("\nPWMset="); USB_sendstr(u2str(PWMset)); + newline(); +#endif + setHeaters(PWMset); + } +} diff --git a/F3:F303/MLX90640-allsky/heater.h b/F3:F303/MLX90640-allsky/heater.h new file mode 100644 index 0000000..9eaec71 --- /dev/null +++ b/F3:F303/MLX90640-allsky/heater.h @@ -0,0 +1,26 @@ +/* + * This file is part of the ir-allsky project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +void heater_process(); +uint8_t getThold(float *T); +void clearThold(); +int setThold(int Th); diff --git a/F3:F303/MLX90640-allsky/ir-allsky.creator.user b/F3:F303/MLX90640-allsky/ir-allsky.creator.user index c32ec9c..2440c59 100644 --- a/F3:F303/MLX90640-allsky/ir-allsky.creator.user +++ b/F3:F303/MLX90640-allsky/ir-allsky.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/F3:F303/MLX90640-allsky/ir-allsky.files b/F3:F303/MLX90640-allsky/ir-allsky.files index fdf0783..04d62be 100644 --- a/F3:F303/MLX90640-allsky/ir-allsky.files +++ b/F3:F303/MLX90640-allsky/ir-allsky.files @@ -6,6 +6,8 @@ commproto.cpp commproto.h hardware.c hardware.h +heater.c +heater.h i2c.c i2c.h main.c diff --git a/F3:F303/MLX90640-allsky/main.c b/F3:F303/MLX90640-allsky/main.c index d83ae9a..4e6495a 100644 --- a/F3:F303/MLX90640-allsky/main.c +++ b/F3:F303/MLX90640-allsky/main.c @@ -18,6 +18,7 @@ #include "adc.h" #include "hardware.h" +#include "heater.h" #include "i2c.h" #include "mlxproc.h" #include "commproto.h" @@ -103,5 +104,6 @@ int main(void){ if(ans) usart_sendstr(ans); } bme_process(); + heater_process(); } } diff --git a/F3:F303/MLX90640-allsky/mlxproc.c b/F3:F303/MLX90640-allsky/mlxproc.c index 0e62161..7a95092 100644 --- a/F3:F303/MLX90640-allsky/mlxproc.c +++ b/F3:F303/MLX90640-allsky/mlxproc.c @@ -71,7 +71,7 @@ int mlx_setaddr(int n, uint8_t addr){ } uint8_t mlx_getaddr(int n){ if(n < 0 || n >= N_SENSORS) return 0; - return sens_addresses[n]; + return sens_addresses[n] >> 1; } // pause state machine and stop void mlx_pause(){