This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

API reference

Welcome to the API reference for BadgePython.

This reference describes all officially supported APIs of our platform. We try to keep these APIs as stable as possible. There are many more (undocumented) APIs in the firmware, all of which may change at any time!

BadgePython uses MicroPython at it’s core.The libraries and APIs from the upstream MicroPython project are available in BadgePython.

The MicroPython documentation describes the builtin libraries and functions.

We’ve added some badge specific APIs for efficiently controlling hardware like the display and reading the buttons. By implementing the resource intensive parts of driving the hardware in C. This allows for a much more speedy experience and a lot more possibilities and flexibility.

If you want to help with firmware development please tell us! We’re always happy to accept PRs and improvements.

Should you have ideas, problems or observations but no means to act on them then you can always create an issue on Github.

BadgePython APIs

LibraryFunctionMCM2022SHA2017Disobey 2019Disobey 2020HackerHotel 2019CampZone 2019CampZone 2020
displayControl the display of your badge: create and display text and graphics
buttonsRead button status and attach callback functions to button interactions
mch22MCH2022 specific functions

Other libraries and APIs

This section lists most of the other libraries that you can use in your apps.

LibraryFunctionDocumentation
mathMathematical functionsMicroPython
cmathMathematical functions for complex numbersMicroPython
ubinasciiUtilities for working with binary data (Hex-string, base64 and CRC32 calculationMicroPython
ucollectionsCollection and container typesMicroPython
uerrnoSystem error code referenceMicroPython
uhashlibSHA1 and SHA256 hashing algorithmsMicroPython
uheapqHeap queue algorithmMicroPython
uioInput/output streamsMicroPython
ujsonJSON encoding and decodingMicroPython
uosBasic “operating system” servicesMicroPython
ureSimple regular expressionsMicroPython
uselectWait for events on a set of streamsMicroPython
usocketSockets (TCP, UDP)MicroPython
usslSSL/TLS moduleMicroPython
ustructPack and unpack primitive data typesMicroPython
utimeTime related functionsMicroPython
uzlibZlib decompressionMicroPython
_threadMultithreading supportMicroPython
gcControl the garbage collectorMicroPython
sysSystem specific functionsMicroPython
machineFunctions related to the hardware (Note: different from upstream version)MicroPython
micropythonAccess and control MicroPython internalsMicroPython
networkNetwork configuration (Please use the wifi library instead when possible)MicroPython
espESP32 specific functions (Note: direct flash access has been disabled)MicroPython

1 - Display

The display module is available on platforms which have the framebuffer driver enabled. It allows for controlling the display of your device.

Available on:    ✅ CampZone 2020    ✅ Disobey 2020    ✅ CampZone 2019    ✅ HackerHotel 2019
Disobey 2019    ✅ SHA2017

Reference

CommandParametersDescription
flush[flags]Flush the contents of the framebuffer to the display. Optionally you may provide flags (see the table down below)
size[window]Get the size (width, height) of the framebuffer or a window as a tuple
width[window]Get the width of the framebuffer or a window as an integer
height[window]Get the height of the framebuffer or a window as an integer
orientation[window], [angle]Get or set the orientation of the framebuffer or a window
getPixel[window], x, yGet the color of a pixel in the framebuffer or a window
drawRaw[window], x, y, width, height, dataCopy a raw bytes buffer directly to the framebuffer or the current frame of a window. The length of the bytes buffer must match the formula width*height*(bitsPerPixel//8). This is a direct copy: color format (bitsPerPixel) must match the specific display of the badge this command is used on.
drawPixel[window], x, y, colorDraw a pixel in the framebuffer or a window
drawFill[window], colorFill the framebuffer or a window
drawLine[window], x0, y0, x1, y1, colorDraw a line from (x0, y0) to (x1, y1)
drawTri(angle)[window], x0, y0, x1, y1, x2, y2, colorDraws a filled triangle
drawRect[window], x, y, width, height, filled, colorDraw a rectangle at (x, y) with size (width, height). Set the filled parameter to False to draw only the border, or set it to True to draw a filled rectangle.
drawQuad*[window], x0, y0, x1, y1, x2, y2, x3, y3, colorDraws a four-pointed shape between (x0, y0), (x1, y1), (x2, y2) and (x3, y3), always filled
drawCircle[window], x0, y0, radius, a0, a1, fill, colorDraw a circle with center point (x0, y0) with the provided radius from angle a0 to angle a1, optionally filled (boolean)
drawText[window], x, y, text, [color], [font], [x-scale], [y-scale]Draw text at (x, y) with a certain color and font. Can be scaled (drawn with rects instead of pixels) in both the x and y direction
drawPng[window], x, y, [data or filename]Draw a PNG image at (x, y) from either a bytes buffer or a file
getTextWidthtext, [font]Get the width a string would take if drawn with a certain font
getTextHeighttext, [font]Get the height a string would take if drawn with a certain font
pngInfo[data or filename]Get information about a PNG image
windowCreatename, width, height
windowRemovename
windowMovename, x, y
windowResizename, width, height
windowVisibilityname, [visible]
windowShowname
windowHidename
windowFocusname
windowList-
translate*[window], x, yMove the canvas of the window by (x, y)
rotate*[window], angleRotate the canvas of the window by an angle (in randians)
scale*[window], x, yScale the canvas of the window by (x, y)
pushMatrix*[window]Save the current transformation for later, may be more than one
popMatrix*[window]Restore the transformation pushed earlier, may be more than one
clearMatrix*[window], [keep-stack]Clears the current matrix, and also the matrix stack unless keep-stack is true
getMatrix*[window]Returns an array representing the current matrix for the window
setMatrix*[window], [matrix]Sets the current matrix to the array representing it
matrixSize*[window]Returns the current size of the matrix stack for the window

* This command is only available if you run a firmware with graphics acceleration, and the respective part enabled in the component config under Driver: framebuffer. Currently, badges have these features disabled by default.

flagplatformdescription
FLAG_FORCEAllForce flushing the entire screen.
FLAG_FULLAllForce flushing the entire screen.
FLAG_LUT_GREYSCALEAll with greyscale: SHA2017Simulate greyscale.
FLAG_LUT_NORMALAll with e-inkNormal speed flush.
FLAG_LUT_FASTAll with e-inkFaster flush.
FLAG_LUT_FASTESTAll with e-inkMuch faster flush.

Color representation

Colors are always represented in 24-bit from within Python, in the 0xRRGGBB format. This matches HTML/CSS colors which are #RRGGBB as well.

Devices with a smaller colorspace will not actually store the exact color information provided. For displays with a color depth of less than 24-bit the display driver will automatically mix down the colors to the available color depth. This means that even if you have a black and white display 0x000000 is black and 0xFFFFFF is white.

Examples

Setting one pixel

import display
x = 2
y = 3
display.drawPixel(x, y, 0x00FF00)  # Set one pixel to 100% green
display.flush() # Write the contents of the buffer to the display

Drawing a line

import display
display.drawFill(0x000000) # Fill the screen with black
display.drawLine(10, 10, 20, 20, 0xFFFFFF) # Draw a white line from (10,10) to (20,20)
display.flush() # Write the contents of the buffer to the display

Drawing a line using pixels:

import display, time
display.drawFill(display.BLACK) # Fill the screen with black before drawing the line
displau.flush() # Write the color to the screen before drawing the line
for i in range(80): # Loop for the X axis
    display.drawPixel(i, 1, 0x00FF00) # Set 1 pixel on the X axis i, and the Y axis 1 to 100% green 
    display.flush() # Write the pixel output to the screen
    time.sleep(0.050) # Sleep for 50 milliseconds as to show the line being drawn

Drawing text

import display
display.drawFill(0x000000) # Fill the screen with black
display.drawText(10, 10, "Hello world!", 0xFFFFFF, "permanentmarker22") # Draw the text "Hello world!" at (10,10) in white with the PermanentMarker font with size 22
display.flush() # Write the contents of the buffer to the display

Drawing a rectangle

import display
display.drawFill(0x000000) # Fill the screen with black
display.drawRect(10, 10, 10, 10, False, 0xFFFFFF) # Draw the border of a 10x10 rectangle at (10,10) in white
display.drawRect(30, 30, 10, 10, True, 0xFFFFFF) # Draw a filled 10x10 rectangle at (30,30) in white
display.flush() # Write the contents of the buffer to the display

Spinning a box

Note: as described earlier, matrix transformations are not enabled in the firmware by default

import display, math
# Note: radians are an angle unit where PI (math.pi) is half a rotation
display.clearMatrix() # Clear the matrix stack, just in case it wasn't already
display.translate(display.width() / 2, display.height() / 2) # Go to the middle of the screen
    # Everything is now offset as if the middle of the screen is X/Y (0, 0)
while True:
    display.drawFill(0xffffff) # Fill the screen with white
    display.rotate(math.pi * 0.1) # This will continually rotate the screen by a small amount
    display.drawRect(-20, -20, 40, 40, True, 0x000000) # Simply draw a rectangle, which will then spin
    display.flush() # Flush, show everything

Spinning text

Note: as described earlier, matrix transformations are not enabled in the firmware by default

Similarly to spinning a box, you can also spin text this way.

import display, math
# Note: radians are an angle unit where PI (math.pi) is half a rotation
text = "Well hello there!" # Whatever you want to show
font = "7x5" # Pick a font
scale = 2 # You can scale text, too!
display.clearMatrix() # Clear the matrix stack, just in case it wasn't already
display.translate(display.width() / 2, display.height() / 2) # Go to the middle of the screen
    # Everything is now offset as if the middle of the screen is X/Y (0, 0)
while True:
    display.drawFill(0xffffff) # Fill the screen with white
    display.rotate(math.pi * 0.1) # This will continually rotate the screen by a small amount
    textWidth = display.getTextWidth(text, font) # Get the size so we can center the text
    textHeight = display.getTextHeight(text, font)
    display.pushMatrix() # Save the transformation for later
    display.scale(scale, scale) # Scale the text
    display.translate(-textWidth / 2, -textHeight / 2) # Move the canvas so the text is centered
        # It is important you scale first, then translate
    display.drawText(0, 0, text, 0x000000, font) # Spinny texts
    display.popMatrix() # Restore the transformation
    display.flush() # Flush, show everything

More complex graphics

Note: as described earlier, matrix transformations are not enabled in the firmware by default

Now you’ve spun a box and some text, what about something more complicated?
Let’s say we draw a boat on a wave!

First, we draw the boat using some shapes:

import display, math

def drawBoat():
    display.pushMatrix()
    drawBoatBottom(0x000000)
    display.translate(-4, 0) # Move just a little so the mast lines up nicely
    drawBoatMast(0x000000, 0x000000)
    display.popMatrix()

def drawBoatMast(mastColor, flagColor):
    # The points drawn, by place:
    # 0--1
    # |  |
    # |  6
    # |  |\
    # |  5-4
    # |  |
    # 3--2
    x0, y0 = 0, -23
    x1, y1 = 3, -23
    x2, y2 = 3, 0
    x3, y3 = 0, 0
    x4, y4 = 12, -10
    x5, y5 = 3, -10
    x6, y6 = 3, -20
    display.drawQuad(x0, y0, x1, y1, x2, y2, x3, y3, mastColor) # This is the mast: points 0, 1, 2, 3
    display.drawTri(x4, y4, x5, y5, x6, y6, flagColor) # This is the flag: points 4, 5, 6

def drawBoatBottom(color):
    # The points drawn, by place:
    # 0--------1
    #  \      /
    #   3----2
    x0, y0 = -20, 0
    x1, y1 = 20, 0
    x2, y2 = 16, 8
    x3, y3 = -16, 8
    display.drawQuad(x0, y0, x1, y1, x2, y2, x3, y3, color)

Now, to test your boat drawing action:

import display, math

# Put the boat drawing functions here

display.clearMatrix() # Don't forget
display.translate(30, 30) # Move to where you want to draw the boat
display.drawFill(0xffffff) # Clear the screen once more
drawBoat() # Draw the boat of course
display.flush() # Flush display; boat is now visible

Then, we’ll draw a wave and a sun:

import display, math

def drawSun(color): # Draws the sun with a circle and some lines
    display.pushMatrix()
    display.translate(-3, -3 - display.height()) # This is where the sun will orbit around
        # We do - display.height() here because we set the origin to be at the bottom of the screen earlier
    display.drawCircle(0, 0, 30, 0, 360, True, color) # The sun
    display.rotate(sunOffset)
    for i in range(20): # Draw lines as the sun's rays
        display.rotate(math.pi / 10)
        display.drawLine(0, 35, 0, 45, color)
    display.popMatrix()

# For good measure.
display.clearMatrix()

display.translate(0, display.height())

sunOffset = 0
offset = 0
boatX = display.width() / 6
boatAngle = 0
boatY = 0

while True:
    display.drawFill(0xffffff)
    drawSun(0x000000) # Draw the sun
    for i in range(display.width()): # Draw the waves by plotting points
        wave = math.sin((i + offset) * math.pi / 35) * 8 - 35
        display.drawPixel(i, wave, 0x000000)
        if i & 1:
            for j in range(round(wave - 1) | 1, 0, 2):
                display.drawPixel(i, j + ((i >> 1) & 1) + 1, 0x000000)
    offset += 8 # Move the waves over by a bit
    sunOffset += math.pi * 0.025 # Spin the sun by a bit
    display.flush()

Finally, you can draw the boat on the wave, by adding some code:

while True:
    display.drawFill(0xffffff)
    drawSun(0x000000)
    for i in range(display.width()):
        wave = math.sin((i + offset) * math.pi / 35) * 8 - 35
        display.drawPixel(i, wave, 0x000000)
        if i & 1:
            for j in range(round(wave - 1) | 1, 0, 2):
                display.drawPixel(i, j + ((i >> 1) & 1) + 1, 0x000000)
    # vvvv HERE vvvv
    display.pushMatrix() # Save the transformation, we're going to mess with it
    waterLevelBeforeBoat = math.sin((boatX + 2 + offset) * math.pi / 35) * 8 - 35
    boatY = math.sin((boatX + offset) * math.pi / 35) * 8 - 35
        # Calculate the two water levels, one at and one before the boat
        # By doing this, we know how and where to position the boat
    boatAngle = math.atan2(waterLevelBeforeBoat - boatY, 2) # Using atan2 to find the angle required to rock the boat with the wave
    display.translate(boatX, boatY - 6) # Now, position the boat
    display.rotate(boatAngle)
    drawBoat() # And draw the boat
    display.popMatrix() # Undo our changes to the transformation
    # ^^^^ HERE ^^^^
    offset += 8
    sunOffset += math.pi * 0.025
    display.flush()

The source code for the boat can be found here: gist: boat.py

Available fonts:

The fonts in the latest firmware can be obtained from the sourcecode.

Known problems

  • Rotation of the contents of windows does not work correctly in combination with rotation of the screen itself
  • There is no method available to list the fonts available on your platform
  • There is no method for providing a custom font
  • There is no anti-aliassing support

2 - Buttons

The buttons API allows you to read the state of the buttons on a badge. This API encapsulates the drivers for different button types.

Badge support

This API is currently supported on the following badges:

  • SHA2017
  • Hackerhotel 2019
  • Disobey 2019
  • CampZone 2019
  • Disobey 2020
  • Fri3dcamp 2018

Support for GPIO buttons and touch-buttons via the MPR121 touch controller IC are supported. Touch buttons using the touch button features of the ESP32 can not be used (yet).

Reference

CommandParametersDescription
attachbutton, callback functionAttach a callback to a button
detachbuttonDetach a callback from a button
valuebuttonGet the current value of a button
getCallbackbuttonGet the current callback of a button
pushMapping[mapping]Switch to a new button mapping
popMappingnoneSwitch back to the previous button mapping
rotatedegreesAdapt the button layout to an orientation. Accepts 0, 90, 180 and 270 as values.

Button availability per badge

NameSHA2017Hackerhotel 2019Disobey 2019CampZone 2019Disobey 2020MCH2022
AYesYesYesYesYesYes
BYesYesYesYesYesYes
SELECTYesYesNoNoYesYes
STARTYesYesNoNoYesYes
UPYesYesYesYesYesYes
DOWNYesYesYesYesYesYes
LEFTYesYesYesYesYesYes
RIGHTYesYesYesYesYesYes
HOMENoNoNoNoNoYes
MENUNoNoNoNoNoYes

Default callback per button

NameSHA2017Hackerhotel 2019Disobey 2019CampZone 2019Disobey 2020MCH2022
A
BExit appExit app
SELECT
STARTExit appExit appExit app
UP
DOWN
LEFT
RIGHT
HOMEExit app
MENU

Callback implementation:

to use the buttons, you need to implement a callback function:


import buttons, display # Imports 2 libraries to use the buttons, the display library, as well as the buttons library
def on_action_btn(pressed): # Defines a function on_action_btn with the required parameter pressed
    if pressed: # Uses an if statement to check if the button has been pressed
        display.drawFill(display.BLACK) # If the button is pressed, sets the screen to black
        display.drawText(10,10,"Hack The Planet!!!", display.GREEN, "roboto_regular18") # Draws text if the button is pressed
        display.flush() # Flushes the screen to draw the text and color onto the screen
buttons.attach(buttons.BTN_A, on_action_btn) # Assigns the function on_action_btn to the A button

3 - mch22

FPGA

The ICE40UP5K FPGA on the MCH2022 badge can be controlled using these APIs.

Loading a bitstream

A bitstream can be loaded into the FPGA by calling mch22.fpga_load(x) with x being a bytes object containing the bitstream binary. Loading a bitstream automatically enables the FPGA.

Disabling the FPGA

To disable the FPGA after a bitstream was loaded call mch22.fpga_disable(). Note that to enable the FPGA a bitstream has to be loaded in again.

Communicating with the FPGA

The FPGA is connected to the ESP32 via an SPI bus. Communication over SPI can be done in full-duplex mode (sending while receiving) or in half-duplex mode (only sending or receiving).

The transmitting functions require a bytes object, the receiving functions return a bytes object.

FunctionDirectionBus speed
transactionFull-duplex26.7MHz
receiveRead from FPGA40MHz
sendWrite to FPGA40MHz
send_turboWrite to FPGA40MHz

In case you get communication errors please check the timing constraints of your bitstream.

LCD mode

To connect the LCD to the FPGA call mch22.lcd_mode(True), to connect the LCD to the ESP32 call mch22.lcd_mode(False). To send the contents of the framebuffer to the LCD call display.flush(True), the True argument forces the flush to happen even though the contents of the framebuffer haven’t changed.

GPIO

You can set the direction of a GPIO pin using mch22.set_gpio_dir(pin, direction), where pin is one of mch22.SAO_IO0_PIN, mch22.SAO_IO1_PIN, mch22.PROTO_0_PIN or mch2022.PROTO_1_PIN and direction is False for input or True for output.

Inputs can be read using value = mch22.get_gpio_value(pin) and outputs can be set using mch22.set_gpio_value(pin, value).

Deprecated functions

These functions have been moved to other, more generic APIs. Please use those APIs instead.

Buttons

The mch22 module exposes a direct interface to the button handler code via the buttons and set_handler functions. Please do not use these, use the buttons API instead. Using these APIs directly can cause crashes to occur.

Brightness control

Please use the display API instead, brightness can be set using display.brightness(x) with x being a value from 0 to 255.