In this part you will learn:
Okay, in addition to the display, we also need a way to make entries. We don't always want to be shown just one value, but rather choose what we want to be shown. I chose a rotary encoder here. This allows you to navigate through the menu. The one I chose even had a switch built in. The display that I use has an I2C adapter board soldered onto it. So we can even save some pins (although we wouldn't have to do that in this project).
Here is my schematic:
For the display and the rotary encoder you need other libraries that I do not want to upload for copyright reasons. You can download it here:
rotary_irq_esp.py
and rotary.py
esp8266_i2c_lcd.py
, lcd_api.py
Copy them into the directory on your ESP32 / pyboard / lib
. Then you can with upip.install ("micropython-eydam-prototyping-lcd-menu")
install my module.
Then you can try briefly whether everything worked:
>>> import machine
>>> from esp8266_i2c_lcd import I2cLcd
>>> from rotary_irq_esp import RotaryIRQ
>>> i2c = machine.I2C(0, scl=machine.Pin(22), sda=machine.Pin(21))
>>> lcd = I2cLcd(i2c, 39, 2, 16)
>>> lcd.clear()
>>> lcd.putstr("Hello World") # "Hello World" should be displayed
>>> r = RotaryIRQ(18,19,0,10,False)
>>> r.value() # turn rotary
0
>>> r.value()
16
OK. In order to present a menu on the display, we first have to think about a structure. I came up with the following (I'll do it in English so that it will look exactly like this in the files later. You can do it directly in German if you want. But make sure that, depending on the display, umlauts may not work ). The menu entry is always in the first line, what is in brackets is in the second:
To illustrate this, let's create the file menu.json
(please refer github) and copy it to the ESP32. Then we need a little more code to display the menu as well. Put the file for you menu.py
with the following content:
import machine
import ep_lcd_menu
from rotary_irq_esp import RotaryIRQ
from esp8266_i2c_lcd import I2cLcd
def setup(temps, sm):
i2c = machine.I2C(0, scl=machine.Pin(22), sda=machine.Pin(21))
lcd = I2cLcd(i2c, 39, 2, 16)
lcd.clear()
r = RotaryIRQ(18, 19, 0, 10, False)
menu = ep_lcd_menu.menu_rot_enc(
menu_config_file="menu.json", display_type="1602", display=lcd, rotary=r, button_pin=5)
menu.load()
menu.render()
return menu
In the main.py
you have to add the following:
import ep_logging
import ep_wifi
import ep_config
import heater_http
import onewire
import ds18x20
import machine
import ubinascii
import statemachine
import menu
import gc
wifi = ep_wifi.wifi("./network_config.json", max_time_wait_for_connect=10)
wlan, ssid, bssid = wifi.connect()
ip = wlan.ifconfig()[0]
logger = ep_logging.colored_logger(appname="main")
logger.notice("WiFi connected")
logger_http = ep_logging.colored_logger(appname="http")
ow = onewire.OneWire(machine.Pin(4))
ds = ds18x20.DS18X20(ow)
ht_config = ep_config.config("ht_config.json")
ht_config.load()
thresh = ht_config.get("")
ds_config = ep_config.config("ds_config.json")
ds_config.load()
temps = ds_config.get("")
get_temp = lambda name: temps[name]["value"] if (name in temps) and ("value" in temps[name]) else 0
sm = statemachine.setup(
get_temp,
lambda name: thresh.get(name, 0)
)
sm.init()
sm.step_until_stationary()
def read_temps(timer, ds, temps, sm):
ds.convert_temp()
for key in temps:
temps[key]["value"] = ds.read_temp(ubinascii.unhexlify(temps[key]["id"]))
sm.step()
gc.collect()
tim_ds = machine.Timer(0)
tim_ds.init(mode=machine.Timer.PERIODIC, period=5000, callback=lambda timer: read_temps(timer, ds, temps, sm))
http_server = heater_http.setup(wlan, logger_http, ds)
http_server.start()
m = menu.setup()
If you restart the ESP32 now, the menu should appear on the display. With the Rotaty Encoder you can navigate through the menu and with a click on the rotary button you can enter the menu item. However, no values are displayed yet. In the menu.json
we have defined which functions should be called in order to display something in the second line:
We need the function print_T_Oven
(and all other functions) still register. Adds the following to this:
import ep_logging
import ep_wifi
import ep_config
import heater_http
import onewire
import ds18x20
import machine
import ubinascii
import statemachine
import menu
import gc
wifi = ep_wifi.wifi("./network_config.json", max_time_wait_for_connect=10)
wlan, ssid, bssid = wifi.connect()
ip = wlan.ifconfig()[0]
logger = ep_logging.colored_logger(appname="main")
logger.notice("WiFi connected")
logger_http = ep_logging.colored_logger(appname="http")
ow = onewire.OneWire(machine.Pin(4))
ds = ds18x20.DS18X20(ow)
ht_config = ep_config.config("ht_config.json")
ht_config.load()
thresh = ht_config.get("")
ds_config = ep_config.config("ds_config.json")
ds_config.load()
temps = ds_config.get("")
get_temp = lambda name: temps[name]["value"] if (name in temps) and ("value" in temps[name]) else 0
sm = statemachine.setup(
get_temp,
lambda name: thresh.get(name, 0)
)
sm.init()
sm.step_until_stationary()
def read_temps(timer, ds, temps, sm):
ds.convert_temp()
for key in temps:
temps[key]["value"] = ds.read_temp(ubinascii.unhexlify(temps[key]["id"]))
sm.step()
gc.collect()
tim_ds = machine.Timer(0)
tim_ds.init(mode=machine.Timer.PERIODIC, period=5000, callback=lambda timer: read_temps(timer, ds, temps, sm))
http_server = heater_http.setup(wlan, logger_http, ds)
http_server.start()
m = menu.setup(get_temp, sm)
m.display_funcs["print_ssid"] = lambda: "{}".format(ssid)
m.display_funcs["print_ip"] = lambda: "{}".format(ip)
m.display_funcs["print_rssi"] = lambda: "{} db".format(wlan.status("rssi")) if wlan.isconnected() else "---"
Or.:
import machine
import ep_lcd_menu
from rotary_irq_esp import RotaryIRQ
from esp8266_i2c_lcd import I2cLcd
import esp32
import gc
def setup(temps, sm):
i2c = machine.I2C(0, scl=machine.Pin(22), sda=machine.Pin(21))
lcd = I2cLcd(i2c, 39, 2, 16)
lcd.clear()
r = RotaryIRQ(18, 19, 0, 10, False)
menu = ep_lcd_menu.menu_rot_enc(
menu_config_file="menu.json", display_type="1602", display=lcd, rotary=r, button_pin=5)
menu.display_funcs = {
"print_T_Oven": lambda: "{:.2f} C".format(temps("T_Oven")),
"print_T_TankU": lambda: "{:.2f} C".format(temps("T_TankU")),
"print_T_TankL": lambda: "{:.2f} C".format(temps("T_TankL")),
"print_state": lambda: "{}".format(sm.state),
"print_cpu_temp": lambda: "{:.2f} C".format((esp32.raw_temperature()-32)*5/9),
"print_ram": lambda: "{:.1f}/{:.1f}kB".format(gc.mem_free()/1024, (gc.mem_alloc()+gc.mem_free())/1024),
}
menu.load()
menu.render()
return menu
We could do everything in that too main.py
write, but I put as much as possible in the menu.py
, so that main.py
remains as clear as possible.
Our menu is now finished. Feel free to expand it if you want. In the next chapter you will find out how to send the data to an MQTT broker via MQTT.
Eydam prototyping
Saccasner Strasse 19
03096 Schmogrow-Fehrow
Germany