本文章介紹在Raspberry Pi Pico W 使用Btstack來實現使用BLE的Advertisement來發送安裝在BLE peripheral(broadcaster)上的溫濕度sensor的數據給所有centrals(observers)。
本實驗使用一個Raspberry Pi Pico W連接DHT11,把收集到環境的溫濕度透過BLE advertisement給附近所有的BLE observer。
另一個Raspberry Pi Pico W當作Central(Observer)接收advertisement data顯示在TFT上。
另一個Android Phone安裝Nordic nRF Connect scan 附近BLE broadcaster並顯示收集到的資料。
本實驗僅透過Advertisement data來傳送資料,不需要connect BLE peripheral。
每一個advertisement資料結構分成三個欄位:length, type and data。在本實驗中相對應程式的結構如下:
2. UUID 0x2A6F表示一個byte的濕度資料。
Peripheral端設定advertisement param, data後enable advertisement。
length, type and data。程式碼:
1. Peripheral(broadcaster)端:
#include <stdio.h>
#include "pico/stdlib.h"
#include "btstack.h"
#include "pico/cyw43_arch.h"
#include "dht11/dht11.h"
#include "math.h"
#include "stdlib.h"
#define ADV_TEMP_HUMI_PERIOD_MS 3000
uint8_t adv_data[] = {
// Flags: 0x02: General Discoverable Mode, 0x04:BR/EDR Not Supported
0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
// Name
0x0a, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'P', 'i', 'c', 'o', 'W', '-', 'B', 'L', 'E',
0x03, BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 0x1a, 0x18,
//Temperature UUID:0x2A5E
0x04, BLUETOOTH_DATA_TYPE_SERVICE_DATA, 0x6E,0x2A,0x00,
//Humidity UUID: 0x2A6F
0x04, BLUETOOTH_DATA_TYPE_SERVICE_DATA, 0x6F,0x2A,0x00,
};
uint8_t adv_data_len = sizeof(adv_data);
static btstack_timer_source_t adv_temp_humi;
uint8_t temp=0, humi=0;
static void adv_temp_humi_handler(struct btstack_timer_source *ts){
//gap_advertisements_enable(false);
float t,h;
if (dht11_get_data(&t, &h)) {
temp = (int)round(t);
humi = (int)h;
}
adv_data[adv_data_len-1] = humi;
adv_data[adv_data_len-6] = temp;
printf("Temp:%d °C, Humi:%d %c\n", temp, humi, '%');
gap_advertisements_set_data(adv_data_len, adv_data);
//gap_advertisements_enable(true);
btstack_run_loop_set_timer(ts, ADV_TEMP_HUMI_PERIOD_MS);
btstack_run_loop_add_timer(ts);
}
int main()
{
stdio_init_all();
if (cyw43_arch_init()) {
printf("cyw43_init error\n");
return 0;
}
l2cap_init();
sm_init();
dht11_init();
// setup advertisements
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
// ADV_TYPE:
// 0b0000: ADV_IND, 0b0001: ADV_DIRECT_IND, 0b0010: ADV_NONCONN_IND, 0b0011: ADV_SCAN_IND
uint8_t adv_type = 2; // only advertisement, not connectable.
bd_addr_t null_addr;
memset(null_addr, 0, 6);
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
gap_advertisements_enable(true);
adv_temp_humi.process = &adv_temp_humi_handler;
btstack_run_loop_set_timer(&adv_temp_humi, ADV_TEMP_HUMI_PERIOD_MS);
btstack_run_loop_add_timer(&adv_temp_humi);
hci_power_control(HCI_POWER_ON);
while(1) {
tight_loop_contents();
}
return 0;
}2. Central(Observer)端:
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "btstack.h"
#include "fonts/font_fixedsys_mono_16.h"
#include "pico_tft.h"
#include "tft_string.h"
#define BROADCAST_DEVICE "PicoW-BLE"
static btstack_packet_callback_registration_t hci_event_callback_registration;
uint8_t broadcast_device_name[20];
uint8_t service_16_uuid[2];
uint8_t temp=0;
uint8_t humi=0;
bool bNewAdvData=false;
static void show_received_advertisement_data(const uint8_t * adv_data, uint8_t adv_size){
ad_context_t context;
for (ad_iterator_init(&context, adv_size, (uint8_t *)adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){
uint8_t data_type = ad_iterator_get_data_type(&context);
uint8_t size = ad_iterator_get_data_len(&context);
const uint8_t * data = ad_iterator_get_data(&context);
switch (data_type) {
case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME:
memcpy(broadcast_device_name, data,size);
broadcast_device_name[size]='\0';
break;
case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
memcpy(service_16_uuid, data,size);
break;
case BLUETOOTH_DATA_TYPE_SERVICE_DATA:
if (data[0]==0x6E && data[1]==0x2A) {
temp = data[2];
}
if (data[0]==0x6F && data[1]==0x2A) {
humi = data[2];
}
break;
}
}
if (strcmp(broadcast_device_name, BROADCAST_DEVICE) == 0 &&
service_16_uuid[0]==0x1A && service_16_uuid[1]==0x18) {
bNewAdvData=true;
}
}
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
if (packet_type != HCI_EVENT_PACKET) return;
switch (hci_event_packet_get_type(packet)) {
case GAP_EVENT_ADVERTISING_REPORT:{
uint8_t length = gap_event_advertising_report_get_data_length(packet);
const uint8_t * data = gap_event_advertising_report_get_data(packet);
show_received_advertisement_data(data, length);
break;
}
default:
break;
}
}
uint8_t ts[50];
int main() {
stdio_init_all();
if (cyw43_arch_init()) {
printf("cyw43_init error\n");
return 0;
}
gap_set_scan_parameters(0,4800,48); // scan type: 0: passive, 1:active
//scan interval 0.625*n,
gap_start_scan();
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
hci_power_control(HCI_POWER_ON);
tft_init();
while (1) {
if (bNewAdvData) {
bNewAdvData=false;
printf("local name:%s, temp=%d, humi=%d\n", broadcast_device_name, temp, humi);
tft_fill_rect(0,0,TFT_WIDTH, TFT_HEIGHT, 0xffff);
sprintf(ts, "Device:%s", broadcast_device_name);
tft_draw_string(2,10,ts, 0x001f, &font_fixedsys_mono_16);
sprintf(ts, "Temp:%02dC", temp);
tft_draw_string(2,50,ts, 0x001f, &font_fixedsys_mono_16);
sprintf(ts, "Humi:%02d%c", humi, '%');
tft_draw_string(2,80,ts, 0x001f, &font_fixedsys_mono_16);
}
}
}

沒有留言:
張貼留言