diff --git a/Makefile b/Makefile index 75544dd..642542a 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,8 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx + @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go diff --git a/README.md b/README.md index a555965..767bcd2 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ The following 45 devices are supported. |----------|-------------| | [ADT7410 I2C Temperature Sensor](https://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf) | I2C | | [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C | +| [AMG88xx 8x8 Thermal camera sensor](https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.pdf) | I2C | | [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI | | [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C | | [BH1750 ambient light sensor](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) | I2C | diff --git a/amg88xx/amg88xx.go b/amg88xx/amg88xx.go new file mode 100644 index 0000000..72f99db --- /dev/null +++ b/amg88xx/amg88xx.go @@ -0,0 +1,158 @@ +// Package amg88xx provides a driver for the AMG88XX Thermal Camera +// +// Datasheet: +// https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.pdf +package amg88xx // import "tinygo.org/x/drivers/amg88xx" + +import ( + "machine" + "time" +) + +// Device wraps an I2C connection to a AMG88xx device. +type Device struct { + bus machine.I2C + Address uint16 + data []uint8 + interruptMode InterruptMode + interruptEnable uint8 +} + +type InterruptMode uint8 + +type Config struct { +} + +// New creates a new AMG88xx connection. The I2C bus must already be +// configured. +// +// This function only creates the Device object, it does not touch the device. +func New(bus machine.I2C) Device { + return Device{ + bus: bus, + Address: AddressHigh, + } +} + +// Configure sets up the device for communication +func (d *Device) Configure(cfg Config) { + d.data = make([]uint8, 128) + + d.SetPCTL(NORMAL_MODE) + d.SetReset(INITIAL_RESET) + d.SetFrameRate(FPS_10) + + time.Sleep(100 * time.Millisecond) +} + +// ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius +func (d *Device) ReadPixels(buffer *[64]int16) { + d.bus.ReadRegister(uint8(d.Address), PIXEL_OFFSET, d.data) + for i := 0; i < 64; i++ { + buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i])) + if (buffer[i] & (1 << 11)) > 0 { // temperature negative + buffer[i] &= ^(1 << 11) + buffer[i] = -buffer[i] + } + buffer[i] *= PIXEL_TEMP_CONVERSION + } +} + +// SetPCTL sets the PCTL +func (d *Device) SetPCTL(pctl uint8) { + d.bus.WriteRegister(uint8(d.Address), PCTL, []byte{pctl}) +} + +// SetReset sets the reset value +func (d *Device) SetReset(rst uint8) { + d.bus.WriteRegister(uint8(d.Address), RST, []byte{rst}) +} + +// SetFrameRate configures the frame rate +func (d *Device) SetFrameRate(framerate uint8) { + d.bus.WriteRegister(uint8(d.Address), FPSC, []byte{framerate & 0x01}) +} + +// SetMovingAverageMode sets the moving average mode +func (d *Device) SetMovingAverageMode(mode bool) { + var value uint8 + if mode { + value = 1 + } + d.bus.WriteRegister(uint8(d.Address), AVE, []byte{value << 5}) +} + +// SetInterruptLevels sets the interrupt levels +func (d *Device) SetInterruptLevels(high int16, low int16) { + d.SetInterruptLevelsHysteresis(high, low, (high*95)/100) +} + +// SetInterruptLevelsHysteresis sets the interrupt levels with hysteresis +func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis int16) { + high = high / PIXEL_TEMP_CONVERSION + if high < -4095 { + high = -4095 + } + if high > 4095 { + high = 4095 + } + d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)}) + d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)}) + + low = low / PIXEL_TEMP_CONVERSION + if low < -4095 { + low = -4095 + } + if low > 4095 { + low = 4095 + } + d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)}) + d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)}) + + hysteresis = hysteresis / PIXEL_TEMP_CONVERSION + if hysteresis < -4095 { + hysteresis = -4095 + } + if hysteresis > 4095 { + hysteresis = 4095 + } + d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)}) + d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)}) +} + +// EnableInterrupt enables the interrupt pin on the device +func (d *Device) EnableInterrupt() { + d.interruptEnable = 1 + d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03}) +} + +// DisableInterrupt disables the interrupt pin on the device +func (d *Device) DisableInterrupt() { + d.interruptEnable = 0 + d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03}) +} + +// SetInterruptMode sets the interrupt mode +func (d *Device) SetInterruptMode(mode InterruptMode) { + d.interruptMode = mode + d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03}) +} + +// GetInterrupt reads the state of the triggered interrupts +func (d *Device) GetInterrupt() []uint8 { + data := make([]uint8, 8) + d.bus.ReadRegister(uint8(d.Address), INT_OFFSET, data) + return data +} + +// ClearInterrupt clears any triggered interrupts +func (d *Device) ClearInterrupt() { + d.SetReset(FLAG_RESET) +} + +// ReadThermistor reads the onboard thermistor +func (d *Device) ReadThermistor() int16 { + data := make([]uint8, 2) + d.bus.ReadRegister(uint8(d.Address), TTHL, data) + return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10 +} diff --git a/amg88xx/registers.go b/amg88xx/registers.go new file mode 100644 index 0000000..7819ef2 --- /dev/null +++ b/amg88xx/registers.go @@ -0,0 +1,46 @@ +package amg88xx + +// The I2C address which this device listens to. +const AddressHigh = 0x69 +const AddressLow = 0x68 + +const ( + PCTL = 0x00 + RST = 0x01 + FPSC = 0x02 + INTC = 0x03 + STAT = 0x04 + SCLR = 0x05 + AVE = 0x07 + INTHL = 0x08 + INTHH = 0x09 + INTLL = 0x0A + INTLH = 0x0B + IHYSL = 0x0C + IHYSH = 0x0D + TTHL = 0x0E + TTHH = 0x0F + INT_OFFSET = 0x010 + PIXEL_OFFSET = 0x80 + + // power modes + NORMAL_MODE = 0x00 + SLEEP_MODE = 0x01 + STAND_BY_60 = 0x20 + STAND_BY_10 = 0x21 + + // resets + FLAG_RESET = 0x30 + INITIAL_RESET = 0x3F + + // frame rates + FPS_10 = 0x00 + FPS_1 = 0x01 + + // interrupt modes + DIFFERENCE InterruptMode = 0x00 + ABSOLUTE_VALUE InterruptMode = 0x01 + + PIXEL_TEMP_CONVERSION = 250 + THERMISTOR_CONVERSION = 625 +) diff --git a/examples/amg88xx/colors.go b/examples/amg88xx/colors.go new file mode 100644 index 0000000..1a228aa --- /dev/null +++ b/examples/amg88xx/colors.go @@ -0,0 +1,6 @@ +package main + +import "image/color" + +// palette of colors (iron) taken from http://ramphastosramblings.blogspot.com/2014/03/choosing-appropriate-colour-palette-for.html +var colors = []color.RGBA{{0, 0, 10, 255}, {0, 0, 20, 255}, {0, 0, 30, 255}, {0, 0, 37, 255}, {0, 0, 42, 255}, {0, 0, 46, 255}, {0, 0, 50, 255}, {0, 0, 54, 255}, {0, 0, 58, 255}, {0, 0, 62, 255}, {0, 0, 66, 255}, {0, 0, 70, 255}, {0, 0, 74, 255}, {0, 0, 79, 255}, {0, 0, 82, 255}, {1, 0, 85, 255}, {1, 0, 87, 255}, {2, 0, 89, 255}, {2, 0, 92, 255}, {3, 0, 94, 255}, {4, 0, 97, 255}, {4, 0, 99, 255}, {5, 0, 101, 255}, {6, 0, 103, 255}, {7, 0, 105, 255}, {8, 0, 107, 255}, {9, 0, 110, 255}, {10, 0, 112, 255}, {11, 0, 115, 255}, {12, 0, 116, 255}, {13, 0, 117, 255}, {13, 0, 118, 255}, {14, 0, 119, 255}, {16, 0, 120, 255}, {18, 0, 121, 255}, {19, 0, 123, 255}, {21, 0, 124, 255}, {23, 0, 125, 255}, {25, 0, 126, 255}, {27, 0, 128, 255}, {28, 0, 129, 255}, {30, 0, 131, 255}, {32, 0, 132, 255}, {34, 0, 133, 255}, {36, 0, 134, 255}, {38, 0, 135, 255}, {40, 0, 137, 255}, {42, 0, 137, 255}, {44, 0, 138, 255}, {46, 0, 139, 255}, {48, 0, 140, 255}, {50, 0, 141, 255}, {52, 0, 142, 255}, {54, 0, 142, 255}, {56, 0, 143, 255}, {57, 0, 144, 255}, {59, 0, 145, 255}, {60, 0, 146, 255}, {62, 0, 147, 255}, {63, 0, 147, 255}, {65, 0, 148, 255}, {66, 0, 149, 255}, {68, 0, 149, 255}, {69, 0, 150, 255}, {71, 0, 150, 255}, {73, 0, 150, 255}, {74, 0, 150, 255}, {76, 0, 151, 255}, {78, 0, 151, 255}, {79, 0, 151, 255}, {81, 0, 151, 255}, {82, 0, 152, 255}, {84, 0, 152, 255}, {86, 0, 152, 255}, {88, 0, 153, 255}, {90, 0, 153, 255}, {92, 0, 153, 255}, {93, 0, 154, 255}, {95, 0, 154, 255}, {97, 0, 155, 255}, {99, 0, 155, 255}, {100, 0, 155, 255}, {102, 0, 155, 255}, {104, 0, 155, 255}, {106, 0, 155, 255}, {108, 0, 156, 255}, {109, 0, 156, 255}, {111, 0, 156, 255}, {112, 0, 156, 255}, {113, 0, 157, 255}, {115, 0, 157, 255}, {117, 0, 157, 255}, {119, 0, 157, 255}, {120, 0, 157, 255}, {122, 0, 157, 255}, {124, 0, 157, 255}, {126, 0, 157, 255}, {127, 0, 157, 255}, {129, 0, 157, 255}, {131, 0, 157, 255}, {132, 0, 157, 255}, {134, 0, 157, 255}, {135, 0, 157, 255}, {137, 0, 157, 255}, {138, 0, 157, 255}, {139, 0, 157, 255}, {141, 0, 157, 255}, {143, 0, 156, 255}, {145, 0, 156, 255}, {147, 0, 156, 255}, {149, 0, 156, 255}, {150, 0, 155, 255}, {152, 0, 155, 255}, {153, 0, 155, 255}, {155, 0, 155, 255}, {156, 0, 155, 255}, {157, 0, 155, 255}, {159, 0, 155, 255}, {160, 0, 155, 255}, {162, 0, 155, 255}, {163, 0, 155, 255}, {164, 0, 155, 255}, {166, 0, 154, 255}, {167, 0, 154, 255}, {168, 0, 154, 255}, {169, 0, 153, 255}, {170, 0, 153, 255}, {171, 0, 153, 255}, {173, 0, 153, 255}, {174, 1, 152, 255}, {175, 1, 152, 255}, {176, 1, 152, 255}, {176, 1, 152, 255}, {177, 1, 151, 255}, {178, 1, 151, 255}, {179, 1, 150, 255}, {180, 2, 150, 255}, {181, 2, 149, 255}, {182, 2, 149, 255}, {183, 3, 149, 255}, {184, 3, 149, 255}, {185, 4, 149, 255}, {186, 4, 149, 255}, {186, 4, 148, 255}, {187, 5, 147, 255}, {188, 5, 147, 255}, {189, 5, 147, 255}, {190, 6, 146, 255}, {191, 6, 146, 255}, {191, 6, 146, 255}, {192, 7, 145, 255}, {192, 7, 145, 255}, {193, 8, 144, 255}, {193, 9, 144, 255}, {194, 10, 143, 255}, {195, 10, 142, 255}, {195, 11, 142, 255}, {196, 12, 141, 255}, {197, 12, 140, 255}, {198, 13, 139, 255}, {198, 14, 138, 255}, {199, 15, 137, 255}, {200, 16, 136, 255}, {201, 17, 135, 255}, {202, 18, 134, 255}, {202, 19, 133, 255}, {203, 19, 133, 255}, {203, 20, 132, 255}, {204, 21, 130, 255}, {205, 22, 129, 255}, {206, 23, 128, 255}, {206, 24, 126, 255}, {207, 24, 124, 255}, {207, 25, 123, 255}, {208, 26, 121, 255}, {209, 27, 120, 255}, {209, 28, 118, 255}, {210, 28, 117, 255}, {210, 29, 116, 255}, {211, 30, 114, 255}, {211, 32, 113, 255}, {212, 33, 111, 255}, {212, 34, 110, 255}, {213, 35, 107, 255}, {213, 36, 105, 255}, {214, 37, 103, 255}, {215, 38, 101, 255}, {216, 39, 100, 255}, {216, 40, 98, 255}, {217, 42, 96, 255}, {218, 43, 94, 255}, {218, 44, 92, 255}, {219, 46, 90, 255}, {219, 47, 87, 255}, {220, 47, 84, 255}, {221, 48, 81, 255}, {221, 49, 78, 255}, {222, 50, 74, 255}, {222, 51, 71, 255}, {223, 52, 68, 255}, {223, 53, 65, 255}, {223, 54, 61, 255}, {224, 55, 58, 255}, {224, 56, 55, 255}, {224, 57, 51, 255}, {225, 58, 48, 255}, {226, 59, 45, 255}, {226, 60, 42, 255}, {227, 61, 38, 255}, {227, 62, 35, 255}, {228, 63, 32, 255}, {228, 65, 29, 255}, {228, 66, 28, 255}, {229, 67, 27, 255}, {229, 68, 25, 255}, {229, 69, 24, 255}, {230, 70, 22, 255}, {231, 71, 21, 255}, {231, 72, 20, 255}, {231, 73, 19, 255}, {232, 74, 18, 255}, {232, 76, 16, 255}, {232, 76, 15, 255}, {233, 77, 14, 255}, {233, 77, 13, 255}, {234, 78, 12, 255}, {234, 79, 12, 255}, {235, 80, 11, 255}, {235, 81, 10, 255}, {235, 82, 10, 255}, {235, 83, 9, 255}, {236, 84, 9, 255}, {236, 86, 8, 255}, {236, 87, 8, 255}, {236, 88, 8, 255}, {237, 89, 7, 255}, {237, 90, 7, 255}, {237, 91, 6, 255}, {238, 92, 6, 255}, {238, 92, 5, 255}, {238, 93, 5, 255}, {238, 94, 5, 255}, {239, 95, 4, 255}, {239, 96, 4, 255}, {239, 97, 4, 255}, {239, 98, 4, 255}, {240, 99, 3, 255}, {240, 100, 3, 255}, {240, 101, 3, 255}, {241, 102, 3, 255}, {241, 102, 3, 255}, {241, 103, 3, 255}, {241, 104, 3, 255}, {241, 105, 2, 255}, {241, 106, 2, 255}, {241, 107, 2, 255}, {241, 107, 2, 255}, {242, 108, 1, 255}, {242, 109, 1, 255}, {242, 110, 1, 255}, {243, 111, 1, 255}, {243, 112, 1, 255}, {243, 113, 1, 255}, {243, 114, 1, 255}, {244, 115, 0, 255}, {244, 116, 0, 255}, {244, 117, 0, 255}, {244, 118, 0, 255}, {244, 119, 0, 255}, {244, 120, 0, 255}, {244, 122, 0, 255}, {245, 123, 0, 255}, {245, 124, 0, 255}, {245, 126, 0, 255}, {245, 127, 0, 255}, {246, 128, 0, 255}, {246, 129, 0, 255}, {246, 130, 0, 255}, {247, 131, 0, 255}, {247, 132, 0, 255}, {247, 133, 0, 255}, {247, 134, 0, 255}, {248, 135, 0, 255}, {248, 136, 0, 255}, {248, 136, 0, 255}, {248, 137, 0, 255}, {248, 138, 0, 255}, {248, 139, 0, 255}, {248, 140, 0, 255}, {249, 141, 0, 255}, {249, 141, 0, 255}, {249, 142, 0, 255}, {249, 143, 0, 255}, {249, 144, 0, 255}, {249, 145, 0, 255}, {249, 146, 0, 255}, {249, 147, 0, 255}, {250, 148, 0, 255}, {250, 149, 0, 255}, {250, 150, 0, 255}, {251, 152, 0, 255}, {251, 153, 0, 255}, {251, 154, 0, 255}, {251, 156, 0, 255}, {252, 157, 0, 255}, {252, 159, 0, 255}, {252, 160, 0, 255}, {252, 161, 0, 255}, {253, 162, 0, 255}, {253, 163, 0, 255}, {253, 164, 0, 255}, {253, 166, 0, 255}, {253, 167, 0, 255}, {253, 168, 0, 255}, {253, 170, 0, 255}, {253, 171, 0, 255}, {253, 172, 0, 255}, {253, 173, 0, 255}, {253, 174, 0, 255}, {254, 175, 0, 255}, {254, 176, 0, 255}, {254, 177, 0, 255}, {254, 178, 0, 255}, {254, 179, 0, 255}, {254, 180, 0, 255}, {254, 181, 0, 255}, {254, 182, 0, 255}, {254, 184, 0, 255}, {254, 185, 0, 255}, {254, 185, 0, 255}, {254, 186, 0, 255}, {254, 187, 0, 255}, {254, 188, 0, 255}, {254, 189, 0, 255}, {254, 190, 0, 255}, {254, 192, 0, 255}, {254, 193, 0, 255}, {254, 194, 0, 255}, {254, 195, 0, 255}, {254, 196, 0, 255}, {254, 197, 0, 255}, {254, 198, 0, 255}, {254, 199, 0, 255}, {254, 200, 0, 255}, {254, 201, 1, 255}, {254, 202, 1, 255}, {254, 202, 1, 255}, {254, 203, 1, 255}, {254, 204, 2, 255}, {254, 205, 2, 255}, {254, 206, 3, 255}, {254, 207, 4, 255}, {254, 207, 4, 255}, {254, 208, 5, 255}, {254, 209, 6, 255}, {254, 211, 8, 255}, {254, 212, 9, 255}, {254, 213, 10, 255}, {254, 214, 10, 255}, {254, 215, 11, 255}, {254, 216, 12, 255}, {254, 217, 13, 255}, {255, 218, 14, 255}, {255, 218, 14, 255}, {255, 219, 16, 255}, {255, 220, 18, 255}, {255, 220, 20, 255}, {255, 221, 22, 255}, {255, 222, 25, 255}, {255, 222, 27, 255}, {255, 223, 30, 255}, {255, 224, 32, 255}, {255, 225, 34, 255}, {255, 226, 36, 255}, {255, 226, 38, 255}, {255, 227, 40, 255}, {255, 228, 43, 255}, {255, 228, 46, 255}, {255, 229, 49, 255}, {255, 230, 53, 255}, {255, 230, 56, 255}, {255, 231, 60, 255}, {255, 232, 63, 255}, {255, 233, 67, 255}, {255, 234, 70, 255}, {255, 235, 73, 255}, {255, 235, 77, 255}, {255, 236, 80, 255}, {255, 237, 84, 255}, {255, 238, 87, 255}, {255, 238, 91, 255}, {255, 238, 95, 255}, {255, 239, 99, 255}, {255, 239, 103, 255}, {255, 240, 106, 255}, {255, 240, 110, 255}, {255, 241, 114, 255}, {255, 241, 119, 255}, {255, 241, 123, 255}, {255, 242, 128, 255}, {255, 242, 133, 255}, {255, 242, 138, 255}, {255, 243, 142, 255}, {255, 244, 146, 255}, {255, 244, 150, 255}, {255, 244, 154, 255}, {255, 245, 158, 255}, {255, 245, 162, 255}, {255, 245, 166, 255}, {255, 246, 170, 255}, {255, 246, 175, 255}, {255, 247, 179, 255}, {255, 247, 182, 255}, {255, 248, 186, 255}, {255, 248, 189, 255}, {255, 248, 193, 255}, {255, 248, 196, 255}, {255, 249, 199, 255}, {255, 249, 202, 255}, {255, 249, 205, 255}, {255, 250, 209, 255}, {255, 250, 212, 255}, {255, 251, 216, 255}, {255, 252, 219, 255}, {255, 252, 223, 255}, {255, 253, 226, 255}, {255, 253, 229, 255}, {255, 253, 232, 255}, {255, 254, 235, 255}, {255, 254, 238, 255}, {255, 254, 241, 255}, {255, 254, 244, 255}, {255, 255, 246, 255}} diff --git a/examples/amg88xx/main.go b/examples/amg88xx/main.go new file mode 100644 index 0000000..0b2f3d3 --- /dev/null +++ b/examples/amg88xx/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "image/color" + "machine" + + "tinygo.org/x/drivers/st7735" + + "tinygo.org/x/drivers/amg88xx" +) + +func main() { + + machine.SPI1.Configure(machine.SPIConfig{ + SCK: machine.SPI1_SCK_PIN, + MOSI: machine.SPI1_MOSI_PIN, + MISO: machine.SPI1_MISO_PIN, + Frequency: 8000000, + }) + machine.I2C0.Configure(machine.I2CConfig{SCL: machine.SCL_PIN, SDA: machine.SDA_PIN}) + + display := st7735.New(machine.SPI1, machine.TFT_RST, machine.TFT_DC, machine.TFT_CS, machine.TFT_LITE) + display.Configure(st7735.Config{ + Rotation: st7735.ROTATION_90, + }) + display.FillScreen(color.RGBA{0, 0, 0, 255}) + + camera := amg88xx.New(machine.I2C0) + camera.Configure(amg88xx.Config{}) + + var data [64]int16 + var value int16 + for { + // get the values of the sensor in millicelsius + camera.ReadPixels(&data) + + for j := int16(0); j < 8; j++ { + for i := int16(0); i < 8; i++ { + value = data[63-(i+j*8)] + // treat anything below 18°C as 18°C + if value < 18000 { + value = 0 + } else { + value = (value - 18000) / 36 + // our color array only have 433 values, avoid getting a value that doesn't exist + if value > 432 { + value = 432 + } + } + // show the image on the PyBadge's display + display.FillRectangle(16+i*16, j*16, 16, 16, colors[value]) + } + } + } + +}