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
andcombined.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.
lfuse | 0xff |
hfuse | 0x9e |
efuse | 0xc8 (may read as 0xf8) |
lock | 0xff |
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.
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.
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
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
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.
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.
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 ID | Product ID | |
---|---|---|
0x16d0 | 0x0a85 | production 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 asorg.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).
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:
Field | Size | |
---|---|---|
api | 4 | API identifier |
cmd | 2 | Command identifier |
(padding) | 2 | Ignored |
data | N | Arbitrary, defined by API |
(padding) | 56-N | Ignored |
Response packet:
Field | Size | |
---|---|---|
status | 1 | (see below) |
(padding) | 7 | Ignored |
data | N | Arbitrary, defined by API |
(padding) | 56-N | Ignored |
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 | |
---|---|
0 | Success |
1 | Unsupported 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.
All devices must support the Core API. It provides universal functionality and allows bootstrapping support for other APIs.
The API identifier is 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 whether the device supports a given API.
Command payload:
Field | Size | |
---|---|---|
api | 4 | API identifier for which to query support |
Response payload:
Field | Size | |
---|---|---|
supported | 1 | 1 if API is supported, else 0 |
info | N | API-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.
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.
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.
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.
Perform a full hardware reset.
The command payload is empty.
The response payload is empty.
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.
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.
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.
This API allows access to the device’s onboard byte-addressable nonvolatile data memory.
The API identifier is 2.
Report EEPROM size.
The command payload is empty.
Response payload:
Field | Size | |
---|---|---|
size | 2 | EEPROM size (bytes) |
Read a variable-size block from EEPROM.
Command payload:
Field | Size | |
---|---|---|
offset | 2 | Start offset to read (bytes) |
length | 1 | Length to read (bytes); max 48 |
Response payload:
Field | Size | |
---|---|---|
status | 1 | Indicate error condition |
(padding) | 7 | Ignored |
data | max 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 | |
---|---|
0 | Success |
1 | Out of EEPROM bounds |
2 | Specified length too large for packet format |
Write a variable-size block to EEPROM.
Command payload:
Field | Size | |
---|---|---|
offset | 2 | Start offset to write (bytes) |
length | 1 | Length to write (bytes); max 48 |
(padding) | 5 | Ignored |
data | max 48 | Block to write |
Response payload:
Field | Size | |
---|---|---|
status | 1 | Indicate error condition |
status
is as specified in EEPROM Read
.
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.
Report flash size.
The command payload is empty.
Response payload:
Field | Size | |
---|---|---|
pagesize | 2 | Size of flash pages/buffer |
numpages | 4 | Number of flash pages |
Read a variable-size block from page buffer.
Command payload:
Field | Size | |
---|---|---|
offset | 2 | Start offset to read |
length | 1 | Length to read; max 48 |
Response payload:
Field | Size | |
---|---|---|
status | 1 | Indicate error condition |
(padding) | 7 | Ignored |
data | max 48 | (if successful) |
If the specified region is out of bounds or otherwise unacceptable, an error code is returned:
Code | |
---|---|
0 | Success |
1 | Out of buffer bounds |
2 | Specified length too large for packet format |
Write a variable-size block to page buffer.
Command payload:
Field | Size | |
---|---|---|
offset | 2 | Start offset to write |
length | 1 | Length to write; max 48 |
(padding) | 5 | Ignored |
data | max 48 | Block to write |
Response payload:
Field | Size | |
---|---|---|
status | 1 | Indicate error condition |
status
is as specified in Flash Buffer Read
.
Load flash page into buffer.
Command payload:
Field | Size | |
---|---|---|
pageindex | 4 | Index of flash page to load |
Response payload:
Field | Size | |
---|---|---|
status | 1 | Indicate error condition |
If the specified page index is out of bounds, status
is set to 1. Otherwise,
it is set to 0.
Write page buffer to flash.
Field | Size | |
---|---|---|
pageindex | 4 | Index of flash page to write |
Response payload:
Field | Size | |
---|---|---|
status | 1 | Indicate error condition |
status
is as specified in Flash Page Read
.
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.
Produce the address of the USB endpoint for color streaming.
The command payload is empty.
Response payload:
Field | Size | |
---|---|---|
endpoint | 1 | endpoint 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:
Field | Size | |
---|---|---|
red | 2 | red component |
green | 2 | green component |
blue | 2 | blue 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.
This API provides access to the device’s onboard temperature sensor.
Read raw sensor output. The value is unitless and uncalibrated.
The command payload is empty.
Response payload:
Field | Size | |
---|---|---|
sensorvalue | 4 | current sensor value |
Read the current temperature, calibrated based on the stored calibration data.
The command payload is empty.
Response payload:
Field | Size | |
---|---|---|
temperature | 4 | current temperature (millikelvin) |
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:
Field | Size | |
---|---|---|
a_sensor | 4 | first point sensor value |
a_temp | 4 | first point temperature value (millikelvin) |
b_sensor | 4 | second point sensor value |
b_temp | 4 | second point temperature value (millikelvin) |
Update stored calibration data. (As described by Read Calibration
.)
Command payload:
Field | Size | |
---|---|---|
a_sensor | 4 | first point sensor value |
a_temp | 4 | first point temperature value (millikelvin) |
b_sensor | 4 | second point sensor value |
b_temp | 4 | second point temperature value (millikelvin) |
The response payload is empty.
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:
Field | Size | |
---|---|---|
id | 4 | mostly arbitrary; see below |
length | 1 | length 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
.