I have a setup where power consumption is very important, especially in the winter months. Solar panels are not feasible due to low/no light.
The setup is a CR300 with a COM111 3G modem connected through RS232 port and SW12. Attached to the logger is an SDI12 sensor, which is powered by a separate battery (i.e. not through the logger or the same power source).
The SDI12 sensor is scanned at a high frequency (15s), due to one of the measured variable (velocity) has a high temporal variability that we want to capture, and the system as a whole has a fast response time (therefore also logging average values every 5 minutes). Every 6 hours the logger turns on the modem and sends data via HTTP post. To do so it reads the unsent data from the main table, constructs a string (datap) and sends the data via an HTTPPost() call to a datahost. The modem connectivity has some delays and retries, as it often fails to connect. This might be due to bad code on my part on how the modem and PPP is started - so any input on improving this would be greatly appreciated (the modem actually has a connection and IP - this is confirmed by connecting directly to the modem with USB cable, but the PPP dialing fails so the logger does not get an IP).
The modem currently stays on for 10 minutes every time it turns on - this is to leave time for us to connect to it manually in case of need to reprogram or download the datatables via loggernet. Inbetween this the PPP and SW12 is off (to save power) - are there other things I can turn off in order to reduce power consumption?
In general I'm looking for ways to improve the program to optimize power consumption without the obvious of reducing scan rate (15s) or data transmission rate (6 hours). Also memory usuage are important, as the "datap" string can grow very large if the logger fails to connect for a few cycles. Is there a way to check the maximum size I can preallocate to "datap" without running into risks of out of memory errors?
As you can probably see from the code I don't have a lot of experience in CRBasic, and might have overcomplicated things abit. I would really appreciate all kind of feeback to improve this program!
'CR300 Series ' Declare constants Const NTP_Server = "pool.ntp.org" Const UTC_Offset = +2 Const N = 60 ' Station Latitude Const E = 18 ' Station Longitude Const Al = 10 ' Station Elevation above sea-level ' Declare variables for data pushing Public sc_site_id As String * 12 Public http_header As String * 10 Public http_post_response As String * 200 Public flag_return As String * 20 Public http_response_str_split(3) As String * 50 Public http_response_str_error_split(2) As String * 20 Public http_response_str_reporting_split(4) As String * 20 Public http_post_tx Public i As Long ' Index counter Public timerecordsend(2) As String * 19 Public Rec As Long Public LRecSent As Long = -1 Public LRecSent_tmp As Long = -1 Public RecBack As Long Const GRDataMaxLen = 60 Public GRData As String * GRDataMaxLen Public headerp As String * 50 Public datap As String * 7000 Public max_rows_to_send = 120 ' Number of data rows that will fit into datap length Public ind_last_row_to_send As Long = 1 Public ReportingInterval As Long ' Modem connection Const Modemport = COMRS232 Const Modembaud = 115200 Public timetogooff As Boolean Public retried_http_post As Boolean ' Power management Public SW12State As Boolean Public P3Open As String * 15 Public P3Close As String * 15 'Declare Variables and Units Public BattV Public PTemp_C Public SDI12(4) Public Counter_avg Alias SDI12(1)=Depth Alias SDI12(2)=Velocity Alias SDI12(3)=Temperature Alias SDI12(4)=Battery_SF Alias timerecordsend(1)=TimeRecordSendStr Units BattV=Volts Units PTemp_C=Deg C Units Depth=m Units Velocity=m/s Units Temperature=Deg C Units Battery_SF=Volts Units Counter_avg=Count ' Define Data Tables DataTable(Table1,True,-1) DataInterval(0,5,Min,10) Average(1,Depth,FP2,Depth = NaN) Average(1,Velocity,FP2,Velocity = NaN) Average(1,Temperature,FP2,Temperature = NaN) Sample(1,Battery_SF,FP2) Totalize(1, Counter_avg, FP2, False) Sample(1,BattV, FP2) EndTable DataTable(Table2,True,-1) DataInterval(0,1440,Min,10) Minimum(1,BattV,FP2,False,False) Sample(1,PTemp_C,FP2) EndTable DataTable (ComsLog,True,-1) Sample (1,TimeRecordSendStr,String) Sample (1,Rec,FP2) Sample (1,LRecSent,FP2) Sample (1,RecBack,FP2) Sample (1,flag_return, String) Sample (1,retried_http_post, Boolean) Sample (1,ind_last_row_to_send, Long) EndTable 'Main Program BeginProg ' On boot Counter_avg = 1 sc_site_id = "CR300_"+Status.SerialNumber ' Set a unique site ID based on the logger model and serial number flag_return="false" ' Default to no transmission error http_header = "" timetogooff=false ReportingInterval = 60*60*6 ' 6 hours in seconds retried_http_post = false 'set default to false for retried sending data flag. ind_last_row_to_send = 1 ' Last data row to send, as counted backwards from last record in table ' Sync Clock on boot, need to connect P3Open = PPPOpen 'Start PPP Delay(1,3,sec) SW12State=True SW12(SW12State) Delay(1,30,sec) 'Allow 30 seconds for the modem to power on and connect NetworkTimeProtocol (NTP_Server,UTC_Offset*3600,1000) Delay(1,10,sec) ' Turn off after 10 more seconds SerialOpen (Modemport,Modembaud,0,0,100) SerialOut (Modemport,"+++","OK"+CHR(13),1,150) SerialOut (Modemport,"AT+CFUN=0"+CHR(13),"OK"+CHR(13),1,300) 'Delay to allow deregistration Delay (1,3,sec) SerialClose(Modemport) P3Close = PPPClose SW12State=False SW12(SW12State) 'Main Scan Scan(15,Sec,1,0) 'Default CR300 Datalogger Battery Voltage measurement 'BattV' Battery(BattV) 'Default CR300 Datalogger Processor Temperature measurement 'PTemp_C' PanelTemp(PTemp_C,50) SDI12Recorder(SDI12(),C1,"0","M!",1,0,-1) If SDI12(1)=NaN Then Move(SDI12(),4,NaN,1) Counter_avg = 0 Else Counter_avg = 1 ' Make floating point in expected magnitude for Temp and Battery ' These two lines can be skipped if they consume alot of power SDI12(3) = SDI12(3) * 0.01 SDI12(4) = SDI12(4) * 0.01 EndIf 'Call Data Tables and Store Data CallTable Table1 CallTable Table2 NextScan ' Slow Sequence for data transmission SlowSequence ' Turn on modem 'Run once a (1) minute so can have one minute resolution of timing ' Can be set less often to improve battery???? Scan (1,Min,3,0) 'Every time ReportingInterval happens turn on the modem If TimeIntoInterval (0,ReportingInterval,sec) Then P3Open = PPPOpen 'Start PPP Delay(1,3,sec) SW12State=True SW12(SW12State) Delay(1,5,sec) 'Allow 5 seconds for the modem to power on EndIf ' Tries to turn of modem after 5 minutes if the comport is not active ' Else turn it off anyway after 10 minutes If TimeIntoInterval (300,ReportingInterval,sec) Then timetogooff=true If (timetogooff AND (NOT ComPortIsActive(ComRS232))) OR TimeIntoInterval(600,ReportingInterval,sec) Then SerialOpen (Modemport,Modembaud,0,0,100) SerialOut (Modemport,"+++","OK"+CHR(13),1,150) SerialOut (Modemport,"AT+CFUN=0"+CHR(13),"OK"+CHR(13),1,300) 'Delay to allow deregistration Delay (1,3,sec) SerialClose(Modemport) P3Close = PPPClose 'Set SW12 port to off SW12State=False SW12(SW12State) timetogooff=false EndIf ' Construct and post the data If TimeIntoInterval (0,ReportingInterval,sec) Then Delay(1,90,sec) '1.5 minute after modem and PPP is on to increase chance of connectivity retried_http_post = false ' reset retried flag Rec = Table1.Record 'get number of last record in table If Rec < LRecSent Then LRecSent = -1 'What if LRecSent is preserved but datatable has been reset? Reset LRecSent. RecBack = Rec - LRecSent 'determine how much need to catch up ' Check if RecBack is too long, if its too Long reduce it to the max limit If RecBack > max_rows_to_send Then ind_last_row_to_send = RecBack - max_rows_to_send RecBack = max_rows_to_send Else ind_last_row_to_send = 1 'RecBack stays the same EndIf If RecBack > 0 Then 'If recback is greater than zero then we have records to send flag_return = "true" ' default to error state unless transmission succeeds below SplitStr(timerecordsend(), Public.Timestamp(4,1), ".",2,7) headerp =sc_site_id+","+timerecordsend(1)+","+Table1.Record(1,1)+","+N+","+E+","+Al+CHR(13)+CHR(10) ' build the packet header LRecSent_tmp = RecBack + LRecSent ' To not sure new LRecSent until data is actually sent For i = ind_last_row_to_send To RecBack ' Loop over rows in GRData GetRecord(GRData,Table1,i) 'extract data from data table GetRecord( Dest, TableName, RecsBack ) GRData = Left (GRData,InStr (2,GRData,"""",2)) & Mid (GRData,InStr (2,GRData,"""",2) + 1,1000) ' redundant? GRData = Replace (GRData,"""","") datap=datap + GRData Next i ' attempt to post data http_post_tx = HTTPPost (, headerp+datap, http_post_response, http_header) ' The http_post_response gets split-up into its individual fields SplitStr (http_response_str_split(),http_post_response,",",3,7) SplitStr (http_response_str_error_split(),http_response_str_split(1),":",2,7) SplitStr (http_response_str_reporting_split(),http_response_str_split(2),":",4,7) flag_return=http_response_str_error_split(2) ' returns "false" if no error, as string ' If not successfull, retry one more time by: disconnect modem, stopping PPP, cutting SW12. Waiting 10 seconds. Start PPP, Start SW12, Wait 90 seconds, send again If NOT (flag_return = "false") Then ' In case of error with HTTPPost retry by rebooting modem ' Can this be a function/method? possible to gain on power usuage? SerialOpen (Modemport,Modembaud,0,0,100) SerialOut (Modemport,"+++","OK"+CHR(13),1,150) SerialOut (Modemport,"AT+CFUN=0"+CHR(13),"OK"+CHR(13),1,300) 'Delay to allow deregistration Delay (1,3,sec) SerialClose(Modemport) P3Close = PPPClose SW12State=False SW12(SW12State) ' Wait 10 seconds, restart modem Delay(1,10,sec) P3Open = PPPOpen 'Start PPP Delay(1,3,sec) SW12State=True SW12(SW12State) 'Boot modem ' Wait 90 seconds, try to send again Delay(1,90,sec) http_post_tx = HTTPPost (, headerp+datap, http_post_response, http_header) ' The http_post_response gets split-up into its individual fields SplitStr (http_response_str_split(),http_post_response,",",3,7) SplitStr (http_response_str_error_split(),http_response_str_split(1),":",2,7) SplitStr (http_response_str_reporting_split(),http_response_str_split(2),":",4,7) flag_return=http_response_str_error_split(2) ' returns false if no error retried_http_post = true EndIf If (flag_return = "false") Then ' If no error, clear the buffers, set the last sent record ReportingInterval = http_response_str_reporting_split(4) ' Check for updates in reporting interval from cloud platform headerp = "" LRecSent = LRecSent_tmp EndIf datap="" 'always clear EndIf 'recback ' Sync clock every time the logger connects. Not really necessary, but neglible power consumption?? NetworkTimeProtocol (NTP_Server,UTC_Offset*3600,1000) ' Set time to UTC+offset CallTable (ComsLog) EndIf ' TimeIntoInterval NextScan EndSequence EndProg