# Badger 2040 {{:raspberry_pi:badger_2040.jpg?400|}} A nearly-3 inch E-Ink display bolted onto a Raspberry Pi 2040 with 5 buttons, a LED, and 2 MB of RAM, made by Pimoroni. It can run on battery or USB power, and has ports and pads you can attach things to. ## Where to get it * Pimoroni: https://shop.pimoroni.com/products/badger-2040 * Pi Shop: https://www.pishop.us/product/badger-2040/ ## Alternatives * Tufty 2040: https://shop.pimoroni.com/products/tufty-2040?variant=40036912595027 * The smaller color version of Badger ## Useful Docs * https://learn.pimoroni.com/article/getting-started-with-badger-2040 * Updating firmware: https://github.com/pimoroni/badger2040#badger-2040 * My Badger 2040 did not come with any example folders, so I installed the latest firmware ## Cases * https://www.thingiverse.com/thing:5320100 * Uses coin cell batteries which might not be the best for the Badger: https://forums.pimoroni.com/t/run-badger-2040-from-2xcr2032/21774 * ...but I went with it anyway, using this coin cell holder: https://www.adafruit.com/product/783 * https://www.thingiverse.com/thing:5302585 * Uses a 2xAAA holder * I ended up using this case, using the LiPo back with a hole cut out for the power leads, and attaching this battery holder: https://www.adafruit.com/product/3286 ## Command line access Use MicroPython's `mpremote`: https://pypi.org/project/mpremote/ * Note that you can't use this and Thonny at the same time. * There doesn't seem to be a way to mount a folder on the Badger and edit/copy things on it, but you can do the reverse. So the workflow is going to have to be: * Write code * Run build script * Script copies files to Badger and resets it, then opens console ```bash # get a console on the device mpremote # list files at the root folder mpremote ls ``` TODO: * [ ] Use `mpremote` to construct a `Makefile` or `Rakefile` deploy toolchain to the Badger. ## Using Thonny ### Ubuntu Setup I used `pipx` to install Thonny locally, and I had to add my user to the `dialout` group since Badger 2040 was mounting on `/dev/ttyACM`. ### First steps Turn off the simplified interface. You can barely do anything in this interface beyond the basics. ### File Management There's no rename on the Badger 2040. Name your file correctly, go into the directory in Thonny's file browser (don't just flip it open), and Upload the file from your computer. ### While Badger 2040 is plugged in The dev cycle I've been using is: * Reset connection to Badger 2040 using Thonny Stop/Restart. * In the console, run `import launcher`. * Do stuff until it crashes. You can get `print` statements and exceptions in the console. If you're not using the default launcher, use `import `. ### Targeting a single file This seems to be a smoother approach: * `cd` to the directory of the file * In the REPL, run `import ` * Ctrl-C or wait for the program to halt * Clean out the module and reload (https://forum.micropython.org/viewtopic.php?p=7531#p7531): ```python import sys del sys.modules[''] import ``` This means you can skip `import launcher` and target just your program. ## JPEG Badger 2040 has its own version of JPEGDEC for decoding JPEGs on Pi 2040 devices: * Pimoroni's fork: https://github.com/pimoroni/pimoroni-pico/blob/main/libraries/jpegdec/JPEGDEC.cpp * The original: https://github.com/bitbank2/JPEGDEC ### Decoding Decoding goes directly to the display: ```python import jpegdec import badger2040 display = badger2040.Badger2040() jpeg = jpegdec.JPEG(display.display) jpeg.open_file("/my/image.jpg") jpeg.decode(x_target, y_target) ``` ### Saving compatible JPEG graphics GIMP on Linux by default will not make graphics the JPEG parser MicroPython on the badge likes. Run them through ImageMagick, then upload them to the badge. No other conversion needed, just in and out: ``` convert image-orig.jpg image.jpg ``` ## Activity LED All the docs say you can change the Activity LED's state but neither the REPL nor the actual code that ships with the Badger 2040 seem to be able to do that: * Docs: https://github.com/pimoroni/badger2040/blob/main/docs/reference.md#led ## BadgerOS You're very likely going to be modifying the files in `examples` to start. This means you'll be using the Launcher that comes with the Badger 2040 firmware. ### Data Formats You get JSON reading, no YAML. #### JSON You can't use the streaming JSON interface since opened files in MicroPython aren't streamable. Read the file into memory, then parse it: ```python with open("my_file.json", "r") as file: data = json.loads(file.read()) ``` ### Where is "Push A and C to Exit Launcher" controlled? This is in `badger_os.py` (https://github.com/pimoroni/badger2040/blob/main/firmware/PIMORONI_BADGER2040/lib/badger_os.py#L112-L142): * Launcher uses `badger_os.launch` to load a `.py` file from the `examples` folder: https://github.com/pimoroni/badger2040/blob/main/badger_os/launcher.py#L136 * Interrupt handlers (IRQs) are installed on the A and C buttons: https://github.com/pimoroni/badger2040/blob/main/firmware/PIMORONI_BADGER2040/lib/badger_os.py#L124-L125 * Your code is loaded: https://github.com/pimoroni/badger2040/blob/main/firmware/PIMORONI_BADGER2040/lib/badger_os.py#L128 * Regardless of what you do in the display loop of the app, those IRQs are waiting for button presses. I don't know who receives them first, your app or the handlers. * Once both A and C are pressed, the entire badge is reset and the launcher restarts: https://docs.micropython.org/en/latest/library/machine.html#machine.reset * Since the Launcher stores what app you launched last in a JSON file, it can restore the display and make it look like you never left. ### Running a timer or a clock Extracted from `clock.py`: ```python import time import machine import badger2040 try: badger2040.pcf_to_pico_rtc() except RuntimeError: pass rtc = machine.RTC() # set the clock to a reasonable default value year, month, day, wd, hour, minute, second, _ = rtc.datetime() if (year, month, day) == (2021, 1, 1): rtc.datetime((2022, 2, 28, 0, 12, 0, 0, 0)) while True: # get the clock value year, month, day, wd, hour, minute, second, _ = rtc.datetime() # do stuff here # avoid updating the display unless you absolutely have to! # even fast updates are costly time.sleep(0.01) ```