Skip to content

Firmware Reference Guide

Warning

This documentation is a work in progress. We are regularly updating the content, and appreciate any feedback through the GitHub repo. We expect to have this documentation ready for usage in production products in December 2018.

Afero

"Afero" is a class that is instantiated in firmware as as afero. This class is used to handle the communication between the host microcontroller and the Afero secure radio module using the Aflib C library.

While Aflib supports both SPI and UART communication protocols, the Afero class utilizes only the SPI bus. This is due to the faster data rates of SPI over UART.

You can use the Aflib or Aflib2 or Aflib3 library directly. However, since the Afero class is instantiated by the Cocoa firmware, be sure to call the class destructor first.

1
delete afero;

You can access the SPI bus with the SPIClass instance asrSPI. The chip select for the ASR is on IX and the interrupt request pin is on IY.

afero

afero is the pre-instantiated in the Cocoa firmware. Do not create another instance of Afero as that will collide with the threading of the state machine.

Here's a summary of what happens upon instantiation:

  1. Allocate memory on the heap for a struct dedicated to handling communication between the MCU and ASR.
  2. Instantiate a SPI instance within the transport layer.
  3. Create an instance of the Afero state machine.
  4. Set a timer with callback to 1 second. This allows for a regular flush of the Afero state machine.

Here's the Afero constructor.

1
2
3
4
5
6
7
Afero::Afero()
{
  af_transport_t * af_transport = new af_transport_t();
  af_transport->asrSPI = new SPIClass(HAL_SPI_INTERFACE5); // Use HAL_SPI_INTERFACE5 for ASR SPI communication
  af_lib = af_lib_create(attrSetHandler,attrNotifyHandler, af_transport);
  af_transport->asrTimer = new Timer(1000,af_callback); // default to 1000 ms aflib_loop
}

encryptionSettings()

Use this method to set and configure the encryption of the host MCU image, Afero attribute data, or both.

By default, all encryption is deactivated. You only need to utilize this method if you would like to enable encryption.

Syntax

1
2
3
afero.encryptionSettings(af_ota_encrypt(const char* password), af_attr_encrypt(const char* password));
afero.encryptionSettings(af_ota_encrypt(const char* password));
afero.encryptionSettings(af_attr_encrypt(const char* password));

Arguments

af_ota_encrypt: af_ota_encrypt is a class that contains information regarding the encryption of the host MCU image for over-the-air (OTA). The argument to this class is an AES-256 encryption password. Passing this class to encryptionSettings() with a value other than NULL enables AES-256 decryption of the OTA host image.

Note

Enabling af_ota_encrypt() does not encrypt your host MCU image. This simply provides the required password for decryption upon an update. You can encrypt the host MCU image directly from the Cocoa IDE. More information on encrypting your host MCU image is available here.

Enable this encryption setting if you plan on encrypting the host MCU image for a host MCU update. The password is used to decrypt the image. Use the same password in the Cocoa IDE when encrypting the host MCU image.

The value of encrypting the host MCU image for an OTA update is to prevent any potential security threats by probing the data lines of your product during an OTA image. By encrypting the host image the hacker will have no way to access your source code. Afero has secured the path between the the cloud and the afero secure radio. This secures the path between the Afero secure radio and the host MCU.

More information on securing your source code through host MCU encryption can be found [here].

af_attr_encrypt(): af_attr_encrypt() is a class that contains information regarding the encryption of the data provided to Afero attributes. Enable attribute encryption by passing a non-NULL password as an argument to the __af_attr_encrypt() class.

Afero already encrypts this information from the ASR to the cloud. This encryption is only useful if you are concerned about hackers attempting to reconcile data transferred to the host MCU by probing the data lines with a function generator or oscilloscope.

Encrypt host MCU image and attributes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Macro to easily enable or disable encryption
//#define TESTING_AF

