SOLAR IPS LCD DISPLAY
All the info you need on a little display
This project is a continuation of the Solar LED project, it will display the energy status in W or kW from on a small display using a Pico W.
Although the Solar LED project provides an easy to read status (from miles away due to the bright LED’s), this little display will show you the exact details.
The display shows:
- Solar generation in Green
- Grid consumption in Red
- Return to Grid in White
- Diverted power in Blue
- House consumption in Orange
- Time of last measurement
- % energy independance
When there is no change in readings for a while, the display will turn off and will turn back on once new data arrives (to cope with sunset, nothing to display …).
What will you need:
- Pico WH (WiFi version with header) – Approx. 9 EUR
- Pimoroni Pico Display Pack unit – Approx. 18 EUR
As the hard work has already been done, we only need to expose the JSON file we downloaded on the Pi Zero W to the local internet, on the Pi Zero W enter the following:
cd /var/www/html/ sudo ln -s /tmp/eddi.json eddi.json
To prepare the Pico you can press the display on the header, do it gently and make sure the USB side is indeed on the USB side of the Pico.
Now we can connect the Pico W with Display and with an official USB Pico cable (most cables will not work!) to your Mac. Install Thonny for macOS and you will be able to connect to the Pico.
Download & install the Pimoroni custom MicroPython for the “Pico W” version (keep the boot select button pressed and then plug it into the Mac) – copy the file over and unplug & replug the Pico in the Mac. Then use Thonny to create two files on the Pico. The first one is named “secrets.py” and will hold your WiFi networks name and password:
SSID = "Your Wi-Fi Network" PASS = "Your Password"
The next file should be named “main.py” and will run every time the Pico has power and boots up:
# (c) JF Nutbroek # 08/10/2023 # Pico W + Pico Display Pack from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY from pimoroni import RGBLED import time import network import secrets import urequests from math import floor from machine import Timer, WDT # User defined input # URL to local json file using http # See https://mywebmymail.com/solar-level-indicator-for-myenergi-eddi/ url = "http://192.168.68.110/eddi.json" pollinterval = 30 # Interval in seconds for a new JSON poll thresholdw = 10 # Ignore solar power when below this value (W) brightness = 0.5 # Brightness of the display timeoffset = 120 # Time offset in minutes from UTC sleepafter = 10 # Number of JSON polls without change # ********* Start display = PicoGraphics(display=DISPLAY_PICO_DISPLAY, rotate=0) display.set_font("bitmap8") display.set_backlight(brightness) # Colors BLACK = display.create_pen(0, 0, 0) GREEN = display.create_pen(50, 205, 50) RED = display.create_pen(200, 0, 0) BLUE = display.create_pen(0, 191, 255) WHITE = display.create_pen(255, 255, 255) ORANGE = display.create_pen(255, 140, 0) # Switch LED off led = RGBLED(6, 7, 8) led.set_rgb(0, 0, 0) # Power data, animation coordinates, time helpers diverted_power = 0 solar_power = 0 grid_power = 0 total = 0 in_x = 20 in_y = 34 in_z = 2 out_x = 158 out_y = 75 out_z = 2 timestamp = '00:00:00' oldstamp = '00:00:00' timedout = 0 # Draw the house def house(color, happy, line): display.set_pen(color) display.line(90, 90, 150, 90, line) display.line(150, 90, 150, 50, line) display.line(150, 50, 120, 30, line) display.line(120, 30, 90, 50, line) display.line(90, 50, 90, 90, line) # Draw mouth if happy: display.circle(120, 67, 15) display.set_pen(BLACK) display.circle(120, 64, 15) display.set_pen(color) display.triangle(103, 59, 117, 59, 110, 50) display.triangle(123, 59, 137, 59, 130, 50) else: display.rectangle(105, 54, 10, 5) display.rectangle(125, 54, 10, 5) display.rectangle(105, 74, 30, 3) # Format power in W or kW def formatpower(power): if power < 1000: return (str(power) + " W") else: return (str(floor(power / 100) / 10) + " kW") # Write text centered or right aligned def writetext(text, y, center, size, color): width = display.measure_text(text, size) display.set_pen(color) if center: display.text(text, floor((240 - width) / 2), y, 240, size) else: display.text(text, 240 - width, y, 240, size) # Read JSON def readjson(): global diverted_power, solar_power, grid_power, total global url, timestamp, oldstamp, timedout led.set_rgb(138, 43, 226) try: if wlan.isconnected(): myenergi = urequests.get(url).json() data = myenergi[0]['eddi'][0] diverted_power = 0 solar_power = 0 grid_power = 0 total = 0 else: led.set_rgb(255, 0, 0) writetext("No WiFi", 122, True, 1, RED) return except: led.set_rgb(255, 0, 0) return if 'div' in data: diverted_power = data['div'] if 'gen' in data: solar_power = data['gen'] if 'grd' in data: grid_power = data['grd'] if 'tim' in data: timestamp = data['tim'] if timestamp != oldstamp: oldstamp = timestamp timedout = 0 else: timedout += 1 # Threshold conditions total = solar_power + grid_power if solar_power < thresholdw: solar_power = 0 total = grid_power led.set_rgb(0, 0, 0) def resetwdTimer(wdTimer): global wd wd.feed() # Read JSON def update_display(myTimer): readjson() # Startup house(WHITE, True, 2) writetext("Version 1.2", 102, True, 2, WHITE) display.update() time.sleep(2) # Connect to WiFi wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(secrets.SSID, secrets.PASS) time.sleep(1) # First JSON download if wlan.isconnected(): readjson() else: display.set_pen(RED) text = "No WiFi - Waiting 10s" width = display.measure_text(text, 2) display.text(text, floor((240 - width) / 2), 3, 240, 2) display.update() time.sleep(10) readjson() # Start the timers myTimer = Timer() myTimer.init(period = pollinterval * 1000, mode = Timer.PERIODIC, callback = update_display) wdTimer = Timer() wdTimer.init(period = 5000, mode = Timer.PERIODIC, callback = resetwdTimer) # Automatic reboot after 6 seconds of inactivity wd = WDT(timeout = 6000) while True: # Sleep Mode when no new data is received if timedout == sleepafter: display.set_pen(BLACK) display.clear() display.update() display.set_backlight(0.0) if timedout >= sleepafter: time.sleep(pollinterval * 10) readjson() if timedout == 0: display.set_backlight(brightness) else: continue # Start drawing display.set_pen(BLACK) display.clear() # Animations linewidth = 2 if in_y < 70: in_y += 1 if in_z < 8: in_z += 1 if ((in_y > 69) and (in_x < 82)): in_x += 1 if ((in_x > 74) and (in_z > 1)): in_z -= 1 if in_x > 81: in_x = 20 in_y = 34 in_z = 2 linewidth = 6 if out_x < 220: out_x += 1 if out_z < 8: out_z += 1 if ((out_x > 219) and (out_y < 101)): out_y += 1 if ((out_y > 95) and (out_z > 1)): out_z -= 1 if out_y > 100: out_x = 158 out_y = 75 out_z = 2 if ((diverted_power > 0 ) or (grid_power < 0)): linewidth = 6 # Draw solar power if solar_power > 0: display.set_pen(GREEN) display.text(formatpower(solar_power), 0, 0, 240, 3) display.line(20, 30, 20, 70, 2) display.line(20, 70, 90, 70, 2) display.circle(in_x, in_y, in_z) # Draw energy independance perc = floor(min(100, (solar_power / max(1, total)) * 100)) text = str(int(perc)) + " %" writetext(text, 102, True, 2, WHITE) # Draw grid power text = formatpower(abs(grid_power)) if grid_power > 0: writetext(text, 0, False, 3, RED) display.line(220, 30, 220, 60, 2) display.line(220, 60, 150, 60, 2) display.circle(240 - in_x, min(in_y, 60), in_z) house(RED, False, linewidth) if grid_power < 0: # Draw returned power writetext(text, 0, False, 3, WHITE) display.line(220, 30, 220, 60, 2) display.line(220, 60, 150, 60, 2) display.circle(out_x, 135 - out_y, out_z) if ((diverted_power > 0) and (out_y > 98)): house(BLUE, True, linewidth) else: house(GREEN, True, linewidth) # Draw diverted power text = formatpower(diverted_power) if diverted_power > 0: writetext(text, 111, False, 3, BLUE) display.line(220, 105, 220, 75, 2) display.line(220, 75, 150, 75, 2) display.circle(out_x, out_y, out_z) # Draw house consumption if (total - diverted_power) > 0: display.set_pen(ORANGE) display.text(formatpower(total - diverted_power), 0, 111, 240, 3) # Draw time from last JSON data tmp = timestamp.split(':') if ((len(tmp) == 3) and (timestamp !='00:00:00')): mininday = (int(tmp[0]) * 60) + int(tmp[1]) + timeoffset hours = floor(mininday / 60) minutes = mininday - (hours * 60) text = "0" * (2 - len(str(hours))) + str(hours) + '.' + "0" * (2 - len(str(minutes))) + str(minutes) writetext(text, 3, True, 2, WHITE) # Update everything to the screen display.update() time.sleep(0.01)
That’s it! Find a good spot for the display and you have all the info you need at a glance.