Initial working project
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"net/netip"
|
||||
"time"
|
||||
"image/color"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
"tinygo.org/x/tinyfont"
|
||||
"tinygo.org/x/tinyfont/freesans"
|
||||
"tinygo.org/x/tinyfont/proggy"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultFont = &freesans.Regular9pt7b
|
||||
tinyFont = &proggy.TinySZ8pt7b
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Panic Handler & Entry Point
|
||||
// -------------------------------------------------------------
|
||||
func panicErr(msg string, err error) {
|
||||
if err != nil {
|
||||
println("FATAL:", msg, "-", err.Error())
|
||||
}
|
||||
for {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// syncNTP triggers an NTP request and polls until completed or timed out.
|
||||
func syncNTP(stack *NetworkStack, server netip.Addr, timeout time.Duration) (time.Duration, error) {
|
||||
if err := stack.stack.StartNTP(server); err != nil {
|
||||
return 0, fmt.Errorf("start NTP: %w", err)
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(timeout)
|
||||
for time.Now().Before(deadline) {
|
||||
offset, done := stack.stack.ResultNTPOffset()
|
||||
if done {
|
||||
return offset, nil
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("NTP request timed out")
|
||||
}
|
||||
|
||||
func main() {
|
||||
time.Sleep(2 * time.Second)
|
||||
println("Starting app...")
|
||||
|
||||
// Bump frequency to 400kHz for snappy display flushes
|
||||
i2c := machine.I2C0
|
||||
err := i2c.Configure(machine.I2CConfig{
|
||||
Frequency: 400 * machine.KHz,
|
||||
SDA: machine.GPIO4,
|
||||
SCL: machine.GPIO5,
|
||||
})
|
||||
if err != nil {
|
||||
panicErr("I2C configuration", err)
|
||||
}
|
||||
|
||||
// Instantiate nil oled variable
|
||||
var oled *ssd1306.Device
|
||||
// White color for display
|
||||
white := color.RGBA{R: 255, G: 255, B: 255, A: 255}
|
||||
|
||||
// Probe address 0x3C with a zero-byte or 1-byte read
|
||||
println("Initializing display...")
|
||||
var dummy [1]byte
|
||||
if err := i2c.ReadRegister(0x3C, 0x00, dummy[:]); err == nil {
|
||||
println("SSD1306 detected at 0x3C")
|
||||
dev := ssd1306.NewI2C(i2c)
|
||||
dev.Configure(ssd1306.Config{
|
||||
Address: 0x3C,
|
||||
Width: 128,
|
||||
Height: 64,
|
||||
})
|
||||
oled = dev
|
||||
|
||||
// Run some initial clearing and add booting text to the screen
|
||||
oled.ClearBuffer()
|
||||
tinyfont.WriteLine(oled, defaultFont, 0, 32, "Booting...", white)
|
||||
oled.Display()
|
||||
time.Sleep(1 * time.Second)
|
||||
} else {
|
||||
panicErr("Failed to init display.", err)
|
||||
}
|
||||
|
||||
println("Starting Wi-Fi...")
|
||||
oled.ClearBuffer()
|
||||
tinyfont.WriteLine(oled, defaultFont, 0, 32, "Starting Wi-Fi...", white)
|
||||
oled.Display()
|
||||
|
||||
netStack, err := InitNetwork(WifiSSID, WifiPass)
|
||||
if err != nil {
|
||||
panicErr("network init", err)
|
||||
}
|
||||
|
||||
println("Wi-Fi Connected!")
|
||||
oled.ClearBuffer()
|
||||
tinyfont.WriteLine(oled, defaultFont, 0, 32, "Wi-Fi Connected!", white)
|
||||
oled.Display()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Extract IP string from the returned NetworkStack
|
||||
ipStr := netStack.localIP.String()
|
||||
|
||||
// Sync wall time using public NTP (e.g. Cloudflare: 162.159.200.1 / time.cloudflare.com)
|
||||
println("Synchronizing clock via NTP...")
|
||||
oled.ClearBuffer()
|
||||
tinyfont.WriteLine(oled, defaultFont, 0, 32, "Syncing NTP...", white)
|
||||
oled.Display()
|
||||
ntpServer := netip.MustParseAddr("162.159.200.1")
|
||||
timeOffset, err := syncNTP(netStack, ntpServer, 5*time.Second)
|
||||
if err != nil {
|
||||
panicErr("NTP sync failed", err)
|
||||
}
|
||||
|
||||
println("NTP synced successfully!")
|
||||
oled.ClearBuffer()
|
||||
tinyfont.WriteLine(oled, defaultFont, 0, 32, "NTP synced!", white)
|
||||
oled.Display()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
lastSecond := -1
|
||||
|
||||
for {
|
||||
currentTime := time.Now().Add(timeOffset).UTC()
|
||||
currentSecond := currentTime.Second()
|
||||
|
||||
// Skip display refreshes if the second hasn't changed to save the i2c bus
|
||||
if currentSecond != lastSecond {
|
||||
lastSecond = currentSecond
|
||||
|
||||
// Alternate colon (:) with space ( ) every second
|
||||
timeSeperator := ":"
|
||||
if currentSecond % 2 == 1 {
|
||||
timeSeperator = " "
|
||||
}
|
||||
|
||||
// Create the awful time string before displaying it
|
||||
timeStr := fmt.Sprintf("%02d%s%02d%s%02d UTC",
|
||||
currentTime.Hour(), timeSeperator,
|
||||
currentTime.Minute(), timeSeperator,
|
||||
currentTime.Second())
|
||||
|
||||
oled.ClearBuffer()
|
||||
// Time
|
||||
tinyfont.WriteLine(oled, defaultFont, 8, 32, timeStr, white)
|
||||
// IP Address
|
||||
tinyfont.WriteLine(oled, tinyFont, 0, 60, fmt.Sprintf("IP: %s", ipStr), white)
|
||||
oled.Display()
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user