Added heating elements, small refactor of code
parent
9ef2374cd5
commit
b1f15935d5
|
|
@ -0,0 +1,156 @@
|
||||||
|
#include <LiquidCrystal.h>
|
||||||
|
#include <dht_nonblocking.h>
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#include <WiFiEsp.h>
|
||||||
|
|
||||||
|
// Wifi (Hotspot)
|
||||||
|
const char wifiSSID[] = "FreeWifi";
|
||||||
|
const char wifiPASS[] = "temppassword";
|
||||||
|
|
||||||
|
// ESP8266 (Wifi module, AT firmware)
|
||||||
|
SoftwareSerial wifiSerial(2, 3); // RX = 2, TX = 3
|
||||||
|
WiFiEspClient client;
|
||||||
|
|
||||||
|
// LCD Display
|
||||||
|
#define DHT_SENSOR_TYPE DHT_TYPE_11
|
||||||
|
static const int DHT_SENSOR_PIN = A1;
|
||||||
|
DHT_nonblocking dht_sensor(DHT_SENSxOR_PIN, DHT_SENSOR_TYPE);
|
||||||
|
|
||||||
|
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // RS, EN, D4, D5, D6, D7
|
||||||
|
|
||||||
|
// Light sensor and LEDs
|
||||||
|
int lightPin = A0; // LDR
|
||||||
|
int dataPin = 4; // DS
|
||||||
|
int latchPin = 5; // ST_CP
|
||||||
|
int clockPin = 6; // SH_CP
|
||||||
|
int leds = 0;
|
||||||
|
|
||||||
|
// API
|
||||||
|
const float NO_TEMP = -999.0;
|
||||||
|
const unsigned long API_INTERVAL = 600000UL; // 10m
|
||||||
|
const unsigned long RETRY_INTERVAL = 30000UL; // 30s
|
||||||
|
unsigned long lastApiFetch = 0;
|
||||||
|
unsigned long fetchInterval = API_INTERVAL;
|
||||||
|
float outdoorTemp = NO_TEMP;
|
||||||
|
|
||||||
|
// Connect to hotspot if not connected, then fetch the current temperature
|
||||||
|
float fetchOutdoorTemp() {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
WiFi.begin(wifiSSID, wifiPASS);
|
||||||
|
if (WiFi.status() != WL_CONNECTED) return NO_TEMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.setTimeout(5000);
|
||||||
|
if (!client.connect("api.open-meteo.com", 80)) return NO_TEMP;
|
||||||
|
|
||||||
|
client.print(F("GET /v1/forecast?latitude=49.46998&longitude=8.4819¤t=temperature_2m HTTP/1.1\r\n"
|
||||||
|
"Host: api.open-meteo.com\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n"));
|
||||||
|
|
||||||
|
// Skip ahead to the number in {"current":{..."temperature_2m":12.3}}
|
||||||
|
float t = NO_TEMP;
|
||||||
|
if (client.find("\"current\":") && client.find("\"temperature_2m\":")) {
|
||||||
|
t = client.parseFloat();
|
||||||
|
}
|
||||||
|
client.stop();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to LCD display
|
||||||
|
void lcdLine(uint8_t row, const char *text) {
|
||||||
|
lcd.setCursor(0, row);
|
||||||
|
uint8_t i = 0;
|
||||||
|
for (; text[i] != '\0' && i < 16; i++) lcd.write(text[i]);
|
||||||
|
for (; i < 16; i++) lcd.write(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// LEDs for light sensor
|
||||||
|
void updateShiftRegister() {
|
||||||
|
digitalWrite(latchPin, LOW);
|
||||||
|
shiftOut(dataPin, clockPin, LSBFIRST, leds);
|
||||||
|
digitalWrite(latchPin, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure temperature and humidity
|
||||||
|
static bool measure_environment(float *temp, float *hum) {
|
||||||
|
static unsigned long timestamp = millis();
|
||||||
|
|
||||||
|
if (millis() - timestamp > 3000ul) {
|
||||||
|
if (dht_sensor.measure(temp, hum) == true) {
|
||||||
|
timestamp = millis();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(latchPin, OUTPUT);
|
||||||
|
pinMode(dataPin, OUTPUT);
|
||||||
|
pinMode(clockPin, OUTPUT);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
lcd.begin(16, 2);
|
||||||
|
lcdLine(0, "Connecting WiFi");
|
||||||
|
lcdLine(1, "");
|
||||||
|
|
||||||
|
wifiSerial.begin(9600);
|
||||||
|
WiFi.init(&wifiSerial);
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_NO_SHIELD) {
|
||||||
|
lcdLine(0, "ESP not found");
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
outdoorTemp = fetchOutdoorTemp();
|
||||||
|
lastApiFetch = millis();
|
||||||
|
fetchInterval = (outdoorTemp == NO_TEMP) ? RETRY_INTERVAL : API_INTERVAL;
|
||||||
|
lcd.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
float indoorTemp;
|
||||||
|
float indoorHum;
|
||||||
|
|
||||||
|
if (measure_environment(&indoorTemp, &indoorHum) == true) {
|
||||||
|
char tempStr[10];
|
||||||
|
dtostrf(indoorTemp, 4, 1, tempStr);
|
||||||
|
char line0[20];
|
||||||
|
snprintf(line0, sizeof(line0), "In: %s C", tempStr);
|
||||||
|
lcdLine(0, line0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - lastApiFetch >= fetchInterval) {
|
||||||
|
float t = fetchOutdoorTemp();
|
||||||
|
lastApiFetch = millis();
|
||||||
|
if (t != NO_TEMP) {
|
||||||
|
outdoorTemp = t;
|
||||||
|
fetchInterval = API_INTERVAL;
|
||||||
|
} else {
|
||||||
|
fetchInterval = RETRY_INTERVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outdoorTemp != NO_TEMP) {
|
||||||
|
char outStr[10];
|
||||||
|
dtostrf(outdoorTemp, 4, 1, outStr);
|
||||||
|
char line1[20];
|
||||||
|
snprintf(line1, sizeof(line1), "Out: %s C", outStr);
|
||||||
|
lcdLine(1, line1);
|
||||||
|
} else {
|
||||||
|
lcdLine(1, "Out: API Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
int reading = analogRead(lightPin);
|
||||||
|
int numLEDSLit = reading / 57;
|
||||||
|
|
||||||
|
if (numLEDSLit > 8) numLEDSLit = 8;
|
||||||
|
leds = 0;
|
||||||
|
for (int i = 0; i < numLEDSLit; i++) {
|
||||||
|
leds = leds + (1 << i);
|
||||||
|
}
|
||||||
|
updateShiftRegister();
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,303 @@
|
||||||
|
#include <LiquidCrystal.h>
|
||||||
|
#include <dht_nonblocking.h>
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
|
||||||
|
// Wifi (Hotspot)
|
||||||
|
const char wifiSSID[] = "FreeWifi";
|
||||||
|
const char wifiPASS[] = "temppassword";
|
||||||
|
|
||||||
|
// ESP8266 (Wifi module)
|
||||||
|
SoftwareSerial wifiSerial(2, 3); // RX = 2, TX = 3
|
||||||
|
const bool DEBUG = true; // Serial Monitor debug output
|
||||||
|
const int responseTime = 100;
|
||||||
|
|
||||||
|
// LCD Display
|
||||||
|
#define DHT_SENSOR_TYPE DHT_TYPE_11
|
||||||
|
static const int DHT_SENSOR_PIN = A1;
|
||||||
|
DHT_nonblocking dht_sensor(DHT_SENSOR_PIN, DHT_SENSOR_TYPE);
|
||||||
|
|
||||||
|
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // RS, EN, D4, D5, D6, D7
|
||||||
|
|
||||||
|
// Light sensor and LEDs
|
||||||
|
int lightPin = A0; // LDR
|
||||||
|
int dataPin = 4; // DS
|
||||||
|
int latchPin = 5; // ST_CP
|
||||||
|
int clockPin = 6; // SH_CP
|
||||||
|
int leds = 0;
|
||||||
|
|
||||||
|
// API
|
||||||
|
const float NO_TEMP = -999.0;
|
||||||
|
const unsigned long API_INTERVAL = 600000UL; // 10m
|
||||||
|
const unsigned long RETRY_INTERVAL = 30000UL; // 30s
|
||||||
|
unsigned long lastApiFetch = 0;
|
||||||
|
unsigned long fetchInterval = API_INTERVAL;
|
||||||
|
float outdoorTemp = NO_TEMP;
|
||||||
|
|
||||||
|
// HTTP GET request
|
||||||
|
const char HTTP_REQUEST[] PROGMEM =
|
||||||
|
"GET /v1/forecast?latitude=49.46998&longitude=8.4819¤t=temperature_2m HTTP/1.1\r\n"
|
||||||
|
"Host: api.open-meteo.com\r\n"
|
||||||
|
"Accept-Encoding: identity\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n";
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
// Clear serial buffer
|
||||||
|
void flushInput() {
|
||||||
|
while (wifiSerial.available()) wifiSerial.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard incoming data for a given time
|
||||||
|
void drain(unsigned long ms) {
|
||||||
|
unsigned long start = millis();
|
||||||
|
while (millis() - start < ms) flushInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send AT command
|
||||||
|
void espSend(const __FlashStringHelper *cmd) {
|
||||||
|
flushInput();
|
||||||
|
wifiSerial.println(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for specific responses
|
||||||
|
uint8_t waitForEither(const char *a, const char *b, unsigned long timeoutMs) {
|
||||||
|
const uint8_t na = strlen(a);
|
||||||
|
const uint8_t nb = strlen(b);
|
||||||
|
char wa[24] = {0};
|
||||||
|
char wb[24] = {0};
|
||||||
|
unsigned long start = millis();
|
||||||
|
|
||||||
|
while (millis() - start < timeoutMs) {
|
||||||
|
while (wifiSerial.available()) {
|
||||||
|
char c = wifiSerial.read();
|
||||||
|
if (DEBUG) Serial.write(c);
|
||||||
|
|
||||||
|
memmove(wa, wa + 1, na - 1);
|
||||||
|
wa[na - 1] = c;
|
||||||
|
memmove(wb, wb + 1, nb - 1);
|
||||||
|
wb[nb - 1] = c;
|
||||||
|
|
||||||
|
if (memcmp(wa, a, na) == 0) return 1;
|
||||||
|
if (memcmp(wb, b, nb) == 0) return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waitFor(const char *token, unsigned long timeoutMs) {
|
||||||
|
return waitForEither(token, token, timeoutMs) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse float from serial stream
|
||||||
|
float readNumber(unsigned long timeoutMs) {
|
||||||
|
char buf[10];
|
||||||
|
uint8_t i = 0;
|
||||||
|
bool done = false;
|
||||||
|
unsigned long start = millis();
|
||||||
|
|
||||||
|
while (!done && millis() - start < timeoutMs) {
|
||||||
|
while (wifiSerial.available()) {
|
||||||
|
char c = wifiSerial.read();
|
||||||
|
if (c == ',' || c == '}') { done = true; break; }
|
||||||
|
if (i < sizeof(buf) - 1) buf[i++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[i] = '\0';
|
||||||
|
if (!done || i == 0) return NO_TEMP;
|
||||||
|
return atof(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to Wifi
|
||||||
|
bool joinWifi() {
|
||||||
|
espSend(F("AT+CWMODE=1"));
|
||||||
|
waitForEither("OK", "ERROR", 1000);
|
||||||
|
|
||||||
|
flushInput();
|
||||||
|
wifiSerial.print(F("AT+CWJAP=\""));
|
||||||
|
wifiSerial.print(wifiSSID);
|
||||||
|
wifiSerial.print(F("\",\""));
|
||||||
|
wifiSerial.print(wifiPASS);
|
||||||
|
wifiSerial.println('"');
|
||||||
|
if (waitForEither("OK", "FAIL", 20000) != 1) return false;
|
||||||
|
|
||||||
|
espSend(F("AT+CIPDNS_CUR=1,\"8.8.8.8\",\"8.8.4.4\""));
|
||||||
|
waitForEither("OK", "ERROR", 1000);
|
||||||
|
|
||||||
|
espSend(F("AT+CIPMUX=0"));
|
||||||
|
waitForEither("OK", "ERROR", 1000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Wifi
|
||||||
|
bool ensureWifi() {
|
||||||
|
espSend(F("AT+CWJAP?"));
|
||||||
|
if (waitForEither("+CWJAP:", "No AP", 3000) == 1) return true;
|
||||||
|
Serial.println(F("\nWi-Fi not connected, (re)joining..."));
|
||||||
|
return joinWifi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Temperature
|
||||||
|
float fetchOutdoorTemp() {
|
||||||
|
Serial.println(F("\n--- Fetching outdoor temp ---"));
|
||||||
|
drain(300);
|
||||||
|
|
||||||
|
if (!ensureWifi()) {
|
||||||
|
Serial.println(F("\nERROR: no Wi-Fi"));
|
||||||
|
return NO_TEMP;
|
||||||
|
}
|
||||||
|
drain(200);
|
||||||
|
|
||||||
|
espSend(F("AT+CIPCLOSE"));
|
||||||
|
waitForEither("OK", "ERROR", 1000);
|
||||||
|
|
||||||
|
espSend(F("AT+CIPSTART=\"TCP\",\"api.open-meteo.com\",80"));
|
||||||
|
if (waitForEither("OK", "ERROR", 10000) != 1) {
|
||||||
|
Serial.println(F("\nERROR: could not open TCP socket"));
|
||||||
|
return NO_TEMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
flushInput();
|
||||||
|
wifiSerial.print(F("AT+CIPSEND="));
|
||||||
|
wifiSerial.println(strlen_P(HTTP_REQUEST));
|
||||||
|
if (!waitFor(">", 3000)) {
|
||||||
|
Serial.println(F("\nERROR: no '>' prompt from ESP"));
|
||||||
|
espSend(F("AT+CIPCLOSE"));
|
||||||
|
waitForEither("OK", "ERROR", 1000);
|
||||||
|
return NO_TEMP;
|
||||||
|
}
|
||||||
|
wifiSerial.print((const __FlashStringHelper *)HTTP_REQUEST);
|
||||||
|
|
||||||
|
float t = NO_TEMP;
|
||||||
|
if (waitFor("\"current\":", 8000) && waitFor("\"temperature_2m\":", 2000)) {
|
||||||
|
t = readNumber(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
espSend(F("AT+CIPCLOSE"));
|
||||||
|
waitForEither("OK", "ERROR", 1000);
|
||||||
|
|
||||||
|
if (t == NO_TEMP) {
|
||||||
|
Serial.println(F("\nERROR: no temperature in response"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("\nSUCCESS! Outdoor temp: "));
|
||||||
|
Serial.println(t);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to LCD display
|
||||||
|
void lcdLine(uint8_t row, const char *text) {
|
||||||
|
lcd.setCursor(0, row);
|
||||||
|
uint8_t i = 0;
|
||||||
|
for (; text[i] != '\0' && i < 16; i++) lcd.write(text[i]);
|
||||||
|
for (; i < 16; i++) lcd.write(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// LEDs for light sensor
|
||||||
|
void updateShiftRegister() {
|
||||||
|
digitalWrite(latchPin, LOW);
|
||||||
|
shiftOut(dataPin, clockPin, LSBFIRST, leds);
|
||||||
|
digitalWrite(latchPin, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure temperature and humidity
|
||||||
|
static bool measure_environment(float *temp, float *hum) {
|
||||||
|
static unsigned long timestamp = millis();
|
||||||
|
|
||||||
|
if (millis() - timestamp > 3000ul) {
|
||||||
|
if (dht_sensor.measure(temp, hum) == true) {
|
||||||
|
timestamp = millis();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main methods
|
||||||
|
void setup() {
|
||||||
|
pinMode(latchPin, OUTPUT);
|
||||||
|
pinMode(dataPin, OUTPUT);
|
||||||
|
pinMode(clockPin, OUTPUT);
|
||||||
|
pinMode(13, OUTPUT);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
lcd.begin(16, 2);
|
||||||
|
lcdLine(0, "Init WiFi...");
|
||||||
|
lcdLine(1, "");
|
||||||
|
|
||||||
|
wifiSerial.begin(9600);
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
bool alive = false;
|
||||||
|
for (uint8_t i = 0; i < 3 && !alive; i++) {
|
||||||
|
espSend(F("AT"));
|
||||||
|
alive = (waitForEither("OK", "ERROR", 1000) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!alive) {
|
||||||
|
Serial.println(F("\nESP8266 does not answer."));
|
||||||
|
lcdLine(0, "ESP not found");
|
||||||
|
delay(2000);
|
||||||
|
} else {
|
||||||
|
espSend(F("ATE0"));
|
||||||
|
waitForEither("OK", "ERROR", 1000);
|
||||||
|
|
||||||
|
lcdLine(0, "Connecting WiFi");
|
||||||
|
if (joinWifi()) {
|
||||||
|
lcdLine(0, "WiFi Connected!");
|
||||||
|
} else {
|
||||||
|
lcdLine(0, "WiFi failed");
|
||||||
|
}
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
lcd.clear();
|
||||||
|
|
||||||
|
outdoorTemp = fetchOutdoorTemp();
|
||||||
|
lastApiFetch = millis();
|
||||||
|
fetchInterval = (outdoorTemp == NO_TEMP) ? RETRY_INTERVAL : API_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
float indoorTemp;
|
||||||
|
float indoorHum;
|
||||||
|
|
||||||
|
if (measure_environment(&indoorTemp, &indoorHum) == true) {
|
||||||
|
char tempStr[10];
|
||||||
|
dtostrf(indoorTemp, 4, 1, tempStr);
|
||||||
|
char line0[20];
|
||||||
|
snprintf(line0, sizeof(line0), "In: %s C", tempStr);
|
||||||
|
lcdLine(0, line0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - lastApiFetch >= fetchInterval) {
|
||||||
|
float t = fetchOutdoorTemp();
|
||||||
|
lastApiFetch = millis();
|
||||||
|
if (t != NO_TEMP) {
|
||||||
|
outdoorTemp = t;
|
||||||
|
fetchInterval = API_INTERVAL;
|
||||||
|
} else {
|
||||||
|
fetchInterval = RETRY_INTERVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outdoorTemp != NO_TEMP) {
|
||||||
|
char outStr[10];
|
||||||
|
dtostrf(outdoorTemp, 4, 1, outStr);
|
||||||
|
char line1[20];
|
||||||
|
snprintf(line1, sizeof(line1), "Out: %s C", outStr);
|
||||||
|
lcdLine(1, line1);
|
||||||
|
} else {
|
||||||
|
lcdLine(1, "Out: API Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
int reading = analogRead(lightPin);
|
||||||
|
int numLEDSLit = reading / 57;
|
||||||
|
|
||||||
|
if (numLEDSLit > 8) numLEDSLit = 8;
|
||||||
|
leds = 0;
|
||||||
|
for (int i = 0; i < numLEDSLit; i++) {
|
||||||
|
leds = leds + (1 << i);
|
||||||
|
}
|
||||||
|
updateShiftRegister();
|
||||||
|
|
||||||
|
delay(responseTime);
|
||||||
|
}
|
||||||
|
|
@ -14,25 +14,35 @@ WiFiEspClient client;
|
||||||
// LCD Display
|
// LCD Display
|
||||||
#define DHT_SENSOR_TYPE DHT_TYPE_11
|
#define DHT_SENSOR_TYPE DHT_TYPE_11
|
||||||
static const int DHT_SENSOR_PIN = A1;
|
static const int DHT_SENSOR_PIN = A1;
|
||||||
DHT_nonblocking dht_sensor(DHT_SENSxOR_PIN, DHT_SENSOR_TYPE);
|
DHT_nonblocking dht_sensor(DHT_SENSOR_PIN, DHT_SENSOR_TYPE);
|
||||||
|
|
||||||
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // RS, EN, D4, D5, D6, D7
|
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // RS, EN, D4, D5, D6, D7
|
||||||
|
|
||||||
// Light sensor and LEDs
|
// Light sensor and LEDs
|
||||||
int lightPin = A0; // LDR
|
int lightPin = A0; // LDR
|
||||||
int dataPin = 4; // DS
|
int dataPin = 4; // DS
|
||||||
int latchPin = 5; // ST_CP
|
int latchPin = 5; // ST_CP
|
||||||
int clockPin = 6; // SH_CP
|
int clockPin = 6; // SH_CP
|
||||||
int leds = 0;
|
int leds = 0;
|
||||||
|
|
||||||
// API
|
// API
|
||||||
const float NO_TEMP = -999.0;
|
const float NO_TEMP = -999.0;
|
||||||
const unsigned long API_INTERVAL = 600000UL; // 10m
|
const unsigned long API_INTERVAL = 60000UL; // 60s
|
||||||
const unsigned long RETRY_INTERVAL = 30000UL; // 30s
|
const unsigned long RETRY_INTERVAL = 30000UL; // 30s
|
||||||
unsigned long lastApiFetch = 0;
|
unsigned long lastApiFetch = 0;
|
||||||
unsigned long fetchInterval = API_INTERVAL;
|
unsigned long fetchInterval = API_INTERVAL;
|
||||||
float outdoorTemp = NO_TEMP;
|
float outdoorTemp = NO_TEMP;
|
||||||
|
|
||||||
|
// MOSFET module (heating elements)
|
||||||
|
const int mosfetPin = 13;
|
||||||
|
unsigned long previousMosfetMillis = 0;
|
||||||
|
bool mosfetState = true;
|
||||||
|
const unsigned long mosfetOnTime = 60000UL; // 60 seconds ON
|
||||||
|
const unsigned long mosfetOffTime = 60000UL; // 60 seconds OFF
|
||||||
|
|
||||||
|
// Store the indoor temp
|
||||||
|
float lastIndoorTemp = NO_TEMP;
|
||||||
|
|
||||||
// Connect to hotspot if not connected, then fetch the current temperature
|
// Connect to hotspot if not connected, then fetch the current temperature
|
||||||
float fetchOutdoorTemp() {
|
float fetchOutdoorTemp() {
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
|
@ -47,8 +57,7 @@ float fetchOutdoorTemp() {
|
||||||
"Host: api.open-meteo.com\r\n"
|
"Host: api.open-meteo.com\r\n"
|
||||||
"Connection: close\r\n"
|
"Connection: close\r\n"
|
||||||
"\r\n"));
|
"\r\n"));
|
||||||
|
|
||||||
// Skip ahead to the number in {"current":{..."temperature_2m":12.3}}
|
|
||||||
float t = NO_TEMP;
|
float t = NO_TEMP;
|
||||||
if (client.find("\"current\":") && client.find("\"temperature_2m\":")) {
|
if (client.find("\"current\":") && client.find("\"temperature_2m\":")) {
|
||||||
t = client.parseFloat();
|
t = client.parseFloat();
|
||||||
|
|
@ -72,10 +81,10 @@ void updateShiftRegister() {
|
||||||
digitalWrite(latchPin, HIGH);
|
digitalWrite(latchPin, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Measure temperature and humidity
|
// Measure temperature
|
||||||
static bool measure_environment(float *temp, float *hum) {
|
static bool measure_environment(float *temp, float *hum) {
|
||||||
static unsigned long timestamp = millis();
|
static unsigned long timestamp = millis();
|
||||||
|
// FIXED syntax error below
|
||||||
if (millis() - timestamp > 3000ul) {
|
if (millis() - timestamp > 3000ul) {
|
||||||
if (dht_sensor.measure(temp, hum) == true) {
|
if (dht_sensor.measure(temp, hum) == true) {
|
||||||
timestamp = millis();
|
timestamp = millis();
|
||||||
|
|
@ -89,6 +98,7 @@ void setup() {
|
||||||
pinMode(latchPin, OUTPUT);
|
pinMode(latchPin, OUTPUT);
|
||||||
pinMode(dataPin, OUTPUT);
|
pinMode(dataPin, OUTPUT);
|
||||||
pinMode(clockPin, OUTPUT);
|
pinMode(clockPin, OUTPUT);
|
||||||
|
pinMode(mosfetPin, OUTPUT);
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
lcd.begin(16, 2);
|
lcd.begin(16, 2);
|
||||||
|
|
@ -107,23 +117,59 @@ void setup() {
|
||||||
lastApiFetch = millis();
|
lastApiFetch = millis();
|
||||||
fetchInterval = (outdoorTemp == NO_TEMP) ? RETRY_INTERVAL : API_INTERVAL;
|
fetchInterval = (outdoorTemp == NO_TEMP) ? RETRY_INTERVAL : API_INTERVAL;
|
||||||
lcd.clear();
|
lcd.clear();
|
||||||
|
|
||||||
|
digitalWrite(mosfetPin, HIGH);
|
||||||
|
mosfetState = true;
|
||||||
|
previousMosfetMillis = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
float indoorTemp;
|
unsigned long currentMillis = millis();
|
||||||
float indoorHum;
|
|
||||||
|
|
||||||
if (measure_environment(&indoorTemp, &indoorHum) == true) {
|
// MOSFET timer
|
||||||
char tempStr[10];
|
if (mosfetState) {
|
||||||
dtostrf(indoorTemp, 4, 1, tempStr);
|
if (currentMillis - previousMosfetMillis >= mosfetOnTime) {
|
||||||
char line0[20];
|
mosfetState = false;
|
||||||
snprintf(line0, sizeof(line0), "In: %s C", tempStr);
|
previousMosfetMillis = currentMillis;
|
||||||
lcdLine(0, line0);
|
digitalWrite(mosfetPin, LOW);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentMillis - previousMosfetMillis >= mosfetOffTime) {
|
||||||
|
mosfetState = true;
|
||||||
|
previousMosfetMillis = currentMillis;
|
||||||
|
digitalWrite(mosfetPin, HIGH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (millis() - lastApiFetch >= fetchInterval) {
|
unsigned long remainingSeconds = 0;
|
||||||
|
if (mosfetState) {
|
||||||
|
remainingSeconds = (mosfetOnTime - (currentMillis - previousMosfetMillis)) / 1000;
|
||||||
|
} else {
|
||||||
|
remainingSeconds = (mosfetOffTime - (currentMillis - previousMosfetMillis)) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indoor temp
|
||||||
|
float indoorTemp, indoorHum;
|
||||||
|
if (measure_environment(&indoorTemp, &indoorHum) == true) {
|
||||||
|
lastIndoorTemp = indoorTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LCD row 0 (Temp + Countdown)
|
||||||
|
char line0[20];
|
||||||
|
if (lastIndoorTemp != NO_TEMP) {
|
||||||
|
char tempStr[6];
|
||||||
|
dtostrf(lastIndoorTemp, 4, 1, tempStr);
|
||||||
|
// Formats into exactly 16 chars: "In:22.5C ON 10"
|
||||||
|
snprintf(line0, sizeof(line0), "In:%sC %s %2lu", tempStr, mosfetState ? "ON " : "OFF", remainingSeconds);
|
||||||
|
} else {
|
||||||
|
snprintf(line0, sizeof(line0), "In:--- C %s %2lu", mosfetState ? "ON " : "OFF", remainingSeconds);
|
||||||
|
}
|
||||||
|
lcdLine(0, line0);
|
||||||
|
|
||||||
|
// API outdoor temp
|
||||||
|
if (currentMillis - lastApiFetch >= fetchInterval) {
|
||||||
float t = fetchOutdoorTemp();
|
float t = fetchOutdoorTemp();
|
||||||
lastApiFetch = millis();
|
lastApiFetch = currentMillis;
|
||||||
if (t != NO_TEMP) {
|
if (t != NO_TEMP) {
|
||||||
outdoorTemp = t;
|
outdoorTemp = t;
|
||||||
fetchInterval = API_INTERVAL;
|
fetchInterval = API_INTERVAL;
|
||||||
|
|
@ -132,6 +178,7 @@ void loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LCD row 1
|
||||||
if (outdoorTemp != NO_TEMP) {
|
if (outdoorTemp != NO_TEMP) {
|
||||||
char outStr[10];
|
char outStr[10];
|
||||||
dtostrf(outdoorTemp, 4, 1, outStr);
|
dtostrf(outdoorTemp, 4, 1, outStr);
|
||||||
|
|
@ -142,6 +189,7 @@ void loop() {
|
||||||
lcdLine(1, "Out: API Error");
|
lcdLine(1, "Out: API Error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Light sensor LEDs
|
||||||
int reading = analogRead(lightPin);
|
int reading = analogRead(lightPin);
|
||||||
int numLEDSLit = reading / 57;
|
int numLEDSLit = reading / 57;
|
||||||
|
|
||||||
|
|
@ -152,5 +200,5 @@ void loop() {
|
||||||
}
|
}
|
||||||
updateShiftRegister();
|
updateShiftRegister();
|
||||||
|
|
||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue