SERIAL PROTOCOL FOR FNORDLICHT-NG FIRMWARE
==========================================

Physical bus description
------------------------

The serial bus consists of up to 254 devices, each device having the uart rx
pin connected to the uart tx pin of the predecessor, the uart tx pin connected
to the tx successor.  The uart is used with a baud rate of 19200.  A device
will retransmit each received byte immediately and unaltered, the only
exception is the address byte in the sync sequence (which is incremented by one
before retransmission).  An interrupt line is connected to each device.

Diagram:

                +----------------+         +----------------+
                |    device N    |         |   device N+1   |
UART: ... --> >-|RX            TX|-> --> >-|RX            TX|-> ...
                |      INT       |         |      INT       |
                +-------+--------+         +-------+--------+
                        |                          |
INT: -------------------+--------------------------+----------> ...


Bootloader startup procedure
----------------------------

For updating the firmware installed on the devices of a bus, each device should
be equipped with a bootloader program.  At power-on, the bootloader should be
started, sleep for 100ms and check afterwards, if the INT line is pulled down
(by a bus-master).  If INT is not pulled down, the bootloader is allowed to
start the main firmware.  Otherwise it should not attempt to start the main
firmware, but wait for commands received over the bus.  This procedure allows a
recovery from a broken firmware which does not process commands received over
the bus (such as the BOOTLOADER command).  To enter the bootloader via the
BOOTLOADER command, first pull down the INT line, then issue the command.

Initial sync sequence
---------------------

For the purpose of (automatic) address discovery and resetting the bus, a
sync sequence consisting of 15x ESC (0x1b) followed by an address byte is used.
The controller sends 15x ESC, followed by a null byte. Please note that the
length of the sync sequence (16 byte) is different from the length of a
command packet, which is 15 byte!

A device MUST be able to detect this sequence asynchronously, i.e. even if it is
in the middle of receiving a command packet. The device does not need to
withhold the execution of the received command packet until it is clear if a
sequence of ESC bytes constitutes a sync sequence.

Example:
  A device received 4 bytes, say A B C D, of a packet, then a sync sequence.
  It may (or may not) execute the packet

    A B C D ESC ESC ... ESC

  but the sync sequence must be detected, too.

Asynchronous detection of the sync sequence can be done by just counting the
number of consecutive ESC bytes, as 15 consecutive ESC bytes can only occur
in a sync sequence, never in a command packet (ESC cannot occur as a command in
the second byte of a command packet).

Flow:
* controller sends 15x ESC, followed by the address of the first device
  (usually a null)
* first device on the bus receives and retransmits 15x ESC, receives address
  byte, stores own address in ram, increments address byte and transmits new
  address to next device on the bus

Result: Each device on the bus knows it's own address (=position).


Commands
--------

Commands are sent in packets, 15 bytes in length, and are passed from device
to device unmodified.  The first byte is the destination address (0-254, 255
is broadcast), the second byte contains the command.  The meaning of the
following 13 bytes depends on the value of the second byte (command).  A
device ignores non-broadcast packets for which the address byte does not
match it's own address.  Bytes which are declared as "don't care" bytes SHOULD
be set to zero, this makes future extensions easier.


List of commands:
-----------------

