本文章介紹在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); } } }
沒有留言:
張貼留言