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