Tag Firmware Design Rules

Entry Point for Custom Application

Your application has to be defined in the dedicated C and Headers files. 

The execution of the application is triggered via a callback handler which transfers control from the Sewio Main App to your application.

The application has a single callback handler that must have the following type:

typedef uint8_t (*app_t)(const app_data_t*);
  • The call-back function is executed every blink period.

  • Data from Server are delivered to the tag via the app_data_t structure. Here is a declaration from “app_data_handling.h”:
typedef struct __attribute__((packed, aligned(1))){
    uint16_t app_id;
    uint8_t len;
    uint8_t data[100];

Registering the APP Callback Function

The header file where is this function declared must be included into:

  • “{PROJECT_ROOT_FOLDER}/application/apps/app_handler.c”

In the same file, the function has to be registered into the array of implemented apps which has the following type:

typedef struct{
  uint16_t app_id;
  app_t app_pointer;
} app_struct;   

app_struct implemented_apps[NUM_OF_APPS];

The registration of the application is done in app_handler.c::app_initializer(). Both APP ID and callback pointer have to be assigned.

void app_initializer(void)
  implemented_apps[0].app_id = APP_LED_CTRL_ID;
  implemented_apps[0].app_pointer = (app_t)app_led_ctrl;
  implemented_apps[1].app_id = APP_SOME_APP_1_ID;
  implemented_apps[1].app_pointer = (app_t)app_some_app1;
  implemented_apps[2].app_id = APP_SOME_APP_2_ID;
  implemented_apps[2].app_pointer = (app_t)app_some_app2;

/*add other applications*/

Design Requirements

Following rules must be followed to ensure that the application works as expected and that the stability of FW is not compromised.

Code Architecture

Program flow is driven by events, we recommend using a state machine and non-blocking functions only.

FW is a bare-metal type, no RTOS. If interrupts have to be used within your application, there should be very limited execution time within the interrupt handler. We recommend only the flag assignment within the INT handler while functionality should be implemented within the state machine placed in the APP callback.

Execution Time

The FW has no provision to terminate the execution of any functions. So the execution time must be kept within the specified limits.

The execution time of the application callback should be as low as possible. In the worst case, it should take half of the assumed Refresh Interval/Blink Period. If the application execution time will be higher, it can have an impact on TDOA timing, and it also affects the effectiveness of random access to the channel.

If your application requires a longer execution time you need to decompose the flow via a state machine.

Once the execution time exceeds double of the Refresh Interval value, the FW is going to be unstable - MCU will be restarted by the watchdog timer!

Peripheral Access

The applications can access some peripherals. Some other peripherals are reserved for the system functions, and some can be used in limited mode. See the table.


Access mode



Free for use




Used by system



Use only via APP_timer


Free for use

Used by system



Do not use out of the app.

TWI0 (I2C0)

Free for use


TWI1 (I2C1)


Do not used devices with I2C adresses 0x0C, 0x19, 0x55, 0x68, 0x77. Do not change I2C settings.



Channel 0 and 1 are used by the system.

GPIOs (2, 8, 15, 16, 17, 18, 19, 20, 23, 24, 25, 26, 30)


Used by system

GPIO (5)


LED -it is also used by the system and other apps. Change only the state of the pin. Do not change any other settings of the pin.

GPIO (11)


Pushbutton - it is also used by the system. It’s pulled up input - do not change the pin settings.

GPIO (13)


Buzzer - this device is not mounted on the standard Leonardo Tags - reserved.

GPIO (28, 29)


I2C pins - use it only as I2C_1.

GPIOs ( 9, 12, 14, 22, 31)

Free use

Pins are routed to the

Other peripherals

Free use


The peripherals that have limited access (except RTC2) should be used via functions that are prepared in the peripheral.h. RTC2 can be only used through app_timer.h functions. All other peripherals can be used with their libraries and drivers from Nordic SDK.

Application Timer

If some asynchronous operations are needed (something that must be done at a specific time, no matter when this application will be called again) the application timer can be used. This timer is based on the RTC peripheral and it's shared between all applications that running on the tag. In case of the app need to do it, It passes the callback function (with the defined event) and the time when it should be done. Once the time elapses, the callback function will be called by the application timer handler. The execution of the callback should take as low time as possible, so as not to impact the timing of other tag functions.

An example of the use of the Application timer is in the Custom application tutorial.

On this page: