Skip to content

Instantly share code, notes, and snippets.

@saljam
Created April 2, 2025 12:18
Show Gist options
  • Save saljam/1c0507ee85cba88aed71834f2a3046c9 to your computer and use it in GitHub Desktop.
Save saljam/1c0507ee85cba88aed71834f2a3046c9 to your computer and use it in GitHub Desktop.
// program to demonstrate programming the pimoroni inky impression 7.3" eink display (ac073tc1a) from go.
package main
import (
"fmt"
"log"
"os"
"slices"
"time"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
"periph.io/x/conn/v3/spi/spireg"
"periph.io/x/host/v3"
"periph.io/x/host/v3/rpi"
)
const (
black = 0
white = 1
green = 2
blue = 3
red = 4
yellow = 5
orange = 6
width = 800
height = 480
)
// copied from https://github.com/pimoroni/inky/blob/main/inky/inky_ac073tc1a.py
const (
cmdPSR = 0x00
cmdPWR = 0x01
cmdPOF = 0x02
cmdPOFS = 0x03
cmdPON = 0x04
cmdBTST1 = 0x05
cmdBTST2 = 0x06
cmdDSLP = 0x07
cmdBTST3 = 0x08
cmdDTM = 0x10
cmdDSP = 0x11
cmdDRF = 0x12
cmdIPC = 0x13
cmdPLL = 0x30
cmdTSC = 0x40
cmdTSE = 0x41
cmdTSW = 0x42
cmdTSR = 0x43
cmdCDI = 0x50
cmdLPD = 0x51
cmdTCON = 0x60
cmdTRES = 0x61
cmdDAM = 0x65
cmdREV = 0x70
cmdFLG = 0x71
cmdAMV = 0x80
cmdVV = 0x81
cmdVDCS = 0x82
cmdTVDCS = 0x84
cmdAGID = 0x86
cmdCMDH = 0xAA
cmdCCSET = 0xE0
cmdPWS = 0xE3
cmdTSSET = 0xE6
)
var (
pinBusy = rpi.P1_11
pinReset = rpi.P1_13
pinData = rpi.P1_15
inkyconn spi.Conn
)
func main() {
port := setup()
defer port.Close()
// a demo graphic
img := make([]byte, width*height/2)
for y := range height {
for x := range width {
i, _ := slices.BinarySearch([]int{0, 80, 160, 240, 320}, x-y)
img[(y*width+x)/2] |= []byte{white, red, green, blue, yellow, white}[i] << (4 * (^x & 1))
}
}
paint(img)
}
func setup() spi.PortCloser {
// init spi
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
p, err := spireg.Open("")
if err != nil {
log.Fatal(err)
}
inkyconn, err = p.Connect(5*physic.MegaHertz, spi.Mode0, 8)
if err != nil {
log.Fatal(err)
}
// init gpio
err = pinData.Out(gpio.Low)
if err != nil {
log.Fatal(err)
}
err = pinReset.Out(gpio.High)
if err != nil {
log.Fatal(err)
}
err = pinBusy.In(gpio.PullDown, gpio.RisingEdge)
if err != nil {
log.Fatal(err)
}
// reset
time.Sleep(100 * time.Millisecond)
err = pinReset.Out(gpio.Low)
if err != nil {
log.Fatal(err)
}
time.Sleep(100 * time.Millisecond)
err = pinReset.Out(gpio.High)
if err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Second)
// mystery init sequence from inky_ac073tc1a.py
log.Println("init sequence")
for _, step := range []struct {
cmd byte
data []byte
}{
{cmdCMDH, []byte{0x49, 0x55, 0x20, 0x08, 0x09, 0x18}},
{cmdPWR, []byte{0x3F, 0x00, 0x32, 0x2A, 0x0E, 0x2A}},
{cmdPSR, []byte{0x5F, 0x69}},
{cmdPOFS, []byte{0x00, 0x54, 0x00, 0x44}},
{cmdBTST1, []byte{0x40, 0x1F, 0x1F, 0x2C}},
{cmdBTST2, []byte{0x6F, 0x1F, 0x16, 0x25}},
{cmdBTST3, []byte{0x6F, 0x1F, 0x1F, 0x22}},
{cmdIPC, []byte{0x00, 0x04}},
{cmdPLL, []byte{0x02}},
{cmdTSE, []byte{0x00}},
{cmdCDI, []byte{0x3F}},
{cmdTCON, []byte{0x02, 0x00}},
{cmdTRES, []byte{0x03, 0x20, 0x01, 0xE0}},
{cmdVDCS, []byte{0x1E}},
{cmdTVDCS, []byte{0x00}},
{cmdAGID, []byte{0x00}},
{cmdPWS, []byte{0x2F}},
{cmdCCSET, []byte{0x00}},
{cmdTSSET, []byte{0x00}},
} {
fmt.Fprintf(os.Stderr, ".")
err = write(step.cmd, step.data)
if err != nil {
log.Fatal(err)
}
}
fmt.Fprintf(os.Stderr, "\n")
return p
}
func paint(buf []byte) {
log.Println("painting")
for _, step := range []struct {
cmd byte
data []byte
delay time.Duration
}{
{cmdDTM, buf, time.Second},
{cmdPON, nil, time.Second},
{cmdDRF, []byte{0x00}, 35 * time.Second},
{cmdPOF, []byte{0x00}, time.Second},
} {
fmt.Fprintf(os.Stderr, ".")
err := write(step.cmd, step.data)
if err != nil {
log.Fatal(err)
}
// there's a "busy" gpio pin to synchronise timing, but i found it unreliable.
time.Sleep(step.delay)
}
fmt.Fprintf(os.Stderr, "\n")
}
func write(cmd byte, data []byte) error {
err := inkyconn.Tx([]byte{cmd}, nil)
if err != nil {
return err
}
if data != nil {
pinData.Out(gpio.High)
defer pinData.Out(gpio.Low)
bufsiz := 0
if limits, ok := inkyconn.(conn.Limits); ok {
bufsiz = limits.MaxTxSize()
}
if bufsiz == 0 {
bufsiz = 4096
}
for len(data) > 0 {
chunk := min(len(data), bufsiz)
err := inkyconn.Tx(data[:chunk], nil)
if err != nil {
return err
}
data = data[chunk:]
}
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment