Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tinygo-stuff

This repository contains the source code for a tinygo application firmware/main.go - a real time clock with multiple HD44780 LCD display configuration for rpi pico

The firmware is built by TinyGo and lives in its own directory because it imports machine, which only exists there. Everything at the top level is the host-side tooling and builds with go build . into a single mcu binary.

The time for the RTC is set via timestamp which is passed via --ldflags -X at compile time. No buttons needed!

This source code is used in the hackable clock kit / prototype

lcd-2024-03-29_18.16.39.mp4

Schematic Wiring Diagram

mcu schematic draws the wiring for the firmware's default configuration — both HD44780 displays and the DS1307.

The wiring is a description rather than a drawing: boards are pin names, wires are pairs of pin references, and the schematic package works out the geometry. Retargeting it to different hardware is an edit to the data in schematic_cmd.go, not a re-tuning of coordinates.

mcu schematic             text renderer, color
mcu schematic --plain     no ANSI color
mcu schematic --netlist   the connection table
mcu schematic --graph     asciigraph renderer
mcu schematic --svg out.svg [--light]

schematic wiring diagram pico rtc lcd

The SVG is pico-lcd-rtc-schematic-v2.svg, with a light-background variant for print. The terminal renderer draws the same thing in box characters:

schematic wiring diagram in the terminal

Metaprogramming for microcontrollers

A wrapper for the development workflow (on linux!) and serial interfacer is built with go build .. The top level command starts a bidirectional serial interface

$ go run .
mcu serial interfacer

Usage:
  mcu

Available Commands:
  ef           eval + flash
  eval         generate updated source code
  flash        mcu cli - tinygo flash command generator
  mon          serial monitor
  send         serial send

Flags:
  -b, --baud int     baud rate (default 9600)
  -m, --ser string   block device for serial interface (i.e. "/dev/ttyACM0")
                     if unspecified serial connection will not be attempted

mcu flash

The flash and ef subcommands rely on tinygo and are designed to either invoke tinygo directly or just generate the tinygo command string to run for flashing the application.

If a block device is specified by the -y, --dev flag, the program is compiled and flashed to the device. If no block device is specified, the command string with the default flag values converted to --ldflags -X is generated. Example:

$ GOPROG=firmware/main.go go run . flash
tinygo flash  -ldflags=" -X 'main.timeStamp=2024-03-22T11:56:20Z' -X 'main.offSet=9' -X 'main.rtcFuture=false' -X 'main.enableLED=false'" firmware/main.go

If a serial interface is specified to the -m, --ser flag, a serial connection will be attempted after flashing.

Help menu flags for flash, ef, and eval subcommands are created based on the .go source code file specified in the GOPROG environmental variable. Example:

GOPROG=firmware/main.go go run . flash --help
Tinygo application command line interface
Set global-scope string variables at compile time for tinygo applications
via flags autogenerated for this help menu.

Usage:
 mcu flash

Flags:
	 --timeStamp string   main.timeStamp // Set timestamp $(date '+%Y-%m-%dT%H:%M:%SZ')
(default "2024-03-22T11:59:17Z")
	 --offSet string      main.offSet // seconds to add to timeStamp
(default "9")             
	 --rtcFuture string   main.rtcFuture // set 'true' if RTC is set to the future
(default "false")         
	 --enableLED string   main.enableLED // set 'true' to enable onboard LED as a measure of rtc read time or program loop time indicator
(default "false")         
 -m, --ser string         block device for serial interface (i.e. "/dev/ttyACM0")
						  if unspecified serial connection will not be attempted
 -b, --baud int           baud rate (default 9600)
 -s, --slp duration       seconds to wait before serial connection after flashing (default 3s)
 -y, --dev string         block device to flash (i.e. "/dev/sdx")
						  if unspecified, tinygo flash command is generated
 -z, --target string      tinygo flash target

Notice in the above that timestamp was evaluated by the shell.

mcu eval

Not all standard library packages will work with tinygo:

https://tinygo.org/docs/reference/lang-support/stdlib/

Some are importable but don't pass tests. Some work partially. Among these which are importable but fail tests is encoding/json.