// Instantiating ota and attr encryption class
#ifdef TESTING_AF // if testing, disable encryption
otaSettings af_ota_encrypt(NULL);
attrSettings af_attr_encrypt(NULL);
#else
otaSettings af_ota_encrypt("thisIsABadPassword");
attrSettings af_attr_encrypt("anotherBadPassword");
#endif

int main()
{
  // pass encryption settings to afero
  afero.encryptionSettings(otaSettings,attrSettings)
  while(1)
  {
    // Application here
  }
}
Encrypt host MCU image only
1

Encrypt attribute data only
1


set()


notify()

break

Input/Output

Arduino compatible GPIO functionality. Includes digital and analog functionality. Protocol specific functionality (e.g. SPI) is defined elsewhere.

pinMode()

Configure a single pin as an input, output, or input with internal pullup.

Syntax

1
pinMode(pin,mode); // Syntax

Arguments

pin: The pin number you wish to control (i.e. A3). See the pinout in the Hardware Reference for more information. mode: INPUT, OUTPUT, or INPUT_PULLUP. Not all pins can be configured with INPUT_PULLUP. See the Hardware Reference for more information.

Returns Nothing (void).

pinMode() Example
1
2
3
4
5
6
7
8
9
int main()
{
  pinMode(D3,OUTPUT);         // Set D3 as output pin
  while(1)
  {
    // Do stuff with D3 as an output
  }
  return 0;
}

digitalRead()

Read the value from a specified digital pin. Returns HIGH or LOW.

Syntax

1
digitalRead(pin); // Syntax

Arguments pin: The pin number you wish to control (i.e. D2). See the pinout in the Hardware Reference for more information.

Returns HIGH or LOW

digitalRead() Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Example
#define button D2             // button input tied to pin D2

int main()
{
  uint8_t button_val;         //
  pinMode(button,INPUT);      // Set button pin to input
  while(1)
  {

    button_val = digitalRead(pin);  // Read if button is pressed

    if (button_val == HIGH)
    {
      // do something if button val == HIGH
    }
    else
    {
      // do something if button val != HIGH
    }
    delay(1000);
  }

}

digitalWrite()

Write HIGH or LOW to a GPIO pin.

Syntax

1
  digitalWrite(pin,value); // Syntax

Arguments

pin: const of type pin_t. pin_t is of type uint16_t. Arduino compatible pin number (i.e. D2). See the pinout in the Hardware Reference

value: macro HIGH or LOW

Returns

void


analogRead

Warning - Not Currently Available


analogReference()

Warning - Not Currently Available


analogWrite()

Warning - Not Currently Available

break

SPI

A class for utilization of the available SPI buses available.

MOSI MISO SCK
SPI D15 D16 A4
SPI1 A7 A6 D5

Note, you can use any available GPIO as a chip select with pinMode() and digitalWrite.

begin()

Syntax

1
2
  SPI.begin();
  SPI.begin(cs);

Arguments

void: With a void argument, no chip select is enabled. Make sure you call pinMode(cs,OUTOUT); and digitalWrite(cs,state); to control the chip select.

cs - uint16_t: Used to define the chip select pin. This simply runs pinMode(cs,OUTPUT);. You will still need to manually control the chip select pin with digitalWrite(cs,state);.


end()


setBitOrder()

Set the bit order of SPI transmissions on the chosen SPI bus as either most significant bit first (MSBFIRST) or least significant bit first (LSBFIRST). This is an optional method. By default, each SPI bus will run with MSBFIRST.

Syntax

1
2
3
#define SPI_Bit_Order LSBFIRST  // Alternative def as MSBFIRST
SPI.setBitOrder(SPI_BIT_ORDER); // Set bit order on 'SPI' instantiation
SPI1.setBitOrder(LSBFIRST);     // Set bit order on 'SPI1' instantiation

Arguments

Bit Order - macro: The bit order is set using a single argument, MSBFIRST or LSBFIRST. These macros are defined in firmware, do not redefine either of these macros.


setClockSpeed()


setClockDivider()


setClockDividerReference()


setDataMode()


transfer()


transferCancel()


