esp32速学——eddystone

eddystone

https://baike.baidu.com/item/Eddystone/18103061?fr=aladdin
    Eddystone是谷歌的一个开放信标协议规范,旨在改善“基于邻近性的体验”,
同时支持Android和iOS智能设备平台。一个开源蓝牙信标平台。
这个例子是扫描的,弄错了,emm..

主要文件结构

  • esp_eddystone_api.h 解码API定义的一些数据结构
  • esp_eddystone_api.c 解码API
  • esp_eddystone_protocol.h 解析eddstone协议时用到的数据结构。
  • esp_eddystone_demo.c 主代码,
    在这里插入图片描述

源码阅读n部曲

    代码不多,大概阅读源码来了解整个项目过程还是可行的。阅读源码分以下几步:
    1.主要文件用处
    2.文件中导入的库以及主要数据结构和函数用处
    3.流程

文件主要作用

  • esp_eddystone_api.h 解码API定义的一些数据结构
  • esp_eddystone_api.c 解码API
  • esp_eddystone_protocol.h 解析eddstone协议时用到的数据结构。
  • esp_eddystone_demo.c 主代码,

esp_eddystone_protocol.h

#include "stdbool.h"     bool类型的一些宏以及操作
#include "stdint.h"      int类型的一些宏以及操作

数据结构

一些eddystone协议的宏,包含UUID,协议对应URL等等。
TLE、UID这些概念还不知道啥意思。emmm.
/* Eddystone definitions */
#define EDDYSTONE_SERVICE_UUID          0xFEAA

#define EDDYSTONE_FRAME_TYPE_UID        0x00
#define EDDYSTONE_FRAME_TYPE_URL        0x10
#define EDDYSTONE_FRAME_TYPE_TLM        0x20
#define EDDYSTONE_FRAME_TYPE_EID        0x30
//UID
#define EDDYSTONE_UID_RANG_DATA_LEN     1
#define EDDYSTONE_UID_NAMESPACE_LEN     10
#define EDDYSTONE_UID_INSTANCE_LEN      6
#define EDDYSTONE_UID_RFU_LEN           2
#define EDDYSTONE_UID_DATA_LEN         (EDDYSTONE_UID_RANG_DATA_LEN + EDDYSTONE_UID_NAMESPACE_LEN + EDDYSTONE_UID_INSTANCE_LEN)
//TLM
#define EDDYSTONE_TLM_VERSION_LEN          1
#define EDDYSTONE_TLM_BATTERY_VOLTAGE_LEN  2
#define EDDYSTONE_TLM_TEMPERATURE_LEN      2
#define EDDYSTONE_TLM_ADV_COUNT_LEN        4
#define EDDYSTONE_TLM_TIME_LEN             4
#define EDDYSTONE_TLM_DATA_LEN             (EDDYSTONE_TLM_VERSION_LEN + EDDYSTONE_TLM_BATTERY_VOLTAGE_LEN + \
EDDYSTONE_TLM_TEMPERATURE_LEN + EDDYSTONE_TLM_ADV_COUNT_LEN + EDDYSTONE_TLM_TIME_LEN)
//URL
#define EDDYSTONE_URL_SCHEME_LEN        1
#define EDDYSTONE_URL_ENCODED_MAX_LEN   17
#define EDDYSTONE_URL_MAX_LEN           (EDDYSTONE_URL_SCHEME_LEN + EDDYSTONE_URL_ENCODED_MAX_LEN)
#define EDDYSTONE_URL_TX_POWER_LEN      1

处理协议时候所需要的一些数据结构,一般这种结构都是对应到协议的格式中。attribute((packed))用于编译器优化的。

/* Eddystone UID frame */
typedef struct {
    int8_t    ranging_data;     /*<! calibrated Tx power at 0m */
    uint8_t   namespace_id[10];
    uint8_t   instance_id[6];
    uint8_t   reserved[2];
} __attribute__((packed))esp_eddystone_uid_t;

/* Eddystone URL frame */
typedef struct {
    int8_t    tx_power;         /*<! calibrated Tx power at 0m */
    uint8_t   url_scheme;       /*<! encoded scheme prefix */
    uint8_t   encoded_url[0];   /*<! length 1-17 */
} __attribute__((packed))esp_eddystone_url_t;

/* Eddystone TLM frame */
typedef struct {
    uint8_t    version;        /*<! TLM version,0x00 for now */
    uint16_t   batt;           /*<! battery voltage, 1mV/bit */
    uint16_t   temp;           /*<! beacon temperature */
    uint32_t   adv_count;      /*<! adv pdu count since power-on or reboot */
    uint32_t   time;           /*<! time sence power-on or reboot, a 0.1 second resolution counter */
} __attribute__((packed)) esp_eddystone_tlm_t;

/*  AD Structure of flags */
typedef struct {
    uint8_t     len;
    uint8_t     type;
    uint8_t     flags;
} __attribute__((packed)) esp_eddystone_flags_t;

