Unwired Device 6LoWPAN Protocol

Пакеты в сети состоят из header'а и payload'а. ROOT обрабатывает пакеты с командами и данными только после авторизации. Авторизация проходит в четыри стадии.

Верхняя часть header'а которая никогда не шифруется:

typedef struct {
    uint8_t protocol_version;   /*Текущая версия протокола*/ 
    uint8_t device_id;          /*ID устройства*/
    uint8_t data_type;          /*Тип пакета*/ 
    uint8_t rssi;               /*RSSI*/ 
    uint8_t temperature;        /*Температура*/ 
    uint8_t voltage;            /*Напряжение*/ 
} pack header_up_t;

Нижняя часть header'а которая шифруется после авторизации:

typedef struct {        
    u8_u16_t counter;           /*Счетчик пакетов*/ 
    u8_u16_t crc;               /*CRC16*/ 
    uint8_t length;             /*Размер пакета*/
} pack header_down_t;

Общая структура header'а:

typedef struct {        
    uint8_t protocol_version;   /*Текущая версия протокола*/ 
    uint8_t device_id;          /*ID устройства*/
    uint8_t data_type;          /*Тип пакета*/ 
    uint8_t rssi;               /*RSSI*/ 
    uint8_t temperature;        /*Температура*/ 
    uint8_t voltage;            /*Напряжение*/ 
    u8_u16_t counter;           /*Счетчик пакетов*/ 
    u8_u16_t crc;               /*CRC16*/ 
    uint8_t length;             /*Размер пакета*/
} pack header_t; 

Payload

UNWDS-6LOWPAN_SYSTEM

/*Struct for JOIN_STAGE_1*/
typedef struct {        
    uint8_t module_id;      /*ID устройства: module_id = UNWDS_MODULE_ID*/  
} pack join_stage_1_t;

/*Struct for JOIN_STAGE_2*/
typedef struct {        
    u8_u16_t nonce;         /*Генерируем сессионный ключ nonce = random_rand()*/            
} pack join_stage_2_t;

/*Struct for JOIN_STAGE_3*/
typedef struct {        
    u8_u16_t nonce;         /*Увеличиваем nonce на единицу: nonce += 1*/    
} pack join_stage_3_t;

/*Struct for JOIN_STAGE_4*/
typedef struct {        
    uint8_t status_code;    /*status_code = true если авторизация прошла успешно, иначе status_code = false*/   
} pack join_stage_4_t;

/*Struct for PING*/
typedef struct {        
    u8_u16_t nonce;         /*Отправляем ROOT'у nonce. Если 3 раза не ответит, то перезагружаемся и переавторизируемся*/
} pack ping_t;

/*Struct for PONG*/
typedef struct {        
    uint8_t status_code;    /*status_code = true если настройки шифрования совпадают*/
} pack pong_t;

/*Struct for ACK*/
typedef struct {        
    u8_u16_t counter;       /*Номер подтвержденного пакета*/
} pack ack_t;

/*Struct for NACK*/
typedef struct {        
    u8_u16_t counter;       /*Номер неподтвержденного пакета*/
} pack nack_t;

UNWDS-4BTN

/*Struct for BUTTON_STATUS*/
typedef struct {        
    uint8_t button_status;  /*Номер нажатой кнопки. Если долгое нажатие: button_status |= (1<<7) */
} pack button_status_t;

UNWDS-6FET

/*Struct for PWM_SETTINGS*/
typedef struct {
    uint32_t frequency;     /*Частота ШИМ'а от 100 Hz до 100 kHz*/
    uint8_t channel;        /*Номер канала от 0 до 5*/
    uint8_t duty;           /*Коэффицент заполнения от 0% до 100%*/
} pack pwm_settings_t;

/*Struct for PWM_POWER*/
typedef struct {        
    uint8_t pwm_power;      /*Номер канала от 0 до 5. Для включения: номер канала |= (1<<7)*/
} pack pwm_power_t;

UNWDS-LIT

/*Struct for LIT_MEASURE_STATUS*/
typedef struct {        
    uint32_t lit_measure_status;    /*Значение освещенности в люксах*/
} pack lit_measure_status_t;

Авторизация

Шаг 1:

Направление: DAG->ROOT

device_id = UNWDS_6LOWPAN_SYSTEM_MODULE_ID
data_type = JOIN_STAGE_1

