Skip to content

TeamAntumbra/glow-firmware

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Building

You need:

  • AVR toolchain: avr-binutils, avr-gcc, avr-libc.
  • GNU make.

Run:

git submodule update --init
make

You will get:

  • Bootloader firmware in Intel HEX format (loader.hex)
  • Application firmware in Intel HEX (main.hex) and raw binary (main.bin) format
  • Combined bootloader and application firmware in Intel HEX format (combined.hex)
  • EEPROM contents in raw binary (default.eep and combined.eep)

The only difference between the EEPROM files is that combined.eep contains a flag that tells the bootloader to automatically start the application. In default.eep, the flag is unset.

Firmware builds can be downloaded from GitHub.

Hardware

Chip configuration

lfuse0xff
hfuse0x9e
efuse0xc8 (may read as 0xf8)
lock0xff

Pins

PB1(SCLK)ISP header: SCK
PB2(MOSI)ISP header: MOSI / bootloader switch
PB3(MISO)ISP header: MISO
PB5(OC1A)LED red channel FET
PB6(OC1B)LED green channel FET
PB7(OC1C)LED blue channel FET

Shorting PB2 to GND during reset forces bootloader activation.

Flashing device firmware

The commands given use avrdude with the AVR Dragon. Some versions of avrdude ship without a device definition for the atmega16u4, so one must be provided. This is available in the repository as avrdude-m16u4.conf.

Each avrdude command line starts the same way. For brevity, $CMDLINE has been substituted for the following:

avrdude -C +avrdude-m16u4.conf -c dragon_isp -P usb -p atmega16u4

If the chip is fresh from the factory, it probably shipped with the lock bits programmed. This causes the flashing process to fail. Clear the lock bits by executing a chip erase. (-B 10 is used because the chip is set for a low clock frequency by default.)

$CMDLINE -B 10 -e

Set the configuration fuses:

$CMDLINE -B 10 -U lfuse:w:0xFF:m -U hfuse:w:0x9E:m -U efuse:w:0xC8:m

The highest clock frequency is now configured, and faster programming speeds can be used in subsequent commands.

Some of the methods below use antumbratool (part of libantumbra), a command-line tool for programming and testing Glow devices. Precompiled builds are available from GitHub.

Bootloader by ISP, application by USB

Flash bootloader and EEPROM:

$CMDLINE -B 0.5 -U flash:w:loader.hex:i -U eeprom:w:default.eep:r

Use antumbratool to flash the main application:

antumbratool flash-write main.bin
antumbratool boot-set main
antumbratool reset

Bootloader and application together by ISP

This method will be used in mass production.

Flash bootloader, application, and EEPROM. combined.eep must be used in place of default.eep.

$CMDLINE -B 0.5 -U flash:w:combined.hex:i -U eeprom:w:combined.eep:r

Reflashing application firmware

Use antumbratool:

antumbratool boot-set loader
antumbratool reset
antumbratool flash-write main.bin
antumbratool eeprom-write default.eep
antumbratool boot-set main
antumbratool reset

It is strongly recommended to update the EEPROM when flashing new firmware. Using an old EEPROM with new firmware may result in wildly incorrect behavior.

Protocol

Glow devices speak a simple, extensible protocol over USB. The protocol is not implemented on top of any existing USB Class (such as HID), SubClass, or Protocol.

Device identification

To be recognized as implementing the Glow protocol, a device must conform to a number of conditions. First, it must have one of the following combinations of vendor and product ID:

Vendor IDProduct ID
0x16d00x0a85production ID (via MCS Electronics)

Only the first interface (bInterfaceNumber = 0) of the default configuration (bConfigurationValue = 1) is used. bInterfaceClass, bInterfaceSubClass, and bInterfaceProtocol must all be 0xff. The iInterface string descriptor must be of this form:

io.antumbra.glowapi/<outep>/<inep>/<info>
  • io.antumbra.glowapi: identifies this interface as implementing the Glow protocol described here
  • <outep>: bEndpointAddress of the OUT (host to device) command endpoint
  • <inep>: bEndpointAddress of the IN (device to host) command endpoint
  • <info>: an arbitrary string, but recommended to be in a form that identifies the implementation (such as org.example.glow-compatible-device reverse domain name notation)

<outep> and <inep> are two-digit hexadecimal numbers with no prefix. Both lowercase and uppercase digits are acceptable, but lowercase is recommended for consistency. The high bit of <outep> must be 0 and the high bit of <inep> must be 1; these bits indicate the endpoint direction. Typically <outep> is 01 and <inep> is 82.

Both must be bulk endpoints. There may be other endpoints than those specified by <outep> and <inep>, but they are ignored unless the device advertises an API that uses them (see below).

Base protocol

The required bulk endpoints are used to carry a packet-based command protocol. The host sends commands to the device, and the device must respond to each command.

Commands are specified by an API identifier and a command identifier. The API identifier represents a set of functionality, and the command identifier represents a specific operation within that set.