command |   function        | description
--------------------------------------------------------
   0x01 | FADE_RGB          | set color/fade to color (RGB)
   0x02 | FADE_HSV          | set color/fade to color (HSV)
   0x03 | SAVE_RGB          | save color to EEPROM (RGB)
   0x04 | SAVE_HSV          | save color to EEPROM (HSV)
   0x05 | SAVE_CURRENT      | save current color to EEPROM
   0x06 | CONFIG_OFFSETS    | set global offset values
   0x07 | START_PROGRAM     | start program
   0x08 | STOP              | stop color changing
   0x09 | MODIFY_CURRENT    | modify current color
   0x0A | PULL_INT          | pull down INT line
   0x0B | CONFIG_STARTUP    | configure startup
   0x0C | POWERDOWN         | power down the device
   0x1B |                   | RESERVED (sync-sequence)

   0x80 | BOOTLOADER        | start bootloader
   0x81 | BOOT_CONFIG       | configure bootloader
   0x82 | BOOT_INIT         | initialize bootloader data buffer
   0x83 | BOOT_DATA         | store data in bootloader data buffer
   0x84 | BOOT_CRC_CHECK    | compare check-sum
   0x85 | BOOT_CRC_FLASH    | compare check-sum with flash
   0x86 | BOOT_FLASH        | write provided data to flash
   0x87 | BOOT_ENTER_APP    | start application


    Command: FADE_RGB - set color/fade to color (RGB) (0x01)
    --------------------------------------------------------

      Instruct the device to fade to the specified absolute color, given as a
      RGB value, with a specific speed.  Step and delay might be modified by
      application of the global offsets (see CONFIG_OFFSETS), color values
      (red, green, blue) are used unmodified.

      All values are unsigned 8 bit integers.  If step is set to 255, the target
      color is set without fading.  If delay is 0, the target color is set
      directly after receiving the command packet.

      byte offset | name  |  description
      ---------------------------------
                2 | step  | increment step for fading
                3 | delay | delay between steps when fading (in 10ms)
                4 | red   | red value
                5 | green | green value
                6 | blue  | blue value
             7-14 | -     | don't care


    Command: FADE_HSV - set color/fade to color (HSV) (0x02)
    --------------------------------------------------------

      Instruct the device to fade to the specified absolute color, given as a
      HSV value, with a specific speed.  Step, delay and hue might be modified
      by application of the global offsets, saturation and value might be scaled
      by global scales (see CONFIG_OFFSETS).

      All values are unsigned 8 bit integers, except hue, which is a 16 bit
      little endian integer.  If step is set to 255, the target color is set
      without fading.  If delay is 0, the target color is set directly after
      receiving the command packet.

      byte offset | name        |  description
      ------------------------------------
                2 | step        | increment step for fading
                3 | delay       | delay between steps when fading (in 10ms)
              4-5 | hue         | hue, 0-360, little endian
                6 | saturation  | saturation
                7 | value       | value (brightness)
             8-14 | -           | don't care


    Command: SAVE_RGB - save color to EEPROM (RGB) (0x03)
    -----------------------------------------------------

      Save a color in RGB format, a fade speed (step and delay) and a pause
      length to the EEPROM.  The EEPROM can store 60 color and speed/delay
      values (see section "EEPROM color storage" below).  While writing the
      data to the EEPROM, the INT line is pulled down.

      All values are unsigned 8 bit integers, except pause, which is a little
      endian 16 bit integer.

      byte offset | name  |  description
      ------------------------------
                2 | slot  | slot in the EEPROM (0-59)
                3 | step  | increment step for fading
                4 | delay | delay between steps when fading (in 10ms)
              5-6 | pause | time to wait before fading to next color (in 100ms)
                7 | red   | red value
                8 | green | green value
                9 | blue  | blue value
            10-14 | -     | don't care


    Command: SAVE_HSV - save color to EEPROM (HSV) (0x04)
    -----------------------------------------------------

      Save a color in HSV format, a fade speed (step and delay) and a pause
      length to the EEPROM.  The EEPROM can store 60 color and speed/delay
      values (see section "EEPROM color storage" below).  While writing the
      data to the EEPROM, the INT line is pulled down.

      All values are unsigned 8 bit integers, except pause and hue, which are
      little endian 16 bit integers.

      byte offset | name       |  description
      -----------------------------------
                2 | slot       | slot in the EEPROM (0-59)
                3 | step       | increment step for fading
                4 | delay      | delay between steps when fading (in 10ms)
              5-6 | pause      | time to wait before fading to next color
                  |            | (in 100ms)
              7-8 | hue        | hue, 0-360, little endian
                9 | saturation | saturation
               10 | value      | value (brightness)
            11-14 | -          | don't care


    Command: SAVE_CURRENT - save current color to EEPROM (0x05)
    -----------------------------------------------------------

      Save the current color to EEPROM (RGB), together with a fade speed and a
      pause length.  While writing the data to the EEPROM, the INT line is
      pulled down.

      All values are unsigned 8 bit integers, except pause, which is a little
      endian 16 bit integers.

      byte offset | name       |  description
      ----------------------------------------
                2 | slot       | slot in the EEPROM (0-59)
                3 | step       | increment step for fading
                4 | delay      | delay between steps when fading (in 10ms)
              5-6 | pause      | time to wait before fading to next color
                  |            | (in 100ms)
             7-14 | -          | don't care


    Command: CONFIG_OFFSETS - set global offset values (0x06)
    ---------------------------------------------------------

    Set global values which influence how fast and what colors are when using
    FADE_RGB or FADE_HSV or the static programs documented below.  All bytes are
    signed 8 bit integers, except hue, which is a little endian signed 16 bit
    integer.  Saturation and value are scales.  This means, the final saturation
    and value will be scaled with saturation/255 and value/255, respectively.

        byte offset | name          |  description
        -------------------------------------------
                  2 | step          | increment step for fading (offset)
                  3 | delay         | delay between steps when fading
                    |               | (in 10ms, offset)
                4-5 | hue           | hue offset, signed, little endian
                  6 | saturation    | saturation scale, unsigned, 0-255
                  7 | value         | value scale, unsigned, 0-255
               8-14 | -             | don't care


    Command: START_PROGRAM - start program (0x07)
    ---------------------------------------------

      Start a program (a function compiled into the firmware) with given
      parameters.  This command stops all other programs (and EEPROM fade
      sequences).  For a list of programs and parameters see section
      "Static Programs".

      byte offset | name    |  description
      ---------------------------------
                2 | program | program id, 0-255
             3-11 | params  | 10 byte parameters passed to program
            12-13 | -       | don't care


    Command: STOP - stop color changing (0x08)
    ------------------------------------------

      Stop all processes modifying the current color.  Optionally, also stop
      the current fading process.

      byte offset | name   |  description
      ---------------------------------
                2 | fade   | stop fading if set (1)
             3-14 | -      | don't care


    Command: MODIFY_CURRENT - modify current color (0x09)
    -----------------------------------------------------

      Instruct the device to fade to a new target color, which is determined
      relatively to the one currently visible.  This works only if no
      program (or EEPROM fade sequence) is running.  The current color is
      faded to the target color with the given step and delay values.  The
      RGB offsets are applied before the HSV offsets.  Setting either one to
      zero will not modify the color in that color space.

      Step and delay are unsigned 8 bit integers, all other values are signed 8
      (or 16) bit integers.

      byte offset | name        |  description
      -------------------------------------
                2 | step        | increment step for fading
                3 | delay       | delay between steps when fading (in 10ms)
                4 | red         | red offset
                5 | green       | green offset
                6 | blue        | blue offset
              4-5 | hue         | hue offset
                6 | saturation  | saturation offset
                7 | value       | value offset
             8-14 | -           | don't care


    Command: PULL_INT - pull down INT line (0x0A)
    ---------------------------------------------

      Instruct the adressed device to immediately pull down the INT line
      (connected to all devices in parallel) for a given amount of time.  This
      can be efficiently used to determine the number of devices listening to
      the bus (eg. by binary search).

      byte offset | name    |  description
      ---------------------------------
                2 | delay   | time to wait before releasing the INT line
                            | (in 50ms, maximum 2550ms, jitter +-10ms)
             3-14 | -       | don't care


    Command: CONFIG_STARTUP - configure startup (0x0B)
    --------------------------------------------------

      Configure what a device should perform after power-up.  Mode is an
      unsigned 8 bit integer, selecting the desired startup mode.

      Two different modes can be configured:

       * NOTHING (mode == 0):
                Do nothing after startup (ie do not show any color, stay black)

       * PROGRAM (mode == 1):
                Start a static program compiled into the firmware, using the
                following 10 bytes as parameters for the program (for details
                see section "Static Programs").

      If startup mode is PROGRAM, the CONFIGURE_STARTUP packet is constructed in
      this way (for the program indexes and meaning of the parameters see
      section "Static Programs"):

      byte offset | name       |  description
      ------------------------------------
                2 | mode       | desired startup mode (1 in this case)
                3 | program    | static program index
             4-14 | parameters | parameters to configured program


    Command: POWERDOWN - power down device (0x0C)
    ---------------------------------------------

      Power down the device.  After receiving this command, the device should
      power down all light outputs and suspend itself to reduce the power
      consumption to a minimum.  A device must resume operation after a falling
      edge on the INT pin.  It is allowed to activate a pull-up resistor on
      that pin, to pull the pin to a defined level.

      byte offset | name       |  description
      ------------------------------------
             2-14 | don't care |


    Command: BOOTLOADER - start bootloader (0x80)
    ---------------------------------------------

      If a bootloader is installed, jump to the bootloader, otherwise just
      restart the device.  A magic byte sequence of 0xfc27566b (little endian)
      must follow the command byte.  Pull down the INT line BEFORE sending this
      command, otherwise the bootloader might not be able to detect wether to
      start the main firmware or remain within the bootloader code.

      byte offset | name    |  description
      ---------------------------------
                2 | byte1   | magic byte 1 (0x6b)
                3 | byte2   | magic byte 2 (0x56)
                4 | byte3   | magic byte 3 (0x27)
                5 | byte4   | magic byte 4 (0xfc)
             6-14 | -       | don't care


    Command: BOOT_CONFIG - configure bootloader (0x81)
    --------------------------------------------------

      Set configuration options of the bootloader.  Currently the only
      options is the start address.

      Start address is an unsigned 16-bit integer automatically incremented
      by the configured packet size upon each flash command.

      byte offset | name         |  description
      ---------------------------------
              2-3 | start        | address to flash to
             4-14 | -            | don't care


    Command: BOOT_INIT - initialize bootloader data buffer (0x82)
    -------------------------------------------------------------

      This resets and clears the internal bootloader data buffer.

      byte offset | name    |  description
      ---------------------------------
             2-14 | data    | payload


    Command: BOOT_DATA - store data in the bootloader data buffer (0x83)
    --------------------------------------------------------------------

      Store data (up to 13 data bytes) at the end of the bootloader data
      buffer.  If the buffer is full, additional bytes will be ignored.

      byte offset | name    |  description
      ---------------------------------
             2-14 | data    | payload


    Command: BOOT_CRC_CHECK - compare check-sum (0x84)
    --------------------------------------------------

      Compare provided CRC-16 checksum against calculated checksum over the
      first len bytes of previously received data in the data buffer.  If both
      checksums do not match, the boot loader MUST immediately pull down the
      INT line.  The INT line must be held down for the time specified in delay
      and be released afterwards.  chksum is a little endian unsigned 16 bit
      integer.

      The CRC calculation uses the polynomial x^16 + x^15 + x^2 + 1 (0xa001)
      and 0xffff as initial value.

      byte offset | name    |  description
      ---------------------------------
              2-3 | len     | checksum over the first len buffer bytes in the buffer
              4-5 | chksum  | CRC-16 checksum over the data chunk
                6 | delay   | time to wait before releasing the INT line
                            | (in 50ms, maximum 2550ms, jitter +-10ms)
             7-14 | -       | don't care


    Command: BOOT_CRC_FLASH - compare check-sum with flash (0x85)
    -------------------------------------------------------------

      Compare provided CRC-16 checksum against calculated checksum over
      len bytes from flash starting at addr.  If both checksums do not match,
      the boot loader MUST immediately pull down the INT line.  The INT line
      must be held down for the time specified in delay and be released
      afterwards.  Chksum, start and len are unsigned 16 bit little endian
      integers.

      The CRC calculation uses the polynomial x^16 + x^15 + x^2 + 1 (0xa001)
      and 0xffff as initial value.

      byte offset | name    |  description
      ---------------------------------
              2-3 | addr    | checksum len bytes starting at this address
              4-6 | len     | checksum over the first len buffer bytes in the buffer
              6-8 | chksum  | CRC-16 checksum over the data chunk
                9 | delay   | time to wait before releasing the INT line
                            | (in 50ms, maximum 2550ms, jitter +-10ms)
            10-14 | -       | don't care


    Command: BOOT_FLASH - write provided data to flash (0x86)
    ---------------------------------------------------------

      Write the data provided earlier to the flash memory of the device.

      The device pulls the interrupt line down upon reception of the command
      and releases it on successful completion of the command.  This way the
      master node is capable of detecting when the successing initial data
      packet may be provided by continuously sensing the interrupt line.

      After completion of this command the address where to flash to is
      incremented by the configured chunk data size.


      byte offset | name    |  description
      ---------------------------------
             2-14 | -       | don't care


    Command: BOOT_ENTER_APP - start application (0x87)
    --------------------------------------------------

      Leave bootloader and launch application code.


      byte offset | name    |  description
      ------------------------------------
             2-14 | -       | don't care