typedef struct {        
    uint8_t module_id;      /*ID устройства: module_id = UNWDS_MODULE_ID*/  
} pack join_stage_1_t;
/*Первая стадия авторизации*/
/*Передаём свой серийный номер*/
static void join_stage_1_sender(const uip_ipaddr_t *dest_addr)
{
    /*Проверка на то что передан существующий адрес*/
    if (dest_addr == NULL)
        return;

    uip_ipaddr_t addr;                      /*Выделяем память для адреса на который отправится пакет*/
    uip_ip6addr_copy(&addr, dest_addr);     /*Копируем адрес*/

    /*Вывод информационного сообщения в консоль*/
    printf("[DAG Node] Send join packet to DAG-root node: ");
    uip_debug_ipaddr_print(&addr);
    printf("\n");

    /*Выделяем память под пакет. Общий размер пакета (header + payload)*/   
    uint8_t udp_buffer[HEADER_LENGTH + JOIN_STAGE_1_PAYLOAD_LENGTH];

    /*Отражаем структуры на массив*/ 
    header_t *header_pack = (header_t*)&udp_buffer[HEADER_OFFSET];
    join_stage_1_t *join_stage_1_pack = (join_stage_1_t*)&udp_buffer[PAYLOAD_OFFSET];

    /*Заполняем пакет*/  
    /*Header*/ 
    header_pack->protocol_version = UDBP_PROTOCOL_VERSION;      /*Текущая версия протокола*/ 
    header_pack->device_id = UNWDS_6LOWPAN_SYSTEM_MODULE_ID;    /*ID устройства*/
    header_pack->data_type = JOIN_STAGE_1;                      /*Тип пакета*/  
    header_pack->rssi = get_parent_rssi();                      /*RSSI*/ 
    header_pack->temperature = get_temperature();               /*Температура*/ 
    header_pack->voltage = get_voltage();                       /*Напряжение*/ 
    header_pack->counter.u16 = packet_counter_node.u16;         /*Счетчик пакетов*/ 
    header_pack->length = JOIN_STAGE_1_LENGTH;                  /*Размер пакета*/

    /*Payload*/
    join_stage_1_pack->module_id = UNWDS_MODULE_ID;

    /*CRC16*/ 
    header_pack->crc.u16 = 0;

    /*Отправляем пакет*/ 
    simple_udp_sendto(&udp_connection, udp_buffer, (HEADER_LENGTH + JOIN_STAGE_1_PAYLOAD_LENGTH), &addr);
}

Шаг 2:

Направление: ROOT->DAG

device_id = UNWDS_6LOWPAN_SYSTEM_MODULE_ID
data_type = JOIN_STAGE_2

typedef struct {        
    u8_u16_t nonce;         /*Генерируем сессионный ключ: nonce = random_rand()*/   
} pack join_stage_2_t;
/*Вторая стадия авторизации*/
/*Генерирует сессионный ключ (nonce) и отправляет его зашифрованым AES128-ECB, добавляет маршрут в таблицу*/
static void join_stage_2_sender(const uip_ip6addr_t *dest_addr, const uint8_t *data, const uint16_t length)
{   
    /*Вывод принятого пакета микрокомпьютеру*/ 
    print_cr((uip_ip6addr_t*)dest_addr, (uint8_t*)data, (HEADER_LENGTH + JOIN_STAGE_1_LENGTH));

    /*Выделяем память под пакет. Общий размер пакета (header + payload)*/
    uint8_t udp_buffer[HEADER_UP_LENGTH + JOIN_STAGE_2_PAYLOAD_LENGTH];

    /*Отражаем структуры на массивы*/ 
    header_t *header_pack = (header_t*)&udp_buffer[HEADER_OFFSET];
    join_stage_2_t *join_stage_2_pack = (join_stage_2_t*)&udp_buffer[PAYLOAD_OFFSET];

    /*Заполняем пакет*/  
    /*Header*/ 
    header_pack->protocol_version = UDBP_PROTOCOL_VERSION;      /*Текущая версия протокола*/ 
    header_pack->device_id = UNWDS_6LOWPAN_SYSTEM_MODULE_ID;    /*ID устройства*/
    header_pack->data_type = JOIN_STAGE_2;                      /*Тип пакета*/  
    header_pack->rssi = get_parent_rssi();                      /*RSSI*/ 
    header_pack->temperature = get_temperature();               /*Температура*/ 
    header_pack->voltage = get_voltage();                       /*Напряжение*/ 
    header_pack->counter.u16 = packet_counter_root.u16;         /*Счетчик пакетов*/ 
    header_pack->length = JOIN_STAGE_2_LENGTH;                  /*Размер пакета (незашифрованного)*/

    /*Payload*/ 
    join_stage_2_pack->nonce.u16 = random_rand();               /*Генерируем сессионный ключ*/ 

    /*Добавляем маршрут*/ 
    add_route ( (uip_ip6addr_t*)dest_addr,                      /*Address*/ 
                join_stage_2_pack->nonce.u16);                  /*Nonce*/ 

    /*Дозаполняем блок для шифрования нулями*/ 
    for(uint8_t i = JOIN_STAGE_2_LENGTH; i < (JOIN_STAGE_2_PAYLOAD_LENGTH - HEADER_DOWN_LENGTH); i++)
        udp_buffer[PAYLOAD_OFFSET + i] = 0x00;

    /*CRC16*/ 
    header_pack->crc.u16 = crc16_arc((uint8_t*)&join_stage_2_pack, sizeof(join_stage_2_pack));

    /*Зашифровываем блок*/ 
    aes_ecb_encrypt((uint32_t*)aes_key, (uint32_t*)(&udp_buffer[HEADER_DOWN_OFFSET]), (uint32_t*)(&udp_buffer[HEADER_DOWN_OFFSET]));

    /*Отправляем пакет*/ 
    simple_udp_sendto(&udp_connection, udp_buffer, (HEADER_UP_LENGTH + JOIN_STAGE_2_PAYLOAD_LENGTH), dest_addr);
    packet_counter_root.u16++;  /*Инкрементируем счетчик пакетов*/ 
}

