;******************* ;* PongSat program * ;******************* ;Authors: Joost Kranenborg and Jurjen Kranenborg ;Version 1.1 (NLSE-1) ;Date: 19 April 2008 ;============================================================================ ;This program reads the various sensors, interprets the data and ; stores it in the on-board EEPROM. Measurements are done with an interval ; of approx 20 seconds, each set of measurements is time-stamped using the ; on-board clock. This program runs identically on both the PongSat hardware ; and the prototyping hardware. The program contains both logging as well as ; reporting procedures (the latter for testing), however the pongsat hardware ; has no log/report switch and ; thus uses only the logging part of the code ; Currently implemented sensor functions (7) are: ; - Battery voltage level ; - Temperature (Digital) ; - Temperature (Analog, using special low-temp NTC) ; - Tilt (indicator of wind speed (ascend) / falling speed (descent) ; - Vibration (another indicator of wind speed (ascend) / falling speed (descent) ; - Light level 1 (general) ; - Light level 2 (via short pipe, to get background light level) ; The program contains a compiler switch for enabling test code, it is ; disabled in the final code version for the PongSat hardware ; Additionally the porgram also contains reporting code which is enabled by a ; a hardware switch (Prototype hardware only) ;============================================================================ ;Required program adaptations (- means yet to be done, * means fixed): ; HARDWÁRE: ; * Vcc tracks of Vref and DS18B20 should be connected DIRECTLY to main Vcc ; * What about the logical levels of the other sensors, as they have a picaxe-conmtrolled Vcc too ... ; * add programming interface connector ; * Include the vibration sensor if possible (low profile) ; * connect the 1K resistor from the selection directly to Vcc (backplane) ; * Adapt the prototype board hardware to the current situation (including Vcc line of DS1337) ; * ??? ; SOFTWARE: ; * separate Vcc calculation and result writing into separate subroutines, to allow continuous monitoring at low battery voltage ; * relocate subroutine calls, such that battery test can be done first ; * implement battery test routine (goto to temp longer sleep and then test again) ; * test separately with a test program, in order to see if correct values are read after sleep ; * maybe include short pause statement directly after sleep command ; * insert testmode sertxd output, so that values can be online tested as well ; * Experiment with the tilt sensor subinterval and total interval lengths. ; - add comments on duration of each sensor measurement, and care to adjust the sleep constant as well. ; Add (useful) status flags + intermediate reporting (all in EXTERNAL eeprom, since internal EEPROM is cleared with new prg download) ; check register usage ! No program fault should occur because of erroneous register re-use: ; - document which registers are used locally and globally ; - ??? ; OTHER: ; * Adapt circuit drawings to current situation: ; - change of tilt and vibration sensor inputs (pins 16, 17) ; - Add low-side mosfet switch ; - Add caps parallel to LEDs ; * Finally: do a final test EXACTLY according to pongsat circuit, and then DISABLE testmode #picaxe 18x REM #define testmode REM Aleen voor testen ;Symbols/constants definitions for the main program ;More symbol definitions follow in subsoutines: SYMBOL TRUE = 1 SYMBOL FALSE = 0 SYMBOL Counter = b9 SYMBOL AnodeLeds = 0 SYMBOL CathodeLed1 = 6 SYMBOL Cathodeled2 = 7 SYMBOL MaxNumberOfMeasurements = 1080 REM 6 hours * 180 measurements per hour. SYMBOL BytesPerMeasurement = 16 SYMBOL NumberOfMeasurements = w5 SYMBOL AddUpUntil15Secs = 1 SYMBOL LogData = bit5 SYMBOL EepromMemoryPointer = W6 SYMBOL Scratchpad_register = b8 SYMBOL IO_Direction = b0 SYMBOL IOValue = b0 SYMBOL Vcc_AnalogueSensors = 2 SYMBOL EepromAddress = %10100000 SYMBOL ClockAddress = %11010000 SYMBOL Vcc_Calculated = w1 SYMBOL MinimumBatteryLevel = 2300 REM Minimum battery level (mV) required for logging, otherwise sleep 23 secs SYMBOL EnablingBatteryLevel = 2600 REM Battery level at which logging resumes after too low battery voltage ;**************** ;* MAIN PROGRAM * ;**************** I2CSLAVE EepromAddress, I2CSLOW, I2CWORD PAUSE 1000 REM Determine what to do by reading log/report switch (prototype hardware only) REM Result is stored in boolean "LogData" GOSUB DetermineFunctionality EepromMemoryPointer = 0 LOW CathodeLed1,CathodeLed2,AnodeLeds IF LogData = TRUE THEN REM Data logging selected: GOSUB Indicate_MeasurementStart PAUSE 10000 NumberOfMeasurements = 0 DO REM First check battery level GOSUB ReadBatteryVoltage IF Vcc_Calculated > MinimumBatteryLevel THEN REM Everything OK, start measurement procedure #ifdef testmode REM PULSOUT AnodeLeds, 10000 #endif REM Read and store timestamp GOSUB SetTimeStamp REM Calculate and store current Vcc voltage level (using Vref) GOSUB ReadBatteryVoltage GOSUB StoreBatteryVoltage REM Now read all sensor values and store them one-by-one HIGH Vcc_AnalogueSensors REM Enables the movement and external temperature sensors. GOSUB ReadVibrationSensor GOSUB ReadTiltSensor GOSUB ReadExternalTempSensor LOW Vcc_AnalogueSensors GOSUB ReadInternalTempSensor GOSUB ReadOptoSensors INC NumberOfMeasurements SLEEP AddUpUntil15Secs ELSE REM Battery level too low REM Save power by perform long sleeps, subsequently do REM voltage checks, and resume only after voltage level REM is high enough again (margin 300mV) DO SLEEP 8 GOSUB ReadBatteryVoltage LOOP UNTIL Vcc_Calculated > EnablingBatteryLevel ENDIF LOOP UNTIL NumberOfMeasurements = MaxNumberOfMeasurements ELSE REM Reporting for testing is selected (prototype hardware only) REM Note that this is not a full blown program REM Final reporting is done with a separate program GOSUB Indicate_ReportStart GOSUB ReportMeasurements ENDIF END ;================================================ ;************************************ ;* Subroutines used in Main Program:* ;************************************ DetermineFunctionality: REM Read the log/report switch (prototype hardware only) REM and determine whether data has to be logged or reported REM Firstly, turn output5 (rb5) into an input PEEK $AE,IO_Direction LET IO_Direction = IO_Direction OR %00100000 POKE $AE,IO_Direction REM Read input logical value (with special interest REM in bit5 from functionality switch, this bit is tested in REM the main program) PEEK $06,IOValue RETURN ;------------------------------------------------ SetTimeStamp: REM This routine is called at the start of a measurement series REM to read and store the current time (no date information is stored) SYMBOL Sec = b0 SYMBOL Minute = b1 SYMBOL Hour = b2 I2CSLAVE ClockAddress, I2CSLOW, I2CBYTE READI2C $00, (Sec, Minute, Hour) I2CSLAVE EepromAddress, I2CSLOW, I2CWORD WRITEI2C EepromMemoryPointer,(Sec,Minute,Hour) PAUSE 5 EepromMemoryPointer = EepromMemoryPointer + 3 #ifdef testmode SERTXD("hrs:min:sec: ") REM Convert BCD to ascii for presentation Scratchpad_register = hour / 16 SERTXD(#Scratchpad_register) Scratchpad_register = hour & $0F SERTXD(#Scratchpad_register,":") Scratchpad_register = minute / 16 SERTXD(#Scratchpad_register) Scratchpad_register = minute & $0F SERTXD(#Scratchpad_register,":") Scratchpad_register = sec / 16 SERTXD(#Scratchpad_register) Scratchpad_register = sec & $0F SERTXD(#Scratchpad_register, 13, 10) #endif RETURN ;------------------------------------------------ ReadVibrationSensor: REM Determine vibration level by reading vibration switch REM using the COUNT command for a second SYMBOL Pin_Vibration = 7 SYMBOL CountTime = 1000 SYMBOL Vibrations = w0 COUNT Pin_Vibration,CountTime,Vibrations WRITEI2C EepromMemoryPointer,(b1,b0) PAUSE 5 EepromMemoryPointer = EepromMemoryPointer + 2 #ifdef testmode SERTXD ("Vibrations: ",#w0,13,10) #endif RETURN ;------------------------------------------------ ReadExternalTempSensor: REM Read NTC and store in 10-bits ADC format SYMBOL Pin_ExternalTemp = 2 SYMBOL TempADCValueHigh = b1 SYMBOL TempADCValueLow = b0 SYMBOL TempADCValue = w0 READADC10 Pin_ExternalTemp,TempADCValue WRITEI2C EepromMemoryPointer,(TempADCValueHigh,TempADCValueLow) PAUSE 5 EepromMemoryPointer = EepromMemoryPointer + 2 #ifdef testmode SERTXD ("ExtTemp: ",#TempADCValue,13,10) #endif RETURN ;------------------------------------------------ ReadInternalTempSensor: REM Read digital temperature sensor (DS18B20) SYMBOL Pin_InternalTemp = 6 SYMBOL InternalTemp = b0 READTEMP Pin_InternalTemp, InternalTemp WRITEI2C EepromMemoryPointer,(InternalTemp) PAUSE 5 INC EepromMemoryPointer #ifdef testmode SERTXD ("Internal temp: ",#InternalTemp,13,10) #endif RETURN ;------------------------------------------------ ReadOptoSensors: REM Use the two LEDS as optosensors by reverse charging them and REM subsequently read the cathode voltages after a short while. REM The light-dependent discharge level results in a cathode REM voltage decline REM REM The external sensor (for background light level) is read 5 times REM and the minimum light level gets stored REM REM The internal sensor (for overall light level) is read 5 times REM and the mean ligth level gets stored SYMBOL OptoSensorValue = W3 SYMBOL OptoSensorValue_High = b7 SYMBOL OptoSensorValue_Low = b6 SYMBOL number_of_opto_reads = b3 SYMBOL MinimumValue = W2 SYMBOL MinimumValueHigh = b5 SYMBOL MinimumValueLow = b4 SYMBOL MeanValue = W2 SYMBOL MeanValueHigh = b5 SYMBOL MeanValueLow = b4 REM First determine minimum value of a number of external optosensor reads LET MinimumValue = $FFFF FOR number_of_opto_reads = 1 TO 5 REM Read the optosensor as indicated by ADCON0_register GOSUB ReadExternalOptoSensor #ifdef testmode SERTXD ("External Sensor value: ", #OptoSensorValue, 13, 10) #endif IF OptoSensorValue < MinimumValue THEN MinimumValue = OptoSensorValue ENDIF PAUSE 1000 NEXT REM Write these results to EEPROM WRITEI2C EepromMemoryPointer,(MinimumValueHigh, MinimumValueLow) PAUSE 5 LET EepromMemoryPointer = EepromMemoryPointer + 2 REM Now determine mean value of a number of internal optosensor reads LET MeanValue = 0 FOR number_of_opto_reads = 1 TO 5 REM Read the optosensor as indicated by ADCON0_register GOSUB ReadInternalOptoSensor #ifdef testmode SERTXD ("External Sensor value: ", #OptoSensorValue, 13, 10) #endif MeanValue = MeanValue + OptoSensorValue PAUSE 1000 NEXT Meanvalue = MeanValue / 5 REM Write these results to EEPROM WRITEI2C EepromMemoryPointer,(MeanValueHigh, MeanValueLow) PAUSE 5 LET EepromMemoryPointer = EepromMemoryPointer + 2 POKE $9F,%00000000 RETURN ReadExternalOptoSensor: SYMBOL DischargeTime = 2 REM Reverse-charge the LEDs LOW AnodeLeds HIGH CathodeLed2, CathodeLed1 REM Make cathode an input. PEEK $AE,IO_Direction LET IO_Direction = IO_Direction OR %11000000 POKE $AE,IO_Direction REM Allow incident light to discharge the charged LED during certain time PAUSE DischargeTime REM Read light intensity via AD conversion. REM First enable the ADC functionality POKE $9B,%01000000 REM Set ANSEL register: Analogue inputs enabled on AN5 (RB6), AN6 (RB7) POKE $9F,%10000000 REM Set ADCON1 register POKE $1F,%11110001 REM Set ADCON0 register; Enable the AD module to read the voltage level REM Now do the actual ADC conversion POKE $1F,%11110101 REM Start actual ADC conversion REM PAUSE 1 REM Get 10-bit result in w0 PEEK $1E,OptoSensorValue_High REM Read upper 2 bits of AD convertion result. PEEK $9E,OptoSensorValue_Low REM Read lower 8 bits of AD convertion result. POKE $1F,%11110000 REM Disable AD module REM Make input pins outputs again PEEK $AE,IO_Direction LET IO_Direction = IO_Direction AND %00111111 POKE $AE,IO_Direction LOW AnodeLeds, CathodeLed2, CathodeLed1 REM Test this one! RETURN ReadInternalOptoSensor: REM Reverse-charge the LEDs LOW AnodeLeds HIGH CathodeLed2, CathodeLed1 REM Make cathode an input. PEEK $AE,IO_Direction LET IO_Direction = IO_Direction OR %11000000 POKE $AE,IO_Direction REM Allow incident light to discharge the charged LED during certain time PAUSE DischargeTime REM Read light intensity via AD conversion. REM First enable the ADC functionality POKE $9B,%00100000 REM Set ANSEL register: Analogue inputs enabled on AN5 (RB6), AN6 (RB7) POKE $9F,%10000000 REM Set ADCON1 register POKE $1F,%11101001 REM Set ADCON0 register; Enable the AD module to read the voltage level REM Now do the actual ADC conversion POKE $1F,%11101101 REM Start actual ADC conversion REM PAUSE 1 REM Get 10-bit result in w0 PEEK $1E,OptoSensorValue_High REM Read upper 2 bits of AD convertion result. PEEK $9E,OptoSensorValue_Low REM Read lower 8 bits of AD convertion result. POKE $1F,%11101000 REM Disable AD module REM Make input pins outputs again PEEK $AE,IO_Direction LET IO_Direction = IO_Direction AND %00111111 POKE $AE,IO_Direction LOW AnodeLeds, CathodeLed2, CathodeLed1 REM Test this one! RETURN ;------------------------------------------------ ReadTiltSensor: REM Read the tilt sensor. Since the tilt sensor actually functions REM as a vibration sensor when in ON mode, a number of reads are done in REM sequence, and the difference between the last read and the current read REM (0 changes or more than 0 changes) is used to define whether a REM change in tilt has occurred SYMBOL TotalCount = b1 SYMBOL StatusByte = b0 SYMBOL Status_now = bit0 SYMBOL Status_last = bit1 SYMBOL Tilt = w2 SYMBOL Subcount = b2 SYMBOL Pin_Tilt = 0 SYMBOL CountPause = 125 SYMBOL MaxSubCounts = 40 TotalCount = 0 StatusByte = 0 FOR Subcount = 1 TO MaxSubCounts Status_last = Status_now COUNT Pin_Tilt,CountPause,Tilt IF Tilt > 0 THEN Status_now = 1 ELSE Status_now = 0 ENDIF IF Status_now != Status_last then INC TotalCount ENDIF NEXT WRITEI2C EepromMemoryPointer,(TotalCount) PAUSE 5 EepromMemoryPointer = EepromMemoryPointer + 2 #ifdef testmode SERTXD ("Tilts: ",#TotalCount,13,10) #endif RETURN ;------------------------------------------------ ReadBatteryVoltage: REM Calculate Battery level (Vcc. in mV) using Vref (1261 mV at -50 degr. C) delivered by MAX6018 REM Only 8-bit ADC value is used, accuracy for calculated Vcc in the critical range REM of 2.0V is approx. 10mV, for Vcc of 3.6 (maximum battery voltage) the error is approx 40mV. REM Calculation formula: Vcc(mV) = 1261mV (=Vref) * (255 / ADC_Vref) REM = (1261 * 51 * 5) / ADC_Vref REM = (64311 / ADC_Vref) * 5 REM Note that 16 bits integer calculus is used using as large integers possible REM before a division is done, in order to minimize round-off errors SYMBOL ADC_Vref = b0 SYMBOL Vcc_Calculated_High = b3 SYMBOL Vcc_Calculated_Low = b2 SYMBOL Pin_Vref_MAX6018 = 1 REM Functional pin nr. 1, physical leg nr. 18 ! LET Vcc_Calculated = 0 READADC Pin_Vref_MAX6018, ADC_Vref LET Vcc_Calculated = 64311 / ADC_Vref LET Vcc_Calculated = Vcc_Calculated * 5 REM Vcc calculated in mV's RETURN StoreBatteryVoltage: REM store the battery voltage as calculated by ReadBatteryVoltage WRITEI2C EepromMemoryPointer,(Vcc_Calculated_High, Vcc_Calculated_Low) PAUSE 5 EepromMemoryPointer = EepromMemoryPointer + 2 #ifdef testmode SERTXD ("Battery Voltage: ",#Vcc_Calculated,13,10) #endif RETURN ;------------------------------------------------ Summarize: RETURN ;------------------------------------------------ ReportMeasurements: REM Report on the first 100 stored measurment series REM for testing purposes in order to see whether REM all hardware components (sensors, clock and EEPROM) REM are functioning correctly LOW AnodeLeds FOR Counter = 1 to 100 READI2C EepromMemoryPointer,(sec,minute,hour) SERTXD("hrs:min:sec: ") REM Convert BCD to ascii for presentation Scratchpad_register = hour / 16 SERTXD(#Scratchpad_register) Scratchpad_register = hour & $0F SERTXD(#Scratchpad_register,":") Scratchpad_register = minute / 16 SERTXD(#Scratchpad_register) Scratchpad_register = minute & $0F SERTXD(#Scratchpad_register,":") Scratchpad_register = sec / 16 SERTXD(#Scratchpad_register) Scratchpad_register = sec & $0F SERTXD(#Scratchpad_register, 13, 10) EepromMemoryPointer = EepromMemoryPointer + 3 READI2C EepromMemoryPointer,(b1,b0) SERTXD ("Battery Voltage: ",#w0,13,10) EepromMemoryPointer = EepromMemoryPointer + 2 READI2C EepromMemoryPointer,(b1,b0) SERTXD ("Vibrations: ",#w0,13,10) EepromMemoryPointer = EepromMemoryPointer + 2 READI2C EepromMemoryPointer,(TotalCount) SERTXD ("Tilt: ",#TotalCount,13,10) EepromMemoryPointer = EepromMemoryPointer + 2 READI2C EepromMemoryPointer,(TempADCValueHigh,TempADCValueLow) SERTXD ("ExtTemp: ",#TempADCValue,13,10) EepromMemoryPointer = EepromMemoryPointer + 2 READI2C EepromMemoryPointer,(InternalTemp) SERTXD ("IntTemp: ",#InternalTemp,13,10) EepromMemoryPointer = EepromMemoryPointer + 1 READI2C EepromMemoryPointer,(MinimumValueHigh, MinimumValueLow) SERTXD ("Light Level, external: ",#MinimumValue,13,10) EepromMemoryPointer = EepromMemoryPointer + 2 READI2C EepromMemoryPointer,(MeanValueHigh, MeanValueLow) SERTXD ("Light Level, internal: ",#MeanValue,13,10) EepromMemoryPointer = EepromMemoryPointer + 2 NEXT RETURN ;------------------------------------------------ Indicate_MeasurementStart: FOR Counter = 1 TO 100 TOGGLE AnodeLeds PAUSE 100 NEXT RETURN ;------------------------------------------------ Indicate_ReportStart: FOR Counter = 1 TO 10 TOGGLE AnodeLeds PAUSE 500 NEXT RETURN