Hello,
I've programmed a NodeMCU V3 (china) with ESP8266 module to operate my electric "boiler", which is also heated with solar panels. So basically I made a code to check the OpenWeatherMap every 4pm for the next day weather and if it's clear, the boiler is turned off for the night, if not, it is turned on. I also connected a CT (current transformer) and a thermistor so I can check when the electric heater is on and its temperature. I send both data to ThingSpeak so I can check on it from anywhere. I also send the state of the relay that operates the boiler.
I tested it at work and it worked pretty much perfectly, then I changed the SSID and password and brought it home and it worked only partially: It does connect and send data to both WebSerial and ThingSpeak, but when it gets to the OpenWeatherMap HTTP request (makehttpRequest function in the code) it just doesn't do it and the WebSerial page just reloads without giving me an error. Also, while the TC measurement is stable at work (0.03 to 0.04 A when not clamped to anything) it varies when at home (back and forth between 0.03 and 3.89 A). Anyone know what might cause this?
I'm using Arduino IDE 1.8.9. The hardware should all be in the schematics below. I think the problem is related to some router config but I don't know much about it. At work the board IP was 10.0.0.x and at home it's 192.168.x.x, if that's relevant.
#include <ThingSpeak.h>
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <Wire.h>
#include <ADS1X15.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
#include <EmonLib.h>
#include <NTPClient.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <WebSerial.h>
/* Analog Digital Converter */
ADS1115 ADS(0x48);
// Make a callback method for reading the pin value from the ADS instance (for energy monitor)
int ads1115PinReader(int _pin){
return ADS.readADC(_pin);
}
/* Energy Monitor instance */
EnergyMonitor emon1;
/* WebSerial server */
AsyncWebServer server(80);
/* Wi-Fi connection parameters */
#define WIFI_SSID "ssid"
#define WIFI_PASSWORD "pass"
WiFiClient client;
HTTPClient http;
/* ThingSpeak parameters */
unsigned long ChannelNumber = ######;
const char * ThingSpeakAPIkey = "**********";
/* Current time NTP parameters */
const long utcOffsetInSeconds = -10800; // UTC-3
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "a.st1.ntp.br", utcOffsetInSeconds);
unsigned long previousMillisTime = 30000;
unsigned long intervalCheckTime = 180000;
/* Temperature measument parameters */
const int thermistorPin = 1; // ADS1115 A1
const int powerPin = D5; // use digital pin as Vout
const int numSamples = 5; // temperature samples
int16_t samples[numSamples];
float currentTemp;
unsigned long previousMillisTemp = 45000;
unsigned long intervalCheckTemp = 60000;
/* Current measument parameters */
unsigned long previousMillisCurrent = 0;
unsigned long intervalCheckCurrent = 60000;
const int relayPin = D6;
void setUpOverTheAirProgramming() {
// Change OTA port. Default: 8266
// ArduinoOTA.setPort(8266);
// Change the name of how it is going to show up in Arduino IDE.
// Default: esp8266-[ChipID]
ArduinoOTA.setHostname("ESP-Boiler");
// ArduinoOTA.setPassword("123");
ArduinoOTA.begin();
}
// to request data from OpenWeatherMap
void makehttpRequest() {
WebSerial.print("[HTTP] begin...\n");
if (http.begin(client, "http://api.openweathermap.org/data/2.5/forecast?q=Curitiba,BR&APPID=9a13aa864f6fdeb054132cdd42ab76b2&mode=json&units=metric&cnt=8")) {
WebSerial.print("[HTTP] GET...\n");
int httpCode = http.GET();
//WebSerial.print(httpCode);
if (httpCode > 0) {
WebSerial.printf("[HTTP] GET... code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = http.getString();
// Parse JSON
DynamicJsonDocument doc(2000);
DeserializationError error = deserializeJson(doc, payload);
if (error) {
WebSerial.print("Error parsing JSON: ");
WebSerial.println(error.c_str());
}
else {
/* Check if weather the next day is Clear at least in 2 out of 3 time stamps (09:00, 12:00, 15:00) */
uint8_t j;
int ClearSkyCheck = 0;
int powerState;
for (j=5; j<=7; j++) { //if checked at 16:00, j=0 is 18:00, j=1 is 21:00, ... , j=5 is 09:00, etc
WebSerial.println(doc["list"][j]["weather"][0]["main"].as<String>());
if (doc["list"][j]["weather"][0]["main"].as<String>() == "Clear") {
ClearSkyCheck++;
}
}
if (ClearSkyCheck >= 2) {
digitalWrite(relayPin, LOW);
WebSerial.println("Turn boiler off");
powerState = 0;
}
else {
digitalWrite(relayPin, HIGH);
WebSerial.println("Turn boiler on");
powerState = 1;
}
ThingSpeak.writeField(ChannelNumber, 3, powerState, ThingSpeakAPIkey); // Write to field 3
}
}
}
else {
WebSerial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
else {
WebSerial.println("[HTTP] Unable to connect");
}
}
/* Function getThermistor for temperature reading */
float getThermistor() {
uint8_t i;
double average;
for (i=0; i<numSamples; i++) {
samples[i] = ADS.readADC(thermistorPin); // read analog input in ADS115
delay(50); // sample time
}
average = 0;
for (i=0; i<numSamples; i++) { // obtain average between samples
average += samples[i];
}
average /= numSamples;
average *= ADS.toVoltage(); // convert input value to voltage
// Internal voltage divider composed by thermistor and R1 (98kohm)
// Rt = R1 * ((3.3)/ADC) - 1)
average = 3.3 / average - 1;
average = 98000 * average;
// Calculate temperature from resistance with Steinhart-Hart equation:
// 1/T = A + B * ln(R) + C * ln(R)^3
const double Acoef = 0.4860370506 * pow(10,-3);
const double Bcoef = 2.436146190 * pow(10,-4);
const double Ccoef = 0.4034709646 * pow(10,-7);
float temp;
float lnR = log(average); // ln(R)
temp = Ccoef * pow(lnR, 3); // C * ln(R)^3
temp += Bcoef * lnR; // B * ln(R) + C * ln(R)^3
temp += Acoef; // A + B * ln(R) + C * ln(R)^3
temp = 1.0 / temp; // invert
temp -= 273.15; // convert to Celsius
return temp;
}
void setup() {
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH); // Turn relay off at boot -> turn SSR on at boot (SSR connected to NC)
pinMode(powerPin, OUTPUT);
digitalWrite(powerPin, HIGH);
Serial.begin(115200);
Serial.printf("Connecting to '%s'\n", WIFI_SSID);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(1000);
ESP.restart();
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
setUpOverTheAirProgramming();
Wire.begin();
ADS.begin();
emon1.inputPinReader = ads1115PinReader; // Replace the default pin reader with the customized ads pin reader
emon1.current(0, 3.7); // Current Transformer input pin, calibration value (arbitrary).
timeClient.begin();
ThingSpeak.begin(client);
WebSerial.begin(&server);
server.begin();
}
void loop() {
// Give processing time for ArduinoOTA
ArduinoOTA.handle();
// if WiFi is down, try reconnecting
if(WiFi.status() != WL_CONNECTED){
WebSerial.print("Attempting to connect to SSID: ");
WebSerial.println(WIFI_SSID);
while(WiFi.status() != WL_CONNECTED){
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
WebSerial.print(".");
delay(5000);
}
WebSerial.println("\nConnected.");
}
// Get time. If between 16:00 and 16:59 get weather data, operate relay accordingly and log it to ThingSpeak
unsigned long millisTime = millis();
if (millisTime - previousMillisTime >= intervalCheckTime) {
previousMillisTime = millisTime;
timeClient.update();
int currentHour = timeClient.getHours();
int currentMinute = timeClient.getMinutes();
WebSerial.print("Time: ");
WebSerial.print(currentHour);
WebSerial.print(":");
if (currentMinute < 10) WebSerial.print("0");
WebSerial.println(currentMinute);
if (currentHour == 16) {
makehttpRequest();
}
}
// Get current to boiler and log it to ThingSpeak
unsigned long millisCurrent = millis();
if (millisCurrent - previousMillisCurrent >= intervalCheckCurrent) {
previousMillisCurrent = millisCurrent;
float Irms = emon1.calcIrms(1480); // Calculate Irms only (number of samples)
WebSerial.print("Power: ");
WebSerial.print(Irms*220.0); // Apparent power
WebSerial.print(" W || Current: ");
WebSerial.print(Irms); // Irms
WebSerial.println(" A");
ThingSpeak.writeField(ChannelNumber, 1, Irms, ThingSpeakAPIkey); // Write to field 1
}
delay(15000); // Allow for ThingSpeak upload limit of 15 seconds between data
// Get boiler temperature and log it to ThingSpeak
unsigned long millisTemp = millis();
if (millisTemp - previousMillisTemp >= intervalCheckTemp) {
previousMillisTemp = millisTemp;
currentTemp = getThermistor();
WebSerial.print("Temperature: ");
WebSerial.print(currentTemp, 1);
WebSerial.println(" °C");
ThingSpeak.writeField(ChannelNumber, 2, currentTemp, ThingSpeakAPIkey); // Write to field 2
}
delay(500);
}
Here's the schematics (sorry for the terrible quality)
Also I'm open to suggestions to make this project and code better. This is only my forth or so Arduino project and I have limited knowledge in these things.
