Hey everyone,
I know, the topic about instable esp32 boards/nodes is a reoccuring, but the scenario I have here I cannot explain with what I found before, except with “sometimes it’s just random and that’s how the world turns”.
Short story: the whole setup worked without issues as long as only 2 GPIO inputs were configured in the code, and since I added 3 additional buttons as binary sensors, the board does random reboots or gets unresponsive.
So, situation at hand:
I built a small weather station for the hallway, with an esp32 devkit v1 board, a waveshare 2.9 epaper display, and last but not least 5 physical pushbuttons which I wanted to use to switch display pages and to trigger automations in HA. Before I built an actual case, I tested the whole setup at the final position in the hallway with a simplified version, everything except the pushbuttons fixed to a piece of cardboard:
That setup worked for weeks without any issues.
2 weeks ago I built a “case” for it, but as the location for the device is very narrow, I just took a thin piece of wood as a front plate, and added holders for the display and the board to the back. Then I also added the physical buttons and wired them to a connector as well:
This “final” setup uses the same esp32 board and display and usb cable and powersource (1A) than the prototype, I just transfered them away from the cardboard to the proper setup.
I first only added the code for the two upper buttons to the YAML file, as I didn’t have a usecase yet for the other 3. That worked flawlessly for 2 days:
binary_sensor:
- platform: gpio
pin:
number: 26
mode:
input: true
pullup: true
inverted: true
name: button1
filters:
- delayed_on: 10ms
on_press:
- display.page.show: page1
- delay: 2s
- component.update: eink
- platform: gpio
pin:
number: 27
mode:
input: true
pullup: true
inverted: true
name: button2
filters:
- delayed_on: 10ms
on_press:
- display.page.show: page2
- delay: 2s
- component.update: eink
After everything worked fine for 2 days, I just added the code for the other 3 buttons:
- platform: gpio
pin:
number: 32
mode:
input: true
pullup: true
inverted: true
name: button3
filters:
- delayed_on: 10ms
- platform: gpio
pin:
number: 33
mode:
input: true
pullup: true
inverted: true
name: button4
filters:
- delayed_on: 10ms
- platform: gpio
pin:
number: 25
mode:
input: true
pullup: true
inverted: true
name: button5
filters:
- delayed_on: 10ms
Since then the board became instable. Using the buttons to switch between display page 1 and 2 sometimes works, sometimes the display turns empty and nothing reacts anymore. Checking the logs I get various errors around the node not being reachable, esphome trying to reconnect, sometimes succeeding, sometimes not. Sometimes the device runs on page 1 of the display for a few hours, but trying to switch to another display page crashes the whole thing again. The power LED of the board is on continuously.
About the setup in the room: as mentioned, the main components of the setup did not change between prototype and final setup. Same board, display, usb cable, power supply. The distance to the wifi access points is 2.5 meters, without anything but air inbetween. No firmware updates to the accesspoint or router, no new electronic devices, no new neighbours clogging wifi channels or anything. I tried a powersupply with 2A, no change in behaviour.
Does adding the code for GPIO inputs change the actual electronic “behaviour” on the board itself? Does that switch something on or changes something?
In case it helps, here is the complete YAML for the project:
esphome:
name: epaper
esp32:
board: esp32doit-devkit-v1
framework:
type: arduino
# Enable logging
logger:
json:
# Enable Home Assistant API
api:
password: ""
ota:
password: ""
wifi:
ssid: "XXXXXXX"
password: "XXXXXXX"
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Epaper Fallback Hotspot"
password: "xxxxxxxx"
captive_portal:
font:
- file: 'arial.ttf'
id: font1
size: 24
- file: 'arial.ttf'
id: font2
size: 30
- file: 'arial.ttf'
id: fontsmall
size: 12
- file: 'arial.ttf'
id: fontbig
size: 30
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/']
- file: 'DejaVuSansMono-Bold.ttf'
id: dejamonobold
size: 44
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/']
- file: 'DejaVuSansMono-Bold.ttf'
id: dejamonoboldsmall
size: 24
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/']
image:
- file: "otter.bmp"
id: otter_image
resize: 296x128
- file: "otterbitmap2.bmp"
id: otter_icon
- file: "otter_complete.bmp"
id: otter_complete
- file: "puppy30.bmp"
id: puppy
- file: "moonthick.bmp"
id: moonthick
- file: "moonthin.bmp"
id: moonthin
- file: "clear-night.bmp"
id: clear_night
- file: "cloudy.bmp"
id: cloudy
- file: "exceptional.bmp"
id: exceptional
- file: "fog.bmp"
id: fog
- file: "hail.bmp"
id: hail
- file: "lightning.bmp"
id: lightning
- file: "lightning-rainy.bmp"
id: lightning_rainy
- file: "partlycloudy.bmp"
id: partlycloudy
- file: "pouring.bmp"
id: pouring
- file: "rainy.bmp"
id: rainy
- file: "snowy.bmp"
id: snowy
- file: "snowy-rainy.bmp"
id: snowy_rainy
- file: "sunny.bmp"
id: sunny
- file: "windy.bmp"
id: windy
- file: "windyvariant.bmp"
id: windy_variant
- file: "clearnight45.bmp"
id: clear_night45
- file: "cloudy45.bmp"
id: cloudy45
- file: "exceptional45.bmp"
id: exceptional45
- file: "fog45.bmp"
id: fog45
- file: "hail45.bmp"
id: hail45
- file: "lightning45.bmp"
id: lightning45
- file: "lightningrainy45.bmp"
id: lightning_rainy45
- file: "partlycloudy45.bmp"
id: partlycloudy45
- file: "pouring45.bmp"
id: pouring45
- file: "rainy45.bmp"
id: rainy45
- file: "snowy45.bmp"
id: snowy45
- file: "snowy-rainy45.bmp"
id: snowy_rainy45
- file: "sunny45.bmp"
id: sunny45
- file: "windy45.bmp"
id: windy45
- file: "windy-variant45.bmp"
id: windy_variant45
globals:
- id: dogmeter
type: int
restore_value: no
initial_value: '1'
time:
- platform: homeassistant
id: esptime
on_time:
- hours: 6
minutes: 0
seconds: 0
then:
- globals.set:
id: dogmeter
value: '2'
- hours: 9
minutes: 0
seconds: 0
then:
- globals.set:
id: dogmeter
value: '1'
- hours: 17
minutes: 0
seconds: 0
then:
- globals.set:
id: dogmeter
value: '2'
- hours: 21
minutes: 0
seconds: 0
then:
- globals.set:
id: dogmeter
value: '0'
sensor:
- platform: homeassistant
id: outside_temperature
entity_id: sensor.lumi_lumi_weather_a5f3bb07_temperature
internal: true
- platform: homeassistant
id: windspeed
entity_id: sensor.openweathermap_wind_speed
internal: true
- platform: homeassistant
id: maxtemp
entity_id: sensor.openweathermap_forecast_temperature_2
internal: true
- platform: homeassistant
id: mintemp
entity_id: sensor.openweathermap_forecast_temperature_low_2
internal: true
text_sensor:
- platform: homeassistant
id: outside_weather
entity_id: sensor.openweathermap_condition
internal: true
- platform: homeassistant
id: owm_forecast
entity_id: weather.openweathermap
internal: true
attribute: forecast
- platform: homeassistant
id: owm_forecast_days
entity_id: weather.openweathermap_2
internal: true
attribute: forecast
- platform: homeassistant
id: pageshelper
entity_id: input_select.epaper_page
on_value:
then:
- display.page.show: !lambda |-
if (id(pageshelper).state == "page1") {
return id(page1);
}
else if (id(pageshelper).state == "page2") {
return id(page2);
}
else {
return id(page3);
}
- delay: 2s
- component.update: eink
internal: true
binary_sensor:
- platform: gpio
pin:
number: 26
mode:
input: true
pullup: true
inverted: true
name: button1
filters:
- delayed_on: 10ms
on_press:
- display.page.show: page1
- delay: 2s
- component.update: eink
- platform: gpio
pin:
number: 27
mode:
input: true
pullup: true
inverted: true
name: button2
filters:
- delayed_on: 10ms
on_press:
- display.page.show: page2
- delay: 2s
- component.update: eink
- platform: gpio
pin:
number: 32
mode:
input: true
pullup: true
inverted: true
name: button3
filters:
- delayed_on: 10ms
- platform: gpio
pin:
number: 33
mode:
input: true
pullup: true
inverted: true
name: button4
filters:
- delayed_on: 10ms
- platform: gpio
pin:
number: 25
mode:
input: true
pullup: true
inverted: true
name: button5
filters:
- delayed_on: 10ms
spi:
clk_pin: 19
mosi_pin: 18
display:
- platform: waveshare_epaper
cs_pin: 21
dc_pin: 3
busy_pin: 22
reset_pin: 1
model: 2.90inv2
rotation: 90°
update_interval: 300s
full_update_every: 1
#reset_duration: 2ms
id: eink
pages:
- id: page1
lambda: |-
std::map<std::string, Image *> imgDict;
imgDict["clear-night"] = id(clear_night);
imgDict["cloudy"] = id(cloudy);
imgDict["fog"] = id(fog);
imgDict["hail"] = id(hail);
imgDict["lightning"] = id(lightning);
imgDict["lightning-rainy"] = id(lightning_rainy);
imgDict["partlycloudy"] = id(partlycloudy);
imgDict["pouring"] = id(pouring);
imgDict["rainy"] = id(rainy);
imgDict["snowy"] = id(snowy);
imgDict["snowy-rainy"] = id(snowy_rainy);
imgDict["sunny"] = id(sunny);
imgDict["windy"] = id(windy);
imgDict["windy-variant"] = id(windy_variant);
imgDict["exceptional"] = id(exceptional);
it.image(0, 0, id(otter_complete));
if (id(outside_temperature).has_state()) {
it.printf(80, 0, id(dejamonobold), TextAlign::TOP_LEFT, "%2.0f", id(outside_temperature).state);
it.printf(150, 0, id(dejamonobold), TextAlign::TOP_RIGHT, "°");
it.printf(190, 3, id(dejamonoboldsmall), TextAlign::TOP_RIGHT, "%.0f°", id(maxtemp).state);
it.printf(190, 23, id(dejamonoboldsmall), TextAlign::TOP_RIGHT, "%.0f°", id(mintemp).state);
}
if (id(outside_weather).has_state()) {
it.image(206, 2, imgDict[id(outside_weather).state]);
}
it.printf(206, 60, id(fontsmall), "chance of");
it.printf(206, 70, id(fontsmall), "puppies");
int dogstart = 208;
for (int dog=0; dog <= id(dogmeter); ++dog)
{
it.image(dogstart, 89, id(puppy));
dogstart += 30;
}
it.rectangle(206, 85, 90, 40);
it.printf(80, 50, id(fontsmall), "chance of rain");
it.rectangle(80, 65, 105, 60);
if (id(owm_forecast).has_state())
{
DynamicJsonDocument doc(5048);
deserializeJson(doc, (id(owm_forecast).state.c_str()));
JsonArray root = doc.as<JsonArray>();
int linestart = 82;
for (int d=0; d <= 9; ++d)
{
JsonObject root_1 = root[d];
float root_1_precipitation_probability = root_1["precipitation_probability"];
int heighthelper = (2 + (int)root_1_precipitation_probability) / 2;
int offsethelper = 120 - heighthelper;
it.filled_rectangle(linestart, offsethelper, 8, heighthelper);
linestart += 10;
}
}
- id: page2
lambda: |-
std::map<std::string, Image *> imgDict;
imgDict["clear-night"] = id(clear_night45);
imgDict["cloudy"] = id(cloudy45);
imgDict["fog"] = id(fog45);
imgDict["hail"] = id(hail45);
imgDict["lightning"] = id(lightning45);
imgDict["lightning-rainy"] = id(lightning_rainy45);
imgDict["partlycloudy"] = id(partlycloudy45);
imgDict["pouring"] = id(pouring45);
imgDict["rainy"] = id(rainy45);
imgDict["snowy"] = id(snowy45);
imgDict["snowy-rainy"] = id(snowy_rainy45);
imgDict["sunny"] = id(sunny45);
imgDict["windy"] = id(windy45);
imgDict["windy-variant"] = id(windy_variant45);
imgDict["exceptional"] = id(exceptional45);
std::map<int, const char *> dayDict;
dayDict[1] = "MO";
dayDict[2] = "TU";
dayDict[3] = "WE";
dayDict[4] = "TH";
dayDict[5] = "FR";
dayDict[6] = "SA";
dayDict[7] = "SU";
int day = id(esptime).now().day_of_week;
if (id(owm_forecast_days).has_state())
{
if (day == 7)
{
day = 1;
}
DynamicJsonDocument doc(5048);
deserializeJson(doc, (id(owm_forecast_days).state.c_str()));
JsonArray root = doc.as<JsonArray>();
int linestarticon = 3;
for (int i=1; i <= 6; ++i)
{
JsonObject root_0 = root[i];
float maxtempforecast = root_0["temperature"];
float chanceofrainforecast = root_0["precipitation_probability"];
it.printf(linestarticon, -2, id(dejamonoboldsmall), TextAlign::TOP_LEFT, "%s", dayDict[day]);
it.image(linestarticon, 22, imgDict[root_0["condition"]]);
it.printf(linestarticon, 70, id(dejamonoboldsmall), TextAlign::TOP_LEFT, "%.0f°", maxtempforecast);
if (chanceofrainforecast == 100)
{
it.printf(linestarticon, 93, id(dejamonoboldsmall), TextAlign::TOP_LEFT, "%.0f", chanceofrainforecast);
}
else
{
it.printf(linestarticon, 93, id(dejamonoboldsmall), TextAlign::TOP_LEFT, "%.0f%%", chanceofrainforecast);
}
linestarticon += 48;
day += 1;
}
}
- id: page3
lambda: |-
std::map<std::string, Image *> imgDict;
imgDict["clear-night"] = id(clear_night);
imgDict["cloudy"] = id(cloudy);
imgDict["fog"] = id(fog);
imgDict["hail"] = id(hail);
imgDict["lightning"] = id(lightning);
imgDict["lightning-rainy"] = id(lightning_rainy);
imgDict["partlycloudy"] = id(partlycloudy);
imgDict["pouring"] = id(pouring);
imgDict["rainy"] = id(rainy);
imgDict["snowy"] = id(snowy);
imgDict["snowy-rainy"] = id(snowy_rainy);
imgDict["sunny"] = id(sunny);
imgDict["windy"] = id(windy);
imgDict["windy-variant"] = id(windy_variant);
imgDict["exceptional"] = id(exceptional);
it.image(-10, 60, id(otter_complete));
if (id(outside_temperature).has_state()) {
it.printf(5, 5, id(fontbig), "%.0f°", id(outside_temperature).state);
}
if (id(outside_weather).has_state()) {
it.printf(80, 62, id(fontsmall), "%s", id(outside_weather).state.c_str());
it.image(80, 2, imgDict[id(outside_weather).state]);
}
if (id(windspeed).has_state()) {
it.printf(200, 5, id(fontbig), "%.0f m/s", id(windspeed).state);
}
it.printf(206, 60, id(fontsmall), "chance of");
it.printf(206, 70, id(fontsmall), "puppies");
int dogstart = 208;
for (int dog=0; dog <= id(dogmeter); ++dog)
{
it.image(dogstart, 89, id(puppy));
dogstart += 30;
}
it.rectangle(206, 85, 90, 40);
it.printf(80, 50, id(fontsmall), "chance of rain");
it.rectangle(80, 65, 105, 60);
if (id(owm_forecast).has_state())
{
DynamicJsonDocument doc(5048);
deserializeJson(doc, (id(owm_forecast).state.c_str()));
JsonArray root = doc.as<JsonArray>();
int linestart = 82;
for (int d=0; d <= 9; ++d)
{
JsonObject root_1 = root[d];
float root_1_precipitation_probability = root_1["precipitation_probability"];
int heighthelper = (2 + (int)root_1_precipitation_probability) / 2;
int offsethelper = 120 - heighthelper;
it.filled_rectangle(linestart, offsethelper, 8, heighthelper);
linestart += 10;
}
}
Anyone got an idea? I can of course get another board and try that, but if there is a general “design flaw” in my setup I’d rather get rid of that instead.
Thanks in advance!