MicoAir
Docs directory
Table of contents

Micolink Protocol Definition and Parsing

Micolink is a lightweight communication protocol defined by MicoAir, primarily for transmitting sensor data over a serial connection.

Protocol Definition

Protocol Parsing Reference

mtf01.h

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#define MICOLINK_MSG_HEAD            0xEF
#define MICOLINK_MAX_PAYLOAD_LEN     64
#define MICOLINK_MAX_LEN             MICOLINK_MAX_PAYLOAD_LEN + 7

/*
    Message ID definitions
*/
enum
{
    MICOLINK_MSG_ID_RANGE_SENSOR = 0x51,     // Range sensor
};

/*
    Message structure definition
*/
typedef struct
{
    uint8_t head;                      
    uint8_t dev_id;                          
    uint8_t sys_id;						
    uint8_t msg_id;                        
    uint8_t seq;                          
    uint8_t len;                               
    uint8_t payload[MICOLINK_MAX_PAYLOAD_LEN]; 
    uint8_t checksum;                          

    uint8_t status;                           
    uint8_t payload_cnt;                       
} MICOLINK_MSG_t;

/*
    Payload definitions
*/
#pragma pack (1)
// Range sensor
typedef struct
{
    uint32_t  time_ms;			    // System time, ms
    uint32_t  distance;			    // Distance (mm); minimum valid value is 10, and 0 means unavailable
    uint8_t   strength;	            // Signal strength
    uint8_t   precision;	        // Precision
    uint8_t   tof_status;	        // Status
    uint8_t  reserved1;			    // Reserved
    int16_t   flow_vel_x;	        // Optical-flow velocity, X axis
    int16_t   flow_vel_y;	        // Optical-flow velocity, Y axis
    uint8_t   flow_quality;	        // Optical-flow quality
    uint8_t   flow_status;	        // Optical-flow status
    uint16_t  reserved2;	        // Reserved
} MICOLINK_PAYLOAD_RANGE_SENSOR_t;
#pragma pack ()

mtf01.c

#include "mtf01.h"

/*
Usage: Use micolink_decode as the serial-data processing function.

The minimum valid distance is 10 mm; a value of 0 means that the distance is unavailable.
Optical-flow velocity unit: cm/s at 1 m
In the flight controller, multiply the optical-flow velocity by altitude to obtain the actual horizontal velocity.
Formula: actual velocity (cm/s) = optical-flow velocity × altitude (m)
*/

bool micolink_parse_char(MICOLINK_MSG_t* msg, uint8_t data);

void micolink_decode(uint8_t data)
{
    static MICOLINK_MSG_t msg;

    if(micolink_parse_char(&msg, data) == false)
        return;
    
    switch(msg.msg_id)
    {
        case MICOLINK_MSG_ID_RANGE_SENSOR:
        {
            MICOLINK_PAYLOAD_RANGE_SENSOR_t payload;
            memcpy(&payload, msg.payload, msg.len);

            /*
                Sensor data is available here:
            
                Distance               = payload.distance;
                Strength               = payload.strength;
                Precision              = payload.precision;
                Distance status        = payload.tof_status;
                Optical-flow velocity X = payload.flow_vel_x;
                Optical-flow velocity Y = payload.flow_vel_y;
                Optical-flow quality   = payload.flow_quality;	
                Optical-flow status    = payload.flow_status;
            */
            break;
        } 

        default:
            break;
        }
}

bool micolink_check_sum(MICOLINK_MSG_t* msg)
{
    uint8_t length = msg->len + 6;
    uint8_t temp[MICOLINK_MAX_LEN];
    uint8_t checksum = 0;

    memcpy(temp, msg, length);

    for(uint8_t i=0; i<length; i++)
    {
        checksum += temp[i];
    }

    if(checksum == msg->checksum)
        return true;
    else
        return false;
}

bool micolink_parse_char(MICOLINK_MSG_t* msg, uint8_t data)
{
    switch(msg->status)
    {
    case 0:     // Frame header
        if(data == MICOLINK_MSG_HEAD)
        {
            msg->head = data;
            msg->status++;
        }
        break;
        
    case 1:     // Device ID
        msg->dev_id = data;
        msg->status++;
        break;
    
    case 2:     // System ID
        msg->sys_id = data;
        msg->status++;
        break;
    
    case 3:     // Message ID
        msg->msg_id = data;
        msg->status++;
        break;
    
    case 4:     // Packet sequence
        msg->seq = data;
        msg->status++;
        break;
    
    case 5:     // Payload length
        msg->len = data;
        if(msg->len == 0)
            msg->status += 2;
        else if(msg->len > MICOLINK_MAX_PAYLOAD_LEN)
            msg->status = 0;
        else
            msg->status++;
        break;
        
    case 6:     // Receive payload
        msg->payload[msg->payload_cnt++] = data;
        if(msg->payload_cnt == msg->len)
        {
            msg->payload_cnt = 0;
            msg->status++;
        }
        break;
        
    case 7:     // Frame checksum
        msg->checksum = data;
        msg->status = 0;
        if(micolink_check_sum(msg))
        {
            return true;
        }
        
    default:
        msg->status = 0;
        msg->payload_cnt = 0;
        break;
    }

    return false;
}