Hi guys.
I have a fairly long program that connects to the internet to get data 3 times.
The functions for getting the data is very similar to each other and one is posted here below:
void geoLocation() {
// GEO location api URL
String geolink = String("http://ip-api.com/json/?fields=lat,lon,query");
// Make HTTP request to GEO location API
http.begin(geolink);
geolink = "";
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
// Parse JSON response
String payload = http.getString();
// Deserialize JSON response
DynamicJsonDocument doc(100);
deserializeJson(doc, payload);
payload = "";
// Extract data from JSON
float new_lat = doc["lat"].as<float>();
float new_lon = doc["lon"].as<float>();
String new_ip = doc["query"].as<String>();
lat = new_lat;
lon = new_lon;
publicIP = new_ip;
}
else {
Serial.println("GEO Location HTTP Error: " + String(httpCode));
}
}
My problem is that I get HTTP error codes for 2 out of 3 of these when running after eachother, while they work well if I only call one of them.
And sometimes I get errors like this one:
13:23:52.594 -> Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
13:23:52.594 ->
13:23:52.594 -> Core 0 register dump:
13:23:52.594 -> PC : 0x400fd9aa PS : 0x00060530 A0 : 0x800fda15 A1 : 0x3ffb2e10
13:23:52.594 -> A2 : 0x00000744 A3 : 0x400fda48 A4 : 0x00000000 A5 : 0x00000002
13:23:52.594 -> A6 : 0x3ffd0414 A7 : 0x000000f0 A8 : 0x3ffb8b01 A9 : 0x0040105a
13:23:52.626 -> A10 : 0x3ffb8c5c A11 : 0x00000744 A12 : 0x00060120 A13 : 0x3ffbc700
13:23:52.626 -> A14 : 0x007b8ce0 A15 : 0x003fffff SAR : 0x00000020 EXCCAUSE: 0x0000001c
13:23:52.626 -> EXCVADDR: 0x0040105e LBEG : 0x40089af8 LEND : 0x40089b0e LCOUNT : 0xffffffff
13:23:52.626 ->
13:23:52.626 ->
13:23:52.626 -> Backtrace: 0x400fd9a7:0x3ffb2e10 0x400fda12:0x3ffb2e30 0x400fda64:0x3ffb2e50 0x400fdb09:0x3ffb2e70 0x400f5d21:0x3ffb2e90
13:23:52.660 ->
13:23:52.660 ->
13:23:52.660 ->
13:23:52.660 ->
13:23:52.660 -> ELF file SHA256: 21fbcdcde8693135
13:23:52.660 ->
13:23:52.800 -> Rebooting...
or this one:
13:23:50.423 -> CORRUPT HEAP: Bad head at 0x3ffd024c. Expected 0xabba1234 got 0x00000000
13:23:50.423 ->
13:23:50.423 -> assert failed: multi_heap_free multi_heap_poisoning.c:253 (head != NULL)
13:23:50.423 ->
13:23:50.423 ->
13:23:50.423 -> Backtrace: 0x400839bd:0x3ffb2c40 0x4008d785:0x3ffb2c60 0x40093329:0x3ffb2c80 0x40092f9b:0x3ffb2db0 0x40083e81:0x3ffb2dd0 0x40093359:0x3ffb2df0 0x400f755f:0x3ffb2e10 0x400f75bb:0x3ffb2e30 0x400f75ef:0x3ffb2e50 0x400fdb01:0x3ffb2e70 0x400f5d21:0x3ffb2e90
13:23:50.455 ->
13:23:50.455 ->
13:23:50.455 ->
13:23:50.455 ->
13:23:50.455 -> ELF file SHA256: 21fbcdcde8693135
13:23:50.455 ->
13:23:50.627 -> Rebooting...
This is placed before my setup:
HTTPClient http;
I'm wondering if maybe someone here knows if I need to close the HTTP request between calling them in different functions in some way, or if I need to set up the functions differently for this to work?
Or if it's obvious that it is some other cause?
In case someone wants to look at the full program I'll leave that one down below. Thanks (Sorry about all my terrible solutions, I'm a newbie)
#include <TimeLib.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeMono9pt7b.h>
#include <NewPing.h>
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <time.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#define pump_pin 5 // Output pin 5 for water pump
#define manual_pin 14 // Input pin 14 for manually starting irrigation
#define pot_pin 15 // Pin for potentiometer input
#define SCREEN_ADDRESS 0x3C // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32. GPIO21 for SDA and GPIO22 for SCL in ESP32.
Adafruit_SSD1306 display(128, 64, &Wire, -1);
// HTTP variables
HTTPClient http;
// Declaring variables and settings
int use_geo = 1; // Toggle from 0 to 1 to use geolocation based on public IP
int button;
int isPressed;
int sunup;
int sundown;
int start_day;
int now_day;
int now_hour;
int last_hour;
int now_minute;
int last_minute;
int now_second;
int last_second;
int cloud;
int pres;
int humi;
int symb;
int maxtime = 300;
int has_watered;
int is_watering;
int manual_start;
int basetime = 10;
float temp;
float wind;
float rain;
float watertime;
float lat = 57.319435;
float lon = 15.147673;
String publicIP = "0.0.0.0";
// Add automatic connection to NFC router if possible
// Wifi connection settings
const char* ssid = "SSID";
const char* password = "PASSWORD";
// NTP time synchronization settings
const char* ntpServer = "europe.pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
// Loop time counter
long loopTime = millis();
// Watering timer
long startTime = millis();
void setup() {
Serial.begin(115200); // For ESP32
pinMode(pump_pin, OUTPUT);
pinMode(manual_pin, INPUT_PULLUP);
pinMode(pot_pin, INPUT);
delay(50);
// Connecting to Wifi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
delay(50);
// Mapping potentiometer reading to temperature
int pot_modifier = map(analogRead(pot_pin), 0, 4095, 0, 2); // Range normally from 0-4095
Serial.println("Potentiometer reading: "+String(analogRead(pot_pin)));
if (pot_modifier < 1) {
basetime = basetime*0.8;
Serial.println(F("Basetime modifier 0.8"));
}
else if (pot_modifier < 2) {
basetime = basetime*1;
Serial.println(F("Basetime modifier 1"));
}
else if (pot_modifier < 3) {
basetime = basetime*1.2;
Serial.println(F("Basetime modifier 1.2"));
}
Serial.println(F(" ")); // Adding empty serial row
// Checking internet connection and selecting time setting option
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println(F("Wifi connection failed! Rebooting.."));
delay(5000);
ESP.restart();
}
else {
Serial.println(F("Wifi connection successful!"));
// Initializing NTP time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
fetchLocalTime();
start_day = now_day;
last_minute = now_minute-1;
}
// OTA settings
// Hostname defaults to esp3232-[MAC]
ArduinoOTA.setHostname("HOSTNAME");
// No authentication by default
ArduinoOTA.setPassword("PASSWORD"); // OTA password
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println(F("\nEnd"));
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println(F("Auth Failed"));
else if (error == OTA_BEGIN_ERROR) Serial.println(F("Begin Failed"));
else if (error == OTA_CONNECT_ERROR) Serial.println(F("Connect Failed"));
else if (error == OTA_RECEIVE_ERROR) Serial.println(F("Receive Failed"));
else if (error == OTA_END_ERROR) Serial.println(F("End Failed"));
});
ArduinoOTA.begin();
if (use_geo == 1) {
// Get public IP, longitude and latitude coordinates
delay(500);
geoLocation();
}
// Get sunrise and sunset variables
delay(500);
sunTimes();
Serial.println(F("Network Ready"));
Serial.print(F("Local IP address: "));
Serial.println(WiFi.localIP());
Serial.print(F("Public IP address: "));
Serial.println(publicIP);
// Pause
delay(500);
// Start time for the first loop
last_second = now_second;
loopTime = millis();
}
void sunTimes() {
// Sunrise-Sunset api URL
String sunlink = String("https://api.sunrise-sunset.org/json?lat="+String(lat)+"&lng="+String(lon));
// Make HTTP request to Sunrise-Sunset API
http.begin(sunlink);
sunlink = "";
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
// Parse JSON response
String payload = http.getString();
// Deserialize JSON response
DynamicJsonDocument doc(1000);
deserializeJson(doc, payload);
payload = "";
// Extract data from JSON
String uptime = doc["results"]["sunrise"].as<String>();
String downtime = doc["results"]["sunset"].as<String>();
String upsec = uptime;
String downsec = downtime;
upsec.remove(2,upsec.length()-2);
upsec.remove(0,1);
if (upsec == ":") {
uptime.remove(1,uptime.length()-1);
}
else {
uptime.remove(2,uptime.length()-2);
}
downsec.remove(2,downsec.length()-2);
downsec.remove(0,1);
if (downsec == ":") {
downtime.remove(1,downtime.length()-1);
}
else {
downtime.remove(2,downtime.length()-2);
}
sunup = uptime.toInt()+1;
sundown = downtime.toInt()+11;
}
else {
Serial.println("SunTimes HTTP Error: " + String(httpCode));
}
}
void geoLocation() {
// GEO location api URL
String geolink = String("http://ip-api.com/json/?fields=lat,lon,query");
// Make HTTP request to GEO location API
http.begin(geolink);
geolink = "";
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
// Parse JSON response
String payload = http.getString();
// Deserialize JSON response
DynamicJsonDocument doc(100);
deserializeJson(doc, payload);
payload = "";
// Extract data from JSON
float new_lat = doc["lat"].as<float>();
float new_lon = doc["lon"].as<float>();
String new_ip = doc["query"].as<String>();
lat = new_lat;
lon = new_lon;
publicIP = new_ip;
}
else {
Serial.println("GEO Location HTTP Error: " + String(httpCode));
}
}
void tempMod(){
// Returning watertime based on temperature
if (temp < 14) {
watertime = 0;
}
else if (temp < 18) {
watertime = basetime;
}
else if (temp < 22) {
watertime = basetime*1.5;
}
else if (temp < 25) {
watertime = basetime*1.7;
}
else if (temp < 28) {
watertime = basetime*1.9;
}
else if (temp < 30) {
watertime = basetime*2.1;
}
else {
watertime = basetime*2.3;
}
}
void cloudMod(){
// Function for modufying watering time depending on clouds
if (cloud == 0) {
watertime = watertime*1.7;
}
else if (cloud == 1) {
watertime = watertime*1.4;
}
else if (cloud == 2) {
watertime = watertime*1.2;
}
else if (cloud == 3) {
watertime = watertime*1.1;
}
else {
watertime = watertime;
}
}
void rainMod(){
// Function for modifying watering time depending on rain
if (rain == 0) {
watertime = watertime;
}
else if (rain < 0.1) {
watertime = watertime-1;
}
else if (rain < 0.2) {
watertime = watertime-2;
}
else if (rain < 0.3) {
watertime = watertime-3;
}
else if (rain < 0.4) {
watertime = watertime-4;
}
else if (rain < 0.5) {
watertime = watertime-5;
}
else if (rain < 0.6) {
watertime = watertime-6;
}
else if (rain < 0.7) {
watertime = watertime-7;
}
else if (rain < 0.8) {
watertime = watertime-8;
}
else if (rain < 0.9) {
watertime = watertime-9;
}
else {
watertime = watertime-10;
}
}
void windMod(){
// Function for modifying watering time depending on wind
if (wind < 3) {
watertime = watertime;
}
else if (wind < 5) {
watertime = watertime*0.9;
}
else if (wind < 7) {
watertime = watertime*0.8;
}
else {
watertime = watertime*0.7;
}
}
void setWaterTime(){
// Function for calculating watering time for the day
tempMod();
if ((now_hour > sunup) && (now_hour < sundown)) {
cloudMod();
}
rainMod();
windMod();
Serial.print(F("Watertime: "));
Serial.println(watertime);
}
void doWatering(){
// Function for performing watering
startTime = millis();
digitalWrite(pump_pin, HIGH);
is_watering = 1;
if (manual_start == 1) {
Serial.println(F("Manually starting watering"));
}
else {
Serial.println(F("Starting watering"));
}
}
void stopWatering(){
// Function for stopping watering
if (((millis()-startTime) > (watertime*1000)) && (manual_start == 0) && (is_watering == 1)) {
digitalWrite(pump_pin, LOW);
is_watering = 0;
watertime = basetime;
Serial.println(F("Stopping watering because watertime is finished"));
}
else if (((millis() - startTime) > (1000*maxtime)) && ((millis() - startTime) < 90000000) && (is_watering == 1)) {
digitalWrite(pump_pin, LOW);
is_watering = 0;
Serial.println(F("Stopping watering because time exceeded maximum"));
}
else if ((isPressed == 1) && (is_watering == 1)) {
digitalWrite(pump_pin, LOW);
is_watering = 0;
Serial.println(F("Stopping watering manually"));
}
}
void fetchLocalTime(){
// Function for obtaining current time
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println(F("Failed to obtain time"));
}
now_hour = timeinfo.tm_hour;
now_minute = timeinfo.tm_min;
now_second = timeinfo.tm_sec;
now_day = timeinfo.tm_mday;
}
const char fetchIndex(const char* paramName, DynamicJsonDocument doc){
// Function for fetching the array ID for the parameter needed
int paramIndex = -1;
JsonArray parameters = doc["timeSeries"][0]["parameters"].as<JsonArray>();
for (size_t i = 0; i < parameters.size(); i++) {
const char* name = parameters[i]["name"].as<const char*>();
float data = parameters[i]["values"][0].as<float>();
String strname = name;
String strparam = paramName;
if (strname == strparam) {
paramIndex = i;
break;
}
}
return paramIndex;
}
void fetchWeather(){
// Function for fetching weather from SMHI
String weblink = "https://opendata-download-metanalys.smhi.se/api/category/mesan1g/version/2/geotype/point/lon/"+String(lon)+"/lat/"+String(lat)+"/data.json";
// Make HTTP request to SMHI API
http.begin(weblink);
weblink = "";
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
Serial.println(F("."));
Serial.println(F("."));
// Parse JSON response
String payload = http.getString();
// Deserialize JSON response
DynamicJsonDocument doc(3300);
deserializeJson(doc, payload);
payload = "";
// Extract data from JSON
temp = doc["timeSeries"][0]["parameters"][fetchIndex("t",doc)]["values"][0].as<float>();
rain = doc["timeSeries"][0]["parameters"][fetchIndex("prec1h",doc)]["values"][0].as<float>();
cloud = doc["timeSeries"][0]["parameters"][fetchIndex("tcc",doc)]["values"][0].as<int>();
wind = doc["timeSeries"][0]["parameters"][fetchIndex("ws",doc)]["values"][0].as<float>();
pres = doc["timeSeries"][0]["parameters"][fetchIndex("msl",doc)]["values"][0].as<int>();
humi = doc["timeSeries"][0]["parameters"][fetchIndex("r",doc)]["values"][0].as<int>();
symb = doc["timeSeries"][0]["parameters"][fetchIndex("Wsymb2",doc)]["values"][0].as<int>();
Serial.println(F("."));
Serial.print(F("Temp: "));
Serial.print(temp);
Serial.println(F(" C"));
Serial.print(F("Rain: "));
Serial.print(rain);
Serial.println(F(" mm"));
Serial.print(F("Clouds: "));
Serial.print(cloud);
Serial.println(F(" of 8"));
Serial.print(F("Wind: "));
Serial.print(wind);
Serial.println(F(" m/s"));
Serial.print(F("Pressure: "));
Serial.print(pres);
Serial.println(F(" hPa"));
Serial.print(F("Humidity: "));
Serial.print(humi);
Serial.println(F(" % RH"));
Serial.print(F("Weather: "));
Serial.print(symb);
Serial.println(F(" of 27"));
}
else {
Serial.println("SMHI HTTP Error: " + String(httpCode));
}
}
void loop() {
// OTA
ArduinoOTA.handle();
if (millis() - loopTime > 1000) {
// Getting time
fetchLocalTime();
}
if ((now_hour != last_hour) && (now_hour != 0)) {
fetchWeather();
setWaterTime();
last_hour = now_hour;
}
else if (now_hour != last_hour) {
fetchWeather();
last_hour = now_hour;
}
if ((now_hour == 13) || (now_hour == 23)) {
if (has_watered == 0) {
doWatering();
manual_start = 0;
has_watered = 1;
}
}
else if (has_watered = 1) {
has_watered = 0;
}
// Reading button state for manual irrigation
button = digitalRead(manual_pin);
if ((button == HIGH) && (isPressed == 0)) {
Serial.println(F("Button pressed"));
isPressed = 1;
if (is_watering == 0) {
manual_start = 1;
doWatering();
}
else if (is_watering == 1) {
stopWatering();
}
delay(500);
}
else if (button == LOW) {
isPressed = 0;
}
if (now_second != last_second) {
last_second = now_second;
loopTime = millis();
stopWatering();
// Serial.println("Potentiometer reading: "+String(analogRead(pot_pin)));
}
if (now_minute != last_minute) {
last_minute = now_minute;
}
// Rebooting daily
if (now_day != start_day) {
Serial.println(F("New day = Daily reset. Rebooting.."));
delay(5000);
ESP.restart();
}
}