/********************************************************* GREEN HVAC SCADA (c) 2003,4 Phil Malone Experimantal monitoring applications for Web Based HVAC SACDA Rev Date Comment ---- ------- -------------------------------------------- 1.6 01/14/2004 Added HVAC Control 1.5 01/11/2004 Added Power Monitoring Library 1.2 12/15/2003 Added Beta Brite Library 1.1 12/12/2003 Added 0.1 min rate and data filtering 1.0 12/02/2003 First incorporation of 1-Wire, Post and Server DS1 = Conversion in process DS2 = Post Connection Active DS3 = Posting Data, awaiting reply. **********************************************************/ #class auto // ----------------------------------------------- // Non Volatile UserBlock data // ----------------------------------------------- // Do not change the User Block ID unless you want to // clear out the Non Volatile section of user Storage #define USER_BLOCK_ID 0x00010000l //////////////////////////////////////////////////////////////////////// /* * TCPIP configuration */ #define TCPCONFIG 1 #define TCP_BUF_SIZE 1024 #define MAX_TCP_SOCKET_BUFFERS 8 // DownLoad Manager #define UDP_SOCKETS 2 // allow enough for downloader and DHCP #define MAX_UDP_SOCKET_BUFFERS 2 #define HTTP_MAXSERVERS 1 #define HTTP_MAXBUFFER 1024 #define FORM_ERROR_BUF 256 #define SSPEC_MAXSPEC 80 #define SAMPLE_INTERVAL_SEC 6 #define TEMPERATURE_FILTER_COEFF 0.20 #define RATE_INTERVAL_SEC 30 #define RATE_FILTER_COEFF 0.10 #define REDIRECTHOST _PRIMARY_STATIC_IP #define HTTP_NO_FLASHSPEC #define POST_INTERVAL_SEC 60 #memmap xmem #use "dcrtcp.lib" #use "http.lib" #use "udpdownl.lib" #use "ait_gen.lib" // General Functions etc #use "ait_ow.lib" // One-Wire Functions #use "ait_bb.lib" // Beta-Brite Functions #use "ait_pwr.lib" // Power Monitoring #use "ait_hvac.lib" // HVAC Control #use "ait_web.lib" // WEB Form Handlers // ----------------------------------------------- // Web Hosting files, variables & Constants // ----------------------------------------------- #ximport "index.shtml" index_shtml #ximport "setpoint.shtml" setpoint_shtml /* the default mime type for '/' must be first */ const HttpType http_types[] = { { ".shtml", "text/html", shtml_handler} //ssi }; /* * Remote computer to contact: */ #define DEST "www.ourcoolhouse.com" #define PORT 80 #define WEB_USER "OCH01" #define WEB_PASSWORD "jAzzmIn" // ----------------------------------------------- // One Wire Sensor Constants & Static Variables // ----------------------------------------------- #define MAX_SENSORS 50 const SENSOR_CONST DeviceData[] = { {"1E00080042EC5810",1.8,32.0,"G1"}, // 0 Ground Loop In {"4D00080042E7E710",1.8,32.0,"G2"}, // 1 W-W Return Loop {"9600080042EDA910",1.8,32.0,"G3"}, // 2 W-A Return Loop {"C50008004DABD610",1.8,32.0,"G4"}, // 3 W-W Source Input Sensor {"AC0008004DA95610",1.8,32.0,"G5"}, // 4 W-W Source Output Sensor {"C50008004DABD610",1.8,32.0,"G6"}, // 5 W-A Source Input Sensor {"AC0008004DA95610",1.8,32.0,"G7"}, // 6 W-A Source Output Sensor {"F800080042E47A10",1.8,32.0,"D1"}, // 7 {"A800080042F14410",1.8,32.0,"D2"}, // 8 {"B300080042F33410",1.8,33.0,"D3"}, // 9 {"B400080042E67010",1.8,31.0,"D4"}, // 10 Tank DHW feed {"A200080042EB6810",1.8,32.0,"D5"}, // 11 {"940008004DB0F310",1.8,32.0,"D6"}, // 12 Lower Tank {"8C0008004DBBC410",1.8,32.0,"D7"}, // 13 Upper Tank {"440008004DA8DC10",1.8,32.0,"A0"}, // 14 Outside Air {"F700080042E59F10",1.8,32.0,"A1"}, // 15 {"ED00080042E55810",1.8,30.0,"A2"}, // 16 {"CC00080042EA7310",1.8,32.0,"A3"}, // 17 {"6700080042E81610",1.8,32.0,"A4"}, // 18 {"2100080042E96810",1.8,33.0,"A5"}, // 19 {"6A00080042E20310",1.8,32.0,"A6"}, // 20 {"AE00080042E3CA10",1.8,32.5,"S1"}, // 21 {"9100080042EF2B10",1.8,32.0,"S2"}, // 22 {"5E00080042E0A610",1.8,31.5,"S3"}, // 23 {"7200080042EB0610",1.8,32.0,"S4"}, // 24 {"4C00080042E21F10",1.8,32.0,"S5"}, // 25 {"3800080042EA1210",1.8,32.0,"S6"}, // 26 {"BC00080042E8A210",1.8,32.0,"S7"}, // 27 {"A700080042F0A110",1.8,32.0,"S8"}, // 28 {"7800080042F45710",1.8,32.0,"S9"}, // 29 {"BC00080042EFED10",1.8,32.0,"S10"}, // 30 {"9F0008004DB67F10",1.8,32.0,"T1"}, // 31 {"410008004DB20C10",1.8,32.0,"T2"}, // 32 {"710008004DAF8110",1.8,32.0,"T3"}, // 33 {"850008004DAFE010",1.8,32.0,"T4"}, // 34 {"65000800401A3F10",1.8,32.0,"XX"}, // 0 Ground Loop (test whip) {0, 0.0,0.0, 0}, }; static SENSOR Devices[MAX_SENSORS]; static int ActiveDevices; // ----------------------------------------------- // One Wire Sensor Constants & Static Variables // ----------------------------------------------- const BB_MESSAGE BB_Messages[] = { {'1', 't', 'e', 200, 0, "\x10A\r \x10N-\x10Q\r\x10I\x10M-\x10B\r\x10J\x10M-\x10C\r\x10K\x10M-\x10D\r\x10L\x10M-\x10E\r\x10F\r\x10G \x10H\rFan-\x10O ERV-\x10P" }, {'A', 's', ' ', BB_MAX_LINE, 14, "Out %5.1f"}, {'B', 's', ' ', BB_MAX_LINE, 31, "Gar %5.1f"}, {'C', 's', ' ', BB_MAX_LINE, 32, "Off %5.1f"}, {'D', 's', ' ', BB_MAX_LINE, 33, "Kit %5.1f"}, {'E', 's', ' ', BB_MAX_LINE, 34, "Bed %5.1f"}, {'F', 's', ' ', BB_MAX_LINE, 36, "Load W %6.0f"}, {'G', 's', ' ', BB_MAX_LINE, 37, "KWH %4.0f "}, {'H', 's', ' ', BB_MAX_LINE, 38, "(%.0f)"}, {'I', 's', ' ', BB_MAX_LINE, 46, "%1.0f"}, // Z1 {'J', 's', ' ', BB_MAX_LINE, 45, "%1.0f"}, // Z2 {'K', 's', ' ', BB_MAX_LINE, 44, "%1.0f"}, // Z3 {'L', 's', ' ', BB_MAX_LINE, 43, "%1.0f"}, // Z4 {'M', 's', ' ', BB_MAX_LINE, 42, "%1.0f"}, // Circ {'N', 's', ' ', BB_MAX_LINE, 39, "%1.0f"}, // WW GSHP {'O', 's', ' ', BB_MAX_LINE, 40, "%1.0f"}, // Fan {'P', 's', ' ', BB_MAX_LINE, 41, "%1.0f"}, // ERV {'Q', 's', ' ', BB_MAX_LINE, 13, "DHW %5.1f"}, // DHW { 0, 0, 0, 0, 0, ""} // Termination }; // ----------------------------------------------- // Local Function Prototypes // ----------------------------------------------- int CGI_LiveData(HttpState *State); int CGI_Set(HttpState* state); int parse_post(HttpState *State); int http_scanfloat(char* tag, char* buffer, float* dest); //////////////////////////////////////////////////////////////////////// // ----------------------------------------------- // Main Program // ----------------------------------------------- main() { // ----------------------------------------------- // 1 Wire Variables // ----------------------------------------------- auto int Dev; auto int Hex; // ----------------------------------------------- // Network Post variables // ----------------------------------------------- auto int bytes_read; auto longword destIP; auto tcp_Socket socket; auto char URL[1024]; auto int D; // Check This auto long Timeout ; // ----------------------------------------------- // Web Server variables // ----------------------------------------------- // ----------------------------------------------- // Beta-Brite variables // ----------------------------------------------- int BBc; // ----------------------------------------------- // Initialize System Data & User Block // ----------------------------------------------- brdInit(); //initialize board for this demo HVAC_Off(); // Turn Off All HVAC Outputs memset(StartTimeText, 0, sizeof(StartTimeText)); memset(StartDateText, 0, sizeof(StartDateText)); memset(TimeText, 0, sizeof(TimeText)); memset(DateText, 0, sizeof(DateText)); AIT_InitUserBlock(); // ----------------------------------------------- // Initialize all 1-Wire interfaces // ----------------------------------------------- OW_Init(); // Initialize One Wire bus ActiveDevices = 0; // Transfer Static data into live data for sensors. // Adjust addresses as required. // This will normally pull from a config files. // Setup Sensor Data and Determine sensor count memset(&Devices, 0, sizeof(Devices)); for (Dev = 0; Dev < MAX_SENSORS; Dev++) { // Stop processing if we find a blank address or name if (!DeviceData[Dev].Name) break; strncpy(Devices[ActiveDevices].Name, DeviceData[Dev].Name, sizeof(Devices[Dev].Name)-1); for (Hex = 0; Hex < OW_ADDRESS_CHARS; Hex+=2) { Devices[ActiveDevices].Addr[Hex]= DeviceData[Dev].Addr[14 - Hex]; Devices[ActiveDevices].Addr[Hex+1]= DeviceData[Dev].Addr[15 - Hex]; } Devices[ActiveDevices].Type = DEV_18S20; // Temperature Sensor Devices[ActiveDevices].Scale = DeviceData[Dev].Scale; Devices[ActiveDevices].Offset = DeviceData[Dev].Offset; Devices[ActiveDevices].FilterTC = TEMPERATURE_FILTER_COEFF ; // Update the number of active devices. ActiveDevices++; } // ----------------------------------------------- // Initialize all Beta-Brite functions // ----------------------------------------------- BB_Init(); // ----------------------------------------------- // Initialize all Network functions // ----------------------------------------------- sock_init(); UDPDL_Init("Green HVAC"); destIP = 0L; // ----------------------------------------------- // Initialize Power Monitoring // ----------------------------------------------- ActiveDevices = PWR_Init(&Devices[0], ActiveDevices); // ----------------------------------------------- // Initialize HVAC Control Loop // ----------------------------------------------- ActiveDevices = HVAC_Init(&Devices[0], ActiveDevices); // ----------------------------------------------- // Initialize all Web Server interfaces // ----------------------------------------------- // Add pages (stored in flash). sspec_addxmemfile("/", index_shtml, SERVER_HTTP); sspec_addxmemfile("/index.shtml", index_shtml, SERVER_HTTP); sspec_addxmemfile("/setpoint.shtml", setpoint_shtml, SERVER_HTTP); sspec_addfunction("/livedata", CGI_LiveData, SERVER_HTTP); sspec_addfunction("/set", CGI_Set, SERVER_HTTP); // Add all the Sensors to the Variable List if(OW_AddServerVars(&Devices[0]) != OW_VARS_OK) puts ("Warning: Not all 1-wire variables registered with server\n"); if(HVAC_AddServerVars(&Devices[0]) != OW_VARS_OK) puts ("Warning: Not all HVAC variables registered with server\n"); http_init(); tcp_reserveport(80); // ----------------------------------------------- // Run main processing cycle. // ----------------------------------------------- while (1) { // This costatement is responsible checking the download Manager costate DlonloadManager always_on { // Run the HVAC Cycle if (UDPDL_Tick()) { HVAC_Off(); // Turn Off All HVAC Outputs AIT_WriteUserData(); while(1) UDPDL_Tick(); } waitfor (IntervalSec(2)); } // Check for Download // This costatement is responsible for scanning the 1-wire net // It runs all the time and shares time with the other costatements costate ScanTemperatures always_on { // Do Conversion wfd OW_ConvertAll(); // Get all temperature device readings and scale values wfd OW_GetTemps(&Devices[0], TRUE); // Filter Readings OW_FilterSensors(&Devices[0]); // Wait for next interval. waitfor (IntervalSec(SAMPLE_INTERVAL_SEC)); } // This costatement is responsible for calculating Temperature rates of change // It runs every RATE_INTERVAL_SEC costate DetermineRates always_on { waitfor (IntervalSec(RATE_INTERVAL_SEC)); // Filter Readings. OW_TempRates(&Devices[0], (float)RATE_INTERVAL_SEC / (float)SEC_PER_HOUR, RATE_FILTER_COEFF); } // This costatement is responsible for posting data online // It runs all the time and shares time with the other costatements // Posts happen every 30 seconds costate PostResults always_on { // Determine site's IP address if not already known if (destIP == 0L) { ledOut(WEB_CONNECT_LED, 1); putchar('i'); destIP = resolve(DEST); ledOut(WEB_CONNECT_LED, 0); waitfor (DelaySec(10)); } // Post current data if we have an IP if (destIP != 0L) { ledOut(WEB_CONNECT_LED, 1); putchar('p'); Timeout = SEC_TIMER + 5; tcp_open(&socket,0,destIP,PORT,NULL); while(!sock_established(&socket) && sock_bytesready(&socket)==-1 && (SEC_TIMER <= Timeout) ) { tcp_tick(NULL); yield ; } // Establish a connection to the server and post data if (sock_established(&socket)) { // Build URL post string OW_BuildWebPost(URL, sizeof(URL), WEB_USER, WEB_PASSWORD, &Devices[0]); // puts( URL ); // Post the URL and all the data it contains Timeout = SEC_TIMER + 5; sock_write(&socket, URL, strlen(URL)); do { yield ; bytes_read=sock_fastread(&socket,URL,sizeof(URL)-1); } while(tcp_tick(&socket) && (SEC_TIMER <= Timeout)); // Kill connection and we are done! sock_abort(&socket); } ledOut(WEB_CONNECT_LED, 0); waitfor (IntervalSec(POST_INTERVAL_SEC)); } } // This costatement is responsible for serving Web Pages // It runs all the time and shares time with the other costatements costate WebServer always_on { http_handler(); } costate BetaBrite always_on { // Set memory Configuration wfd BB_SetMemory(&BB_Messages[0]); wfd BB_SendTextMessages(&BB_Messages[0]); while(1) { // Display on Beta Brite wfd BB_SendStringMessages(&BB_Messages[0], &Devices[0]); } } // This costatement is responsible for calculating power usage costate AccumulatePower always_on { PWR_ReadMeter(&Devices[0]); waitfor (IntervalSec(POWER_INTERVAL_SEC)); } // This costatement is responsible for updating User Block Data costate SaveUserData always_on { waitfor (IntervalSec(USERBLOCK_INTERVAL_SEC)); // Write Updated User Block AIT_WriteUserData(); } // This costatement is responsible for running the control loop costate HVAC_Control always_on { // Start HVAC after suitable delay waitfor (IntervalSec(HVAC_DELAY_SEC)); // Run the HVAC Cycle while (1) { HVAC_Cycle(&Devices[0]); waitfor (IntervalSec(HVAC_INTERVAL_SEC)); } } } PWR_End(); exit(0); } //////////////////////////////////////////////////////////////////////// // ----------------------------------------------- // Web Server CGI Script Functions // ----------------------------------------------- int CGI_LiveData(HttpState *State) { char Temp[128]; if(State->length) { // Empty output buffer SWRITE_BUFFER } else { switch(State->substate) { case 0: // Start by writing header and body ledOut(WEB_FORM, 1); putchar('w'); SSEND("HTTP/1.0 200 OK\r\n\r\n"); SSEND("\n"); SSEND("\n"); SSEND("Running Since: "); SSEND(StartTimeText); SSEND(" "); SSEND(StartDateText); SSEND("
"); SSEND("\n"); SSEND("\n"); SSET_LENGTH State->substate++; break; case 1: // Write one line for each sensor if (Devices[State->subsubstate].Name[0]) { SSEND("\n"); SSET_LENGTH State->subsubstate++; } else { State->subsubstate = 0; State->substate++; } break; case 2: // End the page by closing the table SSEND("
SensorReadingFilterRate
"); SSEND(Devices[State->subsubstate].Name); SSEND(""); sprintf(Temp,"%6.2f", Devices[State->subsubstate].Scaled); SSEND(Temp); SSEND(""); sprintf(Temp,"%6.2f", Devices[State->subsubstate].Filtered); SSEND(Temp); SSEND(""); sprintf(Temp,"%.1f", Devices[State->subsubstate].Rate); SSEND(Temp); SSEND("
\n"); SSET_LENGTH State->substate++; break; default: // End the script State->substate = 0; ledOut(WEB_FORM, 0); return 1; } } return 0; } /* * Form used to set entered variables */ int CGI_Set(HttpState* state) { auto int Status; // Run the generic form for processing submitted data Status = WEB_Form(state); if (state->substate == 1) { AIT_WriteUserData(); cgi_redirectto(state, "http://192.168.0.100/setpoint.shtml"); } return Status; }