diff --git a/.gitignore b/.gitignore index ed5fad1..06e276a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,77 @@ -logs/** -nvm.dat -OpenSprinkler -wtopts.txt -stns.dat +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Icon must ends with two \r. +Icon + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +#Visual Studio files +*.[Oo]bj +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.sbr +*.sdf +*.pyc +*.xml +*.sln +*.elf +*.bin +*.ino.elf +*.ino.bin +*.cpp.o +*.buildinfo +*.vcxproj +*.vcxproj.filters +x64/Release*/* +x64/Debug*/* +ipch/ +obj/* +_vm/* +__vm/ +.vs/* +[Bb]in*/ +[Dd]ebug*/ +[Rr]elease*/ \ No newline at end of file diff --git a/Documents/SG_Alarm&Flow_v4.pdf b/Documents/SG_Alarm&Flow_v4.pdf new file mode 100644 index 0000000..59ee431 Binary files /dev/null and b/Documents/SG_Alarm&Flow_v4.pdf differ diff --git a/EtherCard.cpp b/EtherCard.cpp index 2301a92..0c54b5c 100644 --- a/EtherCard.cpp +++ b/EtherCard.cpp @@ -9,7 +9,7 @@ // // 2010-05-19 -#include +#include "EtherCard.h" #include #include diff --git a/OpenSprinkler.cpp b/OpenSprinkler.cpp index 8922c44..4cbdcfe 100644 --- a/OpenSprinkler.cpp +++ b/OpenSprinkler.cpp @@ -1195,6 +1195,7 @@ void httpget_callback(byte status, uint16_t off, uint16_t len) { * The remote controller is assumed to have the same * password as the main controller */ + void OpenSprinkler::switch_remotestation(RemoteStationData *data, bool turnon) { #if defined(ARDUINO) // construct string diff --git a/mainArduino.ino b/SG21.ino similarity index 97% rename from mainArduino.ino rename to SG21.ino index 66fc480..26de318 100644 --- a/mainArduino.ino +++ b/SG21.ino @@ -21,7 +21,7 @@ * . */ -#include +#include "OpenSprinkler.h" extern OpenSprinkler os; diff --git a/cloud.cpp b/cloud.cpp new file mode 100644 index 0000000..d981e6d --- /dev/null +++ b/cloud.cpp @@ -0,0 +1,841 @@ +#include "cloud.h" +#include "OpenSprinkler.h" +#include "program.h" +#include "weather.h" +#include "server.h" +#include "SensorGroup.h" +extern char tmp_buffer[]; // scratch buffer + +// ========================================== +// ====== PUSH NOTIFICATION FUNCTIONS ======= +// ========================================== +void ip2string(char* str, byte ip[4]) { + for (byte i = 0; i<4; i++) { + itoa(ip[i], str + strlen(str), 10); + if (i != 3) strcat(str, "."); + } +} + +void push_message(byte type, uint32_t lval, float fval, const char* sval) { + +#if !defined(ARDUINO) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + + static const char* server = DEFAULT_IFTTT_URL; + static char key[IFTTT_KEY_MAXSIZE]; + static char postval[TMP_BUFFER_SIZE]; + + //@tcs: return when no internet connection + if (os.status.network_fails != 0) return; //do not post if network failed + + // check if this type of event is enabled for push notification + if ((os.options[OPTION_IFTTT_ENABLE] & type) == 0) return; + key[0] = 0; + read_from_file(ifkey_filename, key); + key[IFTTT_KEY_MAXSIZE - 1] = 0; + + if (strlen(key) == 0) return; + +#if defined(ARDUINO) + uint16_t _port = ether.hisport; // make a copy of the original port + ether.hisport = 80; +#endif + + strcpy_P(postval, PSTR("{\"value1\":\"")); + + switch (type) { + + case IFTTT_STATION_RUN: + + strcat_P(postval, PSTR("Station ")); + os.get_station_name(lval, postval + strlen(postval)); + strcat_P(postval, PSTR(" closed. It ran for ")); + itoa((int)fval / 60, postval + strlen(postval), 10); + strcat_P(postval, PSTR(" minutes ")); + itoa((int)fval % 60, postval + strlen(postval), 10); + strcat_P(postval, PSTR(" seconds.")); + if (os.options[OPTION_FSENSOR_TYPE] == SENSOR_TYPE_FLOW) { + strcat_P(postval, PSTR(" Flow rate: ")); +#if defined(ARDUINO) + dtostrf(sensors.realtime_GPM, 5, 2, postval + strlen(postval)); +#else + sprintf(tmp_buffer + strlen(tmp_buffer), "%5.2f", flow_last_gpm); +#endif + } + break; + + case IFTTT_PROGRAM_SCHED: + + if (sval) strcat_P(postval, PSTR("Manually scheduled ")); + else strcat_P(postval, PSTR("Automatically scheduled ")); + strcat_P(postval, PSTR("Program ")); + { + ProgramStruct prog; + pd.read(lval, &prog); + if (lval0) { + strcat_P(postval, PSTR("External IP updated: ")); + byte ip[4] = { (lval >> 24) & 0xFF,(lval >> 16) & 0xFF,(lval >> 8) & 0xFF,lval & 0xFF }; + ip2string(postval, ip); + } + if (fval >= 0) { + strcat_P(postval, PSTR("Water level updated: ")); + itoa((int)fval, postval + strlen(postval), 10); + strcat_P(postval, PSTR("%.")); + } + + break; + + case IFTTT_REBOOT: +#if defined(ARDUINO) + strcat_P(postval, PSTR("Rebooted. Device IP: ")); + ip2string(postval, ether.myip); + strcat(postval, ":"); + itoa(_port, postval + strlen(postval), 10); +#else + strcat_P(postval, PSTR("Process restarted.")); +#endif + break; + } + + strcat_P(postval, PSTR("\"}")); + + DEBUG_PRINT("THE IFTTT MESSAGE: "); + DEBUG_PRINTLN(postval); + +#if defined(ARDUINO) + + if (!ether.dnsLookup(server, true)) { + // if DNS lookup fails, use default IP + ether.hisip[0] = 54; + ether.hisip[1] = 172; + ether.hisip[2] = 244; + ether.hisip[3] = 116; + } + + ether.httpPostVar(PSTR("/trigger/sprinkler/with/key/"), PSTR(DEFAULT_IFTTT_URL), key, postval, httpget_callback); + for (int l = 0; l<100; l++) ether.packetLoop(ether.packetReceive()); + ether.hisport = _port; + +#else + + EthernetClient client; + struct hostent *host; + + host = gethostbyname(server); + if (!host) { + DEBUG_PRINT("can't resolve http station - "); + DEBUG_PRINTLN(server); + return; + } + + if (!client.connect((uint8_t*)host->h_addr, 80)) { + client.stop(); + return; + } + + char postBuffer[1500]; + sprintf(postBuffer, "POST /trigger/sprinkler/with/key/%s HTTP/1.0\r\n" + "Host: %s\r\n" + "Accept: */*\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "\r\n%s", key, host->h_name, strlen(postval), postval); + client.write((uint8_t *)postBuffer, strlen(postBuffer)); + + bzero(ether_buffer, ETHER_BUFFER_SIZE); + + time_t timeout = now() + 5; // 5 seconds timeout + while (now() < timeout) { + int len = client.read((uint8_t *)ether_buffer, ETHER_BUFFER_SIZE); + if (len <= 0) { + if (!client.connected()) + break; + else + continue; + } + httpget_callback(0, 0, ETHER_BUFFER_SIZE); + } + + client.stop(); + +#endif + +#endif +} + + +//*********************************************** +//****** CLOUD communication +//*********************************************** + +void cloud_json_stations_attrib(const char* name, int addr, char* postval_pt) +{ + DEBUG_PRINTLN(String("NAME: ") + String(name)); + + strcat_P(postval_pt, PSTR("\"")); + strcpy(tmp_buffer, name); + strcat(postval_pt, tmp_buffer); + strcat_P(postval_pt, PSTR("\":[")); + + byte *attrib = (byte*)tmp_buffer; + os.station_attrib_bits_load(addr, attrib); + for (byte i = 0; iday>0 last days history if day > 366 the filename (unix day) + + + ***************** EXAMPLES + + cloud server messages + push_message_cloud(SEND_CLOUD_OPTIONS, 0); //Send options to cloud server + + ************** Cloud Message Type * + #define SEND_CLOUD_OPTIONS 0 + #define SEND_CLOUD_SETTINGS 1 + #define SEND_CLOUD_PROGRAMS 2 + #define SEND_CLOUD_STATIONS 3 + #define SEND_CLOUD_STATUS_SPEC 4 + #define SEND_CLOUD_LOG 5 + + Received cloud server response and commands: + {"result":x} x=1 means "success", see API 217 1st page and Tables_SG20 page API_SG20. + change password + change options, variables(settings), etc. + process by void handle_web_request(char *p) + + */ + + const char cloud_message_id[] PROGMEM = + "rjo\0" //options /jo + "rjc\0" //settings /jc + "rjp\0" //programs /jp + "rjn\0" //station /jn + "rjs\0" //station status and special station data /js, /je + "rjl\0"; //log records /jl + + +#if !defined(ARDUINO) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) + + DEBUG_PRINT("freeRam1: "); + DEBUG_PRINTLN(freeRam()); + + if (os.status.network_fails != 0) return 4; //do not post if network failed + + static const char* server = DEFAULT_CLOUD_URL; + static char postval[POST_BUFFER_SIZE] = { '\0' }; //TMP_BUFFER_SIZE is too small! + char buf[] = { 0,0,0,0,0,0,0,0,0,0,0,0,'\0' }; + byte b, ret_val = 0; + + DEBUG_PRINT("freeRam2: "); + DEBUG_PRINTLN(freeRam()); + + millis_cnt = millis(); + + + // preparing http POST sendings to cloud server + // sending one by one jc, jo jp, jn, js packets + // plus the logrecords in a packet + + // message identifier based on package contenttype + strcpy_P(postval, PSTR("{\"mid\":\"")); + + switch (type) { + case SEND_CLOUD_OPTIONS: + strcpy(tmp_buffer, "rjo\0"); + break; + case SEND_CLOUD_SETTINGS: + strcpy(tmp_buffer, "rjc\0"); + break; + case SEND_CLOUD_PROGRAMS: + strcpy(tmp_buffer, "rjp\0"); + break; + case SEND_CLOUD_STATIONS: + strcpy(tmp_buffer, "rjn\0"); + break; + case SEND_CLOUD_STATUS_SPEC: + strcpy(tmp_buffer, "rjs\0"); + break; + case SEND_CLOUD_LOG: + strcpy(tmp_buffer, "rjl\0"); + break; + + } + /* DEBUG_PRINT("type / tmp: "); + DEBUG_PRINT(type); + DEBUG_PRINT(" / "); + DEBUG_PRINT(tmp_buffer); + DEBUG_PRINTLN(); + */ + + //puts the password and the MAC adress + //the password have to be scrambled (ToDo) + strcat(postval, tmp_buffer); + sprintf(tmp_buffer, "\",\"lpw\":%s,\"macad\":\"", client_pw); + strcat(postval, tmp_buffer); + + for (byte i = 0; i<6; i++) { + b = ether.mymac[i] >> 4; + buf[2 * i] = (b<10) ? (b + 48) : (b + 55); + b = ether.mymac[i] & 0x0F; + buf[2 * i + 1] = (b<10) ? (b + 48) : (b + 55); + } + strcat(postval, buf); + strcat_P(postval, PSTR("\",")); + + // end of message header + + switch (type) { + case SEND_CLOUD_LOG: { + if (!day) day = os.now_tz() / 86400L; + + strcat_P(postval, PSTR("[")); + itoa(day, tmp_buffer, 10); + make_logfile_name(tmp_buffer); + SdFile file; + DEBUG_PRINTLN(tmp_buffer); + +#if defined(ARDUINO) // prepare to open log file for Arduino + if (!sd.exists(tmp_buffer)) { + last_sent_log = 0; + ret_val = 3; //filename error don't send postval + break; + } + file.open(tmp_buffer, O_READ); +#endif // prepare to open log file + + bool comma = 0; + ulong rec_cnt = 0, temp_cnt = 0; + int res; + while (true) { +#if defined(ARDUINO) + res = file.fgets(tmp_buffer, TMP_BUFFER_SIZE); + if (res <= 0) { + file.close(); + ret_val = 2; //end of file, send postval + last_sent_log = 0; + break; + } +#endif + tmp_buffer[TMP_BUFFER_SIZE - 1] = 0; // make sure the search will end + + // if this is the first record, do not print comma + rec_cnt++; + if (rec_cnt > last_sent_log) { //this is the first record to send + if (comma) + strcat_P(postval, PSTR(",")); + else { + comma = 1; + temp_cnt = rec_cnt; + } + + if (strlen(postval) + strlen(tmp_buffer) + 3 > POST_BUFFER_SIZE) { //will fit to postval? + file.close(); + ret_val = 1; //postval is full, send it, the file not finished + break; + } + strcat(postval, tmp_buffer); + + last_sent_log++; + } + } + + strcat_P(postval, PSTR("]}")); + } + break; + + case SEND_CLOUD_OPTIONS: // based on server_json_options_main() in server.cpp + { + + byte oid; + for (oid = 0; oid 1) { + pd.drem_to_relative(prog.days); + } + byte flag_0 = *(char*)(&prog); //read program structure binary attributes (called flag) value + byte soil_flags = *((char*)(&prog) + 1); //read soil sensor program attach flag value + + //bfill.emit_p(PSTR("[$D,$D,$D,$D,["), flag_0, soil_flags, prog.days[0], prog.days[1] + //sprintf(tmp_buffer,"[%d,%d,%d,%d,[", flag_0, soil_flags, prog.days[0], prog.days[1]); + //strcat(postval, tmp_buffer); + strcat_P(postval, PSTR("[")); + itoa(flag_0, tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR(",")); + itoa(soil_flags, tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR(",")); + itoa(prog.days[0], tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR(",")); + itoa(prog.days[1], tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR(",[")); + + // start times data + for (i = 0; i<(MAX_NUM_STARTTIMES - 1); i++) { + //bfill.emit_p(PSTR("$D,"), prog.starttimes[i]); + //sprintf(tmp_buffer,"%d,", prog.starttimes[i]); + //strcat(postval, tmp_buffer); + itoa(prog.starttimes[i], tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR(",")); + + } + //bfill.emit_p(PSTR("$D],["), prog.starttimes[i]); // this is the last element + //sprintf(tmp_buffer,"%d],[", prog.starttimes[i]); // this is the last element + //strcat(postval, tmp_buffer); + itoa(prog.starttimes[i], tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR("],[")); + + // station water time + for (i = 0; i= q->st) ? (q->st + q->dur - curr_time) : q->dur; + if (rem>65535) rem = 0; + } + //bfill.emit_p(PSTR("[$D,$L,$L]"), (qid<255)?q->pid:0, rem, (qid<255)?q->st:0); + strcat_P(postval, PSTR("[")); + itoa(((qid<255) ? q->pid : 0), tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR(",")); + itoa(rem, tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR(",")); + itoa(((qid<255) ? q->st : 0), tmp_buffer, 10); + strcat(postval, tmp_buffer); + strcat_P(postval, PSTR("]")); + + //bfill.emit_p((sidh_addr, 80)) { + client.stop(); + return; + } + + char postBuffer[1500]; + sprintf(postBuffer, "POST /trigger/sprinkler/with/key/%s HTTP/1.0\r\n" + "Host: %s\r\n" + "Accept: */*\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "\r\n%s", key, host->h_name, strlen(postval), postval); + client.write((uint8_t *)postBuffer, strlen(postBuffer)); + + bzero(ether_buffer, ETHER_BUFFER_SIZE); + + time_t timeout = now() + 5; // 5 seconds timeout + while (now() < timeout) { + int len = client.read((uint8_t *)ether_buffer, ETHER_BUFFER_SIZE); + if (len <= 0) { + if (!client.connected()) + break; + else + continue; + } + httpget_callback(0, 0, ETHER_BUFFER_SIZE); + } + + client.stop(); + +#endif + +#endif + + return ret_val; +} + diff --git a/cloud.h b/cloud.h new file mode 100644 index 0000000..d5ee28f --- /dev/null +++ b/cloud.h @@ -0,0 +1,8 @@ +#pragma once + + +void ip2string(char * str, byte ip[4]); +void push_message(byte type, uint32_t lval = 0, float fval = 0.f, const char* sval = NULL); + +void cloud_json_stations_attrib(const char * name, int addr, char * postval_pt); +byte push_message_cloud(byte type, ulong day = 0); \ No newline at end of file diff --git a/main.cpp b/main.cpp index c9793d5..a093641 100644 --- a/main.cpp +++ b/main.cpp @@ -28,7 +28,7 @@ #include "weather.h" #include "server.h" #include "SensorGroup.h" - +#include "cloud.h" #if defined(ARDUINO) @@ -54,13 +54,13 @@ EthernetClient *m_client = 0; void reset_all_stations(); void reset_all_stations_immediate(); -void push_message(byte type, uint32_t lval=0, float fval=0.f, const char* sval=NULL); +//void push_message(byte type, uint32_t lval=0, float fval=0.f, const char* sval=NULL); void manual_start_program(byte, byte); void httpget_callback(byte, uint16_t, uint16_t); // @tcsaba: new functions void check_sensors(ulong curr_time); void check_network(); -byte push_message_cloud(byte type, ulong day=0); +//byte push_message_cloud(byte type, ulong day=0); int freeRam(); void make_logfile_name(char *name); @@ -1010,7 +1010,7 @@ void turn_off_station(byte sid, ulong curr_time) { */ void process_dynamic_events(ulong curr_time) { bool rain = false, soil1_wet = false, soil2_wet = false; - bool en = os.status.enabled ? true : false; + bool en = os.status.enabled ? true : false; bool fatal_closeout = false; // check if rain is detected @@ -1197,839 +1197,6 @@ void manual_start_program(byte pid, byte uwt) { } } -// ========================================== -// ====== PUSH NOTIFICATION FUNCTIONS ======= -// ========================================== -void ip2string(char* str, byte ip[4]) { - for(byte i=0;i<4;i++) { - itoa(ip[i], str+strlen(str), 10); - if(i!=3) strcat(str, "."); - } -} - -void push_message(byte type, uint32_t lval, float fval, const char* sval) { - -#if !defined(ARDUINO) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) - - static const char* server = DEFAULT_IFTTT_URL; - static char key[IFTTT_KEY_MAXSIZE]; - static char postval[TMP_BUFFER_SIZE]; - - //@tcs: return when no internet connection - if(os.status.network_fails != 0) return; //do not post if network failed - - // check if this type of event is enabled for push notification - if((os.options[OPTION_IFTTT_ENABLE]&type) == 0) return; - key[0] = 0; - read_from_file(ifkey_filename, key); - key[IFTTT_KEY_MAXSIZE-1]=0; - - if(strlen(key)==0) return; - - #if defined(ARDUINO) - uint16_t _port = ether.hisport; // make a copy of the original port - ether.hisport = 80; - #endif - - strcpy_P(postval, PSTR("{\"value1\":\"")); - - switch(type) { - - case IFTTT_STATION_RUN: - - strcat_P(postval, PSTR("Station ")); - os.get_station_name(lval, postval+strlen(postval)); - strcat_P(postval, PSTR(" closed. It ran for ")); - itoa((int)fval/60, postval+strlen(postval), 10); - strcat_P(postval, PSTR(" minutes ")); - itoa((int)fval%60, postval+strlen(postval), 10); - strcat_P(postval, PSTR(" seconds.")); - if(os.options[OPTION_FSENSOR_TYPE]==SENSOR_TYPE_FLOW) { - strcat_P(postval, PSTR(" Flow rate: ")); - #if defined(ARDUINO) - dtostrf(sensors.realtime_GPM,5,2,postval+strlen(postval)); - #else - sprintf(tmp_buffer+strlen(tmp_buffer), "%5.2f", flow_last_gpm); - #endif - } - break; - - case IFTTT_PROGRAM_SCHED: - - if(sval) strcat_P(postval, PSTR("Manually scheduled ")); - else strcat_P(postval, PSTR("Automatically scheduled ")); - strcat_P(postval, PSTR("Program ")); - { - ProgramStruct prog; - pd.read(lval, &prog); - if(lval0) { - strcat_P(postval, PSTR("External IP updated: ")); - byte ip[4] = {(lval>>24)&0xFF,(lval>>16)&0xFF,(lval>>8)&0xFF,lval&0xFF}; - ip2string(postval, ip); - } - if(fval>=0) { - strcat_P(postval, PSTR("Water level updated: ")); - itoa((int)fval, postval+strlen(postval), 10); - strcat_P(postval, PSTR("%.")); - } - - break; - - case IFTTT_REBOOT: - #if defined(ARDUINO) - strcat_P(postval, PSTR("Rebooted. Device IP: ")); - ip2string(postval, ether.myip); - strcat(postval, ":"); - itoa(_port, postval+strlen(postval), 10); - #else - strcat_P(postval, PSTR("Process restarted.")); - #endif - break; - } - - strcat_P(postval, PSTR("\"}")); - - DEBUG_PRINT("THE IFTTT MESSAGE: "); - DEBUG_PRINTLN(postval); - -#if defined(ARDUINO) - - if(!ether.dnsLookup(server, true)) { - // if DNS lookup fails, use default IP - ether.hisip[0] = 54; - ether.hisip[1] = 172; - ether.hisip[2] = 244; - ether.hisip[3] = 116; - } - - ether.httpPostVar(PSTR("/trigger/sprinkler/with/key/"), PSTR(DEFAULT_IFTTT_URL), key, postval, httpget_callback); - for(int l=0;l<100;l++) ether.packetLoop(ether.packetReceive()); - ether.hisport = _port; - -#else - - EthernetClient client; - struct hostent *host; - - host = gethostbyname(server); - if (!host) { - DEBUG_PRINT("can't resolve http station - "); - DEBUG_PRINTLN(server); - return; - } - - if (!client.connect((uint8_t*)host->h_addr, 80)) { - client.stop(); - return; - } - - char postBuffer[1500]; - sprintf(postBuffer, "POST /trigger/sprinkler/with/key/%s HTTP/1.0\r\n" - "Host: %s\r\n" - "Accept: */*\r\n" - "Content-Length: %d\r\n" - "Content-Type: application/json\r\n" - "\r\n%s", key, host->h_name, strlen(postval), postval); - client.write((uint8_t *)postBuffer, strlen(postBuffer)); - - bzero(ether_buffer, ETHER_BUFFER_SIZE); - - time_t timeout = now() + 5; // 5 seconds timeout - while(now() < timeout) { - int len=client.read((uint8_t *)ether_buffer, ETHER_BUFFER_SIZE); - if (len<=0) { - if(!client.connected()) - break; - else - continue; - } - httpget_callback(0, 0, ETHER_BUFFER_SIZE); - } - - client.stop(); - -#endif - -#endif -} - - -//*********************************************** -//****** CLOUD communication -//*********************************************** - -void cloud_json_stations_attrib(const char* name, int addr, char* postval_pt) -{ - DEBUG_PRINTLN(String("NAME: ")+ String(name)); - - strcat_P(postval_pt, PSTR("\"")); - strcpy(tmp_buffer, name); - strcat(postval_pt, tmp_buffer); - strcat_P(postval_pt, PSTR("\":[")); - - byte *attrib = (byte*)tmp_buffer; - os.station_attrib_bits_load(addr, attrib); - for(byte i=0;iday>0 last days history if day > 366 the filename (unix day) - - -***************** EXAMPLES - -cloud server messages -push_message_cloud(SEND_CLOUD_OPTIONS, 0); //Send options to cloud server - -************** Cloud Message Type * -#define SEND_CLOUD_OPTIONS 0 -#define SEND_CLOUD_SETTINGS 1 -#define SEND_CLOUD_PROGRAMS 2 -#define SEND_CLOUD_STATIONS 3 -#define SEND_CLOUD_STATUS_SPEC 4 -#define SEND_CLOUD_LOG 5 - -Received cloud server response and commands: -{"result":x} x=1 means "success", see API 217 1st page and Tables_SG20 page API_SG20. -change password -change options, variables(settings), etc. -process by void handle_web_request(char *p) - -*/ - -const char cloud_message_id[] PROGMEM = -"rjo\0" //options /jo -"rjc\0" //settings /jc -"rjp\0" //programs /jp -"rjn\0" //station /jn -"rjs\0" //station status and special station data /js, /je -"rjl\0"; //log records /jl - - -#if !defined(ARDUINO) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) - -DEBUG_PRINT("freeRam1: "); -DEBUG_PRINTLN(freeRam()); - - if(os.status.network_fails != 0) return 4; //do not post if network failed - - static const char* server = DEFAULT_CLOUD_URL; - static char postval[POST_BUFFER_SIZE] = {'\0'}; //TMP_BUFFER_SIZE is too small! - char buf[] = {0,0,0,0,0,0,0,0,0,0,0,0,'\0'}; - byte b, ret_val=0; - -DEBUG_PRINT("freeRam2: "); -DEBUG_PRINTLN(freeRam()); - - millis_cnt = millis(); - - -// preparing http POST sendings to cloud server -// sending one by one jc, jo jp, jn, js packets -// plus the logrecords in a packet - -// message identifier based on package contenttype - strcpy_P(postval, PSTR("{\"mid\":\"")); - - switch(type){ - case SEND_CLOUD_OPTIONS : - strcpy(tmp_buffer, "rjo\0"); - break; - case SEND_CLOUD_SETTINGS: - strcpy(tmp_buffer, "rjc\0"); - break; - case SEND_CLOUD_PROGRAMS: - strcpy(tmp_buffer, "rjp\0"); - break; - case SEND_CLOUD_STATIONS: - strcpy(tmp_buffer, "rjn\0"); - break; - case SEND_CLOUD_STATUS_SPEC: - strcpy(tmp_buffer, "rjs\0"); - break; - case SEND_CLOUD_LOG: - strcpy(tmp_buffer, "rjl\0"); - break; - - } -/* DEBUG_PRINT("type / tmp: "); - DEBUG_PRINT(type); - DEBUG_PRINT(" / "); - DEBUG_PRINT(tmp_buffer); - DEBUG_PRINTLN(); -*/ - -//puts the password and the MAC adress -//the password have to be scrambled (ToDo) - strcat(postval,tmp_buffer); - sprintf(tmp_buffer, "\",\"lpw\":%s,\"macad\":\"",client_pw ); - strcat(postval, tmp_buffer); - - for (byte i = 0; i<6; i++) { - b = ether.mymac[i] >> 4; - buf[2*i]= (b<10) ? (b + 48) : (b + 55); - b = ether.mymac[i] & 0x0F; - buf[2*i+1] = (b<10) ? (b + 48) : (b + 55); - } - strcat(postval, buf); - strcat_P(postval, PSTR("\",")); - -// end of message header - - switch (type){ - case SEND_CLOUD_LOG: { - if(!day) day = os.now_tz() / 86400L; - - strcat_P(postval, PSTR("[")); - itoa(day, tmp_buffer, 10); - make_logfile_name(tmp_buffer); - SdFile file; - DEBUG_PRINTLN(tmp_buffer); - - #if defined(ARDUINO) // prepare to open log file for Arduino - if (!sd.exists(tmp_buffer)){ - last_sent_log = 0; - ret_val = 3; //filename error don't send postval - break; - } - file.open(tmp_buffer, O_READ); - #endif // prepare to open log file - - bool comma = 0; - ulong rec_cnt=0, temp_cnt=0; - int res; - while(true) { - #if defined(ARDUINO) - res = file.fgets(tmp_buffer, TMP_BUFFER_SIZE); - if (res <= 0) { - file.close(); - ret_val = 2; //end of file, send postval - last_sent_log = 0; - break; - } - #endif - tmp_buffer[TMP_BUFFER_SIZE-1]=0; // make sure the search will end - - // if this is the first record, do not print comma - rec_cnt++; - if(rec_cnt > last_sent_log){ //this is the first record to send - if (comma) - strcat_P(postval,PSTR(",")); - else { - comma=1; - temp_cnt = rec_cnt; - } - - if(strlen(postval) + strlen(tmp_buffer) + 3 > POST_BUFFER_SIZE){ //will fit to postval? - file.close(); - ret_val = 1; //postval is full, send it, the file not finished - break; - } - strcat(postval,tmp_buffer); - - last_sent_log++; - } - } - - strcat_P(postval, PSTR("]}")); - } - break; - - case SEND_CLOUD_OPTIONS: // based on server_json_options_main() in server.cpp - { - - byte oid; - for(oid=0;oid 1) { - pd.drem_to_relative(prog.days); - } - byte flag_0 = *(char*)(&prog); //read program structure binary attributes (called flag) value - byte soil_flags = *((char*)(&prog)+1); //read soil sensor program attach flag value - - //bfill.emit_p(PSTR("[$D,$D,$D,$D,["), flag_0, soil_flags, prog.days[0], prog.days[1] - //sprintf(tmp_buffer,"[%d,%d,%d,%d,[", flag_0, soil_flags, prog.days[0], prog.days[1]); - //strcat(postval, tmp_buffer); - strcat_P(postval,PSTR("[")); - itoa( flag_0, tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR(",")); - itoa( soil_flags, tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR(",")); - itoa( prog.days[0], tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR(",")); - itoa(prog.days[1], tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR(",[")); - - // start times data - for (i=0;i<(MAX_NUM_STARTTIMES-1);i++) { - //bfill.emit_p(PSTR("$D,"), prog.starttimes[i]); - //sprintf(tmp_buffer,"%d,", prog.starttimes[i]); - //strcat(postval, tmp_buffer); - itoa(prog.starttimes[i], tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR(",")); - - } - //bfill.emit_p(PSTR("$D],["), prog.starttimes[i]); // this is the last element - //sprintf(tmp_buffer,"%d],[", prog.starttimes[i]); // this is the last element - //strcat(postval, tmp_buffer); - itoa(prog.starttimes[i], tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR("],[")); - - // station water time - for (i=0; i= q->st) ? (q->st+q->dur-curr_time) : q->dur; - if(rem>65535) rem = 0; - } - //bfill.emit_p(PSTR("[$D,$L,$L]"), (qid<255)?q->pid:0, rem, (qid<255)?q->st:0); - strcat_P(postval,PSTR("[")); - itoa( ((qid<255)?q->pid:0), tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR(",")); - itoa( rem, tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR(",")); - itoa( ((qid<255)?q->st:0), tmp_buffer, 10); - strcat(postval, tmp_buffer); - strcat_P(postval,PSTR("]")); - - //bfill.emit_p((sidh_addr, 80)) { - client.stop(); - return; - } - - char postBuffer[1500]; - sprintf(postBuffer, "POST /trigger/sprinkler/with/key/%s HTTP/1.0\r\n" - "Host: %s\r\n" - "Accept: */*\r\n" - "Content-Length: %d\r\n" - "Content-Type: application/json\r\n" - "\r\n%s", key, host->h_name, strlen(postval), postval); - client.write((uint8_t *)postBuffer, strlen(postBuffer)); - - bzero(ether_buffer, ETHER_BUFFER_SIZE); - - time_t timeout = now() + 5; // 5 seconds timeout - while(now() < timeout) { - int len=client.read((uint8_t *)ether_buffer, ETHER_BUFFER_SIZE); - if (len<=0) { - if(!client.connected()) - break; - else - continue; - } - httpget_callback(0, 0, ETHER_BUFFER_SIZE); - } - - client.stop(); - - #endif - -#endif - -return ret_val; -} - - // ================================ // ====== LOGGING FUNCTIONS ======= // ================================ diff --git a/server.cpp b/server.cpp index bec66ce..ca01210 100644 --- a/server.cpp +++ b/server.cpp @@ -1532,7 +1532,7 @@ URLHandler urls[] = { server_delete_program, // dp server_change_program, // cp server_change_runonce, // cr - server_manual_program, // mp + server_manual_program, // mp server_moveup_program, // up server_json_programs, // jp server_change_options, // co