Шаг 3:

Направление: DAG->ROOT

device_id = UNWDS_6LOWPAN_SYSTEM_MODULE_ID
data_type = JOIN_STAGE_3

typedef struct {            
    u8_u16_t nonce;         /*Увеличиваем nonce на единицу: nonce += 1*/    
} pack join_stage_3_t;
/*Третья стадия авторизации*/
/*Получаем nonce и отправляем его root'у зашифрованым AES128-CBC*/
static void join_stage_3_sender(const uip_ipaddr_t *dest_addr,
                                const uint8_t *data,
                                uint16_t datalen)
{
    /*Проверка на то что передан существующий адрес*/
    if (dest_addr == NULL)
        return;

    uip_ipaddr_t addr;                      /*Выделяем память для адреса на который отправится пакет*/
    uip_ip6addr_copy(&addr, dest_addr);     /*Копируем адрес*/

    /*Вывод информационного сообщения в консоль*/
    printf("[DAG Node] Send join packet stage 3 to DAG-root node:");
    uip_debug_ipaddr_print(&addr);
    printf("\n");


    /*Выделяем память под пакет. Общий размер пакета (header + payload)*/
    uint8_t udp_buffer[HEADER_UP_LENGTH + JOIN_STAGE_3_PAYLOAD_LENGTH]; 

    /*Отражаем структуры на массивы*/ 
    header_t *header_pack = (header_t*)&udp_buffer[HEADER_OFFSET];
    join_stage_2_t *join_stage_2_pack = (join_stage_2_t*)&data[PAYLOAD_OFFSET];
    join_stage_3_t *join_stage_3_pack = (join_stage_3_t*)&udp_buffer[PAYLOAD_OFFSET];

    /*Заполняем пакет*/  
    /*Header*/ 
    header_pack->protocol_version = UDBP_PROTOCOL_VERSION;      /*Текущая версия протокола*/ 
    header_pack->device_id = UNWDS_6LOWPAN_SYSTEM_MODULE_ID;    /*ID устройства*/
    header_pack->data_type = JOIN_STAGE_3;                      /*Тип пакета*/  
    header_pack->rssi = get_parent_rssi();                      /*RSSI*/ 
    header_pack->temperature = get_temperature();               /*Температура*/ 
    header_pack->voltage = get_voltage();                       /*Напряжение*/ 
    header_pack->counter.u16 = packet_counter_node.u16;         /*Счетчик пакетов*/ 
    header_pack->length = JOIN_STAGE_3_LENGTH;                  /*Размер пакета*/

    /*Payload*/ 
    /*Расшифровываем блок*/ 
    aes_ecb_decrypt((uint32_t*)aes_key, (uint32_t*)&data[HEADER_DOWN_OFFSET], (uint32_t*)&data[HEADER_DOWN_OFFSET]);

    //CRC16

    /*Копируем полученный nonce и используем его в качестве сессионного ключа (AES128-CBC)*/
    for(int i = 0; i < 16; i += 2)
    {
        nonce_key[i] = join_stage_2_pack->nonce.u8[1];  
        nonce_key[i+1] = join_stage_2_pack->nonce.u8[0];    
    }

    /*Отправляем ROOT'у nonce на еденицу больше для того что бы он был уверен что у нас одинаковое шифрование*/ 
    join_stage_3_pack->nonce.u16 = join_stage_2_pack->nonce.u16 + 1; /*Увеличиваем nonce на единицу: nonce += 1*/   

    /*Дозаполняем блок для шифрования нулями*/ 
    for(uint8_t i = JOIN_STAGE_3_LENGTH; i < (JOIN_STAGE_3_PAYLOAD_LENGTH - HEADER_DOWN_LENGTH); i++)
        udp_buffer[PAYLOAD_OFFSET + i] = 0x00;

    /*CRC16*/ 
    header_pack->crc.u16 = crc16_arc((uint8_t*)&join_stage_3_pack, sizeof(join_stage_3_pack));

    /*Зашифровываем данные*/
    aes_cbc_encrypt((uint32_t*)aes_key, (uint32_t*)nonce_key, (uint32_t*)&udp_buffer[HEADER_DOWN_OFFSET], (uint32_t*)&udp_buffer[HEADER_DOWN_OFFSET], CRYPTO_1_BLOCK_LENGTH);       

    /*Отправляем пакет*/ 
    simple_udp_sendto(&udp_connection, udp_buffer, (HEADER_DOWN_OFFSET + JOIN_STAGE_3_PAYLOAD_LENGTH), &addr);
}