Static Programs
---------------

This section describes the programs which are (in the default version)
compiled into the firmware.

    program "colorwheel", program index 0
    -------------------------------------

    This program fades throught the HSV color space, with fixed saturation and
    value, starting at hue_start.  In each step, hue_step is added to the
    current hue value, afterwards the device fades to the resulting color (using
    fade_step and fade_delay) and waits for the time specified by fade_sleep.

    If add_addr is nonzero (attention: signed integer!), the fade ist not
    started at hue_start but (hue_start + addr_add * device_address * hue_step).

    This program honors the globally defined offsets set with the CONFIG_OFFSETS
    command in each step.  If a global offset is reconfigured, the new value
    will be applied in the next color calculation of this program.

        byte offset | name       | description
        ---------------------------------------------------------
                  0 | fade_step  | used for fading to new color
                    |            |   (unsigned 8 bit integer)
                  1 | fade_delay | used for fading to now color
                    |            |   (unsigned 8 bit integer)
                  2 | fade_sleep | sleep between steps (in seconds)
                    |            |   (unsigned 8 bit integer)
                3-4 | hue_start  | start at this hue value
                    |            |   (unsigned 16 bit integer
                5-6 | hue_step   | add this to the hue in each step
                    |            |   (signed 16 bit integer)
                  7 | add_addr   | add hue_step address-times before start
                    |            |   (signed 8 bit integer)
                  8 | saturation | saturation
                    |            |   (unsigned 8 bit integer)
                  9 | value      | value
                    |            |   (unsigned 8 bit integer)


    program "random", program index 1
    ---------------------------------

    When this program is started, the pseudo-random generator is initialized
    with the seed value.  If the use_address-bit within the flags byte is set,
    the address of a device is XORed with the seed before initializing the
    pseudo-random generator, so the random values also depend on the address of
    the device.

    In each step, this program generates a new random hue value which is at
    least min_distance away from the current hue value.  Afterwards the device
    fades to the new color (using the generated hue and the configured
    saturation and value from the parameters).  The parameters fade_step and
    fade_delay are used in each fade.  If the wait_for_fade-bit within the
    flags byte is set, the device waits until the target color is reached.
    Afterwards the device sleeps for a configurable amount of time (fade_sleep).

    This program honors the globally defined offsets set with the CONFIG_OFFSETS
    command in each step.  If a global offset is reconfigured, the new value
    will be applied in the next color calculation of this program.

        flags (1 byte)
        --------------

        Bits:
        MSB         LSB
        7 6 5 4 3 2 1 0
        |         | | |
        |         | | \- use_address -- XOR device address and seed before
        |         | |    initializing the pseudo random generator
        |         | \--- wait_for_fade -- wait until the (newly generated)
        |         |      target color is reached before sleeping
        \---------+----- reserved

        byte offset | name         | description
        ----------------------------------------------------------------------
                0-1 | seed         | used for initializing the random generator
                    |              |   (unsigned 16 bit integer)
                  2 | flags        | flags
                    |              |   (unsigned 8 bit integer)
                  3 | fade_step    | used for fading to new color
                    |              |   (unsigned 8 bit integer)
                  4 | fade_delay   | used for fading to now color
                    |              |   (unsigned 8 bit integer)
                5-6 | fade_sleep   | sleep between steps (in 100ms)
                    |              |   (unsigned 16 bit integer)
                  7 | saturation   | saturation
                    |              |   (unsigned 8 bit integer)
                  8 | value        | value
                    |              |   (unsigned 8 bit integer)
                  9 | min_distance | minimal distance for new hue value
                    |              |   (unsigned 8 bit integer)


    program "replay", program index 2
    ---------------------------------

    The commands SAVE_RGB, SAVE_HSV and SAVE_CURRENT stores a color,
    fade_step, fade_delay and fade_sleep to the EEPROM.  This program replays
    the stored colors, from start to end.  If repeat is 0, the sequence stops
    when end is reached, if repeat is 1, the sequence is restarted from the
    beginning and if repeat is 2, the color sequence is played in reverse
    order, from end to beginning.

    This program honors the globally defined offsets set with the CONFIG_OFFSETS
    command for hsv colors in each step.  If a global offset is reconfigured,
    the new value will be applied in the next color calculation of this program.

        byte offset | name         | description
        ----------------------------------------------------------------------
                  0 | start        | index of the first color
                    |              |   (unsigned 8 bit integer)
                  1 | end          | index of the last color (included)
                    |              |   (unsigned 8 bit integer)
                  3 | repeat       | configure repetition
                    |              |   (unsigned 8 bit integer)
                4-9 | don't care   |
