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.