It might be relatively trivial to pass json as a string via --ldflags -X and convert that to a struct of the configuration for an LCD, but with a broken library it is not possible.

Other library alternatives such as embed are not working either. So what option exists for configuration?

mcu eval can produce a modified source code, allowing globally-scoped non-string variables to be updated with new values

$ GOPROG=firmware/main.go go run . eval --help
modify initializations in source code for tinygo applications

Usage:
  mcu eval

Flags:
      --multidisplays string   [...]LCDConfig{{DataPins: []m.Pin{m.GP22, m.GP21, m.GP20, m.GP19, m.GP18, m.GP17, m.GP16, m.GP15}, RS: m.GP26, EN: m.GP27, RW: m.NoPin, Contrast: m.GP28, Clvl: 2, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}, {DataPins: []m.Pin{m.GP5, m.GP6, m.GP7, m.GP8, m.GP9, m.GP10, m.GP11, m.GP12}, RS: m.GP4, EN: m.GP3, RW: m.NoPin, Contrast: m.GP2, Clvl: 3, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}}
 (default "[...]LCDConfig{{DataPins: []m.Pin{m.GP22, m.GP21, m.GP20, m.GP19, m.GP18, m.GP17, m.GP16, m.GP15}, RS: m.GP26, EN: m.GP27, RW: m.NoPin, Contrast: m.GP28, Clvl: 2, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}, {DataPins: []m.Pin{m.GP5, m.GP6, m.GP7, m.GP8, m.GP9, m.GP10, m.GP11, m.GP12}, RS: m.GP4, EN: m.GP3, RW: m.NoPin, Contrast: m.GP2, Clvl: 3, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}}")

when the command is run, the updated source code is printed to stdout

mcu ef eval + flash

A combination approach of updated source code + compile time variable assignment is included as mcu ef

First, flags generated for declaration initializations of non string globally scoped variables are assessed to an ephemeral modified version of the source code.

Next, the --ldflags -X for compile-time variable assignments are generated.

Then tinygo flash is invoked with those ldflags to compile the temporary copy of the source code with the modifications ; if the block device to flash is specified

Afterwards, a serial connection is attempted - if the serial interface is specified

$ GOPROG=firmware/main.go go run . ef --help
seamlessly flash modified source code

Usage:
  mcu ef

Flags:
      --multidisplays string   [...]LCDConfig{{DataPins: []m.Pin{m.GP22, m.GP21, m.GP20, m.GP19, m.GP18, m.GP17, m.GP16, m.GP15}, RS: m.GP26, EN: m.GP27, RW: m.NoPin, Contrast: m.GP28, Clvl: 2, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}, {DataPins: []m.Pin{m.GP5, m.GP6, m.GP7, m.GP8, m.GP9, m.GP10, m.GP11, m.GP12}, RS: m.GP4, EN: m.GP3, RW: m.NoPin, Contrast: m.GP2, Clvl: 3, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}}
 (default "[...]LCDConfig{{DataPins: []m.Pin{m.GP22, m.GP21, m.GP20, m.GP19, m.GP18, m.GP17, m.GP16, m.GP15}, RS: m.GP26, EN: m.GP27, RW: m.NoPin, Contrast: m.GP28, Clvl: 2, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}, {DataPins: []m.Pin{m.GP5, m.GP6, m.GP7, m.GP8, m.GP9, m.GP10, m.GP11, m.GP12}, RS: m.GP4, EN: m.GP3, RW: m.NoPin, Contrast: m.GP2, Clvl: 3, Rows: 2, Columns: 16, CursorBlink: false, CursorOnOff: false}}")
      --timeStamp string       main.timeStamp // Set timestamp $(date '+%Y-%m-%dT%H:%M:%SZ')
 (default "2024-03-22T12:24:43Z")
      --offSet string          main.offSet // seconds to add to timeStamp
 (default "9")                 
      --rtcFuture string       main.rtcFuture // set 'true' if RTC is set to the future
 (default "false")             
      --enableLED string       main.enableLED // set 'true' to enable onboard LED as a measure of rtc read time or program loop time indicator
 (default "false")             
  -m, --ser string             block device for serial interface (i.e. "/dev/ttyACM0")
                               if unspecified serial connection will not be attempted
  -b, --baud int               baud rate (default 9600)
  -s, --slp duration           seconds to wait before serial connection after flashing (default 3s)
  -y, --dev string             block device to flash (i.e. "/dev/sdx")
                               if unspecified, tinygo flash command is generated
  -z, --target string          tinygo flash target

