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!
Cheers
Program code:
'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
Bumping thread - any tips on power optimization using external modem?