/* AD Structure of complete 16-bit service uuid */
typedef struct {
    uint8_t     len;
    uint8_t     type;
    uint16_t    uuid;       /*<! complete list of 16-bit service UUIDs data type value */
} __attribute__((packed)) esp_eddystone_uuid_t;

/* AD Structure of eddystone frame*/
typedef struct {
    uint8_t     len;        /*<! length of eddystone data */
    uint8_t     type;       /*<! service data type,must be 0x16 */
    uint16_t    uuid;       /*<! 16-bit eddystone uuid */
    uint8_t     frame_type;
    union {
        esp_eddystone_uid_t     uid;
        esp_eddystone_url_t     url;
        esp_eddystone_tlm_t     tlm;
    } u[0];
} __attribute__((packed)) esp_eddystone_frame_t;

/* eddystone packet type */
typedef struct {
    esp_eddystone_flags_t   flags;
    esp_eddystone_uuid_t    uuid;
    esp_eddystone_frame_t   frame;
} __attribute__((packed)) esp_eddystone_packet_t;

函数

内联代码,相当于把这段代码直接复制到调用处

/*
 * URLs are written only with the graphic printable characters of the US-ASCII coded character set.
 * The octets 00-20 and 7F-FF hexadecimal are not used.
 * See “Excluded US-ASCII Characters” in RFC 2936.
 *
 */
static inline bool esp_eddystone_is_char_invalid(int ch)
{
    return (ch >= 0x00 && ch <= 0x20) || (ch >= 0x7f && ch <= 0xff);
}

#endif /* __ESP_EDDYSTONE_PROTOCOL_H__ */

esp_eddystone_api.h

数据结构

解析协议数据需要的数据结构,可以认为一个把接受到的数据解析成需要处理的数据。
typedef struct {
    struct {
        uint8_t   flags;          /*<! AD flags data */
        uint16_t  srv_uuid;       /*<! complete list of 16-bit service uuid*/
        uint16_t  srv_data_type;  /*<! service data type */
        uint8_t   frame_type;     /*<! Eddystone UID, URL or TLM */
    } common;
    union {
        struct {
            /*<! Eddystone-UID */
            int8_t  ranging_data;     /*<! calibrated Tx power at 0m */
            uint8_t namespace_id[10];
            uint8_t instance_id[6];
        } uid;
        struct {
            /*<! Eddystone-URL */
            int8_t  tx_power;                    /*<! calibrated Tx power at 0m */
            char    url[EDDYSTONE_URL_MAX_LEN];  /*<! the decoded URL */
        } url;
        struct {
            /*<! Eddystone-TLM */
            uint8_t   version;           /*<! TLM version,0x00 for now */
            uint16_t  battery_voltage;   /*<! battery voltage in mV */
            float     temperature;       /*<! beacon temperature in degrees Celsius */
            uint32_t  adv_count;         /*<! adv pdu count since power-up */
            uint32_t  time;              /*<! time since power-up, a 0.1 second resolution counter */
        } tlm;
    } inform;
} esp_eddystone_result_t;

函数

内联函数,还像是将多次调用的代码抽出来。

/* utils */
static inline uint16_t little_endian_read_16(const uint8_t *buffer, uint8_t pos)
{
    return ((uint16_t)buffer[pos]) | (((uint16_t)buffer[(pos)+1]) << 8);
}

static inline uint16_t big_endian_read_16(const uint8_t *buffer, uint8_t pos)
{
    return (((uint16_t)buffer[pos]) << 8) | ((uint16_t)buffer[(pos)+1]);
}

static inline uint32_t big_endian_read_32(const uint8_t *buffer, uint8_t pos)
{
    return (((uint32_t)buffer[pos]) << 24) | (((uint32_t)buffer[(pos)+1]) << 16) | (((uint32_t)buffer[(pos)+2]) << 8) | ((uint32_t)buffer[(pos)+3]);
}

对外暴漏的API.

esp_err_t esp_eddystone_decode(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);

esp_eddystone_api.c

跟上边一样,基本上就是库、数据结构、函数分析下,这个有点多,就抽出来主要的
写下

#include <stdio.h>      标准库
#include <stdint.h>     int处理,因此里面肯定处理了整数
#include <string.h>     同上
#include <stdbool.h>    同上

#include "esp_err.h"    esp运行中一些错误以及错误代码
#include "esp_gap_ble_api.h"  ibeacon相当于广播,就是GAP层
#include "esp_eddystone_protocol.h"  不用多说
#include "esp_eddystone_api.h"       不用多说

数据结构

解析数据所需要的一些匹配项等等。

static const char* eddystone_url_prefix[4] = {
    "http://www.",
    "https://www.",
    "http://",
    "https://"
};

/* Eddystone-URL HTTP URL encoding */
static const char* eddystone_url_encoding[14] = {
    ".com/",
    ".org/",
    ".edu/",
    ".net/",
    ".info/",
    ".biz/",
    ".gov/",
    ".com",
    ".org",
    ".edu",
    ".net",
    ".info",
    ".biz",
    ".gov"
 };