Example:

$ GOPROG=firmware/main.go go run . ef -z pico -y /dev/sdd1 -m /dev/ttyACM?

modified source code would print here ; omitted for brevity

tinygo flash  -target=pico  -ldflags=" -X 'main.timeStamp=2024-03-22T13:05:14Z' -X 'main.offSet=9' -X 'main.rtcFuture=false' -X 'main.enableLED=false'" /tmp/2446216074.go
+ sudo echo 'sudo cache'
sudo cache
+ set +x
+ udisksctl mount -b /dev/sdd1
Mounted /dev/sdd1 at /run/media/user/RPI-RP2
+ set +x
ttyusb found: /dev/ttyACM0

"/dev/ttyACM0"
[2024-03-22 13:05:28]
[2024-03-22 13:05:29]
[2024-03-22 13:05:30]
[2024-03-22 13:05:31]
[2024-03-22 13:05:32]
[2024-03-22 13:05:33]
[2024-03-22 13:05:34]
[2024-03-22 13:05:35]
[2024-03-22 13:05:36]

The timestamp logging at the end is produced from the serial interface with the microcontroller

Time Setting

As should be apparent in examining the above text, the date command (provided in the comment of main.go for var timeStamp) is evaluated by the shell (via bitfield/script) and populates the --timeStamp flag as default value ; and is then used in it's evaluated format as:

tinygo flash  -target=pico  -ldflags=" -X 'main.timeStamp=2024-03-22T13:05:14Z'

the offSet (seconds) is added to the timestamp to compensate for the delay between compilation and setting the time on the RTC, more or less.

 -X 'main.offSet=9'

The strategy for time-setting is to set the time on the RTC as closely as possible to the time on the host system, which is assumed to have the correct desired time. In order to make the time not get directly re-set to the timestamp on every boot cycle, the code as presented simply avoids setting the RTC to a time in the past.

In order to handle the situation where the RTC is stuck in the future, the --rtcFuture=true flag is used to explicitly set the time for the RTC to the timestamp time plus offset time.

The microcontroller should then be re-flashed without specifying --rtcFuture=true or by using the default value.

After compensating for the time offset of your hardware, it should be possible to set the RTC to the current time in just a few flashes.

Dynamic Reconfiguration

Using this wrapper creates, essentially, a command-line interface or help menu for the application you want to run on an MCU. I/O Reconfiguration should not require editing the source code. It should be as easy to move a wire as to change some text supplied to a flag.

While this method requires intentional coding, in such a way that excludes those variables from appearing in a help menu that you don't want the user to set, and other considerations; it is not limited in it's application to the tinygo context and could be employed in go for creating executables with certain default values hard-coded.

Dependency Graph

Made with goda:

go run github.com/loov/goda@latest graph github.com/0magnet/tinygo-stuff/... | dot -Tsvg -o docs/tinygo-stuff-goda-graph.svg

Dependency Graph

Lines of Code

Made with gocloc (excludes vendor/, node_modules/, .git/):

gocloc --not-match-d='(vendor|node_modules|\.git)' .
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Go                               9            235            355           2296
Markdown                         1             72              0            162
HTML                             2              6              2            124
YAML                             1              0             11             99
Makefile                         1             12              9             53
Bourne Shell                     1              8             16             30
JSON                             2              0              0             28
-------------------------------------------------------------------------------
TOTAL                           17            333            393           2792
-------------------------------------------------------------------------------

About

Tinygo HD44780 LCD RTC clock source code - tested with rpi pico

Resources

Stars

1 star

Watchers

0 watching

Forks

Used by

Contributors

Languages