Common Firmware Library

The common library provides the functionality to send and receive wireless commands. It runs on attiny2313 processors, however, has been verified to run on Arduinos also with some minor tweaks.

The functionality is split into the following aspects:

Receive/Transmit via the Hardware USART

Provides lowest-level functionality to use the microcontroller’s built-in UART to send and receiver data from the RF modules. It is configured to run at 1200 BAUD which is the highest reliable setting for the RF Link modules in my testing.

Methods provided are:

  • void uart_initialize(uint8_t rxEnabled, uint8_t txEnabled)
    Initialize the UART individually enabled on the transmitter/receiver (pass 1 for on, 0 for off)
  • void uart_send(uint8_t data)
    Send a byte through the UART to the attached transmission module
  • uint8_t uart_has_bytes(void)
    Check whether a byte is available in the input buffer (non-blocking)
  • uint8_t uart_receive(void)
    Return the next received byte (blocking)

Files: uart.h, uart.c

The Wireless Protocol

Providing the next level above the UART transport, these functions define the wireless protocol for transmitting and receiving data. Parsers and Serializers are provided and 13-bit checksums are automatically enforced. The protocol further implements the concept of idempotent request IDs. These request IDs are used to distinguish between two identical commands being sent on purpose or as part of a re-send functionality. If two correctly received subsequent commands with the same request ID are received, the second one is ignored.

The wireless protocol is defined by the following byte structure:

+-------------------------+--------------------------------------------------+-------+
| Pre Header              | Header                                           | Data  |
+------------+------------+--------+------------+---------------+------------+-------+
| PHDR       | SBT        | ADR    | CMD        | INF           | CHK        | DT    |
+------------+------------+--------+------------+---------------+------------+-------+
| pre-header | start byte | source | command &  | DT Len, ACK,  | checksum   | dat a |
|            |            | target | request id | checksum      |            | (0-4) |
+------------+------------+--------+------------+---------------+------------+-------+

A pre-header byte (0×0) is transmitted multiple times to tune in the receiver. This is followed by a start-byte (0xa) and the following fields:

  • ADR: The source (upper four bits) and target (lower four bits) address
  • CMD: The ID of the command to be sent (see below) and the request Id
  • INF: Contains the number of data bytes to expect (upper two bits), a reserved bit, as well as the upper 5 bits of the checksum
  • CHK: The lower 8 bits of the checksum
  • DT: Zero to Four data bytes (per data size in INF field)

Device IDs are currently 4-bit values, allowing a total of 16 devices. 0×0 is reserved for broadcast (command to everyone), 0xF is reserved for the master device. This essentially leaves 14 remote sensors in this version of the protocol.

The transmission further allows the declaration of two function pointers for functionality to be called before and after a transmission of  a command is passed to the UART. This is used by the firmwares to enable and disable the transmission circuits.

The following methods are provided:

  • uint8_t parse_incoming(uint8_t incoming)
    Process an incoming byte. Returns 0 if this yielded nothing, returns 1 if a ready and valid command object can be retrieved via the currentCommand variable. Notice that this returns 1 for every valid command and does not filter by the device ids (see command processor below)
  • void assemble_command(uint8_t command, uint8_t requestId, uint8_t targetAddress, uint8_t dataSize, uint8_t data[])
    Assemble a command into the outgoingCommand structure. The current device id is also autmatically emdedded into the command object.
  • setPreSendHook(ptr)
    Set the pre send hook
  • setPostSendHook(ptr)
    Set the post send hook

Files: protocol.h, protocol.c

The Command Set

The data packets defined by the protocol contain a unique command, much like an op-code that tells the receiving device what to do with this command. The following commands are currently implemented (there’s space for 5 more in the 4-bit address range!):

  • ON
    Turn on a port on a remote device. Expects one data byte containing the port number.
  • OFF
    Turn off a port on a remote device. Expects one data byte containing the port number.
  • PULSE
    Pulse (quick on and then off) a port on a remote device. Expects two data bytes, one containing the port number, the second the interval to pulse. This command relieves the need for sending separate ON and then OFF commands (and hence eliminates of the chance of one getting lost).
  • TX32
    Transmit up to four bytes of data. It’s up to the receiver and sender to interpret the data.
  • PING
    Ping a device (which would require TX and RX capabilities to respond)
  • PONG
    Respond to a PING
  • LOCK
    Lock a remote device. No data required.
  • UNLOCK
    Un-lock a remote device. No data required.
  • ACK
    Acknowledge the receipt of a command (currently not implemented)
  • RESET
    Resets the target device. It’s up to the target firmware to implement this (e.g. turn off all attached devices, etc).

Files: commands.h

The Receiver Command Filter

This is a very simple command processor shell which handles incoming commands. It filters out commands not meant for this device (e.g. in slave mode handles only commands addressed to this device and broadcast commands). The command processor further implements the idempotent request filtering by request ids. Ff two subsequent commands with the same request id are received, the latter is ignored.

The command processor requires the caller to register a function pointer to a command handler which is called if the filtering logic lets the command pass. It further filters out all commands except for UNLOCK commands if the device is in LOCKED state. Note that this functionality is strictly a filter – it will not set or unset the lock state of the device!

Methods provided are:

  • void handleCommand(struct Command *aCmd)
    Filter an incoming command and call the handling pointer if it passes the filter.
  • setCommandHandler ( (*void)(struct Command *) )
    Set the command handler function pointer

Files: commandFilter.h, commandFilter.c

Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!