函数

英语一翻译以下应该就知道了。

/* Declare static functions */
static esp_err_t esp_eddystone_uid_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
static esp_err_t esp_eddystone_url_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
static char* esp_eddystone_resolve_url_scheme(const uint8_t* url_start, const uint8_t* url_end);
static esp_err_t esp_eddystone_tlm_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
static esp_err_t esp_eddystone_get_inform(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);

真正解析广播数据以获取有用数据操作的。其实就是按数据格式处理,从中加工出来

esp_err_t esp_eddystone_decode(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res)
{
    if (len == 0 || buf == NULL || res == NULL) {
        return -1;
    }
    uint8_t pos=0;
    while(res->common.srv_data_type != EDDYSTONE_SERVICE_UUID)
    {
        pos++;
        if(pos >= len ) {
            return -1;
        }
        uint8_t ad_type = buf[pos++];
        switch(ad_type)
        {
            case ESP_BLE_AD_TYPE_FLAG: {
                res->common.flags = buf[pos++];
                break;
            }
            case ESP_BLE_AD_TYPE_16SRV_CMPL: {
                uint16_t uuid = little_endian_read_16(buf, pos);
                if(uuid != EDDYSTONE_SERVICE_UUID) {
                    return -1;
                }
                res->common.srv_uuid = uuid;
                pos += 2;
                break;
            }
            case ESP_BLE_AD_TYPE_SERVICE_DATA: {
                uint16_t type = little_endian_read_16(buf, pos);
                pos += 2;
                uint8_t frame_type = buf[pos++];
                if(type != EDDYSTONE_SERVICE_UUID || !(frame_type == EDDYSTONE_FRAME_TYPE_UID || frame_type == EDDYSTONE_FRAME_TYPE_URL ||
                   frame_type == EDDYSTONE_FRAME_TYPE_TLM)) {
                    return -1;
                }
                res->common.srv_data_type = type;
                res->common.frame_type = frame_type;
                break;
            }
            default:
                break;
        }
    }
    return esp_eddystone_get_inform(buf+pos, len-pos, res);
}

esp_eddystone_demo.c

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

#include "esp_bt.h"       
#include "nvs_flash.h"      nvs存储
#include "esp_log.h"        esp_log
#include "esp_bt_defs.h"    defs不知道
#include "esp_bt_main.h"    bt_main这里面才有真正的
#include "esp_gatt_defs.h"  GATT层defs是什么呢
#include "esp_gattc_api.h"
#include "esp_gap_ble_api.h"
#include "freertos/FreeRTOS.h"   esp32使用freertos,所以因此。

#include "esp_eddystone_protocol.h"
#include "esp_eddystone_api.h"

函数

esp_gap_cb() gap事件回调

/* declare static functions */
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
static void esp_eddystone_show_inform(const esp_eddystone_result_t* res);
//注册回调
void esp_eddystone_appRegister(void)
{
    esp_err_t status;

    ESP_LOGI(DEMO_TAG,"Register callback");

    /*<! register the scan callback function to the gap module */
    if((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK) {
        ESP_LOGE(DEMO_TAG,"gap register error: %s", esp_err_to_name(status));
        return;
    }
}
// esp中host协议栈初始化部分,bluedroid协议栈对应整个协议栈host层面。
void esp_eddystone_init(void)
{
    esp_bluedroid_init();        //初始化
    esp_bluedroid_enable();      //使能
    esp_eddystone_appRegister();  //注册本次需要使用的回调
}
//主函数。
void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    esp_bt_controller_init(&bt_cfg);
    esp_bt_controller_enable(ESP_BT_MODE_BLE);

    esp_eddystone_init();

    /*<! set scan parameters */
    esp_ble_gap_set_scan_params(&ble_scan_params);
}


例子流程

eso32这些单片机首先肯定是main函数来初始化一些东西 app_main(),
其中依次是外设、controller层、host层。然后是设置设备状态。最后不应该是一个whle(1)吗?有这个疑问很正常,但是还可以通过其他方式来实现哦,就是中断喽,上边哪个回调函数就可以,每次一个事件来设置一个状态。
其过程主要为以下代码。

void app_main(void)
{	//外设初始化 nvs
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    //controller层初始化使能
    esp_bt_controller_init(&bt_cfg);
    esp_bt_controller_enable(ESP_BT_MODE_BLE);
     
    //host层初始化
    esp_eddystone_init();
	
	//设置为扫描状态
    /*<! set scan parameters */
    esp_ble_gap_set_scan_params(&ble_scan_params);
}

void esp_eddystone_init(void)
{	//host层初始化
    esp_bluedroid_init();
    //使能
    esp_bluedroid_enable();
    //注册回调函数
    esp_eddystone_appRegister();
}

但是,esp32不是这样实现的,往下扒代码可知道,主线程初始化完之后就停止了,esp32又是双核的,所以应该是host层和ll层线程来完成整个整个过程的。esp32没有暴漏这两层的细节,知识猜测

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值