concertina

Elecronic concertina
git clone https://git.woozle.org/neale/concertina.git

commit
f751184
parent
a3d4bd2
author
Neale Pickett
date
2026-02-16 21:43:10 -0700 MST
I don't remember, it was a day
7 files changed,  +61, -51
M go.mod
M go.sum
M cmd/concertina/board_qtpy.go
+2, -2
 1@@ -5,7 +5,7 @@ package main
 2 import "machine"
 3 
 4 const (
 5-	VS1053_TX = machine.UART_TX_PIN
 6-	VS1053_RX = machine.UART_RX_PIN
 7+	VS1053_TX    = machine.UART_TX_PIN
 8+	VS1053_RX    = machine.UART_RX_PIN
 9 	VS1053_RESET = machine.D3
10 )
M cmd/concertina/board_xiao.go
+0, -2
1@@ -13,5 +13,3 @@ func blink() {
2 	machine.LED.Low()
3 	time.Sleep(200 * time.Millisecond)
4 }
5-	
6-	
M cmd/concertina/concertina.go
+38, -31
  1@@ -5,10 +5,14 @@ import (
  2 	"image/color"
  3 	"machine"
  4 	"time"
  5-	"tinygo.org/x/drivers"
  6-	"tinygo.org/x/drivers/ssd1306"
  7+
  8+	"git.woozle.org/neale/concertina/pkg/layouts"
  9 	"git.woozle.org/neale/concertina/pkg/led"
 10 	"git.woozle.org/neale/concertina/pkg/pcf8575"
 11+	"tinygo.org/x/drivers"
 12+	"tinygo.org/x/drivers/ssd1306"
 13+	"tinygo.org/x/tinyfont"
 14+	"tinygo.org/x/tinyfont/proggy"
 15 )
 16 
 17 // For crying out loud.
 18@@ -17,17 +21,19 @@ var White = color.RGBAModel.Convert(color.White).(color.RGBA)
 19 var Black = color.RGBAModel.Convert(color.Black).(color.RGBA)
 20 
 21 type Concertina struct {
 22-	display *ssd1306.Device
 23-	tonegen MidiWriter
 24-	expanders[4] pcf8575.Device
 25+	display       *ssd1306.Device
 26+	tonegen       MidiWriter
 27+	expanders     [4]pcf8575.Device
 28+	layouts       []*layouts.Layout
 29+	currentLayout int
 30 }
 31 
 32 func NewConcertina(i2c drivers.I2C) (*Concertina, error) {
 33 	// Initialize tone generator
 34 	if err := machine.DefaultUART.Configure(machine.UARTConfig{
 35 		BaudRate: 31250,
 36-		TX: VS1053_TX,
 37-		RX: VS1053_RX,
 38+		TX:       VS1053_TX,
 39+		RX:       VS1053_RX,
 40 	}); err != nil {
 41 		return nil, err
 42 	}
 43@@ -40,56 +46,59 @@ func NewConcertina(i2c drivers.I2C) (*Concertina, error) {
 44 		return nil, err
 45 	}
 46 
 47-	c := &Concertina {
 48+	c := &Concertina{
 49 		tonegen: MidiWriter{Writer: machine.DefaultUART},
 50 	}
 51 
 52-	for i := range(4) {
 53+	for i := range c.expanders {
 54 		c.expanders[i] = pcf8575.Device{
 55-			Addr: 0x20,
 56-			I2C: i2c,
 57+			Addr: uint16(0x20 + i),
 58+			I2C:  i2c,
 59 		}
 60 	}
 61 
 62-	<-resetTimer // Wait until this timer has elapsed
 63-	VS1053_RESET.High()
 64+	if false {
 65+		if l, err := layouts.DefaultLayouts(); err != nil {
 66+			return nil, err
 67+		} else {
 68+			c.layouts = l
 69+		}
 70+	}
 71 
 72 	c.display = ssd1306.NewI2C(i2c)
 73 	c.display.Configure(
 74 		ssd1306.Config{
 75-			Width: 128,
 76-			Height: 32,
 77+			Width:   128,
 78+			Height:  32,
 79 			Address: 0x3C,
 80 		},
 81 	)
 82 	c.display.ClearDisplay()
 83+	tinyfont.WriteLine(c.display, &proggy.TinySZ8pt7b, 0, 20, "Ruby", White)
 84 	c.display.Display()
 85 
 86+	<-resetTimer // Wait until this timer has elapsed
 87+	VS1053_RESET.High()
 88 
 89-	c.tonegen.SetPatch(22)  // General MIDI harmonica
 90+	c.tonegen.SetPatch(22) // General MIDI harmonica
 91 
 92 	return c, nil
 93 }
 94 
 95-var x int16
 96 func (c *Concertina) Loop() {
 97 	var buttons [4]uint16
 98 
 99-	led.Builtin.SetColor(color.RGBA{uint8(x % 2), 0, 0, 0})
100-
101-	c.display.ClearBuffer()
102-	for i := range(4) {
103-		buttons[i] = c.expanders[i].Get()
104-		for bit := range(16) {
105+	c.display.FillRectangle(124, 0, 4, 16, Black)
106+	for x := range 4 {
107+		buttons[x] = c.expanders[x].Get()
108+		for bit := range 16 {
109 			pc := White
110-			if buttons[i] & (1<<bit)  != 0 {
111+			if buttons[x]&(1<<bit) != 0 {
112 				pc = Black
113 			}
114-			c.display.SetPixel(int16(bit), int16(16+3*i), pc)
115+			c.display.SetPixel(int16(124+x), int16(bit), pc)
116 		}
117 	}
118-	c.display.FillRectangle(x, 0, 5, 5, White)
119-	x = (x + 1) % 120
120 	c.display.Display()
121 
122 	//c.tonegen.NoteOn(byte(x))
123@@ -100,7 +109,7 @@ func (c *Concertina) Loop() {
124 func main() {
125 	// Blink the LED during startup.
126 	blinkCtx, blinkCancel := context.WithCancel(context.Background())
127-	go led.Flash(blinkCtx, led.Builtin, time.Second / 4, time.Second / 4)
128+	go led.Flash(blinkCtx, led.Builtin, time.Second/4, time.Second/4)
129 
130 	c, err := NewConcertina(machine.I2C0)
131 	if err != nil {
132@@ -112,9 +121,7 @@ func main() {
133 
134 	// Okay, you can stop blinking now
135 	blinkCancel()
136-	//led.Builtin.Disable()
137-
138-	led.Builtin.SetColor(color.RGBA{0, 0, 10, 0})
139+	led.Builtin.Disable()
140 
141 	for {
142 		c.Loop()
D cmd/concertina/display.go
+0, -12
 1@@ -1,12 +0,0 @@
 2-package main
 3-
 4-import (
 5-	"tinygo.org/x/tinyfont"
 6-	"tinygo.org/x/tinyfont/gophers"
 7-)
 8-
 9-func (c *Concertina) Print(s string) {
10-	c.display.ClearDisplay()
11-	tinyfont.WriteLine(c.display, &gophers.Regular14pt, 18, 90, s, Black)
12-	c.display.Display()
13-}
M cmd/concertina/midi.go
+18, -1
 1@@ -5,10 +5,12 @@ import "io"
 2 type MidiWriter struct {
 3 	io.Writer
 4 	Channel byte
 5+	Playing *MidiNotes
 6 }
 7 
 8 func (w MidiWriter) NoteOnVelocity(note byte, velocity byte) {
 9 	w.Write([]byte{0x90 + w.Channel, note, velocity})
10+	w.Playing.On(note)
11 }
12 
13 func (w MidiWriter) NoteOn(note byte) {
14@@ -17,12 +19,27 @@ func (w MidiWriter) NoteOn(note byte) {
15 
16 func (w MidiWriter) NoteOffVelocity(note byte, velocity byte) {
17 	w.Write([]byte{0x80 + w.Channel, note, velocity})
18+	w.Playing.Off(note)
19 }
20 
21 func (w MidiWriter) NoteOff(note byte) {
22 	w.NoteOffVelocity(note, 127)
23 }
24-	
25+
26 func (w MidiWriter) SetPatch(patch byte) {
27 	w.Write([]byte{0xC0 + w.Channel, patch})
28 }
29+
30+type MidiNotes [2]uint64
31+
32+func (n *MidiNotes) Equal(a MidiNotes) bool {
33+	return (n[0] == a[0]) && (n[1] == a[1])
34+}
35+
36+func (n *MidiNotes) On(note byte) {
37+	n[note/64] |= 1 << (note % 64)
38+}
39+
40+func (n *MidiNotes) Off(note byte) {
41+	n[note/64] &= ^(1 << (note % 64))
42+}
M go.mod
+1, -1
1@@ -4,7 +4,7 @@ go 1.24.4
2 
3 require (
4 	tinygo.org/x/drivers v0.34.0
5-	tinygo.org/x/tinyfont v0.3.0
6+	tinygo.org/x/tinyfont v0.6.0
7 )
8 
9 require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
M go.sum
+2, -2
1@@ -2,5 +2,5 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU
2 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
3 tinygo.org/x/drivers v0.34.0 h1:lw8ePJeUSn9oICKBvQXHC9TIE+J00OfXfkGTrpXM9Iw=
4 tinygo.org/x/drivers v0.34.0/go.mod h1:ZdErNrApSABdVXjA1RejD67R8SNRI6RKVfYgQDZtKtk=
5-tinygo.org/x/tinyfont v0.3.0 h1:HIRLQoI3oc+2CMhPcfv+Ig88EcTImE/5npjqOnMD4lM=
6-tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk=
7+tinygo.org/x/tinyfont v0.6.0 h1:GibXDSFz6xrWnEDkDRo6vsbOyRw0MVj/eza3zNHMSHs=
8+tinygo.org/x/tinyfont v0.6.0/go.mod h1:onflMSkpWl7r7j4MIqhPEVV39pn7yL4N3MOePl3G+G8=