onSelect()


available()


beginTransaction()


endTransaction()


SPISettings

break

Wire

A class for utilization of the available I2C two-wire buses.

There are two (2) I2C busses available. Custom firmware implementations may be restructured to include more or less I2C buses.

SCK SDA
Wire (I2C0) D21 D12
Wire1 (I2C1) D10 D11

available()

Check if there are any bytes to be read in the I2C receive buffer.

Syntax

1
2
  Wire.available();  // Check availability of data within the buffer of the I2C0 bus.
  Wire1.available(); // Check availability of data within the buffer of the I2C1 bus.

Arguments

void: This method does not contain any arguments. Make sure you are checking availability on the respective bus.

Returns

int: 0 if no data available in buffer, and 1 if the buffer is ready to be read.


setSpeed()

Warning - Not Currently Available

Set the clock speed of the specified I2C bus to either 100kHz or 400kHz.

Syntax

1
2
3
  #define I2C_CLOCK_SPEED I2C_CLOCK_100KHZ // Alternatively, use I2C_CLOCK_400KHZ
  Wire.setSpeed(I2C_CLOCK_SPEED);  // Set I2C clock speed on Wire (I2C0) bus.
  Wire1.setSpeed(I2C_CLOCK_SPEED); // Set I2C clock speed on Wire (I2C1) bus.

Arguments

I2C Bus Clock Speed - macro: Utilize either I2C_CLOCK_100KHZ or I2C_CLOCK_400KHZ macros for 100kHz and 400kHz clock speed, respectively.


stretchClock()

Warning - Not Currently Available


begin()

Enable a Wire instance. This will set the respective pins to the correct mode and initialize a buffer for reading and writing.

This method can be used to enable the I2C bus as either a master or slave device. If the function is called without any arguments, the bus will be set up as a master device. If the method is called with an argument, it will be set up as a slave with that address.

Syntax

1
2
3
4
  Wire.begin();        // Enable the I2C0 bus as a master
  Wire1.begin();       // Enable the I2C1 bus as a master
  Wire.begin(ADDR);    // Enable the I2C0 bus as a slave with address ADDR.
  Wire1.begin(ADDR);   // Enable the I2C1 bus as a slave with address ADDR.

Arguments

void: With a void argument, the I2C bus will be enabled in master mode in order to communicate with other slave devices.

Slave Address - uint8_t:

Returns

void


end()

Method to disable the I2C bus and release the pins for other use.

Syntax

1
2
  Wire.end();        // Disable the I2C0 bus.
  Wire1.end();       // Disable the I2C1 bus.

Arguments

void

Returns

void


isEnabled()

Method to check if the I2C bus has been enabled with the begin() method.

Syntax

1
2
  Wire.isEnabled();   // Check if I2C0 bus is enabled
  Wire1.isEnabled();  // Check if I2C1 bus is enabled

Arguments

void

Returns

Enablement of I2C Bus - bool TRUE if I2C bus is enabled, FALSE if not.

Wire.isEnabled() Example
1
2
3
4
5
6
7
8
9
// Example
bool I2C0_enabled;

I2C0_enabled = Wire.isEnabled();

if (I2C0_enabled)
{
  // Do things
}

reset()


beginTransmission()

Flush the I2C buffers and prepare the bus for a new transmission. The user can then use the write() method to queue bytes for transfer.

Note: This does not actually perform any communication on the I2C bus. It just prepares for a new line of communication along the I2C bus.

Syntax

1
2
  Wire.beginTransmission(ADDR);  // Begin new transmission on I2C0 to slave device with address, ADDR
  Wire1.beginTransmission(ADDR); // Begin new transmission on I2C1 to slave device with address, ADDR

Arguments

Slave Address - uint8_t: The 7-bit address of the slave device with which you intend to send data.

Returns

void


endTransmission()


write()


read()


peek()


onReceive()


onRequest()

break

Serial

begin()


end()


available()


availableForWrite()


write()


read()


print()


println()


flush()

break