structure7 / hvacmonitor Goto Github PK
View Code? Open in Web Editor NEWMonitoring supply/return air temps, run status, and on/off durations.
License: MIT License
Monitoring supply/return air temps, run status, and on/off durations.
License: MIT License
To avoid "wasting cooled coils" (and this is probably long term), but force blower to run longer (FAN ON mode only) until supply temp equals (or is closer to) return?
Like
void loop() {
if (connect(server)) {
if (sendRequest(server, resource) && skipResponseHeaders()) {
char response[MAX_CONTENT_SIZE];
readReponseContent(response, sizeof(response));
UserData userData;
if (parseUserData(response, &userData)) {
printUserData(&userData);
}
}
disconnect();
}
wait();
}
// Close the connection with the HTTP server
void disconnect() {
Serial.println("Disconnect");
client.stop();
}
// Pause for a 1 minute
void wait() {
Serial.println("Wait 60 seconds");
delay(60000);
}
Local guy was running 105F when airport was 99F or so. Heck... maybe just the airport!
/api/myApiKey/conditions/q/KPHX.json
By way of Blynk.virtualWrite(0, tempFreezer + String("°F"));
Use keyboard ALT-248 for ° in lieu of copy/paste from web
Only if needed for debugging.
Like ERR, but OFF while blower status is off.
Plenty of virtual pins to go around!
-166F or +185F really screws up the history graph. Screen these so they report (maybe) 65.4321 if <0F or >150F.
Just to avoid issues with RTC server... if there are any connection issues. Lost internet connection won't lose that data.
*** Will an RTC run on an ESP8266-01?!
Temperature data trend line looks funny and of course I can't control that.
"753 minutes" is kind of random! Not sure where to cut it off (over two hours? over four? Maybe over 1 hours call it ** min(s), **hr(s)?
If temp split is too high after running for X minutes. Probably between 3-5, the watching of which is stopped when the blower stops. Need to record how quickly the split shrinks when the compressor stops and the blower still runs to avoid false positives.
Since V18 eeCurrent essentially stores the number of HVAC runs, maybe use this so that runtime displays looks something like 16 min. (2 times)
.
Also, instead of 0 minutes on no runtime, maybe None
.
Pseudo:
Y = (eeCurrent - 1); // The EEPROM next address (1 means hasn't run yet. 2 means 1 run, etc.)
if (runtime == 0)
{display("0 minutes")};
else if (runtime > 0)
{display("X mins. (Y runs))};
Perhaps an array collecting data under the condition that day == some number.
int blowerArray[];
int blowerStop, blowerStart, blowerDuration,
if // Blower stopped
{
blowerStop = (millis or timelib unix time);
blowerStopDay = day();
blowerDuration = (blowerStop - blowerStart);
blowerDuration = (blowerDuration / 1000); // Convert to minutes or something
blowerArray[] = blowerDuration; // Can this work? Just filling array as it goes? Might need FOR loop.
lcd.print(0,0, "Today's runtime:")
lcd.print(0,1, (Sum of everything in blowerArray[]);
}
else // Blower started
{
if blowerStartDay == blowerStopDay
{
blowerStart = (millis or timelib unix time);
blowerStartDay = day();
}
else
{
blowerArray[] = 0; // Clear the array
}
}
Look at annual high and low and report ERR if out of bounds?
Unless it beats up on EEPROM but should just be a matter of extracting from the array.
http://cloud.blynk.cc:8080/myBlynkAuth/pin/v1
gives supply temp/status
Maybe deposit into an array, pull the lowest SA and RA?
All I know is that this works. Credit [email protected]
from here. Now just to see if it plays nice with Blynk.
#include <ESP8266WiFi.h>
#ifndef min
#define min(x,y) (((x)<(y))?(x):(y))
#endif
#ifndef max
#define max(x,y) (((x)>(y))?(x):(y))
#endif
#include <ArduinoJson.h>
static const char SSID[] = "ssid";
static const char PASSWORD[] = "pw";
// Use your own API key by signing up for a free developer account.
// http://www.wunderground.com/weather/api/
//#define WU_API_KEY "myAPIkey"
// Specify your favorite location one of these ways.
//#define WU_LOCATION "pws:KAZTEMPE29"
// US ZIP code
//#define WU_LOCATION "90210"
// Country and city
#define WU_LOCATION "Australia/Sydney"
// 5 minutes between update checks. The free developer account has a limit
// on the number of calls so don't go wild.
#define DELAY_NORMAL (5*60*1000)
// 20 minute delay between updates after an error
#define DELAY_ERROR (20*60*1000)
/***********************************************************
*
* Sample requests and responses
*
http://api.wunderground.com/api/c596b7af83bae426/conditions/q/CA/San_Francisco.json
{
"response": {
...don't care
}
},
"current_observation": {
"image": {
...don't care
},
"display_location": {
...don't care
},
"observation_location": {
...don't care
},
"estimated": {},
"station_id": "KCASANFR58",
"observation_time": "Last Updated on June 27, 5:27 PM PDT",
"observation_time_rfc822": "Wed, 27 Jun 2012 17:27:13 -0700",
"observation_epoch": "1340843233",
"local_time_rfc822": "Wed, 27 Jun 2012 17:27:14 -0700",
"local_epoch": "1340843234",
"local_tz_short": "PDT",
"local_tz_long": "America/Los_Angeles",
"local_tz_offset": "-0700",
"weather": "Partly Cloudy",
"temperature_string": "66.3 F (19.1 C)",
"temp_f": 66.3,
"temp_c": 19.1,
"relative_humidity": "65%",
"wind_string": "From the NNW at 22.0 MPH Gusting to 28.0 MPH",
"wind_dir": "NNW",
"wind_degrees": 346,
"wind_mph": 22.0,
"wind_gust_mph": "28.0",
"wind_kph": 35.4,
"wind_gust_kph": "45.1",
"pressure_mb": "1013",
"pressure_in": "29.93",
"pressure_trend": "+",
"dewpoint_string": "54 F (12 C)",
"dewpoint_f": 54,
"dewpoint_c": 12,
"heat_index_string": "NA",
"heat_index_f": "NA",
"heat_index_c": "NA",
"windchill_string": "NA",
"windchill_f": "NA",
"windchill_c": "NA",
"feelslike_string": "66.3 F (19.1 C)",
"feelslike_f": "66.3",
"feelslike_c": "19.1",
"visibility_mi": "10.0",
"visibility_km": "16.1",
"solarradiation": "",
"UV": "5",
"precip_1hr_string": "0.00 in ( 0 mm)",
"precip_1hr_in": "0.00",
"precip_1hr_metric": " 0",
"precip_today_string": "0.00 in (0 mm)",
"precip_today_in": "0.00",
"precip_today_metric": "0",
"icon": "partlycloudy",
"icon_url": "http://icons-ak.wxug.com/i/c/k/partlycloudy.gif",
"forecast_url": "http://www.wunderground.com/US/CA/San_Francisco.html",
"history_url": "http://www.wunderground.com/history/airport/KCASANFR58/2012/6/27/DailyHistory.html",
"ob_url": "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.773285,-122.417725"
}
}
TODO alerts
http://api.wunderground.com/api/c596b7af83bae426/alerts/q/IA/Des_Moines.json
{
"response": {
"version": "0.1",
"termsofService": "http://www.wunderground.com/weather/api/d/terms.html",
"features": {
"alerts": 1
}
},
"alerts": [{
"type": "HEA",
"description": "Heat Advisory",
"date": "11:14 am CDT on July 3, 2012",
"date_epoch": "1341332040",
"expires": "7:00 AM CDT on July 07, 2012",
"expires_epoch": "1341662400",
"message": "\u000A...Heat advisory remains in effect until 7 am CDT Saturday...\u000A\u000A* temperature...heat indices of 100 to 105 are expected each \u000A afternoon...as Max temperatures climb into the mid to upper \u000A 90s...combined with dewpoints in the mid 60s to around 70. \u000A Heat indices will remain in the 75 to 80 degree range at \u000A night. \u000A\u000A* Impacts...the hot and humid weather will lead to an increased \u000A risk of heat related stress and illnesses. \u000A\u000APrecautionary/preparedness actions...\u000A\u000AA heat advisory means that a period of hot temperatures is\u000Aexpected. The combination of hot temperatures and high humidity\u000Awill combine to create a situation in which heat illnesses are\u000Apossible. Drink plenty of fluids...stay in an air-conditioned\u000Aroom...stay out of the sun...and check up on relatives...pets...\u000Aneighbors...and livestock.\u000A\u000ATake extra precautions if you work or spend time outside. Know\u000Athe signs and symptoms of heat exhaustion and heat stroke. Anyone\u000Aovercome by heat should be moved to a cool and shaded location.\u000AHeat stroke is an emergency...call 9 1 1.\u000A\u000A\u000A\u000AMjb\u000A\u000A\u000A",
"phenomena": "HT",
"significance": "Y",
"ZONES": [
{
"state":"UT",
"ZONE":"001"
}
],
"StormBased": {
"vertices":[
{
"lat":"38.87",
"lon":"-87.13"
}
,
{
"lat":"38.89",
"lon":"-87.13"
}
,
{
"lat":"38.91",
"lon":"-87.11"
}
,
{
"lat":"38.98",
"lon":"-86.93"
}
,
{
"lat":"38.87",
"lon":"-86.69"
}
,
{
"lat":"38.75",
"lon":"-86.3"
}
,
{
"lat":"38.84",
"lon":"-87.16"
}
],
"Vertex_count":7,
"stormInfo": {
"time_epoch": 1363464360,
"Motion_deg": 243,
"Motion_spd": 18,
"position_lat":38.90,
"position_lon":-86.96
}
}
}]
}
*************************************************************/
#define WUNDERGROUND "api.wunderground.com"
// HTTP request
const char WUNDERGROUND_REQ[] =
"GET /api/a76b5f288962f088/conditions/q/pws:KAZTEMPE29.json HTTP/1.1\r\n"
"User-Agent: ESP8266/0.1\r\n"
"Accept: */*\r\n"
"Host: " WUNDERGROUND "\r\n"
"Connection: close\r\n"
"\r\n";
void setup()
{
Serial.begin(115200);
// We start by connecting to a WiFi network
Serial.println();
Serial.println();
Serial.print(F("Connecting to "));
Serial.println(SSID);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.println(F("WiFi connected"));
Serial.println(F("IP address: "));
Serial.println(WiFi.localIP());
}
static char respBuf[4096];
bool showWeather(char *json);
void loop()
{
// TODO check for disconnect from AP
// Open socket to WU server port 80
Serial.print(F("Connecting to "));
Serial.println(WUNDERGROUND);
// Use WiFiClient class to create TCP connections
WiFiClient httpclient;
const int httpPort = 80;
if (!httpclient.connect(WUNDERGROUND, httpPort)) {
Serial.println(F("connection failed"));
delay(DELAY_ERROR);
return;
}
// This will send the http request to the server
Serial.print(WUNDERGROUND_REQ);
httpclient.print(WUNDERGROUND_REQ);
httpclient.flush();
// Collect http response headers and content from Weather Underground
// HTTP headers are discarded.
// The content is formatted in JSON and is left in respBuf.
int respLen = 0;
bool skip_headers = true;
while (httpclient.connected() || httpclient.available()) {
if (skip_headers) {
String aLine = httpclient.readStringUntil('\n');
//Serial.println(aLine);
// Blank line denotes end of headers
if (aLine.length() <= 1) {
skip_headers = false;
}
}
else {
int bytesIn;
bytesIn = httpclient.read((uint8_t *)&respBuf[respLen], sizeof(respBuf) - respLen);
Serial.print(F("bytesIn ")); Serial.println(bytesIn);
if (bytesIn > 0) {
respLen += bytesIn;
if (respLen > sizeof(respBuf)) respLen = sizeof(respBuf);
}
else if (bytesIn < 0) {
Serial.print(F("read error "));
Serial.println(bytesIn);
}
}
delay(1);
}
httpclient.stop();
if (respLen >= sizeof(respBuf)) {
Serial.print(F("respBuf overflow "));
Serial.println(respLen);
delay(DELAY_ERROR);
return;
}
// Terminate the C string
respBuf[respLen++] = '\0';
Serial.print(F("respLen "));
Serial.println(respLen);
//Serial.println(respBuf);
if (showWeather(respBuf)) {
delay(DELAY_NORMAL);
}
else {
delay(DELAY_ERROR);
}
}
bool showWeather(char *json)
{
StaticJsonBuffer<3*1024> jsonBuffer;
// Skip characters until first '{' found
// Ignore chunked length, if present
char *jsonstart = strchr(json, '{');
//Serial.print(F("jsonstart ")); Serial.println(jsonstart);
if (jsonstart == NULL) {
Serial.println(F("JSON data missing"));
return false;
}
json = jsonstart;
// Parse JSON
JsonObject& root = jsonBuffer.parseObject(json);
if (!root.success()) {
Serial.println(F("jsonBuffer.parseObject() failed"));
return false;
}
// Extract weather info from parsed JSON
JsonObject& current = root["current_observation"];
const float temp_f = current["temp_f"];
Serial.print(temp_f, 1); Serial.print(F(" °F "));
/*
const float temp_c = current["temp_c"];
Serial.print(temp_c, 1); Serial.print(F(" C, "));
const char *humi = current[F("relative_humidity")];
Serial.print(humi); Serial.println(F(" RH"));
const char *weather = current["weather"];
Serial.println(weather);
const char *pressure_mb = current["pressure_mb"];
Serial.println(pressure_mb);
const char *observation_time = current["observation_time_rfc822"];
Serial.println(observation_time);
*/
//Date/time string looks like this
//Wed, 27 Jun 2012 17:27:14 -0700
//012345678901234567890
// 1 2
// LCD has 14 characters per line so show date on one line and time on the
// next. And shorten the date so it fits.
/*
char date[14+1];
const char *time;
//Wed, 27 Jun 12
memcpy(date, observation_time, 12);
memcpy(&date[12], &observation_time[14], 2);
date[14] = '\0';
//17:27:14 -0700
time = &observation_time[17];
*/
return true;
}
Below the first "1" in 11:30 will blink.
HVAC OFF SINCE
11:30AM on 12/1
WITH alarm notification!
i.e.: One time timer code (or for loop, or something) to send notification if sensor reads < 0 or > 184 (at least for supply and return) indicating an error (units in error will report -196.6 or 185).
Maybe build in a 5 second "grace period:"
Can probably just "freeze" this readout until a blower state change.
Maybe use a divider like /2
or /4
Probably need to append with RTC info (Unix time or the like) for each Tweet to be unique: https://github.com/blynkkk/blynk-library/blob/master/examples/Widgets/Twitter/Twitter.ino
Would be great to find a service where I can just jump log-type date to replace terminal/Twitter. Would be great if I had the option to make it publicly accessible if I want... hmmm... sounds like I found another reason to start learning SQL programming.
How much does it cache?
Experiment with how this affects Blynk after app startup. Maybe need to use that project sync code?
Idea is that virtual LCD doesn't repeatedly generate.
Maybe note delta from Attic temp at the time.
Says 24,000,000 minutes or something
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.