Use Pico to Track Time

Currently I track my working time with org-mode in Emacs. This works reasonably well for me, but it is time to try something different and see if it works better.

I want to use a Pico LCD 1.3 addon to a Raspberry PI Pico to track my worktime. The Pico will call the toggl API and show the current state on the display. There are a lot of alternatives to toggl, but their API looks nice and a friend already used it successfully.

My first try was to use urequests on the Pico to directly talk with the toggl API. This failed first because of SSL and then because of Content Length not correctly parsed/sent.

So this version is using a minimal Flask server running on a "normal" Raspberry PI to proxy. I use a few Raspberry PIs as sensors in my appartment, so I will run it on a PI that already runs. The code for the Flask server is in this Repository https://github.com/mfa/pico-toggl and is not part of this blogpost.

We will use urequests on the Pico to call the Flask server. To get the file I used curl to download and rshell to put on the Pico:

curl https://raw.githubusercontent.com/pfalcon/pycopy-lib/master/urequests/urequests/__init__.py > urequests.py
rshell "cp urequests.py /pyboard/"

Before starting using the Flask server we want to check if urequests can do simple requests. The Pico has no datetime (and we actually don't need it anymore, because we use the datetime of the Flask server). Still let's use this to check if http works by calling worldtimeapi.org.

Run this code in a rshell REPL:

# connect to wifi
import network
nic = network.WLAN(network.STA_IF)
nic.active(True)
nic.connect('your-ssid', 'your-key')
print(nic)

from urequests import request
r = request("GET", "http://worldtimeapi.org/api/timezone/UTC")
print(r.json()["datetime"])

This should return the current datetime in the UTC timezone. So Network and DNS works. We don't use SSL because when writing this post it failed for me.

The display was tricky to get to work. I tried both drivers from my previous post but failed, so I decided to use the code from Waveshare and modify it for this. The needed display.py is in the Flask repo in the pico folder: https://github.com/mfa/pico-toggl

Checking the switching of buttons is a bit tricky, but with a short delay and a few ifs manageable. The button used for this is the top button on the right (A) which is GP15.

The first working iteration (main.py):

import machine
import network
import utime
from display import LCD_1inch3
from urequests import request

def call_flask(path):
    r = request("GET", f"http://<ip-of-the-flask-server>:5000/{path}")
    return r.json()

def show_text(string):
    display.fill(0)
    display.text(string, 1, 1, display.white)
    display.show()

def show_state(state):
    if result.get("start") and result.get("stop") is None:
        show_text("started: " + result["start"])
    else:
        show_text("no entry started")

# connect to wifi
nic = network.WLAN(network.STA_IF)
nic.active(True)
nic.connect("wifi-ssid", "wifi-password")

# init display
display = LCD_1inch3()
display.fill(0)
display.show()

button_a = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_UP)
once = True

# show initial state when started
result = call_flask("state")
show_state(result)

while True:
    # check for button press + release
    button_a_press = button_a.value()
    utime.sleep_ms(10)
    button_a_release = button_a.value()

    if button_a_press and not button_a_release and once:
        once = False
        result = call_flask("toggle")
        show_state(result)
    elif not button_a_press and button_a_release:
        once = True

This version is by far not perfect and is only a working demo. For example is the font size way to small:

img1

There are lots of possible improvements: use the other buttons, show images instead of text, use more of the display, add error handling when the Flask server is not reachable and probably a lot more.