concertina

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

commit
823b4c6
parent
c18a343
author
Neale Pickett
date
2026-02-24 19:04:55 -0700 MST
the little light is blinking
10 files changed,  +864, -420
D go.mod
D go.sum
A .cargo/config.toml
+15, -0
 1@@ -0,0 +1,15 @@
 2+# samd21 is a Cortex-M0 and thus thumbv6m
 3+
 4+[build]
 5+target = "thumbv6m-none-eabi"
 6+
 7+[target.thumbv6m-none-eabi]
 8+runner = 'arm-none-eabi-gdb'
 9+rustflags = [
10+
11+  # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
12+  # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
13+  "-C", "link-arg=--nmagic",
14+
15+  "-C", "link-arg=-Tlink.x",
16+]
M .gitignore
+2, -0
1@@ -1,5 +1,7 @@
2 build/
3 
4+target/
5+
6 3d/*.stl
7 3d/*.3mf
8 
A Cargo.lock
+752, -0
  1@@ -0,0 +1,752 @@
  2+# This file is automatically @generated by Cargo.
  3+# It is not intended for manual editing.
  4+version = 4
  5+
  6+[[package]]
  7+name = "aes"
  8+version = "0.8.4"
  9+source = "registry+https://github.com/rust-lang/crates.io-index"
 10+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
 11+dependencies = [
 12+ "cfg-if",
 13+ "cipher",
 14+ "cpufeatures",
 15+]
 16+
 17+[[package]]
 18+name = "atsamd-hal"
 19+version = "0.14.0"
 20+source = "registry+https://github.com/rust-lang/crates.io-index"
 21+checksum = "1cddc374559f476b8225083732b400544075c67616e1b1e1635b0efe84bef158"
 22+dependencies = [
 23+ "atsamd21e",
 24+ "bitfield",
 25+ "bitflags 1.3.2",
 26+ "cortex-m",
 27+ "embedded-hal 0.2.7",
 28+ "modular-bitfield",
 29+ "nb 0.1.3",
 30+ "num-traits",
 31+ "paste",
 32+ "rand_core 0.5.1",
 33+ "replace_with",
 34+ "seq-macro 0.2.2",
 35+ "typenum",
 36+ "usb-device",
 37+ "vcell",
 38+ "void",
 39+]
 40+
 41+[[package]]
 42+name = "atsamd-hal"
 43+version = "0.23.2"
 44+source = "registry+https://github.com/rust-lang/crates.io-index"
 45+checksum = "15ecba6d9da0e33fe34876782b771b0777d2515eab00da77fce41fa8824cfda9"
 46+dependencies = [
 47+ "aes",
 48+ "atsamd-hal-macros",
 49+ "bitfield",
 50+ "bitflags 2.11.0",
 51+ "cipher",
 52+ "cortex-m",
 53+ "critical-section",
 54+ "embedded-hal 0.2.7",
 55+ "embedded-hal 1.0.0",
 56+ "embedded-hal-nb",
 57+ "embedded-io",
 58+ "fugit",
 59+ "heapless",
 60+ "modular-bitfield",
 61+ "nb 1.1.0",
 62+ "num-traits",
 63+ "opaque-debug",
 64+ "paste",
 65+ "rand_core 0.9.5",
 66+ "seq-macro 0.3.6",
 67+ "sorted-hlist",
 68+ "typenum",
 69+ "void",
 70+]
 71+
 72+[[package]]
 73+name = "atsamd-hal-macros"
 74+version = "0.3.0"
 75+source = "registry+https://github.com/rust-lang/crates.io-index"
 76+checksum = "ae9d1e473cf24a5deb7966c2e2559e0d739ca7734c0f1b9a058ef31e8188e9f4"
 77+dependencies = [
 78+ "litrs",
 79+ "phf",
 80+ "phf_codegen",
 81+ "serde",
 82+ "serde_yaml",
 83+]
 84+
 85+[[package]]
 86+name = "atsamd21e"
 87+version = "0.11.0"
 88+source = "registry+https://github.com/rust-lang/crates.io-index"
 89+checksum = "52a7080de6a50cc0a1125b004771d8e8b1ed10d60bc88e92a38a6260a5806364"
 90+dependencies = [
 91+ "bare-metal",
 92+ "cortex-m",
 93+ "cortex-m-rt",
 94+ "vcell",
 95+]
 96+
 97+[[package]]
 98+name = "autocfg"
 99+version = "1.5.0"
100+source = "registry+https://github.com/rust-lang/crates.io-index"
101+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
102+
103+[[package]]
104+name = "bare-metal"
105+version = "0.2.5"
106+source = "registry+https://github.com/rust-lang/crates.io-index"
107+checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
108+dependencies = [
109+ "rustc_version",
110+]
111+
112+[[package]]
113+name = "bitfield"
114+version = "0.13.2"
115+source = "registry+https://github.com/rust-lang/crates.io-index"
116+checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
117+
118+[[package]]
119+name = "bitflags"
120+version = "1.3.2"
121+source = "registry+https://github.com/rust-lang/crates.io-index"
122+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
123+
124+[[package]]
125+name = "bitflags"
126+version = "2.11.0"
127+source = "registry+https://github.com/rust-lang/crates.io-index"
128+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
129+
130+[[package]]
131+name = "bytemuck"
132+version = "1.25.0"
133+source = "registry+https://github.com/rust-lang/crates.io-index"
134+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
135+
136+[[package]]
137+name = "byteorder"
138+version = "1.5.0"
139+source = "registry+https://github.com/rust-lang/crates.io-index"
140+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
141+
142+[[package]]
143+name = "cfg-if"
144+version = "1.0.4"
145+source = "registry+https://github.com/rust-lang/crates.io-index"
146+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
147+
148+[[package]]
149+name = "cipher"
150+version = "0.4.4"
151+source = "registry+https://github.com/rust-lang/crates.io-index"
152+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
153+dependencies = [
154+ "crypto-common",
155+ "inout",
156+]
157+
158+[[package]]
159+name = "concertina"
160+version = "0.1.0"
161+dependencies = [
162+ "atsamd-hal 0.14.0",
163+ "atsamd-hal 0.23.2",
164+ "cortex-m",
165+ "cortex-m-rt",
166+ "panic-halt",
167+ "qt_py_m0",
168+ "smart-leds",
169+ "usb-device",
170+ "usbd-serial",
171+ "ws2812-timer-delay",
172+]
173+
174+[[package]]
175+name = "cortex-m"
176+version = "0.7.7"
177+source = "registry+https://github.com/rust-lang/crates.io-index"
178+checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
179+dependencies = [
180+ "bare-metal",
181+ "bitfield",
182+ "embedded-hal 0.2.7",
183+ "volatile-register",
184+]
185+
186+[[package]]
187+name = "cortex-m-rt"
188+version = "0.7.5"
189+source = "registry+https://github.com/rust-lang/crates.io-index"
190+checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
191+dependencies = [
192+ "cortex-m-rt-macros",
193+]
194+
195+[[package]]
196+name = "cortex-m-rt-macros"
197+version = "0.7.5"
198+source = "registry+https://github.com/rust-lang/crates.io-index"
199+checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
200+dependencies = [
201+ "proc-macro2",
202+ "quote",
203+ "syn 2.0.117",
204+]
205+
206+[[package]]
207+name = "cpufeatures"
208+version = "0.2.17"
209+source = "registry+https://github.com/rust-lang/crates.io-index"
210+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
211+dependencies = [
212+ "libc",
213+]
214+
215+[[package]]
216+name = "critical-section"
217+version = "1.2.0"
218+source = "registry+https://github.com/rust-lang/crates.io-index"
219+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
220+
221+[[package]]
222+name = "crypto-common"
223+version = "0.1.7"
224+source = "registry+https://github.com/rust-lang/crates.io-index"
225+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
226+dependencies = [
227+ "generic-array",
228+ "typenum",
229+]
230+
231+[[package]]
232+name = "embedded-hal"
233+version = "0.2.7"
234+source = "registry+https://github.com/rust-lang/crates.io-index"
235+checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
236+dependencies = [
237+ "nb 0.1.3",
238+ "void",
239+]
240+
241+[[package]]
242+name = "embedded-hal"
243+version = "1.0.0"
244+source = "registry+https://github.com/rust-lang/crates.io-index"
245+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
246+
247+[[package]]
248+name = "embedded-hal-nb"
249+version = "1.0.0"
250+source = "registry+https://github.com/rust-lang/crates.io-index"
251+checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
252+dependencies = [
253+ "embedded-hal 1.0.0",
254+ "nb 1.1.0",
255+]
256+
257+[[package]]
258+name = "embedded-io"
259+version = "0.6.1"
260+source = "registry+https://github.com/rust-lang/crates.io-index"
261+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
262+
263+[[package]]
264+name = "equivalent"
265+version = "1.0.2"
266+source = "registry+https://github.com/rust-lang/crates.io-index"
267+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
268+
269+[[package]]
270+name = "fugit"
271+version = "0.3.9"
272+source = "registry+https://github.com/rust-lang/crates.io-index"
273+checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6"
274+dependencies = [
275+ "gcd",
276+]
277+
278+[[package]]
279+name = "gcd"
280+version = "2.3.0"
281+source = "registry+https://github.com/rust-lang/crates.io-index"
282+checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
283+
284+[[package]]
285+name = "generic-array"
286+version = "0.14.7"
287+source = "registry+https://github.com/rust-lang/crates.io-index"
288+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
289+dependencies = [
290+ "typenum",
291+ "version_check",
292+]
293+
294+[[package]]
295+name = "hash32"
296+version = "0.3.1"
297+source = "registry+https://github.com/rust-lang/crates.io-index"
298+checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
299+dependencies = [
300+ "byteorder",
301+]
302+
303+[[package]]
304+name = "hashbrown"
305+version = "0.16.1"
306+source = "registry+https://github.com/rust-lang/crates.io-index"
307+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
308+
309+[[package]]
310+name = "heapless"
311+version = "0.8.0"
312+source = "registry+https://github.com/rust-lang/crates.io-index"
313+checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
314+dependencies = [
315+ "hash32",
316+ "stable_deref_trait",
317+]
318+
319+[[package]]
320+name = "indexmap"
321+version = "2.13.0"
322+source = "registry+https://github.com/rust-lang/crates.io-index"
323+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
324+dependencies = [
325+ "equivalent",
326+ "hashbrown",
327+]
328+
329+[[package]]
330+name = "inout"
331+version = "0.1.4"
332+source = "registry+https://github.com/rust-lang/crates.io-index"
333+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
334+dependencies = [
335+ "generic-array",
336+]
337+
338+[[package]]
339+name = "itoa"
340+version = "1.0.17"
341+source = "registry+https://github.com/rust-lang/crates.io-index"
342+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
343+
344+[[package]]
345+name = "libc"
346+version = "0.2.182"
347+source = "registry+https://github.com/rust-lang/crates.io-index"
348+checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
349+
350+[[package]]
351+name = "litrs"
352+version = "0.4.2"
353+source = "registry+https://github.com/rust-lang/crates.io-index"
354+checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
355+dependencies = [
356+ "proc-macro2",
357+]
358+
359+[[package]]
360+name = "modular-bitfield"
361+version = "0.11.2"
362+source = "registry+https://github.com/rust-lang/crates.io-index"
363+checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
364+dependencies = [
365+ "modular-bitfield-impl",
366+ "static_assertions",
367+]
368+
369+[[package]]
370+name = "modular-bitfield-impl"
371+version = "0.11.2"
372+source = "registry+https://github.com/rust-lang/crates.io-index"
373+checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
374+dependencies = [
375+ "proc-macro2",
376+ "quote",
377+ "syn 1.0.109",
378+]
379+
380+[[package]]
381+name = "nb"
382+version = "0.1.3"
383+source = "registry+https://github.com/rust-lang/crates.io-index"
384+checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
385+dependencies = [
386+ "nb 1.1.0",
387+]
388+
389+[[package]]
390+name = "nb"
391+version = "1.1.0"
392+source = "registry+https://github.com/rust-lang/crates.io-index"
393+checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
394+
395+[[package]]
396+name = "num-traits"
397+version = "0.2.19"
398+source = "registry+https://github.com/rust-lang/crates.io-index"
399+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
400+dependencies = [
401+ "autocfg",
402+]
403+
404+[[package]]
405+name = "opaque-debug"
406+version = "0.3.1"
407+source = "registry+https://github.com/rust-lang/crates.io-index"
408+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
409+
410+[[package]]
411+name = "panic-halt"
412+version = "0.2.0"
413+source = "registry+https://github.com/rust-lang/crates.io-index"
414+checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
415+
416+[[package]]
417+name = "paste"
418+version = "1.0.15"
419+source = "registry+https://github.com/rust-lang/crates.io-index"
420+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
421+
422+[[package]]
423+name = "phf"
424+version = "0.11.3"
425+source = "registry+https://github.com/rust-lang/crates.io-index"
426+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
427+dependencies = [
428+ "phf_shared",
429+]
430+
431+[[package]]
432+name = "phf_codegen"
433+version = "0.11.3"
434+source = "registry+https://github.com/rust-lang/crates.io-index"
435+checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
436+dependencies = [
437+ "phf_generator",
438+ "phf_shared",
439+]
440+
441+[[package]]
442+name = "phf_generator"
443+version = "0.11.3"
444+source = "registry+https://github.com/rust-lang/crates.io-index"
445+checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
446+dependencies = [
447+ "phf_shared",
448+ "rand",
449+]
450+
451+[[package]]
452+name = "phf_shared"
453+version = "0.11.3"
454+source = "registry+https://github.com/rust-lang/crates.io-index"
455+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
456+dependencies = [
457+ "siphasher",
458+]
459+
460+[[package]]
461+name = "proc-macro2"
462+version = "1.0.106"
463+source = "registry+https://github.com/rust-lang/crates.io-index"
464+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
465+dependencies = [
466+ "unicode-ident",
467+]
468+
469+[[package]]
470+name = "qt_py_m0"
471+version = "0.10.1"
472+source = "registry+https://github.com/rust-lang/crates.io-index"
473+checksum = "0a56e549059a2819576ebd50ab61ab9a289a2f22ce8c536d8e613daff887b0d3"
474+dependencies = [
475+ "atsamd-hal 0.14.0",
476+ "cortex-m-rt",
477+]
478+
479+[[package]]
480+name = "quote"
481+version = "1.0.44"
482+source = "registry+https://github.com/rust-lang/crates.io-index"
483+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
484+dependencies = [
485+ "proc-macro2",
486+]
487+
488+[[package]]
489+name = "rand"
490+version = "0.8.5"
491+source = "registry+https://github.com/rust-lang/crates.io-index"
492+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
493+dependencies = [
494+ "rand_core 0.6.4",
495+]
496+
497+[[package]]
498+name = "rand_core"
499+version = "0.5.1"
500+source = "registry+https://github.com/rust-lang/crates.io-index"
501+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
502+
503+[[package]]
504+name = "rand_core"
505+version = "0.6.4"
506+source = "registry+https://github.com/rust-lang/crates.io-index"
507+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
508+
509+[[package]]
510+name = "rand_core"
511+version = "0.9.5"
512+source = "registry+https://github.com/rust-lang/crates.io-index"
513+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
514+
515+[[package]]
516+name = "replace_with"
517+version = "0.1.8"
518+source = "registry+https://github.com/rust-lang/crates.io-index"
519+checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884"
520+
521+[[package]]
522+name = "rgb"
523+version = "0.8.52"
524+source = "registry+https://github.com/rust-lang/crates.io-index"
525+checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
526+dependencies = [
527+ "bytemuck",
528+]
529+
530+[[package]]
531+name = "rustc_version"
532+version = "0.2.3"
533+source = "registry+https://github.com/rust-lang/crates.io-index"
534+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
535+dependencies = [
536+ "semver",
537+]
538+
539+[[package]]
540+name = "ryu"
541+version = "1.0.23"
542+source = "registry+https://github.com/rust-lang/crates.io-index"
543+checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
544+
545+[[package]]
546+name = "semver"
547+version = "0.9.0"
548+source = "registry+https://github.com/rust-lang/crates.io-index"
549+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
550+dependencies = [
551+ "semver-parser",
552+]
553+
554+[[package]]
555+name = "semver-parser"
556+version = "0.7.0"
557+source = "registry+https://github.com/rust-lang/crates.io-index"
558+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
559+
560+[[package]]
561+name = "seq-macro"
562+version = "0.2.2"
563+source = "registry+https://github.com/rust-lang/crates.io-index"
564+checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99"
565+
566+[[package]]
567+name = "seq-macro"
568+version = "0.3.6"
569+source = "registry+https://github.com/rust-lang/crates.io-index"
570+checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
571+
572+[[package]]
573+name = "serde"
574+version = "1.0.228"
575+source = "registry+https://github.com/rust-lang/crates.io-index"
576+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
577+dependencies = [
578+ "serde_core",
579+ "serde_derive",
580+]
581+
582+[[package]]
583+name = "serde_core"
584+version = "1.0.228"
585+source = "registry+https://github.com/rust-lang/crates.io-index"
586+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
587+dependencies = [
588+ "serde_derive",
589+]
590+
591+[[package]]
592+name = "serde_derive"
593+version = "1.0.228"
594+source = "registry+https://github.com/rust-lang/crates.io-index"
595+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
596+dependencies = [
597+ "proc-macro2",
598+ "quote",
599+ "syn 2.0.117",
600+]
601+
602+[[package]]
603+name = "serde_yaml"
604+version = "0.9.34+deprecated"
605+source = "registry+https://github.com/rust-lang/crates.io-index"
606+checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
607+dependencies = [
608+ "indexmap",
609+ "itoa",
610+ "ryu",
611+ "serde",
612+ "unsafe-libyaml",
613+]
614+
615+[[package]]
616+name = "siphasher"
617+version = "1.0.2"
618+source = "registry+https://github.com/rust-lang/crates.io-index"
619+checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
620+
621+[[package]]
622+name = "smart-leds"
623+version = "0.3.0"
624+source = "registry+https://github.com/rust-lang/crates.io-index"
625+checksum = "38dd45fa275f70b4110eac5f5182611ad384f88bb22b68b9a9c3cafd7015290b"
626+dependencies = [
627+ "smart-leds-trait",
628+]
629+
630+[[package]]
631+name = "smart-leds-trait"
632+version = "0.2.1"
633+source = "registry+https://github.com/rust-lang/crates.io-index"
634+checksum = "ebf6d833fa93f16a1c1874e62c2aebe8567e5bdd436d59bf543ed258b6f7a8e3"
635+dependencies = [
636+ "rgb",
637+]
638+
639+[[package]]
640+name = "sorted-hlist"
641+version = "0.2.0"
642+source = "registry+https://github.com/rust-lang/crates.io-index"
643+checksum = "e3b3c9d254b6644002aecb837882062fbca9af420d2cd4c2f0e4da248d80571b"
644+dependencies = [
645+ "typenum",
646+]
647+
648+[[package]]
649+name = "stable_deref_trait"
650+version = "1.2.1"
651+source = "registry+https://github.com/rust-lang/crates.io-index"
652+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
653+
654+[[package]]
655+name = "static_assertions"
656+version = "1.1.0"
657+source = "registry+https://github.com/rust-lang/crates.io-index"
658+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
659+
660+[[package]]
661+name = "syn"
662+version = "1.0.109"
663+source = "registry+https://github.com/rust-lang/crates.io-index"
664+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
665+dependencies = [
666+ "proc-macro2",
667+ "quote",
668+ "unicode-ident",
669+]
670+
671+[[package]]
672+name = "syn"
673+version = "2.0.117"
674+source = "registry+https://github.com/rust-lang/crates.io-index"
675+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
676+dependencies = [
677+ "proc-macro2",
678+ "quote",
679+ "unicode-ident",
680+]
681+
682+[[package]]
683+name = "typenum"
684+version = "1.19.0"
685+source = "registry+https://github.com/rust-lang/crates.io-index"
686+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
687+
688+[[package]]
689+name = "unicode-ident"
690+version = "1.0.24"
691+source = "registry+https://github.com/rust-lang/crates.io-index"
692+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
693+
694+[[package]]
695+name = "unsafe-libyaml"
696+version = "0.2.11"
697+source = "registry+https://github.com/rust-lang/crates.io-index"
698+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
699+
700+[[package]]
701+name = "usb-device"
702+version = "0.2.9"
703+source = "registry+https://github.com/rust-lang/crates.io-index"
704+checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508"
705+
706+[[package]]
707+name = "usbd-serial"
708+version = "0.1.1"
709+source = "registry+https://github.com/rust-lang/crates.io-index"
710+checksum = "db75519b86287f12dcf0d171c7cf4ecc839149fe9f3b720ac4cfce52959e1dfe"
711+dependencies = [
712+ "embedded-hal 0.2.7",
713+ "nb 0.1.3",
714+ "usb-device",
715+]
716+
717+[[package]]
718+name = "vcell"
719+version = "0.1.3"
720+source = "registry+https://github.com/rust-lang/crates.io-index"
721+checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
722+
723+[[package]]
724+name = "version_check"
725+version = "0.9.5"
726+source = "registry+https://github.com/rust-lang/crates.io-index"
727+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
728+
729+[[package]]
730+name = "void"
731+version = "1.0.2"
732+source = "registry+https://github.com/rust-lang/crates.io-index"
733+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
734+
735+[[package]]
736+name = "volatile-register"
737+version = "0.2.2"
738+source = "registry+https://github.com/rust-lang/crates.io-index"
739+checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
740+dependencies = [
741+ "vcell",
742+]
743+
744+[[package]]
745+name = "ws2812-timer-delay"
746+version = "0.3.0"
747+source = "registry+https://github.com/rust-lang/crates.io-index"
748+checksum = "b9b339c25c6d44131844c4c5ac0c7344d32080daaa38a6e9beaba927cbe28800"
749+dependencies = [
750+ "embedded-hal 0.2.7",
751+ "nb 0.1.3",
752+ "smart-leds-trait",
753+]
A Cargo.toml
+22, -0
 1@@ -0,0 +1,22 @@
 2+[package]
 3+name = "concertina"
 4+version = "0.1.0"
 5+edition = "2024"
 6+
 7+[dependencies]
 8+cortex-m-rt = { version = "0.7", optional = true }
 9+usb-device = { version = "0.2", optional = true }
10+atsamd-hal-new = { version = "0.23.2", package = "atsamd-hal" }
11+atsamd-hal = { version = "0.14" }
12+smart-leds = "0.3"
13+usbd-serial = "0.1"
14+panic-halt = "0.2"
15+ws2812-timer-delay = { version = "0.3", features = ["slow"] }
16+cortex-m = "0.7"
17+qt_py_m0 = "0.10.1"
18+
19+[features]
20+default = ["rt", "atsamd-hal/samd21e"]
21+rt = ["cortex-m-rt", "atsamd-hal/samd21e-rt"]
22+use_semihosting = []
23+usb = ["atsamd-hal/usb", "usb-device"]
D go.mod
+0, -10
 1@@ -1,10 +0,0 @@
 2-module git.woozle.org/neale/concertina
 3-
 4-go 1.24.4
 5-
 6-require (
 7-	tinygo.org/x/drivers v0.34.0
 8-	tinygo.org/x/tinyfont v0.6.0
 9-)
10-
11-require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
D go.sum
+0, -6
1@@ -1,6 +0,0 @@
2-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
3-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
4-tinygo.org/x/drivers v0.34.0 h1:lw8ePJeUSn9oICKBvQXHC9TIE+J00OfXfkGTrpXM9Iw=
5-tinygo.org/x/drivers v0.34.0/go.mod h1:ZdErNrApSABdVXjA1RejD67R8SNRI6RKVfYgQDZtKtk=
6-tinygo.org/x/tinyfont v0.6.0 h1:GibXDSFz6xrWnEDkDRo6vsbOyRw0MVj/eza3zNHMSHs=
7-tinygo.org/x/tinyfont v0.6.0/go.mod h1:onflMSkpWl7r7j4MIqhPEVV39pn7yL4N3MOePl3G+G8=
D src/Makefile
+0, -38
 1@@ -1,38 +0,0 @@
 2-MCU = attiny85
 3-
 4-CC = avr-gcc
 5-CFLAGS += -mmcu=$(MCU)
 6-CFLAGS += -Os
 7-CFLAGS += -w
 8-
 9-## XXX does this really need to be duplicated?
10-LDFLAGS += -mmcu=$(MCU)
11-
12-all: $(TARGETS)
13-
14-blink.elf: blink.o
15-	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
16-
17-%.hex: %.elf
18-	avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@
19-
20-clean:
21-	rm -f *.elf *.o *.hex
22-
23-
24-
25-##
26-## Helpful targets for development
27-##
28-
29-
30-AVDFLAGS += -p $(MCU)
31-AVDFLAGS += -c usbtiny
32-
33-upload: blink.hex
34-	avrdude $(AVDFLAGS) -U flash:w:$<
35-
36-fuses: FUSES += -U lfuse:w:0x62:m
37-fuses: FUSES += -U hfuse:w:0xDF:m
38-fuses:
39-	avrdude $(AVDFLAGS) $(FUSES)
D src/concertina.ino
+0, -258
  1@@ -1,258 +0,0 @@
  2-#include <U8g2lib.h>
  3-#include <Adafruit_Keypad.h>
  4-#include <Adafruit_DotStar.h>
  5-#include "layouts.h"
  6-#include "ruby.h"
  7-
  8-#define VS1053_RESET 2 // This is the pin that connects to the RESET pin on VS1053
  9-
 10-// See http://www.vlsi.fi/fileadmin/datasheets/vs1053.pdf Pg 31
 11-#define VS1053_BANK_DEFAULT 0x00
 12-#define VS1053_BANK_DRUMS1 0x78
 13-#define VS1053_BANK_DRUMS2 0x7F
 14-#define VS1053_BANK_MELODY 0x79
 15-
 16-// See http://www.vlsi.fi/fileadmin/datasheets/vs1053.pdf Pg 32 for more!
 17-#define VS1053_GM1_OCARINA 21
 18-
 19-#define MIDI_NOTE_ON  0x90
 20-#define MIDI_NOTE_OFF 0x80
 21-#define MIDI_CHAN_MSG 0xB0
 22-#define MIDI_CHAN_BANK 0x00
 23-#define MIDI_CHAN_VOLUME 0x07
 24-#define MIDI_CHAN_PROGRAM 0xC0
 25-
 26-// Serial1 on the ItsyBitsy M0 Express is marked on the silkscreen
 27-#define VS1053_MIDI Serial1
 28-
 29-// There's only one DotStar
 30-Adafruit_DotStar DotStar(DOTSTAR_NUM, PIN_DOTSTAR_DATA, PIN_DOTSTAR_CLK, DOTSTAR_BRG);
 31-
 32-// An itsy bitsy OLED display
 33-U8G2_SSD1306_64X32_1F_F_HW_I2C Display(U8G2_R0);
 34-
 35-/* Maps grid positions to layout positions.
 36-
 37-   It's in octal: "0%d%d%d" % (side, row, col)
 38-
 39-   There are four possible sides: left, right, meta, and undefined.
 40-   There are eight possible rows.
 41-   There are eight possible columns.
 42-*/
 43-#define ROWS 6
 44-#define COLS 6
 45-byte keyMap[ROWS][COLS] = {
 46-  { 0020, 0010, 0000, 0104, 0114, 0124 },
 47-  { 0021, 0011, 0001, 0103, 0113, 0123 },
 48-  { 0022, 0012, 0002, 0102, 0112, 0122 },
 49-  { 0023, 0013, 0003, 0101, 0111, 0121 },
 50-  { 0024, 0014, 0004, 0100, 0110, 0120 },
 51-  { 0200, 0201, 0202, 0203, 0204, 0205 },
 52-};
 53-byte rowPins[ROWS] = {14, 15, 16, 17, 18, 19};
 54-byte colPins[COLS] = { 5,  7,  9, 10, 11, 12};
 55-Adafruit_Keypad Keypad((byte *)keyMap, rowPins, colPins, ROWS, COLS);
 56-
 57-int currentLayoutIdx = 0;
 58-layout_t currentLayout;
 59-
 60-// Load a layout into our button mapping
 61-void nextLayout(int adj) {
 62-  int nlayouts = sizeof(layouts) / sizeof(*layouts);
 63-  currentLayoutIdx = (currentLayoutIdx + nlayouts + adj) % nlayouts;
 64-  currentLayout = layouts[currentLayoutIdx];
 65-}
 66-
 67-void error() {
 68-  bool led = false;
 69-  for (;;) {
 70-    led = !led;
 71-    digitalWrite(LED_BUILTIN, led);
 72-    delay(250);
 73-  }
 74-}
 75-
 76-void setup() {
 77-  Serial.begin(9600);
 78-  Serial.println("Ruby MIDI Concertina");
 79-
 80-  pinMode(LED_BUILTIN, OUTPUT);
 81-  pinMode(VS1053_RESET, OUTPUT);
 82-
 83-  // Reset VS1053
 84-  digitalWrite(VS1053_RESET, LOW);
 85-  delay(10);
 86-  digitalWrite(VS1053_RESET, HIGH);
 87-  delay(10);
 88-  VS1053_MIDI.begin(31250); // MIDI uses a 'strange baud rate'
 89-
 90-  midiSetChannelBank(0, VS1053_BANK_MELODY);
 91-  midiSetInstrument(0, VS1053_GM1_OCARINA);
 92-  midiSetChannelVolume(0, 127);
 93-
 94-  // Set up keyboard
 95-  Keypad.begin();
 96-  nextLayout(0);
 97-
 98-  // Use the display
 99-  Display.begin();
100-  Display.firstPage();
101-  Display.setFont(u8g2_font_helvR08_tf);
102-  Display.drawUTF8(0, 10, currentLayout.name);
103-  Display.nextPage();
104-
105-  // Turn off the DotStar
106-  DotStar.begin();
107-  DotStar.clear();
108-  DotStar.show();
109-}
110-
111-void displayNoteName(int8_t note) {
112-  char base = 0;
113-  int accidental = 0;
114-  char out[32] = {};
115-  int pos = 0;
116-
117-  switch (note % 12) {
118-  case 10:
119-    accidental = -1;
120-  case 11:
121-    base = 'B';
122-    break;
123-  case 9:
124-    base = 'A';
125-    break;
126-  case 8:
127-    accidental = 1;
128-  case 7:
129-    base = 'G';
130-    break;
131-  case 6:
132-    accidental = 1;
133-  case 5:
134-    base = 'F';
135-    break;
136-  case 4:
137-    base = 'E';
138-    break;
139-  case 3:
140-    accidental = 1;
141-  case 2:
142-    base = 'D';
143-    break;
144-  case 1:
145-    accidental = 1;
146-  case 0:
147-    base = 'C';
148-    break;
149-  }
150-
151-  if (note > 71) {
152-    base += 32;
153-  }
154-  out[pos++] = base;
155-
156-  switch (accidental) {
157-  case -1:
158-    out[pos++] = '_';
159-    break;
160-  case 1:
161-    out[pos++] = '^';
162-    break;
163-  }
164-
165-  while (note > 83) {
166-    out[pos++] = '\'';
167-    note -= 12;
168-  }
169-  while (note < 60) {
170-    out[pos++] = ',';
171-    note += 12;
172-  }
173-  out[pos++] = ' ';
174-
175-  Display.print((char *)out);
176-}
177-
178-void loop() {
179-  Keypad.tick();
180-
181-  if (Keypad.available()) {
182-    int push = Keypad.isPressed(0205) ? 1 : 0;
183-
184-    Display.setFont(u8g2_font_helvR08_tr);
185-    Display.setDrawColor(0);
186-    Display.drawBox(0, 10, 64, 12);
187-    Display.setDrawColor(1);
188-    Display.setCursor(0, 20);
189-    while (Keypad.available()) {
190-      keypadEvent k = Keypad.read();
191-
192-      int side = (k.bit.KEY >>6) & 0b11;
193-      int row = (k.bit.KEY >> 3) & 0b111;
194-      int col = (k.bit.KEY >> 0) & 0b111;
195-      bool pressed = (k.bit.EVENT == KEY_JUST_PRESSED);
196-
197-      // Now look up the note
198-      int8_t note = currentLayout.notes[side][row][col][push];
199-
200-      Serial.printf("%03o %d,%d,%d %d %d\n", k.bit.KEY, side, col, row, note, k.bit.EVENT);
201-      //Display.fillRect(0, 16, 40, 8, BLACK);
202-      if (pressed) {
203-        displayNoteName(note);
204-      }
205-      VS1053_MIDI.write(pressed ? MIDI_NOTE_ON : MIDI_NOTE_OFF);
206-      VS1053_MIDI.write(note);
207-      VS1053_MIDI.write(127);
208-    }
209-    Display.nextPage();
210-  }
211-}
212-
213-void midiSetInstrument(uint8_t chan, uint8_t inst) {
214-  if (chan > 15) return;
215-  inst --; // page 32 has instruments starting with 1 not 0 :(
216-  if (inst > 127) return;
217-  
218-  VS1053_MIDI.write(MIDI_CHAN_PROGRAM | chan);  
219-  VS1053_MIDI.write(inst);
220-}
221-
222-
223-void midiSetChannelVolume(uint8_t chan, uint8_t vol) {
224-  if (chan > 15) return;
225-  if (vol > 127) return;
226-  
227-  VS1053_MIDI.write(MIDI_CHAN_MSG | chan);
228-  VS1053_MIDI.write(MIDI_CHAN_VOLUME);
229-  VS1053_MIDI.write(vol);
230-}
231-
232-void midiSetChannelBank(uint8_t chan, uint8_t bank) {
233-  if (chan > 15) return;
234-  if (bank > 127) return;
235-  
236-  VS1053_MIDI.write(MIDI_CHAN_MSG | chan);
237-  VS1053_MIDI.write((uint8_t)MIDI_CHAN_BANK);
238-  VS1053_MIDI.write(bank);
239-}
240-
241-void midiNoteOn(uint8_t chan, uint8_t n, uint8_t vel) {
242-  if (chan > 15) return;
243-  if (n > 127) return;
244-  if (vel > 127) return;
245-  
246-  VS1053_MIDI.write(MIDI_NOTE_ON | chan);
247-  VS1053_MIDI.write(n);
248-  VS1053_MIDI.write(vel);
249-}
250-
251-void midiNoteOff(uint8_t chan, uint8_t n, uint8_t vel) {
252-  if (chan > 15) return;
253-  if (n > 127) return;
254-  if (vel > 127) return;
255-  
256-  VS1053_MIDI.write(MIDI_NOTE_OFF | chan);
257-  VS1053_MIDI.write(n);
258-  VS1053_MIDI.write(vel);
259-}
A src/main.rs
+73, -0
 1@@ -0,0 +1,73 @@
 2+#![no_std]
 3+#![no_main]
 4+
 5+//! Neopixel example for the Adafruit QT Py board. Demonstrates powering up the
 6+//! neopixel using the attached GPIO line.
 7+//!
 8+//! *NOTE*: This example currently only works in release mode.
 9+
10+use hal::ehal::digital::v1_compat::OldOutputPin;
11+use panic_halt as _;
12+use smart_leds::hsv::hsv2rgb;
13+use smart_leds::hsv::Hsv;
14+use smart_leds::SmartLedsWrite;
15+use ws2812_timer_delay::Ws2812;
16+
17+use bsp::entry;
18+use bsp::hal;
19+use bsp::Pins;
20+use hal::clock::GenericClockController;
21+use hal::delay::Delay;
22+use hal::pac::CorePeripherals;
23+use hal::pac::Peripherals;
24+use hal::prelude::*;
25+use hal::timer::TimerCounter;
26+use qt_py_m0 as bsp;
27+
28+#[entry]
29+fn main() -> ! {
30+    let mut peripherals = Peripherals::take().unwrap();
31+    let core = CorePeripherals::take().unwrap();
32+    let mut clocks = GenericClockController::with_internal_8mhz(
33+        peripherals.GCLK,
34+        &mut peripherals.PM,
35+        &mut peripherals.SYSCTRL,
36+        &mut peripherals.NVMCTRL,
37+    );
38+
39+    let pins = Pins::new(peripherals.PORT).split();
40+
41+    let gclk0 = clocks.gclk0();
42+    let timer_clock = clocks.tcc2_tc3(&gclk0).unwrap();
43+    let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.PM);
44+    timer.start(3.mhz());
45+
46+    // The neopixel sources power from a GPIO pin. It must be driven high to power
47+    // up the neopixel before it can be used.
48+    pins.neopixel
49+        .power
50+        .into_push_pull_output()
51+        .set_high()
52+        .unwrap();
53+
54+    let neopixel_data: OldOutputPin<_> = pins.neopixel.data.into_push_pull_output().into();
55+    let mut neopixel = Ws2812::new(timer, neopixel_data);
56+    let mut delay = Delay::new(core.SYST, &mut clocks);
57+
58+    loop {
59+        for j in 0..255u8 {
60+            neopixel
61+                .write(
62+                    [hsv2rgb(Hsv {
63+                        hue: j,
64+                        sat: 255,
65+                        val: 16,
66+                    })]
67+                    .iter()
68+                    .cloned(),
69+                )
70+                .unwrap();
71+            delay.delay_ms(5u8);
72+        }
73+    }
74+}
D src/mklayouts.py
+0, -108
  1@@ -1,108 +0,0 @@
  2-#! /usr/bin/python3
  3-
  4-import re
  5-import sys
  6-
  7-baseMidiNote = {
  8-    'C': 60,
  9-    'D': 62,
 10-    'E': 64,
 11-    'F': 65,
 12-    'G': 67,
 13-    'A': 69,
 14-    'B': 71,
 15-}
 16-
 17-def abc2midi(s):
 18-    if s in ('', '-'):
 19-        return -1
 20-    note = baseMidiNote[s[0].upper()]
 21-    if s[0] in 'cdefgab':
 22-        note += 12
 23-    for m in s[1:]:
 24-        if m == '^':
 25-            note += 1
 26-        elif m == '_':
 27-            note -= 1
 28-        elif m == ',':
 29-            note -= 12
 30-        elif m == "'":
 31-            note += 12
 32-    return note
 33-
 34-assert abc2midi('C') == 60
 35-assert abc2midi('c') == 72
 36-assert abc2midi("c^'") == 85
 37-
 38-
 39-class Layout:
 40-    def __init__(self, name):
 41-        self.name = name
 42-        self.notes = [[], []]
 43-
 44-    def add_row(self, buttons):
 45-        assert len(buttons) == 10
 46-        self.notes[0].append(buttons[:5])
 47-        self.notes[1].append(buttons[5:])
 48-
 49-    def write(self, fd):
 50-        assert len(self.notes) == 2
 51-        fd.write('  { "%s",\n' % self.name)
 52-        fd.write('    { // sides\n');
 53-        for side in self.notes:
 54-            assert len(side) == 4
 55-            fd.write('      { // rows\n')
 56-            for row in side:
 57-                assert len(row) == 5
 58-                fd.write('        { // buttons\n')
 59-                for button in row:
 60-                    fd.write('          {%d, %d}, // %s\n' % button)
 61-                fd.write('        },\n')
 62-            fd.write('      },\n')
 63-        fd.write('    }\n')
 64-        fd.write('  },\n')
 65-
 66-with sys.stdout as f:
 67-    f.write('''/* Generated from layouts.txt by mklayouts.py */
 68-#pragma once
 69-
 70-struct layout_t {
 71-  char const *name;
 72-  int8_t notes[2][4][5][2]; /* side, row, col, push/draw */
 73-};
 74-
 75-const layout_t layouts[] = {
 76-''')
 77-    layout = None
 78-    for line in open(sys.argv[1]):
 79-        line = line.strip()
 80-        parts = line.split()
 81-        if line == '':
 82-            continue
 83-        if len(parts) < 11:
 84-            if layout:
 85-                layout.write(f)
 86-            layout = Layout(line)
 87-            continue
 88-        if len(parts) != 11:
 89-            raise RuntimeError("Wrong number of buttons")
 90-        if parts[5] != '|':
 91-            raise RuntimeError("Expecting separator, got %s" % parts[5])
 92-        buttons = []
 93-        for part in parts:
 94-            if part == '|':
 95-                continue
 96-            button = part.replace('♯', '^').replace('♭', '_')
 97-            names = button.split('/')
 98-            if len(names) == 1:
 99-                names.append(names[0])
100-            notes = [abc2midi(n) for n in names]
101-            buttons.append((notes[0], notes[1], button))
102-        layout.add_row(buttons)
103-
104-    if layout:
105-        layout.write(f)
106-    f.write("};\n")
107-
108-            
109-