All packets are of fixed 64-byte size. Where applicable, big-endian order is assumed.

Command packet:

FieldSize
api4API identifier
cmd2Command identifier
(padding)2Ignored
dataNArbitrary, defined by API
(padding)56-NIgnored

Response packet:

FieldSize
status1(see below)
(padding)7Ignored
dataNArbitrary, defined by API
(padding)56-NIgnored

The device must respond to commands in the order that they were received. The host should wait until the response is received before sending additional commands.

The status field indicates protocol-level errors:

Status
0Success
1Unsupported API or command

APIs may not use the status field for their own purposes. If it is necessary to represent the occurence of an API-level error, this must be done in the response payload. The status field exists at a different conceptual layer.

Responses with nonzero status have an empty payload.

Core API

All devices must support the Core API. It provides universal functionality and allows bootstrapping support for other APIs.

The API identifier is 0.

Echo (0)

Can be used to synchronize the host and device at the beginning of a session, in case the device is still in the process of sending responses from a previous session. For this application, the host should send an Echo command with a reasonably unique payload and discard response packets until it receives one with the same payload as the command.

The command payload contains arbitrary data and is assumed to occupy the maximum available space in the packet. The response payload is equal to the command payload.

Ask (1)

Ask whether the device supports a given API.

Command payload:

FieldSize
api4API identifier for which to query support

Response payload:

FieldSize
supported11 if API is supported, else 0
infoNAPI-defined information, if supported; else empty

If the host queries API 0 (Core), the device must respond with affirmative support. No info field is defined for this case.

Diagnostic (2)

Report problem conditions such as hardware failures, configuration errors, etc.

The command payload is empty.

The response payload contains arbitrary data of a format specific to the device firmware. However, the presence of any nonzero byte in the payload will be assumed to indicate the existence of at least one problem condition.

Implementation ID (3)

Produce human-readable information that identifies the firmware implementation. A reverse domain name is one possible form.

The command payload is empty.

The response payload is a human-readable string of up to 56 bytes in an unspecified encoding. If its length is less than 56 bytes, there must be a 0 byte immediately after the end of the string. Payload data after the 0 byte is ignored.

Device ID (4)

Produce an identifier for this particular device that may be reasonably assumed to distinguish it from others of its model and all other Glow protocol devices in existence.

The command payload is empty.

The response payload contains the 56-byte unique identifier.

Reset (5)

Perform a full hardware reset.

The command payload is empty.

The response payload is empty.

Hardware ID (6)

Produce human-readable information that identifies the hardware type. This is distinct from the Implementation ID in that multiple firmware implementations may target the same hardware type, and each such implementation should report the same hardware type.

Known hardware types:

  • io.antumbra.glow.v3: Original Antumbra Glow public-release board. ATmega16u4 microcontroller, micro-USB power and control, 1.5 x 1.5 inches.

The command payload is empty.

The response payload is as with the Implementation ID.

APIs

Boot Control (1)

The Glow includes a firmware bootloader that can be used to update the main application without a flash programmer. While the bootloader can be activated at power-on by shorting pads on the board, it is often necessary to activate the bootloader without physical intervention. This API controls bootloader startup purely through software.

The API identifier is 1.

Set Boot (0)

Select whether the bootloader or the main application will be started at power-on.

The command payload is one byte. If the byte is zero, the main application will be started at power-on; if the byte is nonzero, the bootloader will be started. This setting is persistent and remains in effect until explicitly modified. Hardware boot switches, if applicable, override this setting.

The response payload is empty.

EEPROM (2) <<eepromapi>>

This API allows access to the device’s onboard byte-addressable nonvolatile data memory.

The API identifier is 2.

EEPROM Info (0)

Report EEPROM size.

The command payload is empty.

Response payload:

FieldSize
size2EEPROM size (bytes)

EEPROM Read (1)

Read a variable-size block from EEPROM.

Command payload:

FieldSize
offset2Start offset to read (bytes)
length1Length to read (bytes); max 48

Response payload:

FieldSize
status1Indicate error condition
(padding)7Ignored
datamax 48(if successful)

If the specified region is out of bounds (offset + length > size given by EEPROM Info) or otherwise unacceptable, an error code is returned:

Code
0Success
1Out of EEPROM bounds
2Specified length too large for packet format

EEPROM Write (2)

Write a variable-size block to EEPROM.

Command payload:

FieldSize
offset2Start offset to write (bytes)
length1Length to write (bytes); max 48
(padding)5Ignored
datamax 48Block to write

Response payload:

FieldSize
status1Indicate error condition

status is as specified in EEPROM Read.

Flash (3)

This API allows access to the device’s page-based nonvolatile program memory.

Since flash pages are generally much larger than packets, I/O occurs in two stages using a page-sized intermediate buffer. To read a page, one command dumps the page into the buffer, and the buffer contents are then read out over multiple commands. To write a page, the buffer contents are loaded over multiple commands, and a final command writes the page to flash.

