Measures the output power from a boiler system and transmits it to open energy monitor system
Dependencies: mbed RFM69 MSF_Time ds3231 TextLCD
Revision 0:17c0192cb3d7, committed 2022-01-07
- Comitter:
- dswood
- Date:
- Fri Jan 07 12:23:44 2022 +0000
- Commit message:
- Working but still a number of errors
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BME280_driver.lib Fri Jan 07 12:23:44 2022 +0000 @@ -0,0 +1,1 @@ +https://github.com/BoschSensortec/BME280_driver.git/#3d686a326565b9cadb8507f73d7576ec0cc891f2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MSF_Time.lib Fri Jan 07 12:23:44 2022 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/dswood/code/MSF_Time/#340305453f64
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RFM69.lib Fri Jan 07 12:23:44 2022 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/dswood/code/RFM69/#37f3683b3648
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TextLCD.lib Fri Jan 07 12:23:44 2022 +0000 @@ -0,0 +1,1 @@ +https://mbed.org/users/simon/code/TextLCD/#308d188a2d3a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ds3231.lib Fri Jan 07 12:23:44 2022 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/teams/Maxim-Integrated/code/ds3231/#11630748e2f2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Jan 07 12:23:44 2022 +0000 @@ -0,0 +1,989 @@ +// Solid Fuel Energy monitor +/* Monitor water temperature and flow +The energy flow is proportional to the flow of water multiplied by +the temperature difference between in and out. Energy flow per second will +give you output power. +*/ +#include "mbed.h" +#include "TextLCD.h" +#include "MSF_Time.h" +#include "RFM69.h" +#include "bme280.h" +#include "ds3231.h" +#include "string" +char Version[]="Version 1.01"; +Serial Mypc(USBTX, USBRX, 115200); //for debugging + +#define RF_freq RF69_433MHZ +#define Col 20 //display cols +#define Row 4 // display rows +TextLCD lcd(D10, D9, D6, D5, D4, D3, TextLCD::LCD20x4 ); // rs, e, d4-d7,Display type +//DigitalOut led1(LED1); // LED pin used for spi interface - it flashes on each access +#define My_scl D15 +#define My_sda D14 +Ds3231 rtc(My_sda, My_scl); +MSF_Time MSF(D2,0); +//AnalogIn adc_temp(ADC_TEMP); +//AnalogIn adc_vref(ADC_VREF); +//AnalogIn adc_vbat(ADC_VBAT); +AnalogIn adc_Water_Temp(A0); +AnalogIn adc_Water_Diff(A1); +AnalogIn adc_Room_Temp(A2); +//the size of the arrays holding the measured data +// TM is ten minutes worth of data +// H hourly data, D day, M Month, Y for year. +const int SizeOfTM=13,SizeOfH=14,SizeOfD=14,SizeOfW=7,SizeOfM=12,SizeOfY=12; +uint64_t TenMins[SizeOfTM],Hour[SizeOfH],Day[SizeOfD],Week[SizeOfW],Month[SizeOfM],Year[SizeOfY]; +//The memory location is in the store and read functions but an offset is needed +//so each array is sored in a unique location. +//Each element of data is 64 bit or 2 words or 8 bytes. So 8 offsets per item. Stored as bytes. +const int MemOffsetTM=0; +const int MemOffsetH=SizeOfTM*8; +const int MemOffsetD=MemOffsetH+(SizeOfH*8); +const int MemOffsetW=MemOffsetD+(SizeOfD*8); +const int MemOffsetM=MemOffsetW+(SizeOfW*8); +const int MemOffsetY=MemOffsetM+(SizeOfM*8); +struct TimeRecord { + int TM; // ten mins and the only reason I did not just use tm struct + int H; // hour + int D; // day (week is day divided by seven) + int M; // month + int Y; // year +}; +struct TimeRecord Current; +time_t MySeconds; +InterruptIn Flow_meter(D7); +unsigned int Flow_duration=0xffff; +Timer Flow_timer; // Measure time between flow pulses +Timer Time_Data_timer; // Measure the length of data pulses +const float Flow_Detect_Time=10.0; // must be shorter than timer overflow 30s +const float Display_time=1.0; //used to calc flow too +float TempS,PressureS,HumidityS,LongAvePressureS,ShortAvePressureS,AvePressureS; +float Flow,Power,Water_Temp,Room_Temp; +uint64_t TotalEnergyJ=0,LastEnergy=0; +int Press; +float Temp_Diff=0,Temperatur,Pressure,Humidity; +bool Flow_Detected=false; +Ticker No_Flow_Detect; +Ticker Display_Ticker; +void Flow_Stopped(void); +float Calc_Flow(int fd); +float Calc_Power(float Fl, float Td); +char Timebuff[20]; +char MyBuffer[80]; +bool IsTimeSet=false,RTCValid=false; +RFM69 MyRadio(D11,D12,D13,PC_5,PC_6); +bool Sensing=false; +const int nodeID_emonpwr = 7; +const int nodeID_emonenv = 19; // emonTx RFM12B node ID +const int networkGroup = 210; +const int PulsesPerLitre=288; + +I2C SensorI2C(My_sda,My_scl); //I2C sensor bme280 +struct bme280_dev MySensor; +int8_t rslt = BME280_OK; +struct bme280_data SensorData; +uint8_t SensorSettings; +int8_t SensorSetup(); +int8_t user_i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len); +int8_t user_i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len); +int8_t SensorReadAll(); +void user_delay_ms(uint32_t period); + +//DigitalOut GreenLED(LED1); +struct tm * NowTime; +typedef struct { + int16_t Power,Flow, TempOut,TempDiff; +} PowerPayloadTX; // create structure - a neat way of packaging data for RF comms +PowerPayloadTX emonPowerTX; +typedef struct { + int16_t temperature, pressure, humidity; +} EnviromentPayloadTX; +EnviromentPayloadTX emonEnvTX; +void send_power_rf_data(int ToAddress, int FromAddress); +bool Flag_send_power_rf_data=false; +void send_enviro_rf_data(int ToAddress, int FromAddress); +InterruptIn PageButton(PC_9); +Timeout DebounceTimer; +int PageNumber=0; +const int MaxPages=14; +bool Debouncing(); +bool DeBounce=false; +void PageControl(); +void Timed_out(); +void Page1(); +void Page2(); +void Page3(); +void Page4(); +void Page5(); +void Page6(); +void Page7(); +void Page8(); +void Page9(); +void Page10(); +void Page11(); +void Page12(); +void Page13(); +void Page14(); + +void Rotate(uint64_t Arr[], int size); +bool Set_RTC_Time(time_t epoch_time); +void FillTimeRecord(struct TimeRecord *TR); +bool FlagWriteDataToEEPROM=false; + +HAL_StatusTypeDef writeEEPROMByte(uint32_t address, uint8_t data); +uint8_t readEEPROMByte(uint32_t address) ; +void ConvertJouleTokWh(uint64_t Joule[], float kWh[], int size); +void ConvertJouleToKiloJoule(uint64_t Watts[], float kW[], int size); +void WriteDataToEEPROM(); +void ReadDataFromEEPROM(); +void WriteDataToEEPROM(uint64_t Target[], int size, uint32_t address); +void ReadDataFromEEPROM(uint64_t Target[], int size, uint32_t address); +struct ScreenSt { + char Memory[Col+1][Row+1]; + int PersistTime[Row+1]; + Ticker PersistTicker; +}; +bool DisplayChanged=false; +ScreenSt ScreenMemory; +void DisplayRefresh(); +void PersistDelay(); +void WriteToScreenMemory(char Buff[],int x,int y,int p=0); +void ClearScreen(); + +int8_t user_i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len) +{ + int i; + char addr[1],Buff[len]; + addr[0]= (char)reg_addr; + //MySerial.printf("Read ID %d: addr %d: len %d\n\r",dev_id,reg_addr,len); + int8_t rslt = 0; /* Return 0 for Success, non-zero for failure */ + + + /* + * The parameter dev_id can be used as a variable to store the I2C address of the device + */ + + /*SensorI2C + * Data on the bus should be like + * |------------+---------------------| + * | I2C action | Data | + * |------------+---------------------| + * | Start | - | + * | Write | (reg_addr) | + * | Stop | - | + * | Start | - | + * | Read | (reg_data[0]) | + * | Read | (....) | + * | Read | (reg_data[len - 1]) | + * | Stop | - | + * |------------+---------------------| + */ + + rslt=SensorI2C.write(dev_id,addr,1,true); + rslt+=SensorI2C.read(dev_id,Buff,len); + for (i=0; i<len; i++) reg_data[i]=(uint8_t)Buff[i]; + return rslt; +} +int8_t user_i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len) +{ + + int i; + char addr[1],Buff[len]; + addr[0]= (char)reg_addr; + for (i=0; i<len; i++) Buff[i]=(char)reg_data[i]; + int8_t rslt = 0; /* Return 0 for Success, non-zero for failure */ +//MySerial.printf("Write ID %d: addr %d: len %d\n\r",dev_id,reg_addr,len); + /* + * The parameter dev_id can be used as a variable to store the I2C address of the device + */ + + /* + * Data on the bus should be like + * |------------+---------------------| + * | I2C action | Data | + * |------------+---------------------| + * | Start | - | + * | Write | (reg_addr) | + * | Write | (reg_data[0]) | + * | Write | (....) | + * | Write | (reg_data[len - 1]) | + * | Stop | - | + * |------------+---------------------| + */ + rslt=SensorI2C.write(dev_id,addr,1,true); + rslt+=SensorI2C.write(dev_id,Buff,len); + return rslt; +} + +int8_t SensorSetup() +{ + uint8_t settings_sel; + int8_t rslt; + MySensor.dev_id = BME280_I2C_ADDR_PRIM<<1; + MySensor.intf = BME280_I2C_INTF; + MySensor.read = user_i2c_read; + MySensor.write = user_i2c_write; + MySensor.delay_ms = user_delay_ms; + rslt = bme280_init(&MySensor); + /* Recommended mode of operation: Indoor navigation */ + MySensor.settings.osr_h = BME280_OVERSAMPLING_1X; + MySensor.settings.osr_p = BME280_OVERSAMPLING_16X; + MySensor.settings.osr_t = BME280_OVERSAMPLING_2X; + MySensor.settings.filter = BME280_FILTER_COEFF_16; + MySensor.settings.standby_time = BME280_STANDBY_TIME_62_5_MS; + settings_sel = BME280_OSR_PRESS_SEL; + settings_sel |= BME280_OSR_TEMP_SEL; + settings_sel |= BME280_OSR_HUM_SEL; + settings_sel |= BME280_STANDBY_SEL; + settings_sel |= BME280_FILTER_SEL; + rslt = bme280_set_sensor_settings(settings_sel, &MySensor); + //Mypc.printf("result %d\n\r",rslt); + rslt += bme280_set_sensor_mode(BME280_NORMAL_MODE, &MySensor); + //Mypc.printf("result %d\n\r",rslt); + MySensor.delay_ms(70); // Should be ready after this. + return rslt; +} +int8_t SensorReadAll() +{ + int8_t rslt; + //MySerial.printf("SensorRealAll\n\r"); + rslt = bme280_get_sensor_data(BME280_ALL, &SensorData, &MySensor); + //MySerial.printf("result %d\n\r",rslt); + return rslt; +} +void user_delay_ms(uint32_t period) +{ + /* + * Return control or wait, + * for a period amount of milliseconds + */ + wait_ms(period); +} + + +void PersistDelay() +{ + int i; + for (i=0 ; i<Row; i++)if (ScreenMemory.PersistTime[i]>0)ScreenMemory.PersistTime[i]--; +} + +void WriteToScreenMemory(char Buff[],int x,int y,int p) +{ + if (strlen( Buff)==0)return; + if (x>Col-1) return; + if (y>Row-1)return; + int i; + for (i=0; i< strlen( Buff); i++) { + if ((ScreenMemory.PersistTime[y]==0)||(p>0))ScreenMemory.Memory[x][y]=Buff[i]; + if (x<Col-1) x++; + else { + y++; + x=0; + } + if (p>0) ScreenMemory.PersistTime[y]=p; + if (y>Row) return; + } + DisplayChanged=true; +} + +void DisplayRefresh() +{ + DisplayChanged=false; + lcd.locate(0,0); + int x,y; + for (y=0; y<Row; y++) { + for (x=0; x<Col; x++) { + lcd.putc(ScreenMemory.Memory[x][y]); + } + } +} + +void PageControl() +{ + Press++; + if (Debouncing()) return; // not the first press within the bounce period + PageNumber++; + if (PageNumber>(MaxPages-1)) PageNumber=0; + ClearScreen(); +} +void ClearScreen() +{ + sprintf(MyBuffer," "); + //12345678901234567890 + WriteToScreenMemory(MyBuffer,0,0); + WriteToScreenMemory(MyBuffer,0,1); + WriteToScreenMemory(MyBuffer,0,2); + WriteToScreenMemory(MyBuffer,0,3); //worlds most inelegant clear screen +} + +void Timed_out() +{ + DeBounce=false;// debouncing time is over +} + +bool Debouncing() +{ + if (DeBounce) return true; + DeBounce=true; + DebounceTimer.attach_us(&Timed_out,250000); //250ms period of debounce + return false; +} + + +void Flow_pulse() +{ + float Energy; + if (Flow_Detected) { //Flow was detected before + Flow_duration=Flow_timer.read_us(); //How long ago was that? + } + Flow_timer.reset(); + No_Flow_Detect.attach(&Flow_Stopped, Flow_Detect_Time);//Reset our watchdog timer + Flow_Detected=true; + Energy=165.0*adc_Water_Diff.read()*4026.4/PulsesPerLitre; // 4.0264 kJ/l @ 50C Constant volume + TotalEnergyJ+=(uint64_t)Energy; + +} +void Display()// Display and read the temps and calc power,energy +{ + //Mypc.printf("Starting display\n\r"); + uint64_t TempEnergy; + unsigned int Energy; + struct TimeRecord temp; + Temp_Diff=165*adc_Water_Diff.read(); //20mV per C + Water_Temp=165*adc_Water_Temp.read(); //20mV per C + Room_Temp=330*adc_Room_Temp.read(); //10mV per C + TempEnergy=TotalEnergyJ-LastEnergy; //Using a TempEnergy to force conversion to 32 bit after the subtraction + //Energy=1234;//Fixme delete me + LastEnergy=TotalEnergyJ; + Energy=TempEnergy; + + + if (Flow_Detected) { + Flow=Calc_Flow(Flow_duration); + Power=Energy/Display_time; // Power in watts equals energy per second + } else { + Flow=0; + Power=0; + } + emonPowerTX.Power=Power; + emonPowerTX.Flow=Flow*60; //flow in litres per minute --per second is always small and rounded to 0 + emonPowerTX.TempOut=Water_Temp*10; + emonPowerTX.TempDiff=Temp_Diff*10; + Flag_send_power_rf_data=true; + MySeconds=time(null); + //SizeOfTM=13,SizeOfH=14,SizeOfD=14,SizeOfW=7,SizeOfM=12,SizeOfY=12; + if (RTCValid) { + FillTimeRecord(&temp); + //Mypc.printf("%dm %dh %dd %dw %dm %dy\n\r",temp.TM,temp.H,temp.D,temp.D/7,temp.M,temp.Y); + if (int(Current.TM/10)!=int(temp.TM/10)) { + //Mypc.printf("Ten min %d %d\n\r",Current.TM,temp.TM); + Current.TM=temp.TM; + Rotate(TenMins,SizeOfTM); + if ((Current.TM==30) || (Current.TM==0)) { + FlagWriteDataToEEPROM=true; + Mypc.printf("stored data\n\r"); + } + if (Current.H!=temp.H) { + //Mypc.printf("Hour %d %d\n\r",Current.H,temp.H); + Rotate(Hour,SizeOfH); + Current.H=temp.H; + if (Current.D!=temp.D) { + //Mypc.printf("Day %d %d\n\r",Current.D,temp.D); + Rotate(Day,SizeOfD); + if (int(Current.D/7)!=int(temp.D/7)) { + //Mypc.printf("Week %d %d\n\r",Current.D/7,temp.D/7); + Rotate(Week,SizeOfW); + } + Current.D=temp.D; + if (Current.M!=temp.M) { + //Mypc.printf("Month %d %d\n\r",Current.M,temp.M); + Current.M=temp.M; + Rotate(Month,SizeOfM); + if (Current.Y!=temp.Y) { + //Mypc.printf("Year %d %d\n\r",Current.Y,temp.Y); + Current.Y=temp.Y; + Rotate(Year,SizeOfY); + } + } + } + } + } + } + TenMins[0]+=Energy; + Hour[0]+=Energy; + Day[0]+=Energy; + Week[0]+=Energy; + Month[0]+=Energy; + Year[0]+=Energy; + switch (PageNumber) { + case 0: + Page1(); + break; + case 1: + Page2(); + break; + case 2: + Page3(); + break; + case 3: + Page4(); + break; + case 4: + Page5(); + break; + case 5: + Page6(); + break; + case 6: + Page7(); + break; + case 7: + Page8(); + break; + case 8: + Page9(); + break; + case 9: + Page10(); + break; + case 10: + Page11(); + break; + case 11: + Page12(); + break; + case 12: + Page13(); + break; + case 13: + Page14(); + break; + } +} +void Page1() +{ + + sprintf(MyBuffer,"Flw=%3.2f Pwr=%4.0fW ",Flow*60,Power); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Water Temp=%3.1f%cC ", Water_Temp,223); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Water Diff=%3.2f%cC ", Temp_Diff,223); + WriteToScreenMemory(MyBuffer,0,2); + if (Sensing) sprintf(MyBuffer,"Rm Temp=%3.1f%cC %s", TempS,223,MSF.Valid()? "MSF":" "); + else sprintf(MyBuffer,"Sensor Fail %s",MSF.Valid()? "MSF":" "); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page2() +{ + strftime(Timebuff, 20, "%a %d %b %T", localtime(&MySeconds) ); + if (Sensing) sprintf(MyBuffer,"Room=%2.1f%cC ",TempS,223); + else sprintf(MyBuffer,"Sensor Fail "); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Humidity=%2.1f%% ",HumidityS); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Atm Pressure=%-4.1f ",PressureS); + WriteToScreenMemory(MyBuffer,0,2); + WriteToScreenMemory(Timebuff,0,3); +} + +void Page3() +{ + float kJ[SizeOfTM]; + ConvertJouleToKiloJoule(TenMins,kJ,SizeOfTM); + sprintf(MyBuffer,"Energy kJ in 10mins"); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"E0=%-6.1f,E1=%-7.1f",kJ[0],kJ[1]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"E2=%-6.1f,E3=%-7.1f",kJ[2],kJ[3]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"E6=%-6.1f,E5=%-7.1f",kJ[4],kJ[5]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page4() +{ + float kJ[SizeOfTM]; + ConvertJouleToKiloJoule(TenMins,kJ,SizeOfTM); + sprintf(MyBuffer,"kJ/10min E6=%-7.1f",kJ[6]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"E7=%-6.1f,E8=%-7.1f",kJ[7],kJ[8]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"E9=%-6.1f,E10=%-6.0f",kJ[9],kJ[10]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"E11=%-5.0f,E12=%-5.0f",kJ[11],kJ[12]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page5() +{ + float kWh[SizeOfH]; + ConvertJouleTokWh(Hour,kWh,SizeOfH); + sprintf(MyBuffer,"kWh/Hr Hr0=%-4.2f",kWh[0]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Hr1=%-5.2f,Hr2=%-5.2f",kWh[1],kWh[2]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Hr3=%-5.2f,Hr4=%-5.2f",kWh[3],kWh[4]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"Hr5=%-5.2f,Hr6=%-5.2f",kWh[5],kWh[6]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page6() +{ + //SizeOfTM=13,SizeOfH=14,SizeOfD=14,SizeOfW=7,SizeOfM=12,SizeOfY=12; + float kWh[SizeOfH]; + ConvertJouleTokWh(Hour,kWh,SizeOfH); + sprintf(MyBuffer,"Hr7=%-5.2f,Hr8=%-5.2f",kWh[7],kWh[8]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Hr9=%-5.2f,Hr10=%-5.2f",kWh[9],kWh[10]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Hr11=%-5.2f",kWh[11]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"Hr12=%-5.2f",kWh[12]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page7() +{ + float kWh[SizeOfD]; + ConvertJouleTokWh(Day,kWh,SizeOfD); + sprintf(MyBuffer,"kWh/Day Dy0=%-5.2f",kWh[0]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Dy1=%-5.2f,Dy2=%-5.2f",kWh[1],kWh[2]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Dy3=%-5.2f,Dy4=%-5.2f",kWh[3],kWh[4]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"Dy5=%-5.2f,Dy6=%-5.2f",kWh[5],kWh[6]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page8() +{ + float kWh[SizeOfD]; + ConvertJouleTokWh(Day,kWh,SizeOfD); + sprintf(MyBuffer,"kWh wk2 Dy0=%-5.2f",kWh[7]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Dy1=%-5.2f,Dy2=%-5.2f",kWh[8],kWh[9]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Dy3=%-5.2f,Dy4=%-5.2f",kWh[10],kWh[11]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"Dy5=%-5.2f,Dy6=%-5.2f",kWh[12],kWh[13]); + WriteToScreenMemory(MyBuffer,0,3); +} + +void Page9() +{ + float kWh[SizeOfW]; + ConvertJouleTokWh(Week,kWh,SizeOfW); + sprintf(MyBuffer,"kWh/wk wk0=%-5.1f",kWh[0]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"wk1=%-5.1f,wk2=%-5.1f",kWh[1],kWh[2]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"wk3=%-5.1f,wk4=%-5.1f",kWh[3],kWh[4]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"wk5=%-5.1f,wk6=%-5.1f",kWh[5],kWh[6]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page10() +{ + float kWh[SizeOfM]; + ConvertJouleTokWh(Month,kWh,SizeOfM); + sprintf(MyBuffer,"kWh/Mth M0=%-5.0f",kWh[0]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"M1=%-5.0f,M2=%-5.0f",kWh[1],kWh[2]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"M3=%-5.0f,M4=%-5.0f",kWh[3],kWh[4]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"M5=%-5.0f,M6=%-5.0f",kWh[5],kWh[6]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page11() +{ + float kWh[SizeOfM]; + ConvertJouleTokWh(Month,kWh,SizeOfM); + sprintf(MyBuffer,"kWh/Mth M7=%-5.0f",kWh[7]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"M8=%-5.0f,M9=%-5.0f",kWh[8],kWh[9]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"kWh/M10=%-5.0f",kWh[10]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"kWh/M11=%-5.0f",kWh[11]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page12() +{ + float kWh[SizeOfY]; + ConvertJouleTokWh(Year,kWh,SizeOfY); + sprintf(MyBuffer,"kWh/Yr Y0=%-5.0f",kWh[0]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Y1=%-5.0f,Y2=%-5.0f",kWh[1],kWh[2]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Y3=%-5.0f,Y4=%-5.0f",kWh[3],kWh[4]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"Y5=%-5.0f,Y6=%-5.0f",kWh[5],kWh[6]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page13() +{ + float kWh[SizeOfY]; + ConvertJouleTokWh(Year,kWh,SizeOfY); + sprintf(MyBuffer,"kWh/Yr Y7=%-5.0f",kWh[7]); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Y8=%-5.0f,Y9=%-5.0f",kWh[8],kWh[9]); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Y10=%-5.0f",kWh[10]); + WriteToScreenMemory(MyBuffer,0,2); + sprintf(MyBuffer,"Y11=%-5.0f",kWh[11]); + WriteToScreenMemory(MyBuffer,0,3); +} +void Page14() +{ + strftime(Timebuff, 20, "%a %d %b %T", localtime(&MySeconds) ); + sprintf(MyBuffer,"Atm Pressure=%f-4.1",PressureS); + WriteToScreenMemory(MyBuffer,0,0); + sprintf(MyBuffer,"Shrt term rise=%f3.3",AvePressureS-ShortAvePressureS); + WriteToScreenMemory(MyBuffer,0,1); + sprintf(MyBuffer,"Lng term rise=%f3.3",ShortAvePressureS-LongAvePressureS); + WriteToScreenMemory(MyBuffer,0,2); + //WriteToScreenMemory(Timebuff,0,3); + + WriteToScreenMemory(Version,0,3); +} + +void Rotate(uint64_t Arr[],int size) +{ + for (int i=size-1; i>0 ; i--) { + Arr[i]=Arr[i-1]; + } + Arr[0]=0; +} +void Flow_Stopped() +{ + No_Flow_Detect.detach(); + Flow_Detected=false; +} + +float Calc_Flow(int fd) +{ + //Calculate flow from time in microseconds between pulses +//288 pulses per litre + if (fd==0) { //infinite flow WOW + return 123456; + } else { + return (1000000/(fd*PulsesPerLitre*Display_time)); //Flow in litres per second + } +} + +void send_power_rf_data(int ToAddress, int FromAddress) +{ + MyRadio.sleep(false); //wakeup + MyRadio.setNodeID(FromAddress); + int i = 0; + while (!MyRadio.canSend() && i<10) { + MyRadio.receiveDone(); + i++; + } + MyRadio.send(ToAddress, &emonPowerTX, sizeof emonPowerTX); + MyRadio.sleep(true); + +} + +void send_enviro_rf_data(int ToAddress, int FromAddress) +{ + MyRadio.sleep(false); //wakeup + MyRadio.setNodeID(FromAddress); + int i = 0; + while (!MyRadio.canSend() && i<10) { + MyRadio.receiveDone(); + i++; + } + MyRadio.send(ToAddress, &emonEnvTX, sizeof emonEnvTX); + MyRadio.sleep(true); + +} +bool Set_RTC_Time(time_t epoch_time) +{ + //Taken from here https://os.mbed.com/users/brianclaus/code/ds3231/file/b87f3e7258bb/ds3231.cpp/ + // Original code by Brian Claus + // Many thanks for sharing. + bool success = false; + //system vars + struct tm * sys_time; + //RTC vars + ds3231_time_t rtc_time = {0,0,0,0,0}; + ds3231_calendar_t rtc_calendar = {0,0,0,0}; + Mypc.printf("epoch set to-%d\n\r\n",epoch_time); + sys_time = localtime(&epoch_time); + set_time(epoch_time); + //localtime comes back as 24hour + + rtc_time.mode = 0; + rtc_time.hours = sys_time->tm_hour; + rtc_time.minutes = sys_time->tm_min; + rtc_time.seconds = sys_time->tm_sec; + + rtc_calendar.day = sys_time->tm_wday + 1; + rtc_calendar.date = sys_time->tm_mday; + rtc_calendar.month = sys_time->tm_mon + 1; + rtc_calendar.year = sys_time->tm_year - 100; + + success = 0==rtc.set_calendar(rtc_calendar); + success &= 0==rtc.set_time(rtc_time); + wait_us(250); + + return success; +} + +void FillTimeRecord(struct TimeRecord *TR) +{ + + time_t secs; + struct tm *CT; //current time + secs=time(null); + CT=localtime(&secs); + TR->TM=CT->tm_min; + TR->H=CT->tm_hour; + TR->D=CT->tm_yday; + TR->M=CT->tm_mon; + TR->Y=CT->tm_year; +} +void ConvertJouleTokWh(uint64_t Joule[], float kWh[], int size) +{ + // Function to convert Joules to kWh + // A lot of energy being transfered. Will this function get hot? + float a; + int i; + for (i=0; i<size; i++) { + a=Joule[i]; + kWh[i]=a/3600000; + } +} +void ConvertJouleToKiloJoule(uint64_t Joule[], float kJ[], int size) +{ + float a; + int i; + for (i=0; i<size; i++) { + a=Joule[i]; + kJ[i]=a/1000; + } +} +void ReadDataFromEEPROM(uint64_t Target[], int size, uint32_t address) +{ + int count; + int data; + address = address + 0x08080000; + data = *((uint32_t *)address);//high 4 bytes + Target[0] = data; + //Mypc.printf("Address %d high data %d\n\r",address,data); //fixme + Target[0] = Target[0]<<32; + address+=4; + data = *((uint32_t *)address);//low 4 bytes + //Mypc.printf("Address %d low data %d\n\r",address,data); //fixme + Target[0] |= data; + address+=4; + for (count=1; count<size; count++) { + data = *((uint32_t *)address); + //Mypc.printf("Address %d high data %d\n\r",address,data); //fixme + Target[count] =data; + Target[count] = Target[count]<<32; + address+=4; + data = *((uint32_t *)address); + Target[count] |= data; + //Mypc.printf("Address %d low data %d\n\r",address,data); //fixme + address+=4; + } +} +void WriteDataToEEPROM(uint64_t Target[], int size, uint32_t address) +{ + int count; + uint32_t data; + address = address + 0x08080000; + HAL_FLASHEx_DATAEEPROM_Unlock(); + data= 0xFFFFFFFF&(Target[0]>>32);//high + // HAL_FLASHEx_DATAEEPROM_ProgramDoubleWord(address , Target[0]); + //data=0x000000A0;//fixme + HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, address, data); + //Mypc.printf("Address %d high data %d\n\r",address,data); //fixme + address+=4; + data= 0xFFFFFFFF&Target[0];//low + //data= 0x000000A0;//fixme + HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, address, data); + //Mypc.printf("Address %d low data %d\n\r",address,data); //fixme + address+=4; + for (count=1; count<size; count++) { + data= 0xFFFFFFFF&(Target[count]>>32);//high + HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, address, data); + //Mypc.printf("Address %d high data %d\n\r",address,data); //fixme + address+=4; + data= 0xFFFFFFFF&Target[count];//low + HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, address, data); + //Mypc.printf("Address %d low data %d\n\r",address,data); //fixme + address+=4; + } + HAL_FLASHEx_DATAEEPROM_Lock(); +} +void ReadDataFromEEPROM() +{ + ReadDataFromEEPROM(TenMins,SizeOfTM,MemOffsetTM); + ReadDataFromEEPROM(Hour,SizeOfH,MemOffsetH); + ReadDataFromEEPROM(Day,SizeOfD,MemOffsetD); + ReadDataFromEEPROM(Week,SizeOfW,MemOffsetW); + ReadDataFromEEPROM(Month,SizeOfM,MemOffsetM); + ReadDataFromEEPROM(Year,SizeOfY,MemOffsetY); +} +void WriteDataToEEPROM() +{ + WriteDataToEEPROM(TenMins,SizeOfTM,MemOffsetTM); + WriteDataToEEPROM(Hour,SizeOfH,MemOffsetH); + WriteDataToEEPROM(Day,SizeOfD,MemOffsetD); + WriteDataToEEPROM(Week,SizeOfW,MemOffsetW); + WriteDataToEEPROM(Month,SizeOfM,MemOffsetM); + WriteDataToEEPROM(Year,SizeOfY,MemOffsetY); + +} +void InitSensor() +{ + DigitalInOut pin_sda(My_sda, PIN_INPUT, PullNone, 1); + DigitalInOut pin_scl_in(My_scl, PIN_INPUT, PullNone, 1); + // Read and verify if recovery is required + if (pin_scl_in == 1) { + if (pin_sda == 1) { + // Return successfuly as SDA and SCL is high + if (SensorSetup()==0) Sensing=true; + else Sensing=false; + return; + } + } else { + // Return as SCL is low and no access to become master. + Mypc.printf("I2c clock is being held low. I2C is cripled\n\r"); + return ; + } + DigitalInOut pin_scl_out(My_scl, PIN_OUTPUT, PullNone, 1); + Mypc.printf("I2C busy. Attempt to clock until it has been freed"); + int count = 40; + while ( (!pin_sda) && (count > 0)) { + count--; + pin_scl_out=!pin_scl_out; + wait_us(5); + } + if (count==0)Mypc.printf("Clocking has not worked"); + if (SensorSetup()==0) Sensing=true; + else Sensing=false; + +} +int main() +{ + time_t secs; + Mypc.baud(115200); + Mypc.printf("Starting main\n\r"); + ClearScreen(); + // Uncomment WriteDataToEEPROM to zero data fixme + //WriteDataToEEPROM(); + //Mypc.printf("Stored zeroed data\n\r\n\r"); + if(SensorSetup()==0) Sensing=true;// This setup taken from Bosch sensor github + else Sensing=false; + char buffer[8]; + Flow_meter.fall(&Flow_pulse); + Display_Ticker.attach(&Display,Display_time); + Flow_timer.start(); + PageButton.fall(&PageControl); + PageButton.mode(PullUp); + bool worked=false; + int counter; + ds3231_cntl_stat_t rtc_control_status = {0,0}; + rtc.set_cntl_stat_reg(rtc_control_status); + wait_us(250); + while (!worked) { + worked=MyRadio.initialize(RF_freq,nodeID_emonpwr,networkGroup); + } + Mypc.printf("Radio up\n\r"); + lcd.printf("Radio up "); + MyRadio.sleep(true); + ReadDataFromEEPROM(); + + Mypc.printf("Starting sensor\n\r"); + lcd.printf("Starting sensor "); + InitSensor(); + SensorReadAll(); + TempS=SensorData.temperature; + PressureS=SensorData.pressure/100; + HumidityS=SensorData.humidity; + LongAvePressureS=PressureS; //init value + ShortAvePressureS=PressureS; + AvePressureS=PressureS; + if ((PressureS>825.0) && (PressureS<1300.0))Sensing=true; + else Sensing=false; + Mypc.printf("%f- %f- %f \n\r",TempS,PressureS,HumidityS); + lcd.printf("%f- %f- %f ",TempS,PressureS,HumidityS); + + wait(1); + //Set_RTC_Time(1533992221); + ClearScreen(); + Mypc.printf("Starting loop\n\r"); + while (true) { + //Mypc.printf("Tick\n\r"); + /* RTCValid simply check that the time is not old and fairly recent + so thr RTC has been set in the past + IsTimeSet is true is MSF has been received and was put into the RTC + */ + if (!RTCValid) { + secs=rtc.get_epoch(); + wait_us(250); + if (secs>1572611760) { + set_time(secs); + RTCValid=true; // Real time clock has a real time in it + FillTimeRecord(&Current); + Mypc.printf("%dm %dh %dd %dw %dm %dy\n\r",Current.TM,Current.H,Current.D,Current.D/7,Current.M,Current.Y); + } + } + if ((!IsTimeSet)&&(MSF.Valid())) { + secs=MSF.Read(); + if (secs>1572611760) { + IsTimeSet=Set_RTC_Time(secs); //Set external RTC + internal rtc + // I see this is done by Set_RTC_Time //set_time(secs); //Set internal RTC + Mypc.printf("Time set %d\n\r",secs); + RTCValid=true; // Real time clock has a real time in it + FillTimeRecord(&Current); + Mypc.printf("%dm %dh %dd %dw %dm %dy\n\r",Current.TM,Current.H,Current.D,Current.D/7,Current.M,Current.Y); + } else { + RTCValid=false; // Time is not right + IsTimeSet=false; + Mypc.printf("Time UNset\n\r"); + } + } + secs=time(null);// Read time and set if 02:10 from msf time + strftime(buffer,8,"%T",localtime(&secs)); + if (strncmp(buffer,"02:10:00",8)==0) { //set the time each day + //printf("compare %s",buffer); + wait(1); // wait one second to stop this being called twice Assumes RTC clock is not that bad + IsTimeSet=false; + } + if (SensorReadAll()==0) { + Sensing=true; + TempS=SensorData.temperature; + PressureS=SensorData.pressure/100; + HumidityS=SensorData.humidity; + AvePressureS=(PressureS+AvePressureS)/2; + ShortAvePressureS=(ShortAvePressureS*0.9)+(AvePressureS*0.1); + LongAvePressureS=(LongAvePressureS*0.99)+(ShortAvePressureS*0.01); + } else InitSensor(); + +//Mypc.printf("tick\n\r"); + for (counter=1 ; counter<111; counter++) {//delay + display update if necessary + wait_ms(10); + if (DisplayChanged)DisplayRefresh(); + if (Flag_send_power_rf_data) { + Flag_send_power_rf_data=false; + send_power_rf_data(0,nodeID_emonpwr); + wait_ms(25);// Delay between transmissions - modems are slow + } + if (FlagWriteDataToEEPROM) { //Do we update eeprom? + FlagWriteDataToEEPROM=false; + WriteDataToEEPROM(); + } + } + if (Sensing) { + emonEnvTX.temperature=10*TempS;// times ten so we can divide by 10 at the other end and get 1 decimal place + emonEnvTX.pressure=10*PressureS; + emonEnvTX.humidity=10*HumidityS; + send_enviro_rf_data(0, nodeID_emonenv); + } + //Mypc.printf("Long=%f1.3 Short=%f1.3 \n\r",LongAvePressureS,ShortAvePressureS); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Jan 07 12:23:44 2022 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400 \ No newline at end of file