# Introduzione

Benvenuto nella documentazione di Trackle

## Cosa è Trackle

**Trackle** è la **piattaforma IoT All-Inclusive** che ridefinisce il modo in cui le aziende possono affrontare l'Internet of Things (IoT), offrendo strumenti dedicati alla gestione dei dispositivi IoT, alla sicurezza, all'automazione e alla creazione di applicazioni.

Con Trackle **chiunque può connettere il proprio hardware al cloud** e creare soluzioni innovative, applicazioni web e mobile per analizzare i dati in tempo reale dai dispositivi, controllarli da remoto da qualsiasi parte del mondo, ricevere notifiche e molto altro.&#x20;

## Componenti di Trackle

Ogni componente di Trackle è progettato per offrirti un'**esperienza di gestione IoT** senza precedenti, consentendoti di prendere decisioni basate su dati e di ottenere risultati concreti.

### Trackle Library

Si parte dalla libreria C/C++, *hardware agnostica,* che **integra le** [**funzionalità IoT**](/trackle-library/funzionalita-cloud) **nel tuo dispositivo** conferendo un **livello di sicurezza elevato** e una **protezione completa dei tuoi dati sensibili**. Sia che tu stia gestendo dispositivi industriali o oggetti connessi a scopo personale, Trackle si impegna a garantire che la tua comunicazione sia sempre al sicuro.

### Trackle Cloud

Nel cuore di Trackle c'è **un'infrastruttura Cloud, progettata per scalare con il tuo business**, che alimenta i micro servizi specifici per la comunicazione, la gestione dei dispositivi, la sicurezza, il monitoraggio, gli [aggiornamenti OTA](/trackle-cloud/aggiornamenti-firmware-ota), le [automazioni](/trackle-cloud/integrazioni) e l'integrazione tramite [API REST](/trackle-cloud/cloud-api) potenti per **modellare l'esperienza IoT in base alle tue esigenze specifiche**.

### Trackle Console

<figure><img src="https://2788415725-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mc8MVXhOCSERBlDiYfv%2Fuploads%2Fwn7zYLZZEs21srItqs0X%2FSchermata%202023-08-24%20alle%2019.01.48.png?alt=media&amp;token=e151c6a6-6bd2-412b-85ef-0b1c2ca2ed5a" alt=""><figcaption></figcaption></figure>

