Vous êtes sur la page 1sur 8

irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

Explore Features Enterprise Pricing Sign up Sign in

google / irrduino Watch 4 Star 25 Fork 6

master irrduino / IrrduinoController / IrrduinoController.pde

jjinux on 2 Aug 2012 Merge https://code.google.com/p/irrduino

1 contributor

612 lines (499 sloc) 18.317 kB Raw Blame History

// Copyright 2012 Google Inc.


//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
Irrduino v0.8.2 by Joe Fernandez

Issues:
- need a nanny process to check a max run time for valves (regardless of commands or program)
- not working on an Arduino Ethernet board

Change Log:
- 2012-01-09 - changed reporting to point to /log (instead of /reports)
- 2012-01-07 - added run time in seconds (web command syntax option and
update to CommandDispatch handling)
- 2011-12-14 - added function to retrieve current settings
- 2011-12-13 - added REST command for system settings /settings?rr
- 2011-12-02 - added function to report zone runs checkAndPostReport()
- 2011-12-02 - add per-zone status reporting, with zone IDs and remaining times
- 2011-11-10 - add global status option
- 2011-10-27 - added code to turn on and blink an LED on pin 13 while zone running
- 2011-10-07 - fixed problem with home page not displaying after first request
- fixed problem mismatch between zone/pin selection
- 2011-10-05 - Version 0.6 completed
- 3 file split, commandDispatch[] infrastructure implemented
- fixed problem with zone command being run repeatedly
- 2011-09-28 - Split pde into parts for easier code management
- Introduced new commandDispatch[] object for storing command parameters
- 2011-09-25 - Added web UI for more zones and "ALL OFF" function
- 2011-09-xx - Entended parser to turn off zones indifvidually
- updated to use timer for executing irrigation zone runs; now runs can be interrupted

An irrigation control program with a REST-like http


remote control protocol.

Based on David A. Mellis's "Web Server" ethernet


shield example sketch.

REST implementation based on "RESTduino" sketch


by Jason J. Gullickson
*/

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetDNS.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xE2, 0xEB }; //physical mac address
byte ip[] = { 192, 168, 1, 15 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte dnsServerIp[] = { 192, 168, 1, 1}; // DNS server IP (typically your gateway)
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask

Server server(80); //server port


Client client = NULL; // client

1 de 8 05/09/2015 01:38 a.m.


irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

// zone to pin mapping


int zone1 = 2; //pin 2
int zone2 = 3; //pin 3
int zone3 = 4; //pin 4
int zone4 = 5; //pin 5
int zone5 = 6; //pin 6
int zone6 = 7; //pin 7
int zone7 = 8; //pin 8
int zone8 = 9; //pin 9

int zone9 = A0; //pin A0


int zone10 = A1; //pin A1
int zone11 = A2; //pin A2
int zone12 = A3; //pin A3

// LED indicator pin variables


int ledIndicator = 13;
int ledState = LOW;
unsigned long ledFlashTimer = 0; // timer for LED in milliseconds
unsigned long ledFlashInterval = 1000; // flash interval in milliseconds

// set the maximum run time for a zone (safeguard)


unsigned long MAX_RUN_TIME_MINUTES = 30; // Default 30 minutes
unsigned long MAX_RUN_TIME = MAX_RUN_TIME_MINUTES * 60000;

int zones[] = {zone1, zone2, zone3, zone4,


zone5, zone6, zone7, zone8,
zone9, zone10, zone11, zone12};

int zoneCount = 12;

// REST commands
const String REST_CMD_OFF = "off";
const String REST_CMD_STATUS = "status";
const String REST_CMD_ZONE = "zone";
const String REST_CMD_ZONES = "zones";
const String REST_CMD_SETTINGS = "settings";
const String REST_CMD_TESTREPORT = "testreport";

// Command codes
const int OBJ_CMD_ALL_OFF = 1;
const int OBJ_CMD_STATUS = 2;
const int OBJ_CMD_SETTINGS = 3;
const int OBJ_CMD_ZONES = 10;
const int OBJ_CMD_ZONE = 100;
const int OBJ_CMD_PROGRAMS = 20;
const int OBJ_CMD_PROGRAM = 200;

// Reporting constants and variables


int maxReportAttempts = 3;
int reportAttempts = 0;

boolean reportingEnabled = false; // reporting disabled by default


char reportingHostName[512];
int reportingHostPort = 80;

// Command Codes (CC)


const int CC_OFF = 0;
const int CC_ON = 1;
const int CC_STATUS = 3;

// Command Dispatch structure - for processing incoming commands


int commandDispatchLength = 5;
int commandDispatch[] = { 0, // command object type, 0 for none
0, // command object id, 0 for none
0, // command code, 0 for none
0, // value 1, 0 for none
0 // value 2, 0 for none
};
const int CD_OBJ_TYPE = 0;
const int CD_OBJ_ID = 1;
const int CD_CMD_CODE = 2;
const int CD_VALUE_1 = 3;
const int CD_VALUE_2 = 4;

// Command Running structure - for managing running commands


int commandRunningLength = 4;
unsigned long commandRunning[] = {0, // pin ID, 0 for none
0, // run end time in milliseconds, 0 for none
0, // zone ID, 0 for none
0 // run start time in milliseconds, 0 for none

2 de 8 05/09/2015 01:38 a.m.


irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

};
const int CR_PIN_ID = 0;
const int CR_END_TIME = 1;
const int CR_ZONE_ID = 2;
const int CR_START_TIME = 3;

// Command Report structure - for reporting completed runs (use CR_ constants for indexes)
int commandReportLength = 4;
unsigned long commandReport[] = {0, // pin ID, 0 for none
0, // run end time in miliseconds, 0 for none
0, // zone ID, 0 for none
0 // run start time in milliseconds, 0 for none
};

String jsonReply;
String reportData;

void setup(){
// Turn on serial output for debugging
Serial.begin(9600);

Serial.println("Irrduino starting up...");

// Start Ethernet connection and server


Ethernet.begin(mac, ip, gateway, subnet);
server.begin();

// Set the DNS server (for resolving hosts)


EthernetDNS.setDNSServer(dnsServerIp);

//Set relay pins to output


for (int i = 0; i < zoneCount; i++){
pinMode(zones[i], OUTPUT);
}

// set the LED indicator pin for output


// pinMode(ledIndicator, OUTPUT); // (causes failure on Arduino Ethernet board)
}

// url buffer size


#define BUFSIZE 255

void loop(){
char clientLine[BUFSIZE];
int index = 0;

// check on timed runs, shutdown expired runs


checkTimedRun();

// check for pending reports to be sent


checkAndPostReport();

// listen for incoming clients


client = server.available();
if (client) {

// reset input buffer


index = 0;

while (client.connected()) {
if (client.available()) {
char c = client.read();

// fill buffer with url


if(c != '\n' && c != '\r'){

// if we run out of buffer, overwrite the end


if(index >= BUFSIZE) {
break;
//index = BUFSIZE -1;
}

clientLine[index] = c;
index++;

// Serial.print("client-c: ");
// Serial.println(c);
continue;
}
Serial.print("http request: ");
Serial.println(clientLine);

3 de 8 05/09/2015 01:38 a.m.


irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

// convert clientLine into a proper


// string for further processing
String urlString = String(clientLine);

if (urlString.lastIndexOf("TTP/1.1") < 0 ){
Serial.println("no HTTP/1.1, ignoring request");
// not a url request, ignore this
goto finish_http;
}

// extract the operation (GET or POST)


String op = urlString.substring(0,urlString.indexOf(' '));

// we're only interested in the first part...


urlString = urlString.substring(
urlString.indexOf('/'),
urlString.indexOf(' ', urlString.indexOf('/')));

// put what's left of the URL back in client line


urlString.toCharArray(clientLine, BUFSIZE);

// get the parameters


char *arg1 = strtok(clientLine,"/");
Serial.print("arg1: ");
Serial.println(arg1);
char *arg2 = strtok(NULL,"/");
Serial.print("arg2: ");
Serial.println(arg2);
char *arg3 = strtok(NULL,"/");
Serial.print("arg3: ");
Serial.println(arg3);
char *arg4 = strtok(NULL,"/");
Serial.print("arg4: ");
Serial.println(arg4);

if (arg1 == NULL){
// we got no parameters. show default page
httpHomePage();

} else {
// start a json reply
jsonReply = String();
// identify the command
findCmdObject(arg1);

switch (commandDispatch[CD_OBJ_TYPE]) {

case OBJ_CMD_ALL_OFF: // all off command


cmdZonesOff();
break;

case OBJ_CMD_STATUS: // Global status ping


cmdGlobalStatusRequest();
break;

case OBJ_CMD_SETTINGS: // Controller settings


findSettingsCommand(arg1);
break;

case OBJ_CMD_ZONE: // zone command

findZoneCommand(arg2);

switch (commandDispatch[CD_CMD_CODE]){
case CC_OFF:
endTimedRun();
break;
case CC_ON:
findZoneTimeValue(arg3);
cmdZoneTimedRun();
break;
case CC_STATUS:
cmdZoneStatus();
break;
}

break;
case OBJ_CMD_ZONES: // all zones
break;
case OBJ_CMD_PROGRAM: // program command
break;
case OBJ_CMD_PROGRAMS: // all programs

4 de 8 05/09/2015 01:38 a.m.


irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

break;

default:
httpJsonReply("\"ERROR\":\"Command not recognized.\"");
}
}
}
}

// finish http response


finish_http:

// clear Command Dispatch


Serial.println("clearCommandDispatch()");
clearCommandDispatch();

// Clear the clientLine char array


Serial.println("clear client line: clearCharArray()");
clearCharArray(clientLine, BUFSIZE);

// give the web browser time to receive the data


delay(20);

// close the connection:


client.stop();

}
} /// ========= end loop() =========

void findCmdObject(char *cmdObj){

String commandObject = String(cmdObj);


commandObject = commandObject.toLowerCase();

// check for "OFF" shortcut


if (commandObject.compareTo("off") == 0) {
commandDispatch[CD_OBJ_TYPE] = OBJ_CMD_ALL_OFF;
jsonReply += "\"command\":\"zones off\"";
return;
}

// check for global "status" request "/status"


if (commandObject.compareTo(REST_CMD_STATUS) == 0) {
commandDispatch[CD_OBJ_TYPE] = OBJ_CMD_STATUS;
return;
}

// check for settings request "/settings"


if (commandObject.startsWith(REST_CMD_SETTINGS)) {
commandDispatch[CD_OBJ_TYPE] = OBJ_CMD_SETTINGS;
return;
}

// must check for plural form first


if (commandObject.compareTo(REST_CMD_ZONES) == 0) {
commandDispatch[CD_OBJ_TYPE] = OBJ_CMD_ZONES;
jsonReply += "\"zones\":";
return;
}

if (commandObject.startsWith(REST_CMD_ZONE)) {
commandDispatch[CD_OBJ_TYPE] = OBJ_CMD_ZONE; // command object type, 0 for none
jsonReply += "\"zone";

// get zone number


String zoneNumber = commandObject.substring(
commandObject.lastIndexOf('e') + 1,
commandObject.length() );

commandDispatch[CD_OBJ_ID] = stringToInt(zoneNumber); // command object id, 0 for none


jsonReply += zoneNumber;
jsonReply += "\":";
return;
}

// must check for plural form first


if (commandObject.compareTo("programs") == 0) {
commandDispatch[CD_OBJ_TYPE] = OBJ_CMD_PROGRAMS;
jsonReply += "\"programs\":";
return;
}
if (commandObject.startsWith("program")) {

5 de 8 05/09/2015 01:38 a.m.


irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

commandDispatch[CD_OBJ_TYPE] = OBJ_CMD_PROGRAM;
jsonReply += "\"program";

// get program number


String progNumber = commandObject.substring(
commandObject.lastIndexOf('m') + 1,
commandObject.length() );

commandDispatch[CD_OBJ_ID] = stringToInt(progNumber); // command object id, 0 for none


jsonReply += progNumber;
jsonReply += "\":";
return;
}

// interprets the command following the /zoneX/ command prefix


void findZoneCommand(char *zoneCmd){

String zoneCommand = String(zoneCmd);

// if zone command is empty, treat as a status request


if (zoneCommand.length() < 1){
commandDispatch[CD_CMD_CODE] = CC_STATUS;
// clear the json reply
jsonReply = "";
return;
}

zoneCommand = zoneCommand.toLowerCase();

// check for "ON"


if (zoneCommand.compareTo("on") == 0) {
commandDispatch[CD_CMD_CODE] = CC_ON;
jsonReply += "\"on\"";
return;
}

// check for "OFF"


if (zoneCommand.compareTo("off") == 0) {
commandDispatch[CD_CMD_CODE] = CC_OFF;
jsonReply += "\"off\"";
return;
}

// check for "status"


if (zoneCommand.compareTo(REST_CMD_STATUS) == 0) {
commandDispatch[CD_CMD_CODE] = CC_STATUS;
// clear the json reply
jsonReply = "";
return;
}

// Finds and sets the zone run time in seconds


void findZoneTimeValue(char *zoneTimeValue){

String zoneTime = String(zoneTimeValue);

if (zoneTime.lastIndexOf("s") >= 0 ){
// zone run request is in seconds (e.g., 30s)
zoneTime = zoneTime.substring(0, zoneTime.lastIndexOf("s"));
commandDispatch[CD_VALUE_1] = stringToInt(zoneTime);

} else {
int time = atoi(zoneTimeValue);
commandDispatch[CD_VALUE_1] = time * 60;
}

// interpret and execute settings commands


void findSettingsCommand(char *settings){

char *arg, *i;


int count=0;
// get the parameters
arg = strtok_r(settings,"?",&i);
Serial.print("arg ");
Serial.print(count);
Serial.print(": ");

6 de 8 05/09/2015 01:38 a.m.


irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

Serial.println(arg);

while (arg != NULL && count < 16){


arg = strtok_r(NULL,"&",&i);
if (arg != NULL) {
count++;
Serial.print("arg ");
Serial.print(count);
Serial.print(": ");
Serial.println(arg);
setSettings(arg);
}
}

// /settings with no parameters: return setting values


if ( count == 0 ){
jsonReply += "\"reportingEnabled\":";
if (reportingEnabled){
jsonReply += "\"true\"";
} else {
jsonReply += "\"false\"";
}
jsonReply += ",\"reportingHostName\":\"";
jsonReply += reportingHostName;
jsonReply += "\"";
jsonReply += ",\"reportingHostPort\":\"";
jsonReply += reportingHostPort;
jsonReply += "\"";
}

// send reply, send error message if message empty


if (jsonReply == NULL || jsonReply.length() == 0){
jsonReply = "\"settings\":\"parameters not recognized\"";
}
httpJsonReply(jsonReply);

// clear the command, so we don't re-execute


clearCommandDispatch();
}

void setSettings(char *valuePair){


char *aLabel = strtok(valuePair, "=");
char *aValue = strtok(NULL, "=");

String label = String(aLabel);


String value = String(aValue);

// check for "reportingEnabled" setting


if (label.compareTo("reportingEnabled") == 0 ||
label.compareTo("re") == 0) { // label shortcut
if(jsonReply != NULL && jsonReply.length() > 0){
jsonReply += ",";
}
if (value.compareTo("true") == 0) {
reportingEnabled = true;
jsonReply += "\"reportingEnabled\":\"true\"";
}
if (value.compareTo("false") == 0) {
reportingEnabled = false;
jsonReply += "\"reportingEnabled\":\"false\"";
}
return;
}

if (label.compareTo("reportingHostName") == 0 ||
label.compareTo("rhn") == 0){ // label shortcut
if(jsonReply != NULL && jsonReply.length() > 0){
jsonReply += ",";
}
if (value.length() >= 4) {
value.toCharArray(reportingHostName, 512);
jsonReply += "\"reportingHostName\":\"";
jsonReply += value;
jsonReply += "\"";
}
return;
}

if (label.compareTo("reportingHostPort") == 0 ||
label.compareTo("rhp") == 0){ // label shortcut
if(jsonReply != NULL && jsonReply.length() > 0){
jsonReply += ",";

7 de 8 05/09/2015 01:38 a.m.


irrduino/IrrduinoController.pde at master google/irrduino GitHub https://github.com/google/irrduino/blob/master/IrrduinoController/Irrd...

}
if (value.length() >= 1) {
reportingHostPort = stringToInt(value);
jsonReply += "\"reportingHostPort\":\"";
jsonReply += value;
jsonReply += "\"";
}
return;
}
}

// Utility functions

void clearCharArray(char array[], int length){


for (int i=0; i < length; i++) {
//if (array[i] == 0) {break; };
array[i] = 0;
}
}

void clearCommandDispatch(){
for (int i = 0; i < commandDispatchLength; i++){
commandDispatch[i] = 0;
}
}

void clearCommandRunning(){
for (int i = 0; i < commandRunningLength; i++){
commandRunning[i] = 0;
}
}

void clearCommandReport(){
for (int i = 0; i < commandReportLength; i++){
commandReport[i] = 0;
}
}

// standard function for debug/logging


void writeLog(String logMsg){

Serial.println(logMsg);

//TODO: buffer log message for web delivery


//TODO: write log message to SDCard (if available)

int stringToInt(String value){


// remember to add 1 to the length for the terminating null
char buffer[value.length() +1];
value.toCharArray(buffer, value.length() +1 );
return atoi(buffer);
}

2015 GitHub, Inc. Terms Privacy Security Contact Help Status API Training Shop Blog About Pricing

8 de 8 05/09/2015 01:38 a.m.

Vous aimerez peut-être aussi