Before the buffer has been fully populated, its contents are undefined. The read and write commands are assumed to share the same buffer, so they should not be interleaved.

Flash Info (0)

Report flash size.

The command payload is empty.

Response payload:

FieldSize
pagesize2Size of flash pages/buffer
numpages4Number of flash pages

Flash Buffer Read (1)

Read a variable-size block from page buffer.

Command payload:

FieldSize
offset2Start offset to read
length1Length to read; max 48

Response payload:

FieldSize
status1Indicate error condition
(padding)7Ignored
datamax 48(if successful)

If the specified region is out of bounds or otherwise unacceptable, an error code is returned:

Code
0Success
1Out of buffer bounds
2Specified length too large for packet format

Flash Buffer Write (2)

Write a variable-size block to page buffer.

Command payload:

FieldSize
offset2Start offset to write
length1Length to write; max 48
(padding)5Ignored
datamax 48Block to write

Response payload:

FieldSize
status1Indicate error condition

status is as specified in Flash Buffer Read.

Flash Page Read (3)

Load flash page into buffer.

Command payload:

FieldSize
pageindex4Index of flash page to load

Response payload:

FieldSize
status1Indicate error condition

If the specified page index is out of bounds, status is set to 1. Otherwise, it is set to 0.

Flash Page Write (4)

Write page buffer to flash.

FieldSize
pageindex4Index of flash page to write

Response payload:

FieldSize
status1Indicate error condition

status is as specified in Flash Page Read.

Light (4)

This API controls a single RGB LED. The color is not set directly through this API for performance reasons; instead, colors are streamed to a dedicated USB endpoint.

Get Endpoint (0)

Produce the address of the USB endpoint for color streaming.

The command payload is empty.

Response payload:

FieldSize
endpoint1endpoint address

The endpoint address must refer to a bulk OUT endpoint (high bit 0). It expects 6-byte single-packet transfers with the following structure:

FieldSize
red2red component
green2green component
blue2blue component

A component value shall affect the LED brightness as though that LED component is driven by PWM and the component value is proportional to the duty cycle. A value of 65535 indicates maximum brightness (which may be less than 100% duty cycle due to thermal restrictions or power consumption), while a value of 0 indicates that the component is fully off.

Since perceived light intensity is logarithmic with the actual intensity, it may be desirable for the host to exponentially scale the given RGB component values based on the desired perceived brightness. Additionally, equivalent intensities are perceived differently for each color component, so the host may scale the components differently as well. The device shall perform no scaling or adjustment on its own.

Temperature (5)

This API provides access to the device’s onboard temperature sensor.

Read Raw Sensor (0)

Read raw sensor output. The value is unitless and uncalibrated.

The command payload is empty.

Response payload:

FieldSize
sensorvalue4current sensor value

Read Calibrated Temperature (1)

Read the current temperature, calibrated based on the stored calibration data.

The command payload is empty.

Response payload:

FieldSize
temperature4current temperature (millikelvin)

Read Calibration (2)

Read the stored calibration data. Conceptually, this exists as two measurements taken at different temperatures. Each measurement contains the actual temperature and the raw sensor value for that temperature. When a calibrated temperature is to be retrieved, the current sensor value is fitted to the curve given by the two calibration points.

The nature of the curve is unspecified. However, typical on-chip temperature sensors are approximately described by a linear function.

The command payload is empty.

Response payload:

FieldSize
a_sensor4first point sensor value
a_temp4first point temperature value (millikelvin)
b_sensor4second point sensor value
b_temp4second point temperature value (millikelvin)

Write Calibration (3)

Update stored calibration data. (As described by Read Calibration.)

Command payload:

FieldSize
a_sensor4first point sensor value
a_temp4first point temperature value (millikelvin)
b_sensor4second point sensor value
b_temp4second point temperature value (millikelvin)

The response payload is empty.

EEPROM

The format of the device EEPROM (as accessed by the EEPROM API) is implementation-defined. The following implementations are known to use the format described here:

  • io.antumbra.glow.v3.app
  • io.antumbra.glow.v3.ldr

The EEPROM is structured as a list of options, each of which follows a straightforward type-length-value encoding:

FieldSize
id4mostly arbitrary; see below
length1length of payload field
payload(length)arbitrary data; structure defined by option ID

The options are directly concatenated. At the end of the list is the 4-byte option end marker, which may be either 00 00 00 00 or ff ff ff ff. These values are chosen because unprogrammed EEPROM bytes are typically either 00 or ff; thus, if the entire EEPROM is unprogrammed, the option list will be interpreted as having zero length.

Options are looked up via linear search by ID. Since EEPROM access may be slow, implementations are likely to read options once at boot and cache their values thereafter. Therefore, updates to EEPROM may not take effect until reset.

Since 00 00 00 00 and ff ff ff ff are end markers, the option ID cannot be either of these values. All other values are permitted. However, for the sake of a more readable binary representation, it is recommended that the option ID be a sequence of 4 ASCII printable characters. For example, the option ID “TEST” would be encoded as 54 45 53 54.