Шаг 4:

Направление: ROOT->DAG

device_id = UNWDS_6LOWPAN_SYSTEM_MODULE_ID
data_type = JOIN_STAGE_4

typedef struct {        
    uint8_t status_code;    /*status_code = true если авторизация прошла успешно, иначе status_code = false*/   
} pack join_stage_4_t;
/*Четвертая стадия авторизации*/
/*Принимает nonce зашифрованный AES128-CBC. Если сходится с тем что он сгенерировал, то авторизация прошла успешно, настройки шифрования верные. Отправляем пакет с нулями что бы DAG мог убедиться в этом*/
static void join_stage_4_sender(const uip_ip6addr_t *dest_addr, const uint8_t *data, const uint16_t length)
{   
    /*Получаем nonce*/
    u8_u16_t nonce;
    nonce.u16 = get_nonce((uip_ip6addr_t*)dest_addr);   

    /*Копируем полученный nonce и используем его в качестве сессионного ключа (AES128-CBC)*/
    for(int i = 0; i < 16; i += 2)
    {
        nonce_key[i] = nonce.u8[1]; 
        nonce_key[i+1] = nonce.u8[0];   
    }

    /*Расшифровываем данные*/
    aes_cbc_decrypt((uint32_t*)aes_key, (uint32_t*)nonce_key, (uint32_t*)&data[HEADER_DOWN_OFFSET], (uint32_t*)&data[HEADER_DOWN_OFFSET], CRYPTO_1_BLOCK_LENGTH);

    /*Вывод принятого пакета микрокомпьютеру*/ 
    print_cr((uip_ip6addr_t*)dest_addr, (uint8_t*)data, (HEADER_LENGTH + JOIN_STAGE_3_LENGTH));

    /*Отражаем структуры на массивы*/ 
    join_stage_3_t *join_stage_3_pack = (join_stage_3_t*)&data[PAYLOAD_OFFSET];

    /*Заполняем payload*/
    join_stage_4_t join_stage_4_pack;               /*Создаем структуру*/

    /*Проверяем одинаковые ли у нас настройки шифрования*/ 
    if((get_nonce((uip_ip6addr_t*)dest_addr) + 1) != join_stage_3_pack->nonce.u16)
    {
        join_stage_4_pack.status_code = false;

        /*Вывод сообщения об успешной авторизации*/
        printf("[");
        uip_debug_ipaddr_print((uip_ip6addr_t*)dest_addr);
        printf("] Authorization error node\n");
    }

    /*Если nonce'ы совпадают, то авторизация прошла успешно, шифрование настроенно правильно*/ 
    else
    {
        join_stage_4_pack.status_code = true;       /*Статус код*/
        unlock_addr((uip_ip6addr_t*)dest_addr);     /*Разрешаем обрабатывать пакеты принятые с авторизированного устройства*/

        /*Вывод сообщения об успешной авторизации*/
        printf("[");
        uip_debug_ipaddr_print((uip_ip6addr_t*)dest_addr);
        printf("] Authorized node\n");      
    }

    /*Отправляем пакет*/
    pack_sender(dest_addr,                      /*Адрес модуля*/
                UNWDS_6LOWPAN_SYSTEM_MODULE_ID, /*Индентификатор модуля*/
                JOIN_STAGE_4,                   /*Команда 4 стадии авторизации*/
                JOIN_STAGE_4_LENGTH,            /*Размер payload'а*/
                (uint8_t*)&join_stage_4_pack);  /*Payload*/
}

Если устройство прошло авторизацию, то ROOT включает устройство в сеть и обрабатывает входящие пакеты от устройства.

Функция отправки обычных пакетов DAG -> ROOT

/*Конструктор пакета*/
void pack_sender(uint8_t device_id, 
                uint8_t data_type, 
                uint8_t payload_len, 
                uint8_t *payload)
{   
    /*Проверка на то что передан не нулевой адрес буфера*/
    if ((payload == NULL) && (payload_len != 0))
        return;

    /*Выделяем память под пакет. Общий размер пакета (header + payload)*/
    uint8_t crypto_length = iterator_to_byte(HEADER_DOWN_LENGTH + payload_len);
    uint8_t udp_buffer[HEADER_UP_LENGTH + crypto_length];

    /*Отражаем структуры на массивы*/ 
    header_t *header_pack = (header_t*)&udp_buffer[HEADER_OFFSET];

    /*Заполняем пакет*/  
    /*Header*/ 
    header_pack->protocol_version = UDBP_PROTOCOL_VERSION;      /*Текущая версия протокола*/ 
    header_pack->device_id = device_id;                         /*ID устройства*/
    header_pack->data_type = data_type;                         /*Тип пакета*/  
    header_pack->rssi = get_parent_rssi();                      /*RSSI*/ 
    header_pack->temperature = get_temperature();               /*Температура*/ 
    header_pack->voltage = get_voltage();                       /*Напряжение*/ 
    header_pack->counter.u16 = packet_counter_node.u16;         /*Счетчик пакетов*/ 
    header_pack->length = payload_len;                          /*Размер пакета (незашифрованного)*/

    /*Payload*/     
    /*Pаполняем пакет, зашифровываем и отправляем его DAG'у. */ 
    for(uint8_t i = 0; i < (crypto_length - HEADER_DOWN_LENGTH); i++)
    {
        if(i < payload_len)
            udp_buffer[PAYLOAD_OFFSET + i] = payload[i];
        else
            udp_buffer[PAYLOAD_OFFSET + i] = 0x00;
    }

    /*CRC16*/ 
    header_pack->crc.u16 = crc16_arc((uint8_t*)&udp_buffer[PAYLOAD_OFFSET], header_pack->length);

    /*Зашифровываем данные*/
    aes_cbc_encrypt((uint32_t*)aes_key, (uint32_t*)nonce_key, (uint32_t*)(&udp_buffer[HEADER_DOWN_OFFSET]), (uint32_t*)(&udp_buffer[HEADER_DOWN_OFFSET]), crypto_length);

    /*Отправляем пакет*/ 
    simple_udp_sendto(&udp_connection, udp_buffer, (HEADER_UP_LENGTH + crypto_length), (uip_ipaddr_t*)&root_addr);
    packet_counter_node.u16++;      /*Инкрементируем счетчик пакетов*/
}

Функция отправки обычных пакетов ROOT -> DAG

/*Конструктор пакета*/
void pack_sender(const uip_ip6addr_t *dest_addr, 
                uint8_t device_id, 
                uint8_t data_type, 
                uint8_t payload_len, 
                uint8_t *payload)
{
    /*Проверка на то что передан существующий адрес*/
    if (dest_addr == NULL)
        return;

    /*Проверка на то что передан не нулевой адрес буфера*/
    if ((payload == NULL) && (payload_len != 0))
        return;

    /*Выделяем память под пакет. Общий размер пакета (header + payload)*/
    uint8_t crypto_length = iterator_to_byte(HEADER_DOWN_LENGTH + payload_len);
    uint8_t udp_buffer[HEADER_UP_LENGTH + crypto_length];

    /*Отражаем структуры на массивы*/ 
    header_t *header_pack = (header_t*)&udp_buffer[HEADER_OFFSET];

    /*Получаем nonce*/
    u8_u16_t nonce;
    nonce.u16 = get_nonce((uip_ip6addr_t*)dest_addr);   

    /*Копируем полученный nonce и используем его в качестве сессионного ключа (AES128-CBC)*/
    for(int i = 0; i < 16; i += 2)
    {
        nonce_key[i] = nonce.u8[1]; 
        nonce_key[i+1] = nonce.u8[0];   
    }

    /*Заполняем пакет*/  
    /*Header*/ 
    header_pack->protocol_version = UDBP_PROTOCOL_VERSION;      /*Текущая версия протокола*/ 
    header_pack->device_id = device_id;                         /*ID устройства*/
    header_pack->data_type = data_type;                         /*Тип пакета*/  
    header_pack->rssi = get_parent_rssi();                      /*RSSI*/ 
    header_pack->temperature = get_temperature();               /*Температура*/ 
    header_pack->voltage = get_voltage();                       /*Напряжение*/ 
    header_pack->counter.u16 = packet_counter_root.u16;         /*Счетчик пакетов*/ 
    header_pack->length = payload_len;                          /*Размер пакета (незашифрованного)*/

    /*Payload*/     
    /*Pаполняем пакет, зашифровываем и отправляем его DAG'у. */ 
    for(uint8_t i = 0; i < (crypto_length - HEADER_DOWN_LENGTH); i++)
    {
        if(i < payload_len)
            udp_buffer[PAYLOAD_OFFSET + i] = payload[i];
        else
            udp_buffer[PAYLOAD_OFFSET + i] = 0x00;
    }

    /*CRC16*/ 
    header_pack->crc.u16 = crc16_arc((uint8_t*)&udp_buffer[PAYLOAD_OFFSET], header_pack->length);

    /*Зашифровываем данные*/
    aes_cbc_encrypt((uint32_t*)aes_key, (uint32_t*)nonce_key, (uint32_t*)(&udp_buffer[HEADER_DOWN_OFFSET]), (uint32_t*)(&udp_buffer[HEADER_DOWN_OFFSET]), crypto_length);

    /*Отправляем пакет*/ 
    simple_udp_sendto(&udp_connection, udp_buffer, (HEADER_UP_LENGTH + crypto_length), dest_addr);
    packet_counter_root.u16++;      /*Инкрементируем счетчик пакетов*/
}

protocol.h

/*
 * Copyright (c) 2016, Unwired Devices LLC - http://www.unwireddevices.com/
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Unwired Devices nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
/*---------------------------------------------------------------------------*/
/*
* \file
*         Protocol
* \author
*         Manchenko Oleg man4enkoos@gmail.com
*/
/*---------------------------------------------------------------------------*/
#include "net/ip/uip.h"
/*---------------------------------------------------------------------------*/
#ifndef PROTOCOL_H
#define PROTOCOL_H

/*Упаковка структуры*/
#define pack        __attribute__((packed))

/*---------------------------------------------------------------------------*/
/*Типы объединений*/
typedef union u8_u16_t {
    uint16_t u16;
    uint8_t u8[2];
} u8_u16_t;

typedef union u8_i16_t {
    int16_t i16;
    uint8_t u8[2];
} u8_i16_t;

typedef union u8_u32_t {
    uint32_t u32;
    uint8_t u8[4];
} u8_u32_t;

/*---------------------------------------------------------------------------*/
/*ID устройства*/
typedef enum 
{
    UNWDS_GPIO_MODULE_ID = 1,
    UNWDS_4BTN_MODULE_ID = 2,
    UNWDS_GPS_MODULE_ID = 3,
    UNWDS_LSM6DS3_MODULE_ID = 4,
    UNWDS_LM75_MODULE_ID = 5,
    UNWDS_LMT01_MODULE_ID = 6,
    UNWDS_UART_MODULE_ID = 7,
    UNWDS_SHT21_MODULE_ID = 8,
    UNWDS_PIR_MODULE_ID = 9,
    UNWDS_ADC_MODULE_ID = 10,
    UNWDS_LPS331_MODULE_ID = 11,
    UNWDS_COUNTER_MODULE_ID = 12,
    UNWDS_RSSIECHO_MODULE_ID = 13,
    UNWDS_6FET_MODULE_ID = 14,
    UNWDS_LIT_MODULE_ID = 15,
    UNWDS_DALI_MODULE_ID = 16,
    UNWDS_BME280_MODULE_ID = 17,
    UNWDS_MHZ19_MODULE_ID = 18,
    UNWDS_RANGE_MODULE_ID = 19,
    UNWDS_ADXL345_MODULE_ID = 20,
    /* Proprietary 50 to 99 */
    UNWDS_M200_MODULE_ID = 50,
    UNWDS_PULSE_MODULE_ID = 51,
    UNWDS_IBUTTON_MODULE_ID = 52,
    UNWDS_SWITCH_MODULE_ID = 53,
    UNWDS_M230_MODULE_ID = 54,
    UNWDS_IEC61107_MODULE_ID = 55,
    /* Customer 100 to 125*/
    UNWDS_CUSTOMER_MODULE_ID = 100,
    UNWDS_FIREBUTTON_MODULE_ID = 101,
    /* System module 126 */
    UNWDS_CONFIG_MODULE_ID = 126,
    UNWDS_6LOWPAN_SYSTEM_MODULE_ID = 127,
} UNWDS_MODULE_IDS_t;

/*---------------------------------------------------------------------------*/
/*CR->ROOT*/
typedef struct {        
    uip_ip6addr_t dest_addr;    /*Адрес модуля*/
    uint8_t device_id;          /*Индентификатор модуля*/
    uint8_t data_type;          /*Команда*/
    uint8_t payload_len;        /*Размер payload'а*/
} pack uart_header_t;

/*---------------------------------------------------------------------------*/
/*HEADER*/
typedef struct {
    uint8_t protocol_version;   /*Текущая версия протокола*/ 
    uint8_t device_id;          /*ID устройства*/
    uint8_t data_type;          /*Тип пакета*/ 
    uint8_t rssi;               /*RSSI*/ 
    uint8_t temperature;        /*Температура*/ 
    uint8_t voltage;            /*Напряжение*/ 
} pack header_up_t;

typedef struct {        
    u8_u16_t counter;           /*Счетчик пакетов*/ 
    u8_u16_t crc;               /*CRC16*/ 
    uint8_t length;             /*Размер пакета*/
} pack header_down_t;

typedef struct {        
    uint8_t protocol_version;   /*Текущая версия протокола*/ 
    uint8_t device_id;          /*ID устройства*/
    uint8_t data_type;          /*Тип пакета*/ 
    uint8_t rssi;               /*RSSI*/ 
    uint8_t temperature;        /*Температура*/ 
    uint8_t voltage;            /*Напряжение*/ 
    u8_u16_t counter;           /*Счетчик пакетов*/ 
    u8_u16_t crc;               /*CRC16*/ 
    uint8_t length;             /*Размер пакета*/
} pack header_t; 

/*---------------------------------------------------------------------------*/

typedef struct {        
    uint8_t ciphertext[16];
} pack crypto_1_block_t;

/*---------------------------------------------------------------------------*/
/*UNWDS-4BTN*/

/*Struct for BUTTON_STATUS*/
typedef struct {        
    uint8_t button_status;  /*Номер нажатой кнопки. Если долгое нажатие: button_status |= (1<<7) */
} pack button_status_t;

/*---------------------------------------------------------------------------*/
/*UNWDS-6FET*/

/*Struct for PWM_SETTINGS*/
typedef struct {
    uint32_t frequency;     /*Частота ШИМ'а от 100 Hz до 100 kHz*/
    uint8_t channel;        /*Номер канала от 0 до 5*/
    uint8_t duty;           /*Коэффицент заполнения от 0% до 100%*/
} pack pwm_settings_t;

/*Struct for PWM_POWER*/
typedef struct {        
    uint8_t pwm_power;      /*Номер канала от 0 до 5. Для включения: номер канала |= (1<<7)*/
} pack pwm_power_t;

/*---------------------------------------------------------------------------*/
/*UNWDS-LIT*/

/*Struct for LIT_MEASURE_STATUS*/
typedef struct {        
    uint32_t lit_measure_status;    /*Значение освещенности в люксах*/
} pack lit_measure_status_t;


/*---------------------------------------------------------------------------*/
/*UNWDS-6LOWPAN_SYSTEM*/

/*Struct for JOIN_STAGE_1*/
typedef struct {        
    uint8_t module_id;      /*ID устройства: module_id = UNWDS_MODULE_ID*/  
} pack join_stage_1_t;

/*Struct for JOIN_STAGE_2*/
typedef struct {        
    u8_u16_t nonce;         /*Генерируем сессионный ключ nonce = random_rand()*/            
} pack join_stage_2_t;

/*Struct for JOIN_STAGE_3*/
typedef struct {        
    u8_u16_t nonce;         /*Увеличиваем nonce на единицу: nonce += 1*/    
} pack join_stage_3_t;

/*Struct for JOIN_STAGE_4*/
typedef struct {        
    uint8_t status_code;    /*status_code = true если авторизация прошла успешно, иначе status_code = false*/   
} pack join_stage_4_t;

/*Struct for PING*/
typedef struct {        
    u8_u16_t nonce;         /*Отправляем ROOT'у nonce. Если 3 раза не ответит, то перезагружаемся и переавторизируемся*/
} pack ping_t;

/*Struct for PONG*/
typedef struct {        
    uint8_t status_code;    /*status_code = true если настройки шифрования совпадают*/
} pack pong_t;

/*Struct for ACK*/
typedef struct {        
    u8_u16_t counter;       /*Номер подтвержденного пакета*/
} pack ack_t;

/*Struct for NACK*/
typedef struct {        
    u8_u16_t counter;       /*Номер неподтвержденного пакета*/
} pack nack_t;


/*---------------------------------------------------------------------------*/
/*Основные*/
#define UDP_DATA_PORT                   4004
#define UDBP_PROTOCOL_VERSION           1

/*---------------------------------------------------------------------------*/
/*OFFSET*/
#define HEADER_OFFSET                   0
#define HEADER_DOWN_OFFSET              HEADER_OFFSET + HEADER_UP_LENGTH
#define PAYLOAD_OFFSET                  HEADER_OFFSET + HEADER_UP_LENGTH + HEADER_DOWN_LENGTH

/*---------------------------------------------------------------------------*/
/*LENGTH*/
#define HEADER_UP_LENGTH                sizeof(header_up_t)
#define HEADER_DOWN_LENGTH              sizeof(header_down_t)
#define HEADER_LENGTH                   HEADER_UP_LENGTH + HEADER_DOWN_LENGTH

#define CRYPTO_1_BLOCK_LENGTH           sizeof(crypto_1_block_t)

#define JOIN_STAGE_1_PAYLOAD_LENGTH     JOIN_STAGE_1_LENGTH
#define JOIN_STAGE_2_PAYLOAD_LENGTH     CRYPTO_1_BLOCK_LENGTH
#define JOIN_STAGE_3_PAYLOAD_LENGTH     CRYPTO_1_BLOCK_LENGTH

#define JOIN_STAGE_1_LENGTH             sizeof(join_stage_1_t)
#define JOIN_STAGE_2_LENGTH             sizeof(join_stage_2_t)
#define JOIN_STAGE_3_LENGTH             sizeof(join_stage_3_t)
#define JOIN_STAGE_4_LENGTH             sizeof(join_stage_4_t)
#define PING_LENGTH                     sizeof(ping_t)
#define PONG_LENGTH                     sizeof(pong_t)
#define ACK_LENGTH                      sizeof(ack_t)
#define NACK_LENGTH                     sizeof(nack_t)
#define BUTTON_STATUS_LENGTH            sizeof(button_status_t)
#define PWM_SETTINGS_LENGTH             sizeof(pwm_settings_t)
#define PWM_POWER_LENGTH                sizeof(pwm_power_t)
#define LIT_MEASURE_LENGTH              0
#define LIT_MEASURE_STATUS_LENGTH       sizeof(lit_measure_status_t)

/*---------------------------------------------------------------------------*/
/*COMMAND*/

/*UNWDS-6LOWPAN_SYSTEM*/
#define JOIN_STAGE_1                0x00 /*Нода посылает запрос координатору*/
#define JOIN_STAGE_2                0x01 /*Координатор отправляет ecb_encrypt(nonce=rand())*/
#define JOIN_STAGE_3                0x02 /*Нода удостоверяет, что она знает ключ отправляя cbc_encrypt(nonce+1)*/
#define JOIN_STAGE_4                0x03 /*Координатор отвечает ноде что она имеет право быть в сети*/
#define PING                        0x04 /*Ping*/
#define PONG                        0x05 /*Pong*/
#define ACK                         0x07 /*ACK*/
#define NACK                        0x08 /*NACK*/

/*UNWDS-4BTN*/
#define BUTTON_STATUS               0x00 /*Пакет статусом нажатой кнопки*/

/*UNWDS-6FET*/
#define PWM_SETTINGS                0x00 /*Пакет с настройкоами ШИМ канала*/
#define PWM_POWER                   0x01 /*Команда включения/выключения канала ШИМ'а*/

/*UNWDS-LIT*/
#define LIT_MEASURE                 0x00 /*Команда запроса замера освещенности*/
#define LIT_MEASURE_STATUS          0x01 /*Результаты замера освещенности*/

/*---------------------------------------------------------------------------*/
#endif
/*---------------------------------------------------------------------------*/

Алгоритм расчета CRC-16/ARC

CRC-16/ARC, он же CRC-16 classic и CRC-16-ANSI.

Параметры:

Poly 0x8005(x^16 + x^15 + x^2 + 1)
Init 0x0000
RefIn true
RefOut true
XorOut 0x0000

Код расчета на C

uint16_t crc16_arc(uint8_t *data, uint16_t len)
{
   uint16_t crc = 0x0000;
   for (uint16_t j = len; j > 0; j--)
   {
      crc ^= *data++;
      for (uint8_t i = 0; i < 8; i++)
      {
         if (crc & 1)
            crc = (crc >> 1) ^ 0xA001; // 0xA001 is the reflection of 0x8005
         else
            crc >>= 1;
      }
   }
   return (crc);
}

Код расчета на Lua

local function crc16_arc_calc(s)
   assert(type(s) == 'string')
   local crc = 0x0000
   for i = 1, #s do
       local c = s:byte(i)
       crc = bit.bxor(crc, c)
       for j = 1, 8 do
           local k = bit.band(crc, 1)
           crc = bit.rshift(crc, 1)
           if k ~= 0 then
               crc = bit.bxor(crc, 0xA001)
           end
       end
   end
   return crc
end

Калькулятор