Skip to content

Instantly share code, notes, and snippets.

@jhoughjr
Created October 30, 2012 05:55
Show Gist options
  • Save jhoughjr/3978550 to your computer and use it in GitHub Desktop.
Save jhoughjr/3978550 to your computer and use it in GitHub Desktop.
a serial command interface with state tracking
// 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