-
-
Save jhoughjr/3978550 to your computer and use it in GitHub Desktop.
a serial command interface with state tracking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// GardenGuard RC 1 | |
// © 2012 Jimmy Hough for Emrys Engineering | |
// Rights are exclusively granted to Scoot Boldwyn to modify, redistribute or use this code in any way without written permission | |
// I minimize the use of locals to speed things up | |
// I try to minimize context switching so that execution is fastest | |
// | |
// the controller reads data from the serial port, | |
// parses it and responds accordingly | |
// | |
// 2 relays are actuated | |
// time is read over i2c via DS1302 | |
// temperature is measured analogously by the ADC with an LM 34 attached | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <String.h> | |
#include <DS1302.h> | |
/* pin assignments */ | |
// temp sensor pin | |
const uint8_t tempSensorPin = 3; // note this is A3, not D3 when used | |
// i2c bus pins | |
uint8_t CEPin = 5; // chip enable | |
uint8_t ioPin = 6; // data | |
uint8_t sclkPin =7; // clock | |
// built in LED pin | |
const uint8_t builtInLEDPin = 13; | |
// relay pins | |
const uint8_t heaterPin = 3; // this is DIGITAL 3 not ANALOG 3 | |
const uint8_t lightPin = 4; | |
/* end pin assignments */ | |
/* begin globals declarations */ | |
// temperature measurement | |
// these are so we can take an average, to better stabilize the measurement | |
int measurement1; | |
int measurement2; | |
int measurement3; | |
int measurement4; | |
long total; | |
// this was needed after calibration | |
float fudgeFactor = 62.0; // calibrated with RS Temperature Monitor | |
// temperature management | |
float differentialGap = 0.0; | |
// controller state | |
// structure for storing time, to minimize TIme object usage | |
typedef struct{ | |
byte hours; | |
byte minutes; | |
byte seconds; | |
} byteTime; | |
// structure for the state of the system | |
typedef struct{ | |
boolean isManualOverrideForHeaterEneabled; // neable/disable manual control of heater | |
boolean isManualOverrideForLightsEnabled; // enable/disable manual control of light | |
boolean isLightOn; // state of light | |
boolean isHeaterOn; // | |
float temperature; // output of temperature measurement lives here | |
float setPoint; // setPoint is store here | |
boolean isKlaxxonON; // state of klaxxon is here | |
byteTime currentTime; // current time is stored here | |
boolean isCycleOneEnabled; // enable/disable cycle one | |
boolean isCycleTwoEnabled; // enable/disable cycle two | |
byteTime cycleOneONTime; // on time for cycle one | |
byteTime cycleTwoONTime; // off time for cycle onw | |
byteTime cycleOneOFFTime; // on time for cycle two | |
byteTime cycleTwoOFFTime; // off time for cycle two | |
} controllerStateRecord; | |
// communication | |
String receivedString = String(""); // buffer for received bytes | |
char receivedCharacter; // buffer for each individual byte | |
// const char[] stored in flash to save RAM | |
// | |
// these help find the arugument in the receivedString, they are args of substring() | |
byte startIndex; | |
byte endIndex; | |
// this is where the stae lives | |
controllerStateRecord controllerState; | |
// DS1302 object. This provides timekeeping functions | |
DS1302 rtc(CEPin, ioPin, sclkPin); | |
// this runs once after reset | |
void setup(){ | |
// setup temperature sensor pin | |
pinMode(tempSensorPin, INPUT); | |
// setup internal LED | |
pinMode(builtInLEDPin, OUTPUT); | |
// setup the heater and lioght controls | |
pinMode( heaterPin, OUTPUT); | |
pinMode( lightPin, OUTPUT); | |
// setup i2c bus | |
pinMode(CEPin, OUTPUT); | |
pinMode(sclkPin, OUTPUT); | |
pinMode(ioPin, OUTPUT); | |
// use internal 1.1V ref voltage for ADC readings | |
// analogReference(INTERNAL); | |
// start serial communictaion | |
Serial.begin(9600); | |
//yyyy.mm.d.hh.mm.ss.wd | |
Time defaultTime(2009, 5, 19, 21, 16, 37, 3); | |
// make sure the DS1302 will listen | |
rtc.write_protect(false); | |
// set the time | |
rtc.halt(true); | |
rtc.time(defaultTime); | |
rtc.halt(false); | |
Serial.println("serial began @9600bps"); | |
} | |
// this runs infinitely as fast as it can | |
void loop(){ | |
/* begin runloop */ | |
// do we have vytes available to read? if so receive and parse them, then execute the commands | |
if ( Serial.available() > 0 ) { | |
// if so, read the char | |
receivedCharacter = Serial.read(); | |
// append it | |
receivedString = receivedString + receivedCharacter; | |
// ; means end of line, so begin parsing the buffer and obeying commands | |
if ( receivedString.endsWith( ";" ) ){ | |
// send the controllerstate structure | |
if ( receivedString.startsWith( "sendState" ) ) { | |
// send each datum on its own line | |
// send the bools | |
Serial.println( controllerState.isManualOverrideForLightsEnabled ); | |
Serial.println( controllerState.isManualOverrideForHeaterEneabled ); | |
Serial.println( controllerState.isHeaterOn ); | |
Serial.println( controllerState.isLightOn ); | |
Serial.println( controllerState.isKlaxxonON ); | |
Serial.println( controllerState.isCycleOneEnabled ); | |
Serial.println( controllerState.isCycleTwoEnabled ); | |
// send the lcimate info | |
Serial.println( controllerState.temperature ); | |
Serial.println( controllerState.setPoint ); | |
// send the time | |
Serial.println( controllerState.currentTime.hours ); | |
Serial.println( controllerState.currentTime.minutes ); | |
Serial.println( controllerState.currentTime.seconds ); | |
// send cycle one on time | |
Serial.println( controllerState.cycleOneONTime.hours ); | |
Serial.println( controllerState.cycleOneONTime.minutes ); | |
Serial.println( controllerState.cycleOneONTime.seconds ); | |
// send cycle one off time | |
Serial.println( controllerState.cycleOneOFFTime.hours ); | |
Serial.println( controllerState.cycleOneOFFTime.minutes ); | |
Serial.println( controllerState.cycleOneOFFTime.seconds ); | |
// send cycle two on time | |
Serial.println( controllerState.cycleTwoONTime.hours ); | |
Serial.println( controllerState.cycleTwoONTime.minutes ); | |
Serial.println( controllerState.cycleTwoONTime.seconds ); | |
// send cycle two off time | |
Serial.println( controllerState.cycleTwoOFFTime.hours ); | |
Serial.println( controllerState.cycleTwoOFFTime.minutes ); | |
Serial.println( controllerState.cycleTwoOFFTime.seconds ); | |
} | |
// enable manual control of the light | |
if ( receivedString.startsWith( "enableManualOverrideForLight" ) ) { | |
controllerState.isManualOverrideForLightsEnabled = true; | |
} | |
// disbale manual control of the light. // this must be done for controlLoght() to work | |
if ( receivedString.startsWith( "disableManualOverrideForLight") ) { | |
controllerState.isManualOverrideForLightsEnabled = false; | |
} | |
// turn the light on | |
if ( receivedString.startsWith( "turnLightON" ) ){ | |
turnLightON(); | |
} | |
// turn the light off | |
if ( receivedString.startsWith( "turnLightOFF" ) ){ | |
turnLightOFF(); | |
} | |
//enable manual heater control | |
if (receivedString.startsWith( "enableManualOverrideForHeater" ) ) { | |
controllerState.isManualOverrideForHeaterEneabled = true; | |
} | |
// disable manual heater control, this must be done for controlTemperature() to work | |
if ( receivedString. startsWith( "disableManualOverrideForHeater" ) ) { | |
controllerState.isManualOverrideForHeaterEneabled = false; | |
} | |
// turn heater on | |
if ( receivedString.startsWith( "turnHeaterON" ) ) { | |
turnHeaterON(); | |
} | |
// turn the heater off | |
if ( receivedString.startsWith( "turnHeaterOFF" ) ) { | |
turnHeaterOFF(); | |
} | |
if ( receivedString.startsWith( "setPoint:" ) ){ | |
// find the beginning and end of the arguments | |
startIndex = receivedString.indexOf( ":"); | |
endIndex = receivedString.lastIndexOf( ";"); | |
// create a string object for the argument | |
String setPointString = String( receivedString.substring( startIndex +1, endIndex ) ); | |
// declare a char[] for the argument | |
char setPointChars[5]; | |
// assign the char[] for the argument | |
setPointString.toCharArray( setPointChars, 5); | |
// convert the argument from ASCII bytes to a float and store it in the controllerStateRecord structure | |
controllerState.setPoint = atof(setPointChars); | |
} | |
// set the time | |
if ( receivedString.startsWith( "setTime:" ) ){ | |
// find the beginning and end of the arguments | |
startIndex = receivedString.indexOf( ":"); | |
endIndex = receivedString.lastIndexOf( ";"); | |
// create a string object for the argument | |
String timeString = String( receivedString.substring( startIndex + 1, endIndex ) ); | |
// declare and assign char[]s for each component of the time in the argument | |
char hours[3] = { timeString.charAt(0), timeString.charAt(1) } ; | |
char mins[3] = { timeString.charAt(3), timeString.charAt(4) }; | |
char secs[3] = { timeString.charAt(6), timeString.charAt(7) }; | |
// convert the components from ASCII bytes to a byte of the same numeric value | |
byte h = atoi(hours); | |
byte m = atoi(mins); | |
byte s = atoi(secs); | |
// stop the clock | |
rtc.halt(true); | |
// set the time | |
rtc.hour(h); | |
rtc.minutes(m); | |
rtc.seconds(s); | |
// start the clock with the new time | |
rtc.halt(false); | |
} | |
// enable cycle one | |
if ( receivedString.startsWith( "enableCycleOne") ){ | |
controllerState.isCycleOneEnabled = true; | |
} | |
// disbale cycle one | |
if ( receivedString.startsWith( "disableCycleOne" ) ) { | |
controllerState.isCycleOneEnabled = false; | |
} | |
// enable cycle two | |
if (receivedString.startsWith( "enableCycleTwo" ) ) { | |
controllerState.isCycleTwoEnabled = true; | |
} | |
// disable cycle two | |
if (receivedString.startsWith( "disableCycleTwo" ) ) { | |
controllerState.isCycleTwoEnabled = false; | |
} | |
// set cycle one on time | |
if ( receivedString.startsWith( "cycleOneONTime:" ) ){ | |
// find the beginning and end of the arguments | |
startIndex = receivedString.indexOf( ":"); | |
endIndex = receivedString.lastIndexOf( ";"); | |
// create a string object for the argument | |
String timeString = String( receivedString.substring( startIndex + 1, endIndex ) ); | |
// declare and assign char[]s for each component of the time in the argument | |
char hours[3] = { timeString.charAt(0), timeString.charAt(1) } ; | |
char mins[3] = { timeString.charAt(3), timeString.charAt(4) }; | |
char secs[3] = { timeString.charAt(6), timeString.charAt(7) }; | |
// convert the components from ASCII bytes to a byte of the same numeric value | |
byte h = atoi(hours); | |
byte m = atoi(mins); | |
byte s = atoi(secs); | |
// set the time in the controllerStateRecord structure | |
controllerState.cycleOneONTime.hours = h; | |
controllerState.cycleOneONTime.minutes = m; | |
controllerState.cycleOneONTime.seconds = s; | |
} | |
// set cycle one off time | |
if ( receivedString.startsWith("cycleOneOFFTime:" ) ) { | |
// find the beginning and end of the arguments | |
startIndex = receivedString.indexOf( ":"); | |
endIndex = receivedString.lastIndexOf( ";"); | |
// create a string object for the argument | |
String timeString = String( receivedString.substring( startIndex + 1, endIndex ) ); | |
// declare and assign char[]s for each component of the time in the argument | |
char hours[3] = { timeString.charAt(0), timeString.charAt(1) } ; | |
char mins[3] = { timeString.charAt(3), timeString.charAt(4) }; | |
char secs[3] = { timeString.charAt(6), timeString.charAt(7) }; | |
// convert the components from ASCII bytes to a byte of the same numeric value | |
byte h = atoi(hours); | |
byte m = atoi(mins); | |
byte s = atoi(secs); | |
// set the time in the controllerStateRecord structure | |
controllerState.cycleOneOFFTime.hours = h; | |
controllerState.cycleOneOFFTime.minutes = m; | |
controllerState.cycleOneOFFTime.seconds = s; | |
} | |
// set cycle two on time | |
if ( receivedString.startsWith( "cycleTwoONTime:" ) ) { | |
// find the beginning and end of the arguments | |
startIndex = receivedString.indexOf( ":"); | |
endIndex = receivedString.lastIndexOf( ";"); | |
// create a string object for the argument | |
String timeString = String( receivedString.substring( startIndex + 1, endIndex ) ); | |
// declare and assign char[]s for each component of the time in the argument | |
char hours[3] = { timeString.charAt(0), timeString.charAt(1) } ; | |
char mins[3] = { timeString.charAt(3), timeString.charAt(4) }; | |
char secs[3] = { timeString.charAt(6), timeString.charAt(7) }; | |
// convert the components from ASCII bytes to a byte of the same numeric value | |
byte h = atoi(hours); | |
byte m = atoi(mins); | |
byte s = atoi(secs); | |
// set the time in the controllerStateRecord structure | |
controllerState.cycleTwoONTime.hours = h; | |
controllerState.cycleTwoONTime.minutes = m; | |
controllerState.cycleTwoONTime.seconds = s; | |
} | |
// set cycle two off time | |
if ( receivedString.startsWith( "cycleTwoOFFTime:") ) { | |
// find the beginning and end of the arguments | |
startIndex = receivedString.indexOf( ":"); | |
endIndex = receivedString.lastIndexOf( ";"); | |
// create a string object for the argument | |
String timeString = String( receivedString.substring( startIndex + 1, endIndex ) ); | |
// declare and assign char[]s for each component of the time in the argument | |
char hours[3] = { timeString.charAt(0), timeString.charAt(1) } ; | |
char mins[3] = { timeString.charAt(3), timeString.charAt(4) }; | |
char secs[3] = { timeString.charAt(6), timeString.charAt(7) }; | |
// convert the components from ASCII bytes to a byte of the same numeric value | |
byte h = atoi(hours); | |
byte m = atoi(mins); | |
byte s = atoi(secs); | |
// set the time in the controllerStateRecord structure | |
controllerState.cycleTwoOFFTime.hours = h; | |
controllerState.cycleTwoOFFTime.minutes = m; | |
controllerState.cycleTwoOFFTime.seconds = s; | |
} | |
if (receivedString.startsWith("ram")) | |
{ | |
printRAM(); | |
} | |
// clear the string for next command | |
receivedString = ""; | |
} | |
} | |
// get time and temperature and fill in the controllerState members | |
updateState(); | |
// controll the temperature | |
controlTemperature(); | |
// controll the light | |
controlLights(); | |
/* end runloop */ | |
} | |
/* implementations */ | |
// update the time and temperature | |
void updateState(){ | |
// update temeperature | |
updateCurrentTime(); | |
// update temeperature | |
updateTemperature(); | |
} | |
// this averages 4 measurements to yield the temperature sensed by the LM34 | |
void updateTemperature(){ | |
// note the klaxxon will be set here in a later version | |
// take four measurements | |
measurement1 = analogRead(tempSensorPin); | |
measurement2 = analogRead(tempSensorPin); | |
measurement3 = analogRead(tempSensorPin); | |
measurement4 = analogRead(tempSensorPin); | |
// sum them for the average | |
total = measurement1 + measurement2 + measurement3 + measurement4; | |
// divide, cast, correct and assign // divided byten since the result was of by afactor of ten, not sure why yet. | |
controllerState.temperature = ( float( total ) / 4.0 + fudgeFactor) / 10.0 ; | |
} // performs temperature measurement | |
// update the current time from the rtc | |
void updateCurrentTime(){ | |
controllerState.currentTime.hours = rtc.hour(); | |
controllerState.currentTime.minutes = rtc.minutes(); | |
controllerState.currentTime.seconds = rtc.seconds(); | |
} // updates time in controllerState | |
// control the temperature by turning the hearer on or off based on distance from setPoint | |
void controlTemperature(){ | |
if ( controllerState.isManualOverrideForHeaterEneabled == false ) { | |
// control temp | |
} | |
} // control the state of lights based on enable states | |
void controlLights(){ | |
if ( controllerState.isManualOverrideForLightsEnabled == false ) { | |
// control light | |
} | |
} // this impe,emnts light control | |
// relay controls | |
void turnHeaterON(){ | |
digitalWrite(heaterPin,HIGH); // set the relay | |
// update the state | |
controllerState.isHeaterOn = true; | |
} | |
void turnHeaterOFF(){ | |
digitalWrite(heaterPin, LOW); // set the relay | |
// update the state | |
controllerState.isHeaterOn = false; | |
} | |
void turnLightON(){ | |
digitalWrite(lightPin, HIGH); // set the relay | |
//update the state | |
controllerState.isLightOn = true; | |
} | |
void turnLightOFF(){ | |
digitalWrite(lightPin, LOW); // set the relay | |
// update the state | |
controllerState.isLightOn = false; | |
} | |
int freeRam () { | |
extern int __heap_start, *__brkval; | |
int v; | |
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); | |
} | |
void printRAM() | |
{ | |
Serial.print("Free RAM:"); | |
Serial.print(freeRam()); | |
} | |
/* end implementation */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment