diff --git a/Makefile b/Makefile index f7af461..b522abe 100644 --- a/Makefile +++ b/Makefile @@ -239,13 +239,15 @@ endif @md5sum ./build/test.hex tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/scd4x/main.go @md5sum ./build/test.uf2 + tinygo build -size short -o ./build/test.uf2 -target=circuitplay-express ./examples/makeybutton/main.go + @md5sum ./build/test.uf2 DRIVERS = $(wildcard */) NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \ hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \ hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x keypad4x4 max72xx p1am tone tm1637 \ pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192 xpt2046 \ - ft6336 sx126x ssd1289 irremote uc8151 + ft6336 sx126x ssd1289 irremote uc8151 makeybutton TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS)) unit-test: diff --git a/README.md b/README.md index d328977..45a0150 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ func main() { ## Currently supported devices -The following 80 devices are supported. +The following 81 devices are supported. | Device Name | Interface Type | |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| @@ -100,6 +100,7 @@ The following 80 devices are supported. | [LSM6DS3TR accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3tr.pdf) | I2C | | [LSM303AGR accelerometer](https://www.st.com/resource/en/datasheet/lsm303agr.pdf) | I2C | | [LSM9DS1 accelerometer](https://www.st.com/resource/en/datasheet/lsm9ds1.pdf) | I2C | +| [Makey Button](https://makeymakey.com/) | GPIO | | [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C | | [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI | | [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI | diff --git a/examples/makeybutton/main.go b/examples/makeybutton/main.go new file mode 100644 index 0000000..32e11bb --- /dev/null +++ b/examples/makeybutton/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/makeybutton" +) + +var ( + led machine.Pin = machine.LED + button machine.Pin = machine.BUTTON + key *makeybutton.Button +) + +func main() { + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + key = makeybutton.NewButton(button) + + for { + switch key.Get() { + case makeybutton.Pressed: + led.High() + case makeybutton.Released: + led.Low() + } + time.Sleep(30 * time.Millisecond) + } +} diff --git a/makeybutton/buffer.go b/makeybutton/buffer.go new file mode 100644 index 0000000..ea924f0 --- /dev/null +++ b/makeybutton/buffer.go @@ -0,0 +1,46 @@ +package makeybutton + +const bufferSize = 10 + +// Buffer is a buffer to keep track of the most recent readings for a button. +type Buffer struct { + readings [bufferSize]bool + index int +} + +// NewBuffer returns a new buffer. +func NewBuffer() *Buffer { + return &Buffer{} +} + +// Used returns how many bytes in buffer have been used. +func (b *Buffer) Used() int { + return b.index +} + +// Put stores a boolean in the buffer. +func (b *Buffer) Put(val bool) bool { + b.index++ + if b.index >= bufferSize { + b.index = 0 + } + + b.readings[b.index] = val + + return true +} + +// Avg returns the "average" of all the readings in the buffer, by +// treating a true as 1 and a false as -1. +func (b *Buffer) Avg() int { + avg := 0 + for i := 0; i < bufferSize; i++ { + if b.readings[i] { + avg += 1 + } else { + avg -= 1 + } + } + + return avg +} diff --git a/makeybutton/button.go b/makeybutton/button.go new file mode 100644 index 0000000..8447763 --- /dev/null +++ b/makeybutton/button.go @@ -0,0 +1,79 @@ +// Package makeybutton providers a driver for a button that can be triggered +// by anything that is conductive by using an ultra high value resistor. +// +// Inspired by the amazing MakeyMakey +// https://makeymakey.com/ +// +package makeybutton + +import "machine" + +// ButtonState represents the state of a MakeyButton. +type ButtonState int + +const ( + NeverPressed ButtonState = 0 + Press = 1 + Release = 2 +) + +// ButtonEvent represents when the state of a Button changes. +type ButtonEvent int + +const ( + NotChanged ButtonEvent = 0 + Pressed = 1 + Released = 2 +) + +// Button is a "button"-like device that acts like a MakeyMakey. +type Button struct { + pin machine.Pin + state ButtonState + readings *Buffer + HighMeansPressed bool +} + +// NewButton creates a new Button. +func NewButton(pin machine.Pin) *Button { + pin.Configure(machine.PinConfig{Mode: machine.PinInput}) + pin.Set(false) + + return &Button{ + pin: pin, + state: NeverPressed, + readings: NewBuffer(), + HighMeansPressed: false, + } +} + +// Get returns a ButtonEvent based on the most recent state of the button, +// and if it has changed by being pressed or released. +func (b *Button) Get() ButtonEvent { + // if pin is pulled up, a low value means the key is pressed + pressed := !b.pin.Get() + if b.HighMeansPressed { + // otherwise, a high value means the key is pressed + pressed = !pressed + } + + avg := b.readings.Avg() + b.readings.Put(pressed) + + switch { + case pressed && avg > 0: + if b.state == Press { + return NotChanged + } + + b.state = Press + return Pressed + case !pressed: + if b.state == Press { + b.state = Release + return Released + } + } + + return NotChanged +}