La nostra avanzata console di gestione ti dà l'accesso totale ai tuoi dispositivi IoT. Organizza, monitora e **gestisci la tua flotta di dispositivi con facilità**. La console offre [strumenti per sviluppatori](broken://pages/vm30f2VYFCG66u6gHZZF) e il controllo completo dell'intera rete di dispositivi, suddivisa in gruppi, [prodotti](broken://pages/mJMG8sCzwIfY6y0uU6a4) e organizzazioni con funzionalità avanzate di debug, logging a analisi dello stato di salute.


# Dispositivo

Un dispositivo è un hardware basato su MCU (ad es. NodeMCU, Arduino, Prodino, Raspberry Pi, ecc.) oppure un prodotto fisico finito come un termostato o una VMC.

## Autenticazione

Ogni dispositivo su Trackle ha:

* un **deviceID** di 12 byte che lo identifica in maniera univoca che può essere generato dal Cloud oppure deciso in fase di produzione
* una coppia di chiavi pubblica / privata in formato **RPK** (*Raw Public Key*) per l'autenticazione del dispositivo
* la **chiave pubblica** del Cloud per l'autenticazione del server cloud

## Proprietario

Quando connetti un dispositivo al Cloud per la prima volta, questo non avrà alcun **proprietario** quindi nessuno, eccetto nel caso di un dispositivo associato ad un **Prodotto**,  avrà il permesso di controllarlo. Per poter associare un Dispositivo ad un account e poterlo quindi monitorare e controllare è necessario effettuare la **procedura di claim**, direttamente dalla Console o tramite un codice di claim. Dopo questa operazione solo quell'account avrà il permesso di controllare il dispositivo.

## Permessi

Il permesso di accedere alle informazioni di un dispositivo è concesso al proprietario e, nel caso di un dispositivo associato ad un **Prodotto,** agli account autorizzati a gestirlo. Possono accedere al dispositivo anche ad App di terze parti tramite autenticazione **OAuth**.


# Prodotto

I dispositivi possono essere parte di un **Prodotto**. Un prodotto identifica un gruppo di dispositivi con lo stesso hardware e le stesse funzionalità. Immagina dei termostati intelligenti connessi: svolgono tutti una funzione simile e possiamo presumere che abbiano lo stesso hardware, firmware, GPIO, ecc... per questo vengono identificati dal Cloud attraverso un **productID** comune. In questo modo se si vogliono fare modifiche al firmware di tutti i dispositivi si rilascia un aggiornamento OTA di prodotto, aggiornando così l'intera flotta.

<figure><img src="https://2788415725-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mc8MVXhOCSERBlDiYfv%2Fuploads%2FjzbcaTZKxeE7qDtdVRlo%2FSchermata%202023-09-04%20alle%2015.39.09.png?alt=media&amp;token=70944964-a120-4b87-9469-1565d9fa8cfe" alt=""><figcaption></figcaption></figure>

Un prodotto appartiene ad un organizzazione ed ha un proprietario, colui che l'ha creato, che risulta l'amministratore di prodotto. Ogni prodotto può avere un **Team** di più utenti che possono accedere alle informazioni dei dispositivi limitando i permessi di accesso attraverso quattro ruoli: *amministratore, sviluppatore, manutentore* e *sola lettura*.

{% hint style="info" %}
Un utente può anche essere parte del team di organizzazione e questo gli da visibilità su tutti i prodotti di quell'organizzazione.
{% endhint %}

I dispositivi che sono parte di un prodotto non devono avere per forza un proprietario per essere gestiti. E' possibile comunque associare un proprietario per es. uno sviluppatore del prodotto oppure rendere proprietario un *customer* attraverso il processo di claim da una App.

Inoltre è possibile definire i prodotti come parte di un gruppo alla creazione del prodotto oppure in seguito, aggiungendo un dispositivo alla volta oppure aggiungendo un batch di dispositivi.

### Configurazione del Prodotto

Durante la creazione o la modifica di un prodotto, è possibile configurare diversi parametri per personalizzare il comportamento dei dispositivi:

<figure><img src="https://2788415725-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mc8MVXhOCSERBlDiYfv%2Fuploads%2FsVimvERQwJPhM2cdFjWz%2FScreenshot%202025-12-12%20alle%2016.31.58.png?alt=media&amp;token=c3f09268-d107-4d0f-9556-f3a0d92059dd" alt="" width="375"><figcaption></figcaption></figure>

Un prodotto è configurabile attraverso le seguenti opzioni:

* **Nome e Descrizione**: identificano il prodotto all'interno dell'organizzazione
* **Dispositivi non riconosciuti**: definisce se i nuovi dispositivi vengono messi in quarantena o approvati automaticamente
* **Aggiornamenti OTA**: abilita i controlli di sicurezza per gli aggiornamenti firmware over-the-air

**Importante**: Se si abilita l'opzione "Abilita invio della firma di sicurezza con l'aggiornamento OTA", è necessario inserire una chiave privata in formato PEM. Questa chiave viene utilizzata per firmare digitalmente il firmware durante gli aggiornamenti OTA, garantendo l'autenticità e l'integrità degli aggiornamenti distribuiti ai dispositivi.


# Utenti

Per iniziare ad utilizzare Trackle è necessario creare un account. Un account corrisponde ad un *utente* ed è identificato da un indirizzo email. Generalmente un utente di Trackle può essere:

* uno sviluppatore firmware del dispositivo connesso
* uno sviluppatore software delle Applicazioni che interagiscono con il dispositivo
* un responsabile tecnico di un prodotto

## Customer

Un customer è generalmente un utente di un Applicazione, cioè parte di un'anagrafica esterna a Trackle, che però ha bisogno di avere dispositivi associati e poter eseguire azioni di controllo.

Un customer viene identificato in Trackle attraverso un codice univoco condiviso con l'Applicazione e può essere definito a livello di Prodotto oppure di Organizzazione. Le Applicazioni che devono ottenere un accesso per autorizzare un customer dovranno richiedere un token di autorizzazione attraverso un client OAuth predefinito a livello di prodotto o organizzazione.

<figure><img src="https://2788415725-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mc8MVXhOCSERBlDiYfv%2Fuploads%2FocslFrsAIjYTbg2SAW9T%2FSchermata%202023-09-04%20alle%2015.36.14.png?alt=media&amp;token=ee6aa9c8-3d96-493d-8d95-a5fa63ab3265" alt=""><figcaption></figcaption></figure>


# Organizzazione

Un'Organizzazione rappresenta un'entità di livello superiore che può possedere molti prodotti, avere un team di utenti e dei *customer* che possono avere accesso a dispositivi di prodotti diversi, per es. nel caso di utenti di un'App multi prodotto.

<figure><img src="https://2788415725-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mc8MVXhOCSERBlDiYfv%2Fuploads%2FUgsXg9JzgEQzCvyODUae%2FSchermata%202023-09-04%20alle%2015.48.00.png?alt=media&amp;token=2620cba8-530b-44e7-b486-ffc123e45875" alt=""><figcaption></figcaption></figure>

Ciascuna Organizzazione dispone di un team e ciò è molto utile per i membri della tua azienda che dovrebbero avere visibilità su tutte le attività IoT della tua organizzazione. Ogni utente nel team di organizzazione avrà accesso a tutti i Prodotti ereditando il ruolo assegnato nel team di organizzazione. Con una singola azione puoi autorizzare un utente a gestire molti Prodotti.

Anche per quanto l'autenticazione e autorizzazione alle API REST di Prodotto, creando un client OAuth a livello di Organizzazione, un software esterno può ottenere un token di organizzazione che lo autorizza ad accedere alla risorse di tutti i Prodotti dell'organizzazione.


# Panoramica

Trackle Library

Trackle Library è una **libreria software C/C++** che consente agli sviluppatori di connettere facilmente i propri dispositivi a Trackle Cloud e di sfruttare tutte le sue funzionalità trasformando la complessità della gestione IoT in un'esperienza intuitiva e sicura.

## Caratteristiche

### Hardware agnostic

Trackle Library è **hardware agnostica**. Indipendentemente dal tipo di dispositivo che vuoi connettere, la nostra libreria si adatta senza sforzi, offrendoti un'**interoperabilità senza limiti e** semplificando il processo di implementazione.&#x20;

### Comunicazione sicura

Alla base di Trackle c'è una **comunicazione sicura e affidabile**. La nostra **libreria C/C++** agisce da ponte tra i dispositivi e il cloud. Attraverso il **protocollo DTLS**, garantiamo che i tuoi dati sensibili viaggino in modo protetto, aprendo nuovi orizzonti di connettività senza compromessi.

{% hint style="info" %}
**DTLS** è il protocollo di sicurezza consigliato da **GSMA** per le applicazioni IoT che usano le reti mobile di tipo **LTE-M** e **NB-IoT**. [Link](https://www.gsma.com/iot/wp-content/uploads/2019/09/Security-Features-of-LTE-M-and-NB-IoT-Networks.pdf)
{% endhint %}

### Funzionalità integrate

Trackle Library mette a disposizione degli sviluppatori **un'API ricca di funzionalità**, che consente di scrivere applicazioni IoT per il dispositivo, aprendo le porte a un mondo di innovazione e creatività.

### Aggiornamenti OTA

Attraverso gli aggiornamenti OTA, hai la capacità di **implementare nuove funzionalità software** in un prodotto anche dopo che un dispositivo è stato distribuito sul campo. Questo ti permette di **migliorare costantemente le sue funzionalità** nel corso del tempo e di correggere eventuali bug, il tutto senza dover ricorrere a richiami fisici.

### Open Source

La libreria Trackle è rilasciata **Open Source con licenza LGPL 3.0**, poiché desideriamo offrire totale trasparenza e consentire agli utenti di accedere ai sorgenti senza alcuna preoccupazione, permettendo loro di visionarla approfonditamente e adattare rapidamente la libreria alle proprie esigenze specifiche.


# Primo utilizzo

Per iniziare ti invitiamo a dare un'occhiata al repository GitHub della Trackle Library dove mettiamo a disposizione la documentazione aggiornata della libreria e di come installarla ed utilizzarla:

{% embed url="<https://github.com/trackle-iot/trackle-library-cpp#trackle-library>" %}

## Progetti firmware d'esempio&#x20;

Puoi partire anche da un progetto d'esempio scegliendo tra i nostri *boilerplate* creati per differenti piattaforme hardware / framewor&#x6B;*:*

<table data-view="cards" data-full-width="false"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>EspressIf ESP-IDF</strong></td><td>Template per dispositivi basati su MCU EspressIf  ESP32 compatibili con il framework ESP-IDF.</td><td></td><td><a href="https://github.com/trackle-iot/trackle-boilerplate-esp-idf">https://github.com/trackle-iot/trackle-boilerplate-esp-idf</a></td></tr><tr><td><strong>STM32 Nucleo</strong></td><td>Template per dispositivi basati su processori STM32 NUCLEO per l'ambiente di sviluppo STM32CubeIDE</td><td></td><td><a href="https://github.com/trackle-iot/trackle-boilerplate-stm32">https://github.com/trackle-iot/trackle-boilerplate-stm32</a></td></tr><tr><td><strong>MbedOS</strong></td><td>Template per dispositivi compatibili con il framework MbedOS supportato da diverse famiglie di MCU </td><td></td><td><a href="https://github.com/trackle-iot/trackle-boilerplate-mbedos">https://github.com/trackle-iot/trackle-boilerplate-mbedos</a></td></tr></tbody></table>

## Esempio guida per POSIX

Puoi utilizzare questo esempio guida di un firmware C/C++ che implementa Trackle, funzionante su sistemi operativi POSIX (macOS e Linux in particolare), per comprendere come configurare la connettività e come utilizzare le funzionalità della libreria.

{% embed url="<https://github.com/trackle-iot/trackle-library-cpp/tree/release/v4/example/posix>" %}


# Configurazione

La configurazione minima per implementare un client Trackle è la seguente:

{% tabs %}
{% tab title="C" %}

```c
#include <trackle_interface.h>

int main(int argc, char *argv[]) {
    Trackle *trackle_s = newTrackle();
    // Inizializzazione
    trackleInit(trackle_s);
    trackleSetMillis(trackle_s, get_millis_cb);
    // Autenticazione
    trackleSetDeviceId(trackle_s, DEVICE_ID);
    trackleSetKeys(trackle_s, PRIVATE_KEY);
    // Configurazione del socket di comunicazione 
    trackleSetSendCallback(trackle_s, send_cb_udp);
    trackleSetReceiveCallback(trackle_s, receive_cb_udp);
    trackleSetConnectCallback(trackle_s, connect_cb_udp);
    trackleSetDisconnectCallback(trackle_s, disconnect_cb);
    // Connessione
    trackleConnect(trackle_s);
    // Loop
    while (1)
    {
        trackleLoop(trackle_s);
        ...
        // Application Loop
        ...
	usleep(20 * 1000);
    }
    return 0;
}
```

{% endtab %}

{% tab title="C++" %}

```cpp
#include <trackle.h>

int main(int argc, char *argv[]) {
    Trackle trackle;
    // Inizializzazione
    trackle.setMillis(get_millis_cb);
    // Autenticazione
    trackle.setDeviceId(DEVICE_ID);
    trackle.setKeys(PRIVATE_KEY);
    // Configurazione del socket di comunicazione 
    trackle.setSendCallback(send_cb_udp);
    trackle.setReceiveCallback(receive_cb_udp);
    trackle.setConnectCallback(connect_cb_udp);
    trackle.setDisconnectCallback(disconnect_cb);
    // Connessione
    trackle.connect();
    // Loop
    while (1)
    {
        trackle.loop();
        ...
        // Application Loop
        ...
        usleep(20 * 1000);
    }
    return 0;
}
```

{% endtab %}
{% endtabs %}

## Inizializzazione

### Trackle.setMillis()

Imposta una callback che ritorna il numero di millisecondi da cui il software è in esecuzione.

{% tabs %}
{% tab title="C" %}

```c
static system_tick_t get_millis_cb(void) {
    struct timeval tp;
    gettimeofday(&tp, NULL);
    long int ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
    return (uint32_t)ms;
}

trackleSetMillis(trackle_s, get_millis_cb);
```

{% endtab %}

{% tab title="C++" %}

```cpp
static system_tick_t get_millis_cb(void) {
    struct timeval tp;
    gettimeofday(&tp, NULL);
    long int ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
    return (uint32_t)ms;
}

trackle.setMillis(get_millis_cb);
```

{% endtab %}
{% endtabs %}

## Autenticazione &#x20;

Per ogni dispositivo che si vuole connettere a Trackle è necessario possedere un **ID Dispositivo** e una **chiave privata**.&#x20;

Per ottenere un ID dispositivo e una chiave privata puoi seguire questi passaggi:

* Crea un account su Trackle Cloud attraverso la *Console* (<https://trackle.cloud/>)
* Dalla pagina "Dispositivi" clicca sul bottone "Claim di un dispositivo"
* Clicca sul link "Non hai un ID dispositivo?", poi su continua
* L'ID Dispositivo verrà mostrato sullo schermo e il file della chiave privata verrà scaricato automaticamente dal browser con il nome \<id\_dispositivo>.der&#x20;

<figure><img src="https://2788415725-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mc8MVXhOCSERBlDiYfv%2Fuploads%2FyfOeR7X9gZPoWwYVUfTf%2FSchermata%202023-08-25%20alle%2017.49.02.png?alt=media&amp;token=9d6e1842-c249-4542-8d08-d7f06224351d" alt="" width="563"><figcaption></figcaption></figure>

La connessione verso Trackle Cloud è **reciprocamente autenticata** utilizzando coppie di chiavi pubbliche / private in formato **RPK** (*Raw Public Key*).

La chiave privata del dispositivo ottenuta al passaggio precedente deve essere memorizzata sul dispositivo e deve essere essere mantenuta segreta. Trackle Cloud memorizza la chiave pubblica di ogni dispositivo.&#x20;

Per quanto riguarda il cloud, la chiave privata del cloud viene mantenuta segreta, mentre tutti i dispositivi conoscono la chiave pubblica del cloud. La chiave pubblica del cloud non è un segreto.

### Trackle.setDeviceId()

Configura l'ID univoco del dispositivo (*DeviceID*) ottenuto dalla Console. L'ID dispositivo **deve** essere un numero di 12 byte che identifica univocamente il dispositivo.

### Trackle.setKeys()

Configura la *chiave privata* per questo dispositivo. Non è necessario impostare la chiave pubblica del cloud in quanto è già codificata nella libreria.

{% hint style="info" %}
Per ottenere l'array esadecimale dal file .der della chiave privata scaricata dalla Console è possibile usare questo comando su sistemi unix:

`cat private_key.der | xxd -i`
{% endhint %}

{% tabs %}
{% tab title="C" %}

```cpp
// ID univoco del dispositivo
char DEVICE_ID[13] = {0xd1, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06};

// chiave privata del dispositivo
const uint8_t PRIVATE_KEY[PRIVATE_KEY_LENGTH] =
  {0x30, 0x77, 0x02, 0x01, 0x01, 0x04, ... };
  
trackleSetDeviceId(trackle_s, DEVICE_ID);
trackleSetKeys(trackle_s, PRIVATE_KEY);
```

{% endtab %}

{% tab title="C++" %}

```cpp
// ID univoco del dispositivo
char DEVICE_ID[13] = {0xd1, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06};

// chiave privata del dispositivo
const uint8_t PRIVATE_KEY[PRIVATE_KEY_LENGTH] =
  {0x30, 0x77, 0x02, 0x01, 0x01, 0x04, ... };
  
trackle.setDeviceId(DEVICE_ID);
trackle.setKeys(PRIVATE_KEY);
```

{% endtab %}
{% endtabs %}

## Comunicazione

Trackle Library si collega al Cloud attraverso il protocollo di comunicazione **CoAP** (IETF [RFC 7252](https://tools.ietf.org/html/rfc7252)), acronimo di *Constrained Application Protocol.* CoAP è un protocollo leggero, appositamente progettato per dispositivi IoT con capacità di elaborazione limitate che comunicano su reti con banda disponibile ridotta.&#x20;

CoAP utilizza **UDP** come protocollo di trasporto predefinito (ogni messaggio CoAP viene inviato all'interno di un *datagram UDP*) e implementa le funzionalità software per garantire l'invio, la ricezione e l'ordinamento dei pacchetti (ACK e messageId). La **sicurezza** della comunicazione è assicurata dall’implementazione dello standard **DTLS** (*Datagram Transport Layer Security*), un protocollo progettato per proteggere la privacy dei dati e prevenire intercettazioni e manomissioni.

Per permettere ad un client di comunicare con il cloud è necessario implementare le callback che definiscono come creare e distruggere un socket UDP e come inviare e ricevere dati su quel socket UDP. La libreria si occupa di cifrare la comunicazione e di mantenere il canale sempre attivo.

### Trackle.**setConnectCallback**()

Imposta una callback che crea il socket UDP verso il cloud e ritorna il risultato.

{% tabs %}
{% tab title="C" %}

```c
struct sockaddr_in servaddr;
int cloud_socket;

int connect_cb_udp(const char *address, int port)
{
    printf("Connecting socket");
    int addr_family;
    int ip_protocol;
    char addr_str[128];

    struct hostent *res = gethostbyname(address);
    if (res)
    {
        printf("Dns address %s resolved", address);
    }
    else
    {
        printf("error resolving gethostbyname %s resolved", address);
        return -1;
    }

    memcpy(&cloud_addr.sin_addr.s_addr, res->h_addr, sizeof(cloud_addr.sin_addr.s_addr));

    cloud_addr.sin_family = AF_INET;
    cloud_addr.sin_port = htons(port);
    addr_family = AF_INET;
    ip_protocol = IPPROTO_IP;
    inet_ntoa_r(cloud_addr.sin_addr, addr_str, sizeof(addr_str) - 1);

    cloud_socket = socket(addr_family, SOCK_DGRAM, ip_protocol);
    if (cloud_socket < 0)
    {
        printf("Unable to create socket: errno %d", errno);
    }
    printf("Socket created, sending to %s:%d", address, port);

    // setto i timeout di lettura/scrittura del socket
    struct timeval socket_timeout;
    socket_timeout.tv_sec = 0;
    socket_timeout.tv_usec = 1000; // 1ms
    setsockopt(cloud_socket, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&socket_timeout, sizeof(struct timeval));

    return 1;
}

trackleSetConnectCallback(trackle_s, connect_cb_udp);
```

{% endtab %}

{% tab title="C++" %}

```cpp
struct sockaddr_in servaddr;
int cloud_socket;

int connect_cb_udp(const char *address, int port)
{
    printf("Connecting socket");
    int addr_family;
    int ip_protocol;
    char addr_str[128];

    struct hostent *res = gethostbyname(address);
    if (res)
    {
        printf("Dns address %s resolved", address);
    }
    else
    {
        printf("error resolving gethostbyname %s resolved", address);
        return -1;
    }

    memcpy(&cloud_addr.sin_addr.s_addr, res->h_addr, sizeof(cloud_addr.sin_addr.s_addr));

    cloud_addr.sin_family = AF_INET;
    cloud_addr.sin_port = htons(port);
    addr_family = AF_INET;
    ip_protocol = IPPROTO_IP;
    inet_ntoa_r(cloud_addr.sin_addr, addr_str, sizeof(addr_str) - 1);

    cloud_socket = socket(addr_family, SOCK_DGRAM, ip_protocol);
    if (cloud_socket < 0)
    {
        printf("Unable to create socket: errno %d", errno);
    }
    printf("Socket created, sending to %s:%d", address, port);

    // setto i timeout di lettura/scrittura del socket
    struct timeval socket_timeout;
    socket_timeout.tv_sec = 0;
    socket_timeout.tv_usec = 1000; // 1ms
    setsockopt(cloud_socket, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&socket_timeout, sizeof(struct timeval));

    return 1;
}

trackle.setConnectCallback(connect_cb_udp);
```

{% endtab %}
{% endtabs %}

### Trackle.**setDisconnectCallback**()

Imposta una callback che esegue la chiusura del socket UDP e ritorna il risultato.

{% tabs %}
{% tab title="C" %}

```c
int disconnect_cb()
{
    if (cloud_socket)
        close(cloud_socket);
    return 1;
}

trackleSetDisconnectCallback(trackle_s, disconnect_cb);
```

{% endtab %}

{% tab title="C++" %}

```cpp
int disconnect_cb()
{
    if (cloud_socket)
        close(cloud_socket);
    return 1;
}

trackle.setDisconnectCallback(disconnect_cb);
```

{% endtab %}
{% endtabs %}

### Trackle.setSendCallback()

Imposta una callback che esegue la scrittura di un buffer dati sul socket UDP e ritorna il numero di byte inviati.

{% tabs %}
{% tab title="C" %}

```c
int send_cb_udp(const unsigned char *buf, uint32_t buflen, void *tmp)
{
    size_t sent = sendto(cloud_socket, (const char *)buf, buflen, 0, (struct sockaddr *)&cloud_addr, sizeof(cloud_addr));
    return (int)sent;
}

trackleSetSendCallback(trackle_s, send_cb_udp);
```

{% endtab %}

{% tab title="C++" %}

```cpp
int send_cb_udp(const unsigned char *buf, uint32_t buflen, void *tmp)
{
    size_t sent = sendto(cloud_socket, (const char *)buf, buflen, 0, (struct sockaddr *)&cloud_addr, sizeof(cloud_addr));
    return (int)sent;
}

trackle.setSendCallback(send_cb_udp);
```

{% endtab %}
{% endtabs %}

### Trackle.**setReceiveCallback**()

Imposta una callback che legge il socket UDP e ritorna il numero di byte ricevuti.

{% tabs %}
{% tab title="C" %}

```c
int receive_cb_udp(unsigned char *buf, uint32_t buflen, void *tmp)
{
    size_t res = recvfrom(cloud_socket, (char *)buf, buflen, 0, (struct sockaddr *)NULL, NULL);

    // on timeout error, set bytes received to 0
    if ((int)res < 0 && errno == 11)
    {
        res = 0;
    }

    return (int)res;
}

trackleSetReceiveCallback(trackle_s, receive_cb_udp);
```

{% endtab %}

{% tab title="C++" %}

```cpp
int receive_cb_udp(unsigned char *buf, uint32_t buflen, void *tmp)
{
    size_t res = recvfrom(cloud_socket, (char *)buf, buflen, 0, (struct sockaddr *)NULL, NULL);

    // on timeout error, set bytes received to 0
    if ((int)res < 0 && errno == 11)
    {
        res = 0;
    }

    return (int)res;
}

trackle.setReceiveCallback(receive_cb_udp);
```

{% endtab %}
{% endtabs %}

### **Trackle.setRandomCallback**

Imposta una callback per la generazione di un numero random. Se non definita viene utilizzata la funziona rand() che ritorna un numero pseudo-casuale.

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
typedef uint32_t(randomNumberCallback)(void);
void trackleSetRandomCallback(Trackle *v, randomNumberCallback *random);

// ESEMPIO
uint32_t random_callback() {
    return rand();
}

trackleSetRandomCallback(c, random_callback);
```

{% endtab %}

{% tab title="C++" %}

```cpp
// SINTASSI
typedef uint32_t(randomNumberCallback)(void);
void setRandomCallback(randomNumberCallback *random);

// ESEMPIO
uint32_t random_callback() {
    return rand();
}

Trackle.setRandomCallback(random_callback);
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Se l'hardware utilizzato dispone di metodi più sicuri per la generazione di un numero random, è possibile configurarli attraverso questa callback.
{% endhint %}

## Connessione

Dopo aver implementato la gestione del socket il client può tentare la connessione al cloud.

### Trackle.c**onnect**()

Tenta la connessione al cloud. Restituisce il valore `0` se non ci sono errori, un valore `<0` in caso di errore.

### Trackle.connected()

Ritorna `true` se il dispositivo è connesso al cloud, altrimenti `false` .

### Trackle.**loop**()

Esegue il background loop che si occupa della comunicazione bidirezionale tra dispositivo e cloud di mantenere il canale attivo. Se non viene chiamata abbastanza frequentemente, la connessione con il Cloud verrà persa.

{% hint style="warning" %}
`Trackle.loop()` è una funzione che blocca l'esecuzione del firmware per alcuni millisecondi. Più frequentemente viene chiamata, più il tempo di esecuzione si riduce e più il dispositivo risponde con velocità.
{% endhint %}

{% tabs %}
{% tab title="C " %}

```c
int main() {
    trackleConnect(trackle_s);
    while (1)
    {
        trackleLoop(trackle_s);
        ...
        // Application Loop
        ...
        usleep(20 * 1000);
    }
}
```

{% endtab %}

{% tab title="C++" %}

```cpp
int main() {
    trackle.connect();
    while (1)
    {
        trackle.loop();
        ...
        // Application Loop
        ...
        usleep(20 * 1000);
    }
}
```

{% endtab %}
{% endtabs %}

### Trackle.disconnect()

Tenta di disconnettere il dispositivo dal cloud.&#x20;

{% tabs %}
{% tab title="C" %}

<pre class="language-c"><code class="lang-c">int counter = 10000;

bool needConnection() {
  --counter;
  if (0 == counter)
    counter = 10000;
  return (2000 > counter);
}

int main() {
  while(1) {
    if (needConnection()) {
<strong>      if (!trackleConnected(trackle_s))
</strong>        trackleConnect(trackle_s);
    } else {
      if (trackleConnected(trackle_s))
        trackleDisconnect(trackle_s);
    }
    trackleLoop(trackle_s);
    ...
    // Application Loop
    ...
    usleep(20 * 1000);
  }
  return 0;
}
</code></pre>

{% endtab %}

{% tab title="C ++" %}

```cpp
int counter = 10000;

bool needConnection() {
  --counter;
  if (0 == counter)
    counter = 10000;
  return (2000 > counter);
}

int main() {
  while(1) {
    if (needConnection()) {
      if (!trackle.connected())
        trackle.connect();
    } else {
      if (trackle.connected())
        trackle.disconnect();
    }
    trackle.loop();
    ...
    // Application Loop
    ...
    usleep(20 * 1000);
  }
  return 0;
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Quando il dispositivo non è connesso, diverse funzionalità come gli aggiornamenti OTA,  `Trackle.get` e `Trackle.post` non sono disponibili.
{% endhint %}

## Prodotti

Per i dispositivi che fanno parte di un prodotto e' necessario specificare anche un **ID Prodotto** e una **versione del firmware**.&#x20;

### Trackle.setProductId()

Configura l'ID prodotto di cui è parte il dispositivo.

### Trackle.setFirmwareVersion()

Configura la versione firmware del prodotto.

{% tabs %}
{% tab title="C" %}

```c
const uint_16_t PRODUCT_ID = 20;
const uint_16_t PRODUCT_FIRMWARE_VERSION = 1;

trackleSetProductId(trackle_s, PRODUCT_ID);
trackleSetFirmwareVersion(trackle_s, PRODUCT_FIRMWARE_VERSION);
```

{% endtab %}

{% tab title="C++" %}

```cpp
const uint_16_t PRODUCT_ID = 20;
const uint_16_t PRODUCT_FIRMWARE_VERSION = 1;

trackle.setProductId(PRODUCT_ID);
trackle.setFirmwareVersion(PRODUCT_FIRMWARE_VERSION);
```

{% endtab %}
{% endtabs %}

### Trackle.setComponentsList()

Permette di specificare una lista di componenti utilizzati ed inviarli al cloud. Questa informazione può essere utile per monitorare in modo più dettagliato le risorse e le funzionalità disponibili sul dispositivo.

{% tabs %}
{% tab title="C" %}

```c
trackleSetComponentsList(trackle_s, "trackle-library-esp-idf:v2.2.1");
```

{% endtab %}

{% tab title="C++" %}

```cpp
Trackle.setComponentsList("trackle-library-esp-idf:v2.2.1");
```

{% endtab %}
{% endtabs %}

## Blockwise

Il Blockwise Transfer (RFC 7959) permette di suddividere un messaggio CoAP in blocchi numerati, rendendo possibile la gestione di payload molto grandi durante operazioni di Publish e Get senza superare i limiti del protocollo o della memoria disponibile.

La Trackle Library usa un sistema di buffer per memorizzare i blocchi:

* buffer **interno**, gestito automaticamente
* buffer **esterno**, gestito dall’utente (consigliato quando si ha poca RAM o si vuole ottimizzare l’allocazione)

La scelta tra buffer interno ed esterno dipende dai requisiti di memoria del dispositivo.

### Configurazione

La dimensione e la capacità del sistema blockwise vengono configurate via macro di compilazione:

#### **TRACKLE\_BLOCKS\_NUMBER**

Numero massimo di blocchi che la libreria può memorizzare internamente.\
Più blocchi = messaggi più grandi gestibili, ma maggiore consumo di RAM.

Valori possibili: da 1 a 32 (default 4)

```c
#define TRACKLE_BLOCKS_NUMBER 16
```

#### **TRACKLE\_CONCURRENT\_MESSAGES**

Numero massimo di messaggi Blockwise che possono essere gestiti contemporaneamente.

Valori possibili: da 1 a 4 (default 4)

```c
#define TRACKLE_CONCURRENT_MESSAGES 2
```

### Buffer interno vs buffer esterno

#### **Buffer interno (default)**

* Allocato dalla libreria.
* Semplice da usare, nessuna configurazione aggiuntiva.
* Occupa RAM interna alla Trackle Library.
* Non permette un controllo preciso del consumo di memoria.

#### **Buffer esterno**

* Allocato dall’utente.
* Permette di utilizzare:
  * RAM esterna,
  * RAM statica,
  * memoria più efficiente.
* Utile in sistemi embedded a bassa RAM.
* Si abilita tramite la funzione **trackleSetExternalBuffer()**.

{% tabs %}
{% tab title="C" %}

```c
trackleSetExternalBuffer(Trackle *v, uint8_t *extBuffer, size_t size);
```

{% endtab %}

{% tab title="C++" %}

```cpp
boo Trackle.setExternalBuffer(uint8_t *extBuffer, size_t size);
```

{% endtab %}
{% endtabs %}

#### Parametri

* **extBuffer** – puntatore al buffer esterno allocato dall’utente
* **size** – dimensione totale del buffer

#### Valore ritornato

* `true` se il buffer è stato impostato correttamente
* `false` se la dimensione è insufficiente

```c
#define EXTERNAL_BUFFER_SIZE 4096 * 4 

uint8_t external_blockwise_buffer[EXTERNAL_BUFFER_SIZE];

// Imposto un buffer esterno per la gestione dei blocchi
bool ok = trackleSetExternalBuffer(
    trackle_s,
    external_blockwise_buffer,
    EXTERNAL_BUFFER_SIZE
);

if (!ok) {
    printf("Errore: buffer esterno troppo piccolo!\n");
}
```

#### Note importanti

* Il buffer deve essere **statico o globale**, non locale (sullo stack).
* La dimensione deve essere sufficiente per contenere:

  ```
  TRACKLE_BLOCKS_NUMBER × TRACKLE_CONCURRENT_MESSAGES * 1024
  ```
* Se troppo piccolo, la libreria segnala errore.


# Funzionalità cloud

## Trackle.get

Espone una *funzione che ritorna un valore* alle API Cloud che può essere chiamata attraverso una richiesta  `GET /v1/devices/{DEVICE_ID}/{REQUEST}` . Ritorna il valore `true`quando la funzione è stata registrata.

`Trackle.get` permette di eseguire codice sul dispositivo e ottenere una risposta attraverso una richiesta alle API Cloud. Si utilizza questa funzionalità quando si vuole ottenere un valore raw oppure un valore che deve essere calcolato al momento della richiesta in base ad un parametro fornito, per es. interrogare un certo slave su bus per ritornare uno specifico valore.

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
typedef bool (*user_variable_bool_cb_t)(const char *paramString, const char* varKey);
typedef int (*user_variable_int32_cb_t)(const char *paramString, const char* varKey);
typedef double (*user_variable_double_cb_t)(const char *paramString, const char* varKey);
typedef const char *(*user_variable_char_cb_t)(const char *paramString, const char* varKey);

bool trackleGet(Trackle *v, const char *varKey, void *(*varCb)(const char *), Data_TypeDef type);

// ESEMPI
bool myBoolCb(const char *args, const char* varKey) {
    return true;
}

int32_t myIntCb(const char *args, const char* varKey) {
    return 42;
}

double myDoubleCb(const char *args, const char* varKey) {
    return 1.21;
}

const char* myStringCb(const char *args, const char* varKey) {
    return "Hasta la vista, baby.";
}

const char* myJsonCb(const char *args, const char* varKey) {
    return "{"
        "\"title\": \"The Hangover\","
        "\"director\": \"Todd Phillips\","
        "\"year\": \"2009\","
        "\"genre\": \"Comedy\""
    "}";
}

bool success = trackleGet(trackle_s, "showMeYouLearnedKungFu", myBoolCb, VAR_BOOL);
bool success = trackleGet(trackle_s, "answerTofundamentalQuestion", myIntCb, VAR_INT);
bool success = trackleGet(trackle_s, "howManyGigawatts", myDoubleCb, VAR_DOUBLE);
bool success = trackleGet(trackle_s, "seeYouLater", myStringCb, VAR_STRING);
bool success = trackleGet(trackle_s, "suggestMeMovie", myJsonCb, VAR_JSON);
```

{% endtab %}

{% tab title="C ++" %}

```cpp
// SINTASSI
typedef bool (*user_variable_bool_cb_t)(const char *paramString, const char* varKey);
typedef int (*user_variable_int32_cb_t)(const char *paramString, const char* varKey);
typedef double (*user_variable_double_cb_t)(const char *paramString, const char* varKey);
typedef const char *(*user_variable_char_cb_t)(const char *paramString, const char* varKey);

bool get(const char *varKey, user_variable_bool_cb_t varCb);
bool get(const char *varKey, user_variable_int_cb_t varCb);
bool get(const char *varKey, user_variable_double_cb_t varCb);
bool get(const char *varKey, user_variable_char_cb_t var);
bool get(const char *varKey, void *(*varCb)(const char *), Data_TypeDef type);

// ESEMPI
bool myBoolCb(const char *args, const char* varKey) {
    return true;
}

int32_t myIntCb(const char *args, const char* varKey) {
    return 42;
}

double myDoubleCb(const char *args, const char* varKey) {
    return 1.21;
}

const char* myStringCb(const char *args, const char* varKey) {
    return "Hasta la vista, baby.";
}

const char* myJsonCb(const char *args, const char* varKey) {
    return "{"
        "\"title\": \"The Hangover\","
        "\"director\": \"Todd Phillips\","
        "\"year\": \"2009\","
        "\"genre\": \"Comedy\""
    "}";
}

bool success = Trackle.get("showMeYouLearnedKungFu", myBoolCb);
bool success = Trackle.get("answerTofundamentalQuestion", myIntCb);
bool success = Trackle.get("howManyGigawatts", myDoubleCb);
bool success = Trackle.get("seeYouLater", myStringCb);
bool success = Trackle.get("suggestMeMovie", myJsonCb, VAR_JSON);
```

{% endtab %}
{% endtabs %}

Per registrare una GET, l'utente deve fornire una chiave `varKey` che è il nome da utilizzare per effettuara la richiesta GET e una `varCb` che è la callback implementata dalla sua applicazione. La richiesta può ritornare uno tra i cinque tipi di dato supportati:

* `BOOL`
* `INT` (intero con segno a 32 bit)
* `DOUBLE`
* `STRING` (la massima lunghezza è di 32.768 bytes) ([configurazione](/trackle-library/configurazione#blockwise))
* `JSON` (la massima lunghezza è di 32.768 bytes) ([configurazione](/trackle-library/configurazione#blockwise))

Possono essere registrate fino a 20 richieste di dati GET ed il nome di ognuna ha il limite massimo di 32 caratteri.

*`VAR_KEY`*

Alla callback viene passato come parametro `varKey`, che rappresenta il nome con cui la variabile è stata registrata nel cloud. Questo approccio consente di implementare una singola callback per tutte le variabili, permettendo di gestire le operazioni da eseguire utilizzando uno `switch-case` basato sul valore di `varKey`.

## Trackle.post()

Espone una *funzione* alle API Cloud che può essere chiamata attraverso una richiesta `POST /v1/devices/{DEVICE_ID}/{FUNCTION}.` Ritorna il valore `true`quando la funzione è stata registrata.

`Trackle.post` permette di eseguire codice sul dispositivo da una chiamata alle API Cloud. Tipicamente si utilizza questa funzionalità quando si vuole controllare qualcosa sul dispositivo per es. accendere un LED, far suonare un buzzer o controllare una funzione del firmware da Cloud.

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
typedef int(*user_function_int_char_t)(const char *paramString, bool isOwner, const char* funKey);

bool tracklePost(Trackle *v, const char *funcKey, user_function_int_char_t *funcCb, Function_PermissionDef permission);

// ESEMPIO
int startHack(const char* arg, const char* funKey) {
  .....
  return -1;
}

int sendVirus(const char* arg, const char* funKey) {
  .....
  return 1;
}

bool success = TracklePost(trackle_s, "hackNORAD", startHack, OWNER_ONLY);
bool success = TracklePost(trackle_s, "sendVirusToAlienShuttle", sendVirus, ALL_USERS);
```

{% endtab %}

{% tab title="C ++" %}

```cpp
// SINTASSI
typedef int(*user_function_int_char_t)(const char *paramString, bool isOwner, const char* funKey);

bool post(const char *funcKey, user_function_int_char_t *funcCb, Function_PermissionDef permission = ALL_USERS);

// ESEMPIO
int startHack(const char* arg, const char* funKey) {
  .....
  return 1;
}

int sendVirus(const char* arg, const char* funKey) {
  .....
  return 1;
}

bool success = Trackle.post("hackNORAD", startHack, OWNER_ONLY);
bool success = Trackle.post("sendVirusToAlienShuttle", sendVirus, ALL_USERS);
```

{% endtab %}
{% endtabs %}

Per registrare una POST, l'utente deve fornire una chiave `funcKey`, che è il nome da utilizzare per effettuara la chiamata POST e una`funcCb`, che è la callback implementata dalla tua applicazione. Una POST ritorna un numero intero; `-1`è solitamente utilizzato per ritornare un errore.

La POST accetta come parametro una stringa. Questa ha una lunghezza massima limitata a 1024 caratteri ed è codificata in UTF-8.

Possono essere registrate fino a 20 POST, ognuna delle quali ha un nome di massimo 32 caratteri.

*`OWNER_ONLY` flag*

E' possibile **limitare l'accesso** ad una o più funzioni esposte tramite `Trackle.post()` al solo proprietario del dispositivo. In questo modo un dispositivo parte di un Prodotto può avere delle funzioni esclusive per il proprietario del dispositivo che non possono essere chiamate dal manutentore o da altri soggetti a cui è stato concesso il permesso di monitorare i dispositivi di Prodotto.

*`FUN_KEY`*

Alla callback viene passato come parametro `funcKey`, che rappresenta il nome con cui la funzione è stata registrata nel cloud. Questo approccio consente di implementare una singola callback per tutte le funzioni, permettendo di gestire le operazioni da eseguire utilizzando uno `switch-case` basato sul valore di `funcKey`.

## Trackle.publish()

Pubblica un evento sul Cloud che verrà inoltrato a tutti gli ascoltatori registrati, come *proprietà*, *notifiche*, *webhooks,* stream sottoscritti di tipo SSE e altri dispositivi in ascolto via `Trackle.subscribe()`. Ritorna il valore `true`quando l'evento è stato inviato al Cloud.

Questa funzionalità permette al Dispositivo di inviare un evento basato su una condizione. Per esempio puoi collegare un sensore di movimento e generare un messaggio quando viene rilevato un movimento.

Un evento Cloud ha le seguenti proprietà:

* nome (1–32 caratteri ASCII)
* dati fino a 32.768 caratteri ([configurazione](/trackle-library/configurazione#blockwise))
* ttl default 30 secondi
* Event\_Type PUBBLICO / PRIVATO
* Event\_Flags con o senza ACK
* msg\_key una chiave numerica opzionale

Le variabili di tipo String devono essere UTF-8 encoded. Non puoi inviare data binari o altre tipologie di caratteri tipo ISO-8859-1. Se hai la necessità di inviare dati binari, puoi codificarli un un formato text-based tipo [Base64](https://github.com/rickkas7/Base64RK).

{% hint style="info" %}
Al momento un dispositivo può pubblicare con un rate di circa 1 evento / sec, con picchi fino a 4 messaggi al secondo. Recuperare l'invio di 4 messaggi impiegherà 4 second&#x69;**.**
{% endhint %}

{% hint style="warning" %}
I piani di utilizzo di Trackle comprendo un tot di messaggi / mese per dispositivo quindi è bene stare attenti a quanti eventi vengono inviati dal dispositivo per non subire blocchi o aumenti di costo rispetto al piano sottoscritto.
{% endhint %}

La chiamata a`Trackle.publish()` ritorna `false` quando:

* il dispositivo non è connesso al Cloud
* il nome dell'evento inizia con `trackle` o `iotready`, questi eventi sono riservati ai dati ufficialmente originati dal Cloud.
* la dimensione dei dati opzionali è maggiore di 32.768 caratteri
* è stato superato il rate limit di invio
* si verifica un errore di rete

*`NO_ACK` flag*

A meno che non sia specificato, un evento viene inviato al cloud come messaggio affidabile. Il Dispositivo aspetta per un acknowledgement dal cloud che il messaggio sia stato ricevuto, ed effettua il reinvio del messaggio fino a 3 volte in background prima di lasciar perdere.

`NO_ACK` flag disabilita questo comportamento di acknowledge/retry ed invia il messaggio una sola volta. Questo riduce il consumo di dati per evento, ma introduce la possibilità che questo non raggiunga mai il cloud.

Per esempio, il `NO_ACK` flag potrebbe essere utile per inviare dei valori, come ad esempio la lettura di sensori, per cui la perdita occasione le di un dato sia tollerabile.

*`WITH_ACK` flag*

Questo flag fa sì che per l'evento inviato sia atteso l'acknowledgement dal Cloud per verificarne l'effettiva ricezione. In caso di mancata ricezione dell'ack entro un timeout prestabilito, se configurata la callback, verrà notificato l'errore.

*MSG\_KEY*

È possibile specificare un parametro numerico aggiuntivo come ultimo argomento. Questo valore numerico verrà inoltrato come parametro alle funzioni di callback `completedPublishCallback` e `sendPublishCallback`. Questo parametro può essere utilizzato per tracciare e identificare i messaggi pubblicati nel cloud, verificando la loro effettiva ricezione e di gestire manualmente la ripubblicazione in caso di errori. Nel caso in cui non sia specificata (o sia passsato il valore `0`), la msg\_key viene generata dalla libreria.

{% hint style="info" %}
Diversamente da `Trackle.get` e `Trackle.post,`devi chiamare `Trackle.publish` dal loop() (o da una funzione chiamata dal loop).
{% endhint %}

Pubblicare un evento pubblico, senza ack, con un nome ma nessun contenuto e msg\_key

**Ritorno:** Un `bool` indica il successo: (true o false)

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
bool tracklePublish(Trackle *v, const char *eventName, const char *data, int ttl, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);

// ESEMPIO
bool success = tracklePublish(trackle_s, "Black_Pearl", "", 30, PUBLIC, NO_ACK, 0);
```

{% endtab %}

{% tab title="C ++" %}

```cpp
// SINTASSI
bool publish(const char *eventName);
bool publish(string eventName);

// ESEMPIO
bool success = Trackle.publish("Black_Pearl");
```

{% endtab %}
{% endtabs %}

Pubblicare un evento privato, con ack, con un nome, un contenuto e msg\_key

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
bool tracklePublish(Trackle *v, const char *eventName, const char *data, int ttl, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);

// ESEMPIO
bool success = tracklePublish(trackle_s, "Taxi_driver", "You talkin' to me?", 30, PRIVATE, WITH_ACK, 1);
```

{% endtab %}

{% tab title="C ++" %}

```cpp
// SINTASSI
bool publish(const char *eventName, const char *data, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);
bool publish(string eventName, const char *data, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);

// ESEMPIO
bool success = Trackle.publish("Taxi_driver", "You talkin' to me?", PRIVATE, WITH_ACK, 1);
```

{% endtab %}
{% endtabs %}

Pubblicare un evento privato, senza ack, con un nome, un contenuto ma senza msg\_key

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
bool tracklePublish(Trackle *v, const char *eventName, const char *data, int ttl, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);

// ESEMPIO
bool success = tracklePublish(trackle_s, "The_Godfather", "I'll make him an offer he can't refuse.", 30, PRIVATE, NO_ACK, 0);
```

{% endtab %}

{% tab title="C ++" %}

```cpp
// SINTASSI
bool publish(const char *eventName, const char *data, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);
bool publish(string eventName, const char *data, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);

// ESEMPIO
bool success = Trackle.publish("The_Godfather", "I'll make him an offer he can't refuse.", PRIVATE, NO_ACK, 0);
```

{% endtab %}
{% endtabs %}

Pubblicare un evento publico, senza ack, con un nome, un contenuto, il ttl e una msg\_key

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
bool tracklePublish(Trackle *v, const char *eventName, const char *data, int ttl, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);

// ESEMPIO
bool success = tracklePublish(trackle_s, "McClane", "Yippee-Ki-Yay, Motherf*cker!", 60, PUBLIC, NO_ACK, 11);
```

{% endtab %}

{% tab title="C ++" %}

```cpp
// SINTASSI
bool publish(const char *eventName, const char *data, int ttl, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);
bool publish(string eventName, const char *data, int ttl, Event_Type eventType, Event_Flags eventFlag, uint32_t msg_key);

// ESEMPIO
bool success = Trackle.publish("McClane", "Yippee-Ki-Yay, Motherf*cker!", 60, PUBLIC, NO_ACK, 11);
```

{% endtab %}
{% endtabs %}

Sottoscrivere a un Server-Sent Events con [Cloud API](broken://pages/-M0PX-ZaT75QgvEL8ofb#get-a-stream-of-your-events) per un evento pubblico

```
# ESEMPIO
curl -H "Authorization: Bearer {ACCESS_TOKEN_GOES_HERE}" \
    https://api.trackle.io/v1/events/motion-detected

# Will return a stream that echoes text when your event is published
event: motion-detected
data: {"data":"23:23:44","ttl":"60","published_at":"2014-05-28T19:20:34.638Z","deviceid":"0123456789abcdef"}
```

## Trackle.subscribe()

Sottoscrive ad un evento pubblicato da un dispositivo. Ritorna un valore `boolean` indicante il successo della sottoscrizione.

Questa funzionalità permette ai dispositivo di parlarsi tra loro. Ad esempio un dispositivo può pubblicare un evento di un sensore ed un altro può generare un allarme o rispondere pubblicando un altro.

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
bool trackleSubscribe(Trackle *v, const char *eventName, EventHandler handler, Subscription_Scope_Type scope, const char *deviceID);

// ESEMPIO
int i = 0;

void myHandler(const char *event, const char *data)
{
  i++;
  Serial.print(i);
  Serial.print(event);
  Serial.print(", data: ");
  if (data)
    Serial.println(data);
  else
    Serial.println("NULL");
}

int main()
{
  trackleSubscribe(trackle_s, "inception", myHandler, ALL_DEVICES, "");
  while(1) {
    trackleLoop();
  }
  return 0;
}
```

{% endtab %}

{% tab title="C ++" %}

```cpp
// SINTASSI
Trackle.subscribe(const char *eventName, EventHandler handler);
Trackle.subscribe(const char *eventName, EventHandler handler, Subscription_Scope_Type scope);
Trackle.subscribe(const char *eventName, EventHandler handler, const char *deviceID);
Trackle.subscribe(const char *eventName, EventHandler handler, Subscription_Scope_Type scope, const char *deviceId);

// ESEMPIO
int i = 0;

void myHandler(const char *event, const char *data)
{
  i++;
  Serial.print(i);
  Serial.print(event);
  Serial.print(", data: ");
  if (data)
    Serial.println(data);
  else
    Serial.println("NULL");
}

int main()
{
  Trackle.subscribe("inception", myHandler);
  while(1) {
    Trackle.loop();
  }
  return 0;
}
```

{% endtab %}
{% endtabs %}

Puoi metterti in ascolto sugli eventi privati pubblicati dai tuoi dispositivi utilizzando `MY_DEVICES`.&#x20;

* Specificando MY\_DEVICES si ricevono solo eventi PRIVATI.&#x20;
* Specificando ALL\_DEVICES o omettendo il terzo parametro si ricevono solo eventi PUBBLICI (solo per dispositivi che fanno parte di un prodotto).

Una sottoscrizione è come un filtro su un prefisso. Se ti sottoscrivi a "evento", riceverai tutti gli eventi che iniziano con "evento", incluso "evento", "evento1", "evento/prova" ecc...

{% hint style="warning" %}
Un dispositivo può registrare fino a 4 sottoscrizioni. Dalla quinta registrazione ad un evento, la funzione subscribe ritornerà `false`
{% endhint %}

Gli eventi ricevuti vengono passati alla callback registrata; questa deve essere di tipo void ed accettare 2 parametri:

* Il primo parametro è il nome completo dell'evento pubblicato
* Il secondo parametro è il corpo dell'evento, che può essere NULL.

`Trackle.subscribe()` ritorna un `bool` che indica se l'evento è stato sottoscritto con successo. La libreria invierà la sottoscrizione al Cloud non appena si connetterà allo stesso.

Puoi indicare come ultimo parametro il *Device ID* del dispositivo a cui vuoi sottoscriverti, nel caso in cui tu sia interessato ai dati di un singolo dispositivo

{% hint style="info" %}
A differenza delle `Trackle.get` e delle`Trackle.post`, puoi chiamare `Trackle.subscribe` sia prima di effettuare la connessione al Cloud che durante il loop.
{% endhint %}

## Trackle.unsubscribe()

Rimuove tutto le sottoscrizioni precedentemente registrate con`Trackle.subscribe()`.&#x20;

## Trackle.setClaimCode()

Imposta il **codice di claim** precedentemente generato tramite le API Cloud. Se il codice è presente nel momento in cui il Dispositivo si connette al Cloud viene inviato un evento di tipo `trackle/device/claim/code` che serve ad associare il Dispositivo al suo proprietario.

{% tabs %}
{% tab title="C" %}

```c
trackleSetClaimCode(trackle_s, "qSHVg4T111RCJ03i0N.....");
```

{% endtab %}

{% tab title="C++" %}

```cpp
Trackle.setClaimCode("qSHVg4T111RCJ03i0N....."); 
```

{% endtab %}
{% endtabs %}

## Eventi

La libreria permette agli sviluppatori di definire il comportamento del firmware allo scatenarsi di specifici eventi (per es. dopo aver inviato un messaggio al cloud) attraverso l'implementazione di callback, in particolare:

### Trackle.**sendPublishCallback**

chiamata ogni volta in cui viene eseguito un [**publish**](broken://pages/-M0SjKxhiCVUMbejE1MV#iotready-publish). Come parametri, oltre al nome dell'evento e al contenuto, viene passata la msg\_key (l'ultimo parametro della funzione publish), che può essere usata come codice del messaggio. e un booleano che specifica se il messaggio è stato effettivamente inviato;

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
typedef void(publishSendCallback)(const char *eventName, const char *data, uint32_t msg_key, bool published);
void trackleSetSendPublishCallback(Trackle *v, publishSendCallback *publish);

// ESEMPIO
void callback_send_publish(const char *eventName, const char *data, uint32_t msg_key, bool published) {
    printf("Event: %s, key: %d", (published ? "cache" : "republish"), msg_key);
    ...
}

trackleSetSendPublishCallback(trackle_s, callback_send_publish);
```

{% endtab %}

{% tab title="C++" %}

```cpp
// SINTASSI
typedef void(publishSendCallback)(const char *eventName, const char *data, uint32_t msg_key, bool published);
void setSendPublishCallback(publishSendCallback *publish);

// ESEMPIO
void callback_send_publish(const char *eventName, const char *data, uint32_t msg_key, bool published) {
    printf("Event: %s, key: %d", (published ? "cache" : "republish"), msg_key);
    ...
}

Trackle.setSendPublishCallback(callback_send_publish);
```

{% endtab %}
{% endtabs %}

### Trackle.**completedPublishCallback**

Chiamata alla ricezione dell'ack al publish o se il publish fallisce per timeout.&#x20;

* Se error è uguale a 0, significa che il publish è stato eseguito con successo ed è stato ricevuto l'ack dal server, in caso contrario si è verificato un errore ed il publish non è andato a buon fine;
* il puntatore a callbackData contiene la msg\_key, ovvero l'ultimo parametro passato alla funzione publish().

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
typedef void(publishCompletionCallback)(int error, const void *data, void *callbackData, void *reserved);
void trackleSetCompletedPublishCallback(Trackle *v, publishCompletionCallback *publish);

// ESEMPIO
void callback_complete_publish(int error, const void* data, void* callbackData, void* reserved) {
    uint32_t *msg_key = (uint32_t*)callbackData;
    ...
}

trackleSetCompletedPublishCallback(trackle_s, callback_complete_publish);
```

{% endtab %}

{% tab title="C++" %}

```cpp
// SINTASSI
typedef void(publishCompletionCallback)(int error, const void *data, void *callbackData, void *reserved);
void setCompletedPublishCallback(publishCompletionCallback *publish);

// ESEMPIO
void callback_complete_publish(int error, const void* data, void* callbackData, void* reserved) {
    uint32_t *msg_key = (uint32_t*)callbackData;
    ...
}

Trackle.setCompletedPublishCallback(callback_complete_publish);
```

{% endtab %}
{% endtabs %}

### **Trackle.signalCallback**

chiamata quando è ricevuto un messaggio di tipo **signal** dal Cloud. Questa funzione è utile per identificare un dispositivo, per esempio, attraverso l'accensione di un led;

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
typedef void(signalCallback)(bool on, unsigned int param, void *reserved);
void trackleSetSignalCallback(Trackle *v, signalCallback *signal);

// ESEMPIO
void signal_cb(bool on, unsigned int param, void* reserved) {
    printf("signal_cb: %d %d\n", on, param);
}

trackleSetSignalCallback(trackle_s, signal_cb);
```

{% endtab %}

{% tab title="C++" %}

```cpp
// SINTASSI
typedef void(signalCallback)(bool on, unsigned int param, void *reserved);
void setSignalCallback(signalCallback *signal);

// ESEMPIO
void signal_cb(bool on, unsigned int param, void* reserved) {
    printf("signal_cb: %d %d\n", on, param);
}

Trackle.setSignalCallback(signal_cb);
```

{% endtab %}
{% endtabs %}

### **Trackle.systemTimeCallback**

chiamata quando viene ricevuto un messaggio di tipo system time dal Cloud (comunicazione orario del server).

{% tabs %}
{% tab title="C" %}

```c
// SINTASSI
typedef void(timeCallback)(time_t time, unsigned int param, void *);
void trackleSetSystemTimeCallback(Trackle *v, timeCallback *time);

// ESEMPIO

void system_time_cb(time_t time, unsigned int param, void*)
{
    printf("Server time is %lld\n", (long long)time);
}

trackleSetSystemTimeCallback(trackle_s, system_time_cb);
```

{% endtab %}

{% tab title="C++" %}

```cpp
// SINTASSI
typedef void(timeCallback)(time_t time, unsigned int param, void *);
void setSystemTimeCallback(timeCallback *signal);

// ESEMPIO
void system_time_cb(time_t time, unsigned int param, void*)
{
    printf("Server time is %lld\n", (long long)time);
}

Trackle.setSystemTimeCallback(system_time_cb);
```

{% endtab %}
{% endtabs %}


# Data Store

Trackle mette a disposizione di ogni dispositivo un **contenitore di dati in cloud** di tipo JSON. Pensa al Data Store come un **database chiave-valore condiviso** tra il firmware e le App, utile per sapere l'ultimo stato conosciuto di un dispositivo, per salvare le sue configurazioni oppure per aggregare informazioni di diversi dispositivi per esempio in un'applicazione di prodotto.

Il Data Store può essere scritto dalle App, attraverso le API REST, anche quando il dispositivo è offline. Per ogni modifica Trackle invia una notifica al dispositivo, immediata se è subito raggiungibile oppure nel momento in cui torna online. Questo meccanismo permette di costruire potenti logiche di sincronizzazione.

Un Data Store è composto da una o più chiavi, che chiamiamo *proprietà,* definite a livello di Prodotto. Ogni proprietà viene definita attraverso:

* il nome della chiave
* il tipo di dato (*Integer, Double, String, Boolean, Object, Array*)&#x20;
* il tipo di proprietà:
  * **Device only***:* La proprietà può essere scritta solo dal dispositivo.
  * **Cloud only**: La proprietà può essere scritta solo tramite le API Rest.
  * **Sync**: Le modifiche alla proprietà vengono sincronizzate con il dispositivo. Se offline, il dispositivo riceverà la modifica al prossimo handshake se effettuato entro il TTL impostato. Lasciare il TTL vuoto per validità infinita.
* la validità TTL in secondi (se tipo di proprietà *Sync***)**

Ogni dispositivo può aggiornare il valore del datastore per le proprietà di tipo Device only e/o Sync e può ricevere il valore aggiornato dal cloud per le proprietà ti tipo Sync.

## Trackle.syncState()

Aggiorna il data store in cloud attraverso l'invio di una stringa JSON dove ogni chiave deve essere una proprietà definita a livello di prodotto. Il dispositivo può aggiornare solo le proprietà di tipo Device only e/o Sync. Il dispositivo può aggiornare solo le chiavi necessarie.

La dimensione massima di invio è di 4Kb.

{% tabs %}
{% tab title="C" %}

<pre class="language-c"><code class="lang-c"><strong>// SINTASSI
</strong><strong>void trackleSyncState(Trackle * v, const char* data)
</strong>
// ESEMPIO
trackleSyncState(c, "{\"data\":\"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"}");
</code></pre>

{% endtab %}

{% tab title="C++" %}

```cpp
// SINTASSI
void syncState(const char* data)

// ESEMPIO
Trackle.syncState("{\"data\":\"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"}");
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Attenzione: il tipo di dato nel JSON inviato deve corrispondere al tipo di dato definito nel Data Store per quella proprietà altrimenti Trackle ignorerà l'aggiornamento
{% endhint %}

### Proprietà di tipo Object

Per le proprietà di tipo *Object* è possibile eseguire un aggiornamento parziale dell'oggetto del Data Store attraverso l'utilizzo nel JSON di chiavi del tipo "chiave.attributo". Per es. se nel Data Store ho specificato una proprietà di tipo Object con chiave *position* che tra i suoi attributi ha *lat, long, city, country* potrei voler aggiornare solo lat e long. In questo caso il dispositivo dovrebbe inviare un JSON cosi composto:

{% tabs %}
{% tab title="C" %}

```c
// ESEMPIO
trackleSyncState(c, "{\"position.lat\": 42.45, \"position.long\": 9.81 }");
```

{% endtab %}

{% tab title="C++" %}

```cpp
// ESEMPIO
Trackle.syncState("{\"data\":\"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"}");
```

{% endtab %}
{% endtabs %}

## Trackle.setUpdateStateCallback()

La callback viene chiamata quando viene ricevuta un aggiornamento dello store dal cloud per le proprietà ti tipo Sync.

* key: la chiave della proprietà
* value: il valore della proprietà

{% tabs %}
{% tab title="C" %}

```c
void update_state_callback(const char* key, const char *value) {
    // handle data(key, value)
}

trackleSetUpdateStateCallback(c, update_state_callback);
```

{% endtab %}

{% tab title="C++" %}

```cpp
void update_state_callback(const char* key, const char *value) {
    // handle data(key, value)
}

Trackle.setUpdateStateCallback(update_state_callback);
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Come documentato nel paragrafo precedente, la dimensione massima del valore della proprietà è di 1K
{% endhint %}

Se un software, attraverso le API REST, effettua una modifica allo store di una proprietà di tipo **Sync**, se il dispositivo è online riceverà subito l'aggiornamento, mentre se il device è offline, riceverà l'aggiornamento alla prima riconnessione se il TTL è valido.

{% hint style="info" %}
Se è necessario che il dispositivo riceva un comando in tempo reale, solamente se è online, è consigliabile utilizzare una Trackle POST.
{% endhint %}


# Aggiornamenti OTA

Una delle funzionalità più importanti di una piattaforma IoT è quella di permettere ai produttori di eseguire aggiornamenti firmware *over-the-air* ai loro dispositivi connessi. Potrebbe essere necessario aggiornare il firmware ad esempio per aggiungere nuove funzionalità o applicare patch di sicurezza.&#x20;

## Inizializzazione

Trackle Library supporta la funzionalità di aggiornamento OTA in due modi:

1. occupandosi dell'invio del firmware verso i dispositivi connessi garantendo la consegna e l'integrità del file (modalità *push*)
2. inviando al dispositivo le informazioni per eseguire il download del firmware (modalità *sendUrl*)

### Trackle.setOtaMethod

Imposta la modalità di aggiornamento OTA

{% tabs %}
{% tab title="C" %}

<pre class="language-c"><code class="lang-c"><strong>// SINTASSI
</strong><strong>void trackleSetOtaMethod(Trackle * v, Ota_Method method) {
</strong>
// ESEMPIO
trackleSetOtaMethod(trackle_s, PUSH); // Modalità push
trackleSetOtaMethod(trackle_s, SEND_URL); // Modalità sendUrl
</code></pre>

{% endtab %}

{% tab title="C++" %}

```cpp
// SINTASSI
void Trackle.setOtaMethod(Ota_Method method) {

// ESEMPIO
trackle.setOtaMethod(PUSH); // Modalità push
trackle.setOtaMethod(SEND_URL); // Modalità sendUrl
```

{% endtab %}
{% endtabs %}

## Modalità *PUSH*

Per implementare la modalità PUSH la libreria fornisce allo sviluppatore 3 callback: `prepareForFirmwareUpdateCallback()`,  `saveFirmwareChunkCallback()` e `finishFirmwareUpdateCallback()`.&#x20;

L'implementazione delle prime 2 non è obbligatoria; nel caso in cui lo sviluppatore decida di implementare solo la terza callback, all'avvio di un firmware update verrà creata una variabile in RAM della dimensione del firmware, verrà aggiornata alla ricezione di ogni chunk ed al termine verrà passata come parametro alla callback `finishFirmwareUpdateCallback()`. Se il dispositivo dispone di RAM limitata è vivamente consigliato implementare anche le prime due callback e scrivere i chunk ricevuti in flash.

### Trackle.**prepareForFirmwareUpdateCallback**

chiamata quando dal Cloud viene avviato un aggiornamento firmware e il dispositivo è pronto per riceverlo.  Riceve un parametro di tipo Chunk contentente:

* file\_length: dimensione totale del firmware;
* chunk\_size: dimensione di ogni singolo chunk;
* chunk\_count: numero totale di chunk;

{% tabs %}
{% tab title="C" %}

```c
void prepare_firmware_update(Chunk data, uint32_t flags, void* reserved) {
    printf("firmware size %d\n", data.file_length);
    printf("chunk size %d\n", data.chunk_size);
    printf("chunk count %d\n", data.chunk_count);
}

trackleSetPrepareForFirmwareUpdateCallback(c, prepare_firmware_update);
```

{% endtab %}

{% tab title="C++" %}

```cpp
void prepare_firmware_update(Chunk data, uint32_t flags, void* reserved) {
    printf("firmware size %d\n", data.file_length);
    printf("chunk size %d\n", data.chunk_size);
    printf("chunk count %d\n", data.chunk_count);
}

Trackle.setPrepareForFirmwareUpdateCallback(prepare_firmware_update);
```

{% endtab %}
{% endtabs %}

### Trackle.**saveFirmwareChunkCallback**

chiamata alla ricezione di ogni chunk durante un firmware update. Riceve un primo parametro di tipo Chunk contenente:

* file\_length: dimensione totale del firmware;
* chunk\_size: dimensione del chunk;
* chunk\_number: numero del chunk;
* chunk\_offset: posizione di memoria in cui scrivere la porzione di firmware.

Come secondo parametro riceve un `unsigned char*` contenente la porzione di firmware.

{% tabs %}
{% tab title="C" %}

```c
void save_firmware_chunk(struct Chunk data, const unsigned char* chunk, void* reserved) {
    printf("received chunk number %d\n", data.chunk_count);
}

trackleSetSaveFirmwareChunkCallback(c, save_firmware_chunk);
```

{% endtab %}

{% tab title="C++" %}

```cpp
void save_firmware_chunk(struct Chunk data, const unsigned char* chunk, void* reserved) {
    printf("received chunk number %d\n", data.chunk_count);
}

Trackle.setSaveFirmwareChunkCallback(save_firmware_chunk);
```

{% endtab %}
{% endtabs %}

### Trackle.**finishFirmwareUpdateCallback**

chiamata al termine della ricezione del firmware update. &#x20;

* Nel caso in cui non siano state implementate le 2 callback precedenti, riceve come primo parametro un `unsigned char*` contenente il firmware completo e come secondo parametro un `uint32_t` indicante la dimensione totale del firmware.
* Nel caso in cui siano state implementate le 2 callback precedenti, riceve come primo parametro un `unsigned char*` vuoto e come secondo parametro un `uint32_t` indicante la dimensione totale del firmware.

{% tabs %}
{% tab title="C" %}

```cpp
void finish_update(char *data, uint32_t fileSize) {
    printf("Finished fw update, received fw %d\n", fileSize);
}

trackleSetFinishFirmwareUpdateCallback(c, finish_update);
```

{% endtab %}

{% tab title="C++" %}

```cpp
void finish_update(char *data, uint32_t fileSize) {
    printf("Finished fw update, received fw %d\n", fileSize);
}

Trackle.setFinishFirmwareUpdateCallback(finish_update);
```

{% endtab %}
{% endtabs %}

## Modalità *SEND\_URL*

Trackle Library fornisce la possibilità di eseguire l'aggiornamento del firmware Over The Air (OTA) in modalità "send\_url". In questa modalità, il firmware viene notificato di un aggiornamento disponibile e vengono fornite le informazioni necessarie per scaricarlo da un URL e validarlo.

### Trackle.otaUpdateCallback

Questa callback viene chiamata dal Cloud quando è disponibile un aggiornamento firmware con il metodo sendUrl, contiene come parametri:

* url: L'URL da cui scaricare il firmware.
* crc: Il valore CRC32 del firmware. Può essere utilizzato per verificare l'integrità del firmware scaricato.

{% tabs %}
{% tab title="C" %}

```cpp
bool firmware_ota_url(const char *url, uint32_t crc) {
    // configure and start update task
    if (..... ) 
        return true; // if firmware download i
    else
        return false; // if error occured
}

trackleSetOtaUpdateCallback(c, firmware_ota_url);
```

{% endtab %}

{% tab title="C++" %}

```cpp
bool firmware_ota_url(const char *url, uint32_t crc) {
    // configure and start update task
    if (..... ) 
        return true; // if firmware download i
    else
        return false; // if error occured
}

Trackle.setOtaUpdateCallback(c, firmware_ota_url);
```

{% endtab %}
{% endtabs %}

La callback deve ritornare `true` se l'aggiornamento è stato iniziato con successo, `false` in caso di errore.

{% hint style="warning" %}
Questa callback è **bloccante** per l'esecuzione della libreria e deve ritornare un valore `boolean` il prima possibile.

Va utilizzata come notifica di avvio dell'aggiornamento OTA, ma il download e la scrittura del firmware devono essere eseguiti in un altro task non bloccante.
{% endhint %}

È possibile, per valutare l'integrità del file scaricato, confrontare il `crc` del file scaricato con quello contenuto nell'argomento della callback. Il crc (Cyclic Redundancy Check) utilizzato è di tipo CRC32 `Little Endian`.

Al termine dell'aggiornamento deve essere chiamato il metodo `setOtaUpdateDone`, che accetta il parametro `error_code`. Impostare `error_code` a 0 per indicare che l'aggiornamento è stato completato con successo. In caso di errore, impostare `error_code` con il valore corrispondente all'errore che si è verificato.

{% tabs %}
{% tab title="C" %}

```cpp
trackleSetOtaUpdateDone(c, 0);
trackleSetOtaUpdateDone(c, 3);
```

{% endtab %}

{% tab title="C++" %}

```cpp
Trackle.setOtaUpdateDone(0);
Trackle.setOtaUpdateDone(3);
```

{% endtab %}
{% endtabs %}

La mancata chiamata del metodo `setOtaUpdateDone` comporterà, lato Cloud, il fallimento dell'aggiornamento ota per timeout.

## Modalità *SEND\_URL* con verifica della firma

Trackle Library supporta la verifica del firmware durante un aggiornamento OTA in modalità **SEND\_URL.**\
Questa funzionalità aggiuntiva permette di validare la provenienza e l’integrità del firmware attraverso una firma digitale, aumentando significativamente la sicurezza del processo di aggiornamento.

### Trackle.setOtaVerificationKey

Imposta la chiave pubblica utilizzata per verificare la firma del firmware durante una procedura OTA.

* firmware\_key: array di byte contenente la chiave pubblica in formato DER.
* length: numero di byte dell’array della chiave.

{% tabs %}
{% tab title="C" %}

```cpp
trackleSetOtaVerificationKey(c, firmware_key, length);
```

{% endtab %}

{% tab title="C++" %}

```cpp
trackle.setOtaVerificationKey(firmware_key, length);
```

{% endtab %}
{% endtabs %}

Se questa funzione *non* viene chiamata, la verifica della firma è considerata **non attiva** e `verifyOtaSignature` restituirà 0.

### Trackle.verifyOtaSignature

Questa funzione che verifica la firma ricevuta dal Cloud durante un aggiornamento OTA in modalità SEND\_URL.

* firmware\_hash: hash SHA256 del firmware scaricato.
* length: unghezza dell’hash (32 byte).

{% tabs %}
{% tab title="C" %}

```cpp
trackleVerifyOtaSignature(c, hash, length);
```

{% endtab %}

{% tab title="C++" %}

```cpp
trackle.verifyOtaSignature(hash, length);
```

{% endtab %}
{% endtabs %}

**Valori restituiti**

* `1` → Firma verificata correttamente
* `0` → Verifica saltata (chiave non configurata)
* `-1` → Errore nella verifica della firma

La funzione deve essere chiamata **dopo il completamento del download** e solo dopo aver calcolato l’hash SHA256 completo del firmware.

### Integrazione della firma con il processo OTA SEND\_URL

Durante un aggiornamento OTA in modalità SEND\_URL:

1. Il Cloud invia:
   * URL del firmware
   * CRC32
   * Firma digitale del firmware
2. Il dispositivo deve:
   * Scaricare il firmware
   * Verificare il CRC32
   * Calcolare l’hash SHA256 del firmware
   * Eseguire `verifyOtaSignature()`
   * In caso di successo → procedere all’installazione -> inviare trackleSetOtaUpdateDone(trackle\_s, 0);&#x20;
   * In caso di errore → inviare errore OTA trackleSetOtaUpdateDone(trackle\_s, error\_code);

### Generazione della chiave pubblica per la verifica OTA

La firma OTA utilizza una coppia di chiavi **ECC P-256 (secp256r1)**.

* **La chiave privata** deve essere caricata nella [pagina di configurazione](/concetti-generali/prodotto) del prodotto
* **La chiave pubblica** deve essere incorporata nel firmware del dispositivo e viene usata dalla libreria per verificare la firma.

```
# 1) Generate the private key
openssl ecparam -name prime256v1 -genkey -noout -out private.pem

# 2) Generate the public key (PEM format)
openssl ec -in private.pem -pubout -out public.pem

# 3) Export the public key in DER format
openssl ec -in private.pem -pubout -outform DER -out public.der

# 4) Convert the DER public key into a C array
xxd -i public.der > firmware_key.c
```

> Nota: La chiave pubblica deve essere in formato DER per essere compatibile con il metodo `setOtaVerificationKey()`.

## Configurazione OTA

Trackle Library permette allo sviluppatore di poter controllare e far eseguire un aggiornamento firmware solo quando il dispositivo è disponibile a farlo.

Di default gli aggiornamenti sono attivi; lo sviluppatore può scegliere di disabilitare gli aggiornamenti in qualsiasi momento chiamando la funzione `Trackle.disableUpdates()` e riabilitarli nuovamente con `Trackle.enableUpdates()`.

Quando dal Cloud si lancia un aggiornamento in modalità "Fast", questo viene immediatamente eseguito non appena viene chiamato `Trackle.enableUpdates()` mentre in modalità "Standard" l'aggiornamento viene eseguito alla prima riconnessione del dispositivo al Cloud.

{% hint style="info" %}
La chiamata delle funzioni disableUpdates() ed enableUpdates()  genera un messaggio dal dispositivo al Cloud, comportando quindi l'utilizzo di una piccola quantità di dati.
{% endhint %}

### Trackle.disableUpdates()

Disabilita gli aggiornamenti OTA sul dispositivo. Se, dopo aver utilizzato questa funzione, si cerca di eseguire un aggiornamento dal Cloud, il dispositivo lo blocca ed invia un errore. Non sarà più possibile effettuare aggiornamenti OTA a meno di [abilitare la forzatura.](broken://pages/-M0SjKxhiCVUMbejE1MV#iotready-updatesforced)

Mentre gli aggiornamenti sono disabilitati se si tenta di inviare un aggiornamento firmware al dispositivo questo salverà l'informazione del fatto che c'è un aggiornamento in sospeso. Da quel momento la funzione `Trackle.updatesPending()` ritornerà `true`.

### Trackle.enableUpdates()

Abilita gli aggiornamenti OTA sul dispositivo. All'avvio del dispositivo gli aggiornamenti sono sempre abilitati.

Utilizzando questa funzione, se gli aggiornamenti non erano già attivi in precedenza, il dispositivo notificherà al Cloud la sua disponibilità.  Il Cloud a questo punto, se ha degli aggiornamenti da fare, li invierà al dipositivo.&#x20;

### Trackle.updatesEnabled()

Ritorna `true` all'avvio e dopo aver chiamato la funzione`System.enableUpdates()`. Ritorna `false` dopo aver chiamato la funzione `System.disableUpdates()`.

### Trackle.updatesPending()&#x20;

Ritorna `true` se per il dispositivo ci sono aggiornamenti firmware in sospeso. Nel caso in cui gli aggiornamenti siano disabilitati, è possibile effettuare l'aggiornamento o chiamando la funzione `Trackle.enableUpdates()` o inviando un update forzato dal Cloud.

### Trackle.updatesForced()

Quando il dispositivo non è disponibile per gli aggiornamenti, l'aggiornamento del firmware in sospeso non viene normalmente consegnato al dispositivo. Gli aggiornamenti possono essere forzati nel Cloud per ignorare l'impostazione locale sul dispositivo. In questo caso la funzione `Trackle.updatesForced()` ritorna `true`.

In questo modo gli aggiornamenti del firmware vengono consegnati anche quando `System.disableUpdates()` è stato chiamato dall'applicazione del dispositivo.&#x20;


# Diagnostica

Trackle Library offre un set di metodi per inviare le informazioni relative allo stato di salute del dispositivo al Cloud. Questa funzionalità è essenziale per il monitoraggio dei potenziali problemi hardware o di rete che possono emergere dopo l'installazione dei dispositivi sul campo.

Le informazioni di diagnostica (HealthCheck) sono suddivise in tre categorie: **Cloud**, **System e Network.**  La diagnostica Cloud (numero di connessioni, disconnessioni, roundtrip time, ecc...) viene gestita in maniera autonoma dalla Trackle Library.

Per configurare, valorizzare o aggiornare un dato di diagnostica deve essere chiamata la funzione specifica della categoria (**System** o **Network**), indicando la "chiave" del parametro (elencate sotto) ed il relativo valore.&#x20;

Ad esempio, per aggiornare il numero di tentativi di connessione alla rete e specificare la memoria ram totale del dispositivo, devono essere utilizzati i seguenti comandi:

{% tabs %}
{% tab title="C" %}

```c
trackleDiagnosticNetwork(trackle_s, NETWORK_CONNECTION_ATTEMPTS, 10);
trackleDiagnosticSystem(trackle_s, SYSTEM_TOTAL_RAM, 10000);
```

{% endtab %}

{% tab title="C++" %}

```cpp
Trackle.diagnosticNetwork(NETWORK_CONNECTION_ATTEMPTS, 10);
Tracklr.diagnosticSystem(SYSTEM_TOTAL_RAM, 10000);
```

{% endtab %}
{% endtabs %}

**System**: informazioni sul funzionamento del sistema:

* **SYSTEM\_LAST\_RESET\_REASON**: Fornisce il motivo dell'ultimo reset del sistema. Può aiutare a identificare la causa di riavvii o reset del dispositivo.
* **SYSTEM\_FREE\_MEMORY**: Restituisce la quantità di memoria libera disponibile nel sistema. Questa chiave può essere utilizzata per monitorare l'utilizzo della memoria.
* **SYSTEM\_BATTERY\_CHARGE**: Rappresenta lo stato di carica della batteria del dispositivo in percentuale. Questa chiave fornisce informazioni sulla carica residua della batteria.
* **SYSTEM\_SYSTEM\_LOOPS**: Fornisce il numero di cicli di loop del sistema. Questa chiave può essere utilizzata per tenere traccia del numero di iterazioni del ciclo principale del sistema.
* **SYSTEM\_APPLICATION\_LOOPS**: Restituisce il numero di cicli di loop dell'applicazione. Questa chiave può essere utilizzata per monitorare il numero di iterazioni del ciclo dell'applicazione.
* **SYSTEM\_UPTIME**: Rappresenta il tempo trascorso (in secondi) dall'ultima accensione o riavvio del dispositivo.&#x20;
* **SYSTEM\_BATTERY\_STATE**: Lo stato di carica attuale della batteria. I possibili valori sono *unknown, not\_charging, charging, charged, discharging, fault, disconnected.*
* **SYSTEM\_POWER\_SOURCE**: Un'enumerazione che descrive la fonte di energia attuale. I valori possibili sono *unknown, VIN, USB host, USB adapter, USB otg.*
* **SYSTEM\_TOTAL\_RAM**:  la memoria RAM totale a disposizione del dispostivo.
* **SYSTEM\_USED\_RAM**: la memoria RAM utilizzata dal dispositivo.

**Network**: informazioni sulla rete e sulla qualità della connessione:

* **NETWORK\_CONNECTION\_STATUS**: Stato della connessione di rete. Quando si riceve un evento vitale attraverso il Cloud è necessario che sia sempre connesso. I valori possibili sono *turned\_off, turning\_on, disconnected, connecting, connected, disconnecting, turning\_off.*
* **NETWORK\_CONNECTION\_ERROR\_CODE:** Un codice di errore specifico della piattaforma restituito dalla funzione di basso livello del sistema operativo del dispositivo per l'evento di connettività di rete più recente.
* **NETWORK\_DISCONNECTS:** Conteggio delle disconnessioni di rete .
* **NETWORK\_CONNECTION\_ATTEMPTS**: Numero di tentativi necessari per stabilire una connessione di rete .
* **NETWORK\_DISCONNECTION\_REASON**: Ultimo motivo per cui la rete si è disconnessa. I valori possibili sono *none, error, user, network\_off, listening, sleep, reset.*
* **NETWORK\_IPV4\_ADDRESS**: Rappresenta l'indirizzo IPv4 locale del dispositivo. Questo indirizzo è utile per identificare il dispositivo sulla rete.
* **NETWORK\_IPV4\_GATEWAY**: Restituisce l'indirizzo del gateway IPv4 utilizzato dal dispositivo per comunicare con la rete.
* **NETWORK\_RSSI:** misura stimata di quanto bene un dispositivo può sentire, rilevare e ricevere segnali da qualsiasi punto di accesso o da un router specifico.&#x20;
* **NETWORK\_SIGNAL\_STRENGTH\_VALUE:** valore della potenza del segnale utile ricevuto. Varia, circa, da -30 (valore migliore) a -130 (valore peggiore).
* **NETWORK\_SIGNAL\_STRENGTH:** valore della potenza del segnale in percentuale; più si avvicina a 100, più alta è la qualità. Come regola generale, più il dispositivo è vicino a una torre o un router, maggiore sarà la potenza del segnale.
* **NETWORK\_SIGNAL\_QUALITY:** valore della qualità del segnale in percentuale; più si avvicina a 100, più alta è la qualità. Come regola generale, minore è il numero di dispositivi nelle immediate vicinanze che comunicano utilizzando frequenze radio simili, migliore sarà la qualità del segnale
* **NETWORK\_SIGNAL\_QUALITY\_VALUE:** valore che indica il grado di qualità del segnale agganciato considerando gli RB (Resource Block) allocati, l'SNR ed influenzato dal carico della cella specifica agganciata. Varia da -1 (valore migliore) a -20 (valore peggiore). Il range "ottimale" si aggira dai -6 ai -9 e quello di "carico" dai -10 ai -13/-14.
* **NETWORK\_ACCESS\_TECNHOLOGY:** tipo di tecnologia utilizzata (*GSM*, *LTE*, *WI-FI*, ecc...) di tipo `hal_net_access_tech_t`
* **NETWORK\_CELLULAR\_CELL\_GLOBAL\_IDENTITY\_MOBILE\_COUNTRY\_CODE:** valore decimale che identifica la nazionalità dell'operatore all'interno della rete PLMN (Public Land Mobile Network) GSM. Per l'Italia questo codice è 222.
* **NETWORK\_CELLULAR\_CELL\_GLOBAL\_IDENTITY\_MOBILE\_NETWORK\_CODE:** valore decimale che identifica univocamente l'operatore all'interno della PLMN nazionale. In Italia per esempio, vale 01 per Tim, 10 per Vodafone, 88 per Wind.
* **NETWORK\_CELLULAR\_CELL\_GLOBAL\_IDENTITY\_LOCATION\_AREA\_CODE:** valore che identifica univocamente una Location Area entro una PLMN. La grandezza di quest'area non è fissa, ma dipende da come è progettata la rete.
* **NETWORK\_CELLULAR\_CELL\_GLOBAL\_IDENTITY\_CELL\_ID:** valore che identifica la cella attualmente agganciata. Questo numero dipende dal gestore e dall'area.
* **NETWORK\_MAC\_ADDRESS\_OUI**: Restituisce il primo componente dell'indirizzo MAC (Organizationally Unique Identifier) associato al dispositivo.
* **NETWORK\_MAC\_ADDRESS\_NIC**: Restituisce il secondo componente dell'indirizzo MAC (Network Interface Card) associato al dispositivo.

La diaagnostica del dispositivo può essere inviata al Cloud in modo automatico o manuale. \
Per configurare l'invio automatico è necessario definire l'intervallo di tempo, in millisecondi, tra due invii consecutivi attraverso la funzione `setPublishHealthCheckInterval(uint32_t interval)`.

L'invio manuale avviene chiamando funzione `publishHealthCheck().`

{% tabs %}
{% tab title="C" %}

```c
// invia la diagnostica ogni minuto
trackleSetPublishHealthCheckInterval(trackle_s, 60*1000);

// invia la diagnostica
tracklePublishHealthCheck(trackle_s);
```

{% endtab %}

{% tab title="C++" %}

```cpp
// invia la diagnostica ogni minuto
Trackle.setPublishHealthCheckInterval(60*1000);

// invia la diagnostica
Trackle.publishHealthCheck();
```

{% endtab %}
{% endtabs %}


# Panoramica

Trackle Cloud

Trackle è la piattaforma che mette il potere nelle tue mani, consentendoti di esplorare nuove possibilità, automatizzare i processi e mantenere un controllo totale. Scopri come Trackle può trasformare la tua visione IoT in realtà.

## Caratteristiche

### API Rest

Le API Rest sono progettate per semplificare il flusso di dati tra dispositivi IoT e App. Offrono un accesso a tutte le funzionalità di Trackle e consentono l'interazione remota con i dispositivi, attraverso l'invio di comandi e la lettura di dati in tempo reale. Integra i dati con i tuoi sistemi esistenti e con le tue soluzioni di visualizzazione preferite.

### Integrazioni

Puoi configurare azioni personalizzate basate su determinati tipi di messaggi inviati dai tuoi dispositivi. Attraverso web hook i tuoi dispositivi possono interagire con servizi esterni, consentendo di compiere azioni come il salvataggio di dati o l'invio di notifiche e allarmi.

### Aggiornamenti firmware OTA

Un aggiornamento OTA di successo richiede un coordinamento complesso tra hardware, firmware del dispositivo, connettività e cloud. Trackle offre un sistema integrato per permetterti di aggiornare il software dei tuoi dispositivi in modo sicuro e affidabile sia per un dispositivo singolo che per l'intera flotta di prodotto direttamente dalla console o dalle API REST.

### Sicurezza

Trackle nasce *Secure by design* così ti puoi concentrare sul tuo progetto IoT senza preoccuparti degli aspetti di sicurezza della comunicazione. Trackle crea e mantiene una comunicazione sicura tra il firmware e il cloud e si occupa di gestire le autorizzazioni delle App per l'accesso alle risorse in modo tale che solo le App autorizzate possano eseguire operazioni sui dispositivi associati ai loro utenti.


# Cloud API

Le API Cloud di Trackle sono di tipo [REST](http://en.wikipedia.org/wiki/Representational_State_Transfer). REST significa che usiamo l'URL nel modo per cui è stato creato ovvero come "Uniform Resource Locator".&#x20;

La risorsa in questione è il **Dispositivo**. Ogni dispositivo diventa un endpoint, che può essere usato per fare una richiesta `GET` e ottenere un valore, o una `POST` per chiamare una funzione remota o una `PUT` per aggiornare il firmware.&#x20;

Tutte le richieste ai dispositivi passano attraverso il nostro API Gateway con sicurezza **TLS**.

```bash
INDIRIZZO E PROTOCOLLO
"https://api.trackle.io"
```

{% hint style="info" %}
Quando scriviamo un nome preceduto da `:`, quel nome deve essere sostituito con un informazione di tua proprietà. Per esempio quando trovi un URL come `/v1/devices/:deviceId` devi sostituirlo con qualcosa tipo `/v1/devices/e00fce68166c3bbe15df4c6b`
{% endhint %}

## Formato

Le API REST accettano richieste in [JSON](https://www.w3schools.com/js/js_json_intro.asp) (content type `application/json`) e in [form encoded format](https://en.wikipedia.org/wiki/POST_\(HTTP%29) (content type `application/x-www-form-urlencoded`). Rispondono sempre con un JSON (content type `application/json`).

```aspnet
# Esempio in form encoded format
curl https://api.trackle.io/v1/devices/e00fce68166c3bbe15df4c6b/action \
     -d args=open \
     -d access_token=...

# Esempio con JSON
curl "https://api.trackle.io/v1/devices/e00fce68166c3bbe15df4c6b/action?access_token=..." \
     -H "Content-Type: application/json" \
     -d "{\"args\": \"open\"}"
```

In questa documentazione trovi esempi di chiamate alle API utilizzando un programma da terminale chiamato [curl](https://curl.haxx.se/) che dovrebbe essere già installato sul tuo PC.

Gli esempi usano *form encoded data* cosi da essere più semplici da leggere e da capire ma tutti gli endpoint accettano anche oggetti JSON come parametri.

## Errori

Le API Cloud di Trackle usano i codici di risposta HTTP convenzionali per indicare il successo o il fallimento di una richiesta alle API.  In generale: i codici `2xx` indicano successo. I codici `4xx`indicano un errore che riguarda le informazioni fornite (es., un parametro richiesto è stato omesso, etc.).


# Limiti alle richieste

Le richieste alle API sono limitate approssimativamente a 10 al secondo verso `api.trackle.io` per ogni IP pubblico. Questo limite è il numero totale di chiamate da un IP pubblico e non dipendono dall'access token utilizzato o dall'endpoint della richiesta.

## **Attenzione a monitorare i cambiamenti delle variabili** <a href="#beware-of-monitoring-variables-for-change" id="beware-of-monitoring-variables-for-change"></a>

Una situazione che può causare problemi è il continuo monitoraggio delle variabili in attesa di cambiamenti. Se il software sta in polling ogni secondo su una variabile per un solo dispositivo non ci sono problemi, ma se vengono monitorati molti dispositivi, si può facilmente superare il limite.

E' molto più efficiente chiamare `Trackle.publish()` dal dispositivo quando un valore cambia.

## **Gestisci gli errori nel modo corretto** <a href="#make-sure-you-handle-error-conditions-properly" id="make-sure-you-handle-error-conditions-properly"></a>

Se il software ottiene un errore `401 (Unauthorized)` significa che l'access\_token è sicuramente scaduto. Continuare a riprovare con la stessa richiesta non cambia il risultato.

Se si ottiene un errore `429 (Too many requests)` significa che si è già superato il limite delle richieste, quindi continuare con le richieste non aiuta.&#x20;

In generale in caso di errore si può considerare di aspettare un pò prima di eseguire la richiesta successiva.


# Autenticazione

I permessi per controllare e comunicare con i tuoi dispositivi sono gestiti con [OAuth 2.0](https://oauth.net/2/). Per poter accedere alle risorse protette OAuth 2.0 devi utilizzare gli **Access Token**. Un Access Token è una stringa che rappresenta la concessione del permesso. Le API Cloud di Trackle generano un Access Token nel formato [JSON Web Token (JWT)](https://jwt.io/).‌

Quando connetti un dispositivo al cloud per la prima volta, questo non avrà alcun **proprietario** quindi nessuno, eccetto nel caso di un dispositivo associato ad un Prodotto, avrà il permesso di controllarlo. Per poter associare un dispositivo ad un account e quindi poterlo controllare è necessario associarlo tramite la **procedura di claim**, direttamente dalla Console o tramite un codice di claim. Dopo questa operazione solo quell'account avrà il permesso di controllare il dispositivo con il suo access token.

Il permesso di accedere alle informazioni di un dispositivo può essere concesso anche ad App di terze parti tramite la creazione di speciali [Client OAuth](broken://pages/-M0PWjJm66hrpH_q0C8G) tramite i quali è possibile generare degli access token con i permessi per il monitoraggio e il controllo del dispositivo.

## Come inviare l'access token nelle richieste alle API <a href="#how-to-send-your-access-token" id="how-to-send-your-access-token"></a>

Ci sono 3 modi per inviare l'access token nelle richieste:

* tramite HTTP Authorization header (funziona per tutte le richieste)
* nell'URL come parametro query (funziona solo con le richieste di tipo GET)
* come request body (funziona solo con POST, PUT e DELETE quando il content-type è form encoded)

Per inviare un header custom usando curl basta aggiungere il flag `-H.` L'access token è chiamato token "Bearer" e viene scritto nell'HTTP `Authorization` header.

```
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCI..." \
  https://...
```

La stringa dei parametri è la parte dell'URL dopo il punto di domanda `?`. Per inviare l'access token come parametro query basta aggiungere `access_token=38bb...`. L'intero URL deve essere racchiuso tra apici doppi, altrimenti curl pensa che il punto di domanda sia un carattere speciale.

```
curl "https://api.trackle.io/v1/devices?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCI..."
```

La request body è usata per inviare il contenuto delle form nel web. Usando curl, ogni parametro che viene inviato, incluso l'access token, è preceduto da un flag `-d`. Di default, se si aggiunge un flag `-d`, curl pensa che la richiesta sia una POST. Se si vuole fare una richiesta di un altro tipo è necessario aggiungere il flag `-X`, per esempio `-X PUT`.

```
curl -d access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCI... \
  https://...
```

## Genera un access token

<mark style="color:green;">`POST`</mark> `https://api.trackle.io/oauth/token`

Crea un access token che ti da accesso alle API Cloud. Devi inviare un OAuth Client ID e secret validi in HTTP Basic Auth oppure come parametri `client_id` e `client_secret`. Questo endpoint accetta solo richieste di tipo *form encoded*.

#### Headers

| Name          | Type   | Description                                                                                  |
| ------------- | ------ | -------------------------------------------------------------------------------------------- |
| Authorization | string | HTTP Basic Auth dove lo username è il OAuth client\_id e la password è OAuth client\_secret. |

#### Request Body

| Name                                          | Type   | Description                                                             |
| --------------------------------------------- | ------ | ----------------------------------------------------------------------- |
| client\_id                                    | string | OAuth client\_id. Richiesto solo se non si usa Authorization header     |
| client\_secret                                | string | OAuth client\_secret. Richiesto solo se non si usa Authorization header |
| grant\_type<mark style="color:red;">\*</mark> | string | OAuth grant type `authorization_code` o`client_credentials`             |

{% tabs %}
{% tab title="200 " %}

```
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik...",
  "refresh_token": "Ogv4O0c84021LfH7051L9Z2QQVMfpZGh7im2t2zy9xETR",
  "scope": "offline_access",
  "expires_in": 604800,
  "token_type": "Bearer"
}
```

{% endtab %}

{% tab title="400 " %}

```
{
  "error": "{\"error\":\"invalid_grant\",\"error_description\":\"Wrong email or password.\"}",
  "ok": false
}
```

{% endtab %}
{% endtabs %}

## Rigenera un access token (refresh)

<mark style="color:green;">`POST`</mark> `https://api.trackle.io/oauth/token`

Rigenera un access token quando è scaduto.

#### Headers

| Name          | Type   | Description                                                                                                                |
| ------------- | ------ | -------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | HTTP Basic Auth dove lo username è OAuth client\_id e la password è OAuth client\_secret. Si può usare `iotready:iotready` |

#### Request Body

| Name                                             | Type   | Description                                                             |
| ------------------------------------------------ | ------ | ----------------------------------------------------------------------- |
| client\_id                                       | string | OAuth client\_id. Richiesto solo se non si usa Authorization header     |
| client\_secret                                   | string | OAuth client\_secret. Richiesto solo se non si usa Authorization header |
| grant\_type<mark style="color:red;">\*</mark>    | string | OAuth grant type. Usare `refresh_token`                                 |
| refresh\_token<mark style="color:red;">\*</mark> | string | Il refresh token                                                        |

{% tabs %}
{% tab title="200 " %}

```
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik...",
  "scope": "offline_access",
  "expires_in": 604800,
  "token_type": "Bearer"
}
```

{% endtab %}

{% tab title="400 " %}

```
{
  "error": "{\"error\":\"invalid_grant\",\"error_description\":\"Unknown or invalid refresh token.\"}",
  "ok": false
}
```

{% endtab %}
{% endtabs %}


# Client OAuth

Un Client OAuth solitamente rappresenta un App o un applicativo di terze parti a cui si vuol dare il permesso di controllare dei dispositivi o un Prodotto. Ognuno può creare i propri client. E' buona norma creare un client per ogni applicazione web e mobile che vuole fare richieste alle API REST.

{% hint style="danger" %}
**NON esporre mai il client secret al browser.**  Se, per esempio, hai un client che controlla un Prodotto e usi il client secret nel tuo front-end Javascript, un cliente finale che capisce qualcosa di programmazione potrebbe leggere il token attraverso gli strumenti per sviluppatori e utilizzarlo per avere accesso a tutti i dispositivi di quel prodotto.
{% endhint %}

Gli endpoint di Client OAuth possono essere usati anche come endpoint di Client OAuth per Prodotto sostituendo `/v1/clients` con `/v1/products/:productIdOrSlug/clients`.

## Lista dei Client OAuth

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/clients`

Ottieni una lista dei Client OAuth generati dall'utente autenticato oppure associati ad certo Prodotto

#### Path Parameters

| Name            | Type   | Description                                      |
| --------------- | ------ | ------------------------------------------------ |
| productIdOrSlug | string | ID Prodotto o Slug. *Solo per endpoint Prodotto* |

{% tabs %}
{% tab title="200 " %}

```
[
  {
    "id": "aXTUYF51s73TTEh1p4UBWd4k3YM2n6zR",
    "name": "myapp-0c27",
    "org_id": "5c23bd281402995be8561342",
    "scope": "create:customers",
    "secret": "mOfcyM4XaT2RZT4dC-IVBLZnvXpw2seA-QkdKLvGHv_WGBolbs_a7xx2Nz3YSMms",
    "type": "installed"
  }

```

{% endtab %}
{% endtabs %}


# Dispositivi

I **Dispositivi** sono associati al tuo account e possono essere parte di un **Prodotto**. Un Prodotto identifica un gruppo di dispositivi con lo stesso hardware e le stesse funzionalità. Ogni Prodotto ha la sua flotta di dispositivi associati.

Gli endpoint per i dispositivi possono essere usati anche per i dispositivi che sono parte di un prodotto sostituendo `/v1/devices` con `/v1/products/:productIdOrSlug/devices`.

## Lista dispositivi

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/devices`

Ottieni la lista dei dispositivi a cui l'utente autenticato ha accesso. Di default, la lista è ordinata per `last_heard` in ordine decrescente.

{% tabs %}
{% tab title="200 " %}

```
[
  {
    "cellular": true,
    "connected": true,
    "firmware_product_id": 200,
    "firmware_updates_enabled": true,
    "firmware_updates_forced": true,
    "firmware_version": 4,
    "functions": [
      "led",
    ],
    "iccid": "8944502009199676656",
    "id": "e00fce68c6ea8328e3e3b0c7",
    "imei": "357520078513989",
    "last_heard": "2020-02-17T18:00:00.185Z",
    "last_ip_address": "5.35.166.154",
    "name": "Lamp",
    "platform_id": 13,
    "product_id": 200,
    "status": "normal",
    "subscriptions": [],
    "system_firmware_version": "1406",
    "variables": {
      "ledStatus": "int32"
    }
  }
]
```

{% endtab %}
{% endtabs %}

## Lista dispositivi associati ad un prodotto

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/products/:productIdOrSlug/devices`

Ottieni la lista dei dispositivi che sono parte di un Prodotto.

#### Path Parameters

| Name            | Type   | Description        |
| --------------- | ------ | ------------------ |
| productIdOrSlug | string | ID prodotto o Slug |

#### Query Parameters

| Name   | Type   | Description                                                                                                                 |
| ------ | ------ | --------------------------------------------------------------------------------------------------------------------------- |
| groups | string | Lista di nomi di gruppo separati da virgola per filtrare i risultati mostrando solo i dispostivi appartenenti a quei gruppi |

{% tabs %}
{% tab title="200 " %}

```
{
  "customers": [],
  "devices": [
    {
      "cellular": false,
      "connected": true,
      "desired_firmware_version": null,
      "firmware_product_id": 100,
      "firmware_updates_enabled": true,
      "firmware_updates_forced": false,
      "firmware_version": 3,
      "functions": [
        "action"
      ],
      "groups": ["bergamo", "milano"],
      "id": "0fd152b041ec15f00cd6ab96",
      "last_heard": "2020-02-18T13:30:23.229Z",
      "last_ip_address": "78.6.28.126",
      "name": "Fancoil",
      "owner": null,
      "plan": "SF2019",
      "product_id": 100,
      "status": "normal",
      "user_id": "5a0e06b7a9829d48f6e2fb3b",
      "variables": {
        "temp": "int32",
        "status": "boolean"
      }
    } 
  ]
}
```

{% endtab %}

{% tab title="404 " %}

```
{
  "error": "Product not found",
  "ok": false
}
```

{% endtab %}
{% endtabs %}

## Ottieni informazioni di un dispositivo

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/devices/:deviceID`

Ottieni le informazioni di un singolo dispositivo, incluse le variabili e le funzioni esposte.

#### Path Parameters

| Name            | Type   | Description                                      |
| --------------- | ------ | ------------------------------------------------ |
| deviceID        | string | ID Dispositivo                                   |
| productIdOrSlug | string | ID Prodotto o Slug. *Solo per endpoint Prodotto* |

{% tabs %}
{% tab title="200 " %}

```
{
  "cellular": true,
  "connected": true,
  "firmware_product_id": 200,
  "firmware_updates_enabled": true,
  "firmware_updates_forced": false,
  "firmware_version": 4,
  "functions": [
    "led",
  ],
  "groups": [], // Product endpoint only
  "iccid": "8944502009199676656",
  "id": "e00fce68c6ea8328e3e3b0c7",
  "imei": "357520078513989",
  "last_heard": "2020-02-17T18:00:00.185Z",
  "last_ip_address": "5.35.166.154",
  "name": "Lamp",
  "platform_id": 13,
  "product_id": 200,
  "status": "normal",
  "subscriptions": [],
  "system_firmware_version": "1406",
  "variables": {
    "ledStatus": "int32"
  }
}
```

{% endtab %}

{% tab title="404 " %}

```
{
  "error": "No device found",
  "ok": false
}
```

{% endtab %}
{% endtabs %}

## Ottieni il valore di una variabile

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/devices/:deviceID/:variableName`

Ottieni il valore corrente di una variabile esposta dal dispsitivo. Le variabili possono essere lette da un dispositivo di cui l'utente è proprietario oppure da uno che è parte di un Prodotto di cui l'utente è nel team.

#### Path Parameters

| Name            | Type   | Description                                      |
| --------------- | ------ | ------------------------------------------------ |
| deviceID        | string | ID Dispositivo                                   |
| variableName    | string | Nome variabile                                   |
| productIdOrSlug | string | ID Prodotto o Slug. *Solo per endpoint Prodotto* |

{% tabs %}
{% tab title="200 " %}

```
{
  "id": "e00fce68c6ea8328e3e3b0c7",
  "name": "ledStatus",
  "result": 1
}
```

{% endtab %}

{% tab title="404 " %}

```
{
  "error": "Variable not found",
  "ok": false
}
```

{% endtab %}
{% endtabs %}

## Chiama una funzione remota

<mark style="color:green;">`POST`</mark> `https://api.trackle.io/v1/devices/:deviceID/:functionName`

Chiama una funzione remota esposta dal dispositivo passando un parametro di tipo stringa. Le funzioni possono essere chiamate da un dispositivo di cui l'utente è proprietario oppure da uno che è parte di un Prodotto di cui l'utente è nel team.

#### Path Parameters

| Name            | Type   | Description                                      |
| --------------- | ------ | ------------------------------------------------ |
| deviceID        | string | ID Dispositivo                                   |
| functionName    | string | Nome della funzione                              |
| productIdOrSlug | string | ID Prodotto o Slug. *Solo per endpoint Prodotto* |

#### Request Body

| Name | Type   | Description                                                     |
| ---- | ------ | --------------------------------------------------------------- |
| args | string | Parametri della funzione con lunghezza massima di 622 caratteri |

{% tabs %}
{% tab title="200 " %}

```
{
  "id": "e00fce68c6ea8328e3e3b0c7",
  "name": "led",
  "return_value": 1
}
```

{% endtab %}

{% tab title="404 " %}

```
{
  "error": "Function not found",
  "ok": false
}
```

{% endtab %}
{% endtabs %}

## Pinga un dispositivo

<mark style="color:orange;">`PUT`</mark> `https://api.trackle.io/v1/devices/:deviceID/ping`

#### Path Parameters

| Name            | Type   | Description                                       |
| --------------- | ------ | ------------------------------------------------- |
| deviceID        | string | ID Dispositivo                                    |
| productIdOrSlug | string | ID Prodotto or Slug. *Solo per endpoint Prodotto* |

{% tabs %}
{% tab title="200 " %}

```
{
  "lastHeard": "2020-02-18T10:28:38.299Z",
  "online": true
}
```

{% endtab %}

{% tab title="400 " %}

```
{
  "error": "Could not get device for ID. Probably is not connected.",
  "ok": false
}
```

{% endtab %}
{% endtabs %}

## Genera un codice di claim

<mark style="color:green;">`POST`</mark> `https://api.trackle.io/v1/device_claims`

Genera un codice di claim che permette di ad un utente di Trackle di diventare proprietario di un dispositivo e quindi avere il permesso di monitorarlo e controllarlo. Usando l'endpoint di Prodotto è possibile generare un codice di claim per un dispositivo parte di un Prodotto. Il codice deve essere generato usando l'access token dell'utente che vuole diventare proprietario del dispositivo.

#### Path Parameters

| Name            | Type   | Description                                    |
| --------------- | ------ | ---------------------------------------------- |
| productIdOrSlug | string | ID Prodotto o Slug. Solo per endpoint Prodotto |

{% tabs %}
{% tab title="200 " %}

```
{
    "claim_code":"AAG9+vGe430aUxwQAx8xji/RQspya87h+qZSBikjpo6uGVl/lxH9xR+a3bXZNAx",
    "device_ids":[]
}
```

{% endtab %}
{% endtabs %}


# Eventi

I **Dispositivi** inviano eventi al Cloud ogni volta che si connettono e ogni volta che viene chiamato `Trackle.publish()`. E' possibile ricevere gli eventi dei dispositivi attraverso una tecnologia che sfrutta un flusso di dati HTTP chiamata [Server-Sent Events (SSEs)](https://www.w3.org/TR/eventsource/).

{% hint style="info" %}
Puoi filtrare gli eventi da ricevere specificando un *eventName.* Gli eventi ricevuti saranno limitati a quelli con il filtro specificato. Per esempio specificando il nome evento `temp` saranno ricevuti tutti gli eventi il cui nome inizia con `temp`.
{% endhint %}

## Ottieni un flusso di eventi pubblici

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/events/[:eventName]`

Open a stream of Server Sent Events for all public events.

#### Path Parameters

| Name        | Type   | Description                                                                                   |
| ----------- | ------ | --------------------------------------------------------------------------------------------- |
| eventPrefix | string | Filters the stream to only events starting with the specified prefix. Omit to get all events. |

{% tabs %}
{% tab title="200 " %}

```
:ok

event: temperature
data: {"data":"25.34","ttl":"60","published_at":"2015-07-18T00:12:18.174Z","coreid":"0123456789abcdef0123456
```

{% endtab %}
{% endtabs %}

## Ottieni un flusso di eventi dei tuoi dispositivi

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/devices/events/[:eventName]`

Open a stream of Server Sent Events for all public and private events for your devices.

#### Path Parameters

| Name        | Type   | Description                                                                                   |
| ----------- | ------ | --------------------------------------------------------------------------------------------- |
| eventPrefix | string | Filters the stream to only events starting with the specified prefix. Omit to get all events. |

{% tabs %}
{% tab title="200 " %}

```
:ok

event: temperature
data: {"data":"25.34","ttl":"60","published_at":"2015-07-18T00:12:18.174Z","coreid":"0123456789abcdef0123456
```

{% endtab %}
{% endtabs %}

## Ottieni un flusso di eventi per un dispositivo

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/devices/:deviceID/events/[:eventName]`

Open a stream of Server Sent Events for all public and private events for the specified device.

#### Path Parameters

| Name        | Type   | Description                                                                                   |
| ----------- | ------ | --------------------------------------------------------------------------------------------- |
| deviceID    | string | Device ID                                                                                     |
| eventPrefix | string | Filters the stream to only events starting with the specified prefix. Omit to get all events. |

{% tabs %}
{% tab title="200 " %}

```
:ok

event: temperature
data: {"data":"25.34","ttl":"60","published_at":"2015-07-18T00:12:18.174Z","coreid":"0123456789abcdef01234567"}
```

{% endtab %}
{% endtabs %}

## Ottieni un flusso di eventi di un prodotto

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/products/:productIdOrSlug/devices/events/[:eventName]`

Open a stream of Server Sent Events for all public and private events for a product.

#### Path Parameters

| Name            | Type   | Description                                                                                  |
| --------------- | ------ | -------------------------------------------------------------------------------------------- |
| productIdOrSlug | string | Product ID or Slug                                                                           |
| eventPrefix     | string | Filters the stream to only events starting with the specified prefix. Omit to get all events |

{% tabs %}
{% tab title="200 " %}

```
:ok

event: temperature
data: {"data":"25.34","ttl":"60","published_at":"2015-07-18T00:12:18.174Z","coreid":"0123456789abcdef01234567"}
```

{% endtab %}
{% endtabs %}

## Ottieni un flusso di eventi di un dispositivo parte di un prodotto

<mark style="color:blue;">`GET`</mark> `https://api.trackle.io/v1/products/:productIdOrSlug/devices/:deviceID/events/[:eventName]`

Open a stream of Server Sent Events scoped to a particular device in a product

#### Path Parameters

| Name            | Type   | Description                                                                                  |
| --------------- | ------ | -------------------------------------------------------------------------------------------- |
| productIdOrSlug | string | Product ID or Slug                                                                           |
| deviceID        | string | Device ID                                                                                    |
| eventPrefix     | string | Filters the stream to only events starting with the specified prefix. Omit to get all events |

{% tabs %}
{% tab title="200 " %}

```
:ok

event: temperature
data: {"data":"25.34","ttl":"60","published_at":"2015-07-18T00:12:18.174Z","coreid":"0123456789abcdef01234567"}
```

{% endtab %}
{% endtabs %}

## Pubblica un evento

<mark style="color:green;">`POST`</mark> `https://api.trackle.io/v1/devices/events`

Publish an event to your devices stream.

#### Request Body

| Name    | Type    | Description                                   |
| ------- | ------- | --------------------------------------------- |
| name    | string  | Event name                                    |
| data    | string  | Event data. Limited to a maximum of 622 bytes |
| private | boolean | Private or public                             |

{% tabs %}
{% tab title="200 " %}

```
{
  "ok": true
}
```

{% endtab %}

{% tab title="400 " %}

```
{
  "error": "name not provided",
  "ok": false
}
```

{% endtab %}
{% endtabs %}

## Pubblica un evento per un prodotto

<mark style="color:green;">`POST`</mark> `https://api.trackle.io/v1/products/:productIdOrSlug/events`

Publish an event that is sent to the product's event stream.

#### Path Parameters

| Name            | Type   | Description        |
| --------------- | ------ | ------------------ |
| productIdOrSlug | string | Product ID or Slug |

#### Request Body

| Name    | Type    | Description                                   |
| ------- | ------- | --------------------------------------------- |
| name    | string  | Event name                                    |
| data    | string  | Event data. Limited to a maximum of 622 bytes |
| private | boolean | Private or public                             |

{% tabs %}
{% tab title="200 " %}

```
{
  "ok": true
}
```

{% endtab %}

{% tab title="400 " %}

```
{
  "error": "name not provided",
  "ok": false
}
```

{% endtab %}

{% tab title="404 " %}

```
{
  "error": "Product does not exist",
  "ok": false
}
```

{% endtab %}
{% endtabs %}


# Integrazioni

Le integrazioni consentono agli sviluppatori di estendere l'efficacia di Trackle nei loro sistemi esistenti. Forniscono un modo semplice e sicuro per interagire con un servizio basato su Internet.&#x20;

Le integrazioni di Trackle sfruttano l'autenticazione e la crittografia già fornite con la connessione cloud, utilizzano una quantità di dati notevolmente inferiore rispetto alla connessione diretta e consentono di risparmiare spazio sul codice del firmware o dell'applicazione.

Trackle implementa, oltre alle API REST, due tipi di integrazioni a disposizione degli sviluppatori: Webhook e Server-Sent-Events (SSE).

## Webhook

I webhook rappresentano un modo semplice e flessibile per inviare dati dai tuoi dispositivi ad altre app e servizi su Internet. I webhook colmano il divario tra il mondo fisico e quello digitale, aiutandoti a portare i tuoi dati dove ne hai bisogno.

Potresti utilizzare un webhook per salvare per archiviare dati in un database time series in cloud, aggiornare valori di una dashboard, inviare le previsioni meteo ai tuoi dispositivi, attivare un pagamento, inviare un messaggio di testo e molto altro ancora.

Il flusso tipico prevede che il tuo dispositivo esegua `Trackle.publish()` di uno specifico evento che scatena un webhook. È anche possibile che l'integrazione restituisca i dati al dispositivo. È possibile effettuare chiamate API REST standard (GET, POST, PUT e così via).&#x20;

Inoltre, puoi eseguire semplici manipolazioni dei dati utilizzando i modelli Moustache attraverso il toolkit [Handlebars](https://handlebarsjs.com/): puoi modificare l'URL della richiesta, i dati della richiesta e i dati della risposta. Se lo si desidera, il webhook può anche aggiungere header HTTP, argomenti di query, form, autenticazione Basic e molto altro.

## Server-Sent-Events (SSE) <a href="#server-sent-events-sse" id="server-sent-events-sse"></a>

Tradizionalmente, una pagina web deve inviare una richiesta al server per ricevere nuovi dati; cioè, la pagina richiede dati dal server. Con i [*Server-Sent-Events*](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events), è possibile per un server inviare nuovi dati a una pagina web in qualsiasi momento, inviando messaggi alla pagina web. Questi messaggi in arrivo possono essere trattati come un flusso di eventi.

Il flusso di eventi SSE funziona facendo in modo che il tuo client o server effettui una connessione https (crittografata) in uscita al servizio API REST di Trackle. Questa connessione viene mantenuta aperta e, se arrivano nuovi eventi, vengono immediatamente trasmessi su questo flusso.

Con questo tipo di integrazione gli sviluppatori possono ricevere tutti gli eventi di prodotto (o un sottoinsieme usando un filtro basato sul prefisso) stabilendo una sola connessione, utilizzando la stessa autenticazione (token) delle API REST senza applicare modifiche alla rete poiché funziona anche dietro firewall o NAT senza richiedere inoltro delle porte.&#x20;

Uno sviluppatore dovrà scegliere tra webhook e SSE quale tecnologia si la migliore per la propria Applicazione. Di seguito proponiamo una tabella con le differenze tra le due integrazioni.

### Tabella comparativa

| Webhook                                                       | SSE                                         |
| ------------------------------------------------------------- | ------------------------------------------- |
| Richiede un indirizzo IP pubblico                             | Funziona dietro un firewall o NAT           |
| Richiede un certificato SSL affinché il server supporti https | Flusso crittografato senza certificato SSL  |
| La consegna degli eventi è più affidabile                     | Meglio se gli eventi persi non sono critici |
| Può utilizzare load balancer e server ridondanti              | Consente solo un singolo server             |


# Aggiornamenti firmware OTA

Gli aggiornamenti firmware *over-the-air* (OTA) sono una componente fondamentale e critica di qualsiasi sistema IoT. Trackle permette agli sviluppatori di eseguire aggiornare OTA sia per un dispositivo singolo che per l'intera flotta di prodotto direttamente dalla console o dalle API REST.

Il valore di incorporare le funzionalità di aggiornamento OTA in un prodotto connesso include:

* La possibilità di implementare nuove funzionalità software in un prodotto anche dopo che un dispositivo è stato distribuito sul campo.
* &#x20;L'opportunità di rispondere rapidamente a bug e vulnerabilità della sicurezza senza la necessità di richiami fisici dei dispositivi.
* Garantire che gli sviluppatori embedded possano prototipare rapidamente e implementare senza problemi nuove versioni del firmware del dispositivo, accelerando i cicli di innovazione.

Un aggiornamento OTA di successo richiede un coordinamento complesso tra hardware, firmware del dispositivo, connettività e cloud. Trackle offre un sistema integrato per permetterti di aggiornare il software dei tuoi dispositivi in modo sicuro e affidabile.

## Affidabilità

L'invio di un aggiornamento OTA è probabilmente una delle azioni più rischiose che puoi intraprendere su un dispositivo connesso. Una gestione errata degli aggiornamenti OTA potrebbe come minimo causare interruzioni temporanee o, nel peggiore dei casi, forzare il dispositivo in uno stato irrecuperabile.

Attraverso Trackle Library lo sviluppatore ha tutti gli strumenti già integrati con il cloud per poter ricevere un nuovo firmware verificato ed eseguire in completa sicurezza l'aggiornamento senza interrompere la versione corrente e con la possibilità di ripristinare la versione precedente in caso di problemi di connettività.

## Sicurezza

Dato il rischio associato a un aggiornamento OTA, è fondamentale che gli aggiornamenti vengano eseguiti in modo sicuro e protetto.

### Comunicazioni crittografate

Tutti i messaggi tra i dispositivi e il cloud sono sempre crittografati, inclusi i file del firmware. Ciò elimina potenziali attacchi *man-in-the-middl*e che cercano di inviare firmware fraudolento al dispositivo.

### **Verifica del mittente**

Ogni tentativo di aggiornamento OTA viene prima verificato per garantire che l'identità del mittente sia un gestore dispositivi approvato.

## Scalabilità

Trackle offre diversi strumenti OTA adatti alle diverse fasi di crescita della flotta.

### Prototipazione

Durante la prototipazione Trackle consente al tuo team di sviluppo di innovare rapidamente. Gli aggiornamenti OTA possono essere inviati su un singolo dispositivo attraverso la Console o tramite le nostre API REST.

### Produzione&#x20;

Passando alla produzione nasce la necessità di distribuire un aggiornamento ad un numero elevato di dispositivi, quindi è fondamentale avere la possibilità di eseguire in batch in modo sicuro gli aggiornamenti OTA su più dispositivi contemporaneamente. Questo è ciò che ti consente di implementare nuove funzionalità software, correggere bug o correggere buchi di sicurezza nella tua flotta.

A questo scopo, Trackle offre aggiornamenti OTA a livello di flotta, anche in questo caso attraverso la Console o tramite le nostre API REST, in due modalità di rilascio:

* Standard: i dispositivi saranno notificati del nuovo rilascio solo alla prossima riconnessione.
* Veloce: invece di attendere che i dispositivi si riconnettano per ricevere un aggiornamento, invia un aggiornamento a tutto il parco dispositivi il più rapidamente possibile pur consentendo al dispositivo di controllare il momento appropriato per l'aggiornamento.

Attraverso la Console di Trackle è possibile sapere se e quando un dispositivi ha ricevuto un aggiornamento e in caso di problemi avere l'evidenza dell'errore.


# Sicurezza

## Autenticazione e comunicazione

Trackle Cloud implementa l'**autenticazione reciproca** utilizzando coppie di chiavi pubbliche / private in formato **RPK** (*Raw Public Key*) per assicurarsi che il tuo dispositivo **sia il tuo dispositivo** e non un *imitatore* e che il cloud **sia veramente Trackle Cloud** e non un *impostore* man-in-the-middle. In questo modo entrambe le parti possono essere sicure che l’altro sia chi dice di essere.

Il processo di handshake iniziale crea una **sessione crittografata utilizzando DTLS** su UDP (datagram TLS). Ciò garantisce che i tuoi dati non possano essere monitorati o manomessi durante il trasporto.

La connessione al cloud utilizza il protocollo **CoAP** (*Costrained Application Protocol)* su **DTLS**. Tutte le funzionalità come *get*, *post*, *publish*, *subscribe*, e aggiornamenti del firmware OTA avvengono su un'unica connessione CoAP.

Effettuando tutte le connessioni dal dispositivo al cloud, nella maggior parte dei casi è possibile utilizzare i dispositivi sulle reti Wi-Fi senza dover apportare modifiche personalizzate al *port forwarding* o al *firewall*.

## Infrastruttura Cloud

Trackle adotta le *best practice* e azioni automatiche per ridurre al minimo la superficie di attacco sia a livello di servizio che di dati.

Trackle Cloud usa le migliori piattaforme di hosting della categoria con sicurezza fisica e gestione del rischio ISO 27001, 27017 e 27018.

L'infrastruttura di Trackle è monitorata e testata regolarmente per potenziali vulnerabilità con test di penetrazione e revisioni della sicurezza.

Il traffico verso le API REST di Trackle viene bilanciato in modo intelligente per mitigare gli attacchi di forza bruta. Inoltre, ogni nodo o server di Trackle si trova dietro una rete privata rigorosamente protetta da firewall con rilevamento delle intrusioni per evitare accessi non autorizzati.

## Protezione dei dati personali

Trackle limita intenzionalmente l'ambito dei dati utente archiviati nel cloud.

I flussi di informazioni sensibili transitano da Trackle Cloud che protegge i dati ma non li memorizza. Non memorizziamo informazioni o dati di identificazione personale (mascheriamo l'indirizzo IP) che potrebbero essere utilizzati per compromettere prodotti o clienti.


