Arduino IDE2.0 Release Candidate を少し試してみた [Arduino]
基本的な使い方は従来のArduino IDEと変わりません。SpresenseのArduino Libraryもインストールできましたので、ESP8266含めて外部ライブラリは従来どおり使えるようです。
一番の驚きなのはデバッガーをサポートしたことです。Spresenseの場合SWD対応のJTAGデバッガーをつなげればデバッグできるようになります。これは朗報ですね。
【参考】SONY Spresense をLPC-Link2(CMSIS-DAP)でデバックしよう
https://qiita.com/usashirou/items/525eeaa9c43acf860f64
簡易版 Visual Studio Code という感じで気軽に使える開発環境となりそうです。しばらく 2.0 RC を使ってみて気づいた点があったらここで紹介したいと思います!
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
Arduino Uno Mini 爆誕 [Arduino]
気になるスペックはこちら。
MICROCONTROLLER | ATmega328P |
USB CONNECTOR | USB-C |
BUILT-IN LED PIN | 13 |
DIGITAL I/O PINS | 14 |
ANALOG INPUT PINS | 6 |
PWM PINS | 6 |
UART | yes |
I2C | yes |
SPI | yes |
CIRCUIT OPERATING VOLTAGE | 5V |
INPUT VOLTAGE (LIMIT) | 6-12V |
BATTERY CONNECTOR | None. |
DC CURRENT PER I/O PIN | 20 mA |
DC CURRENT FOR 3.3V PIN | 50 mA |
MAIN PROCESSOR | ATmega328P 16 MHz |
USB-SERIAL PROCESSOR | ATmega16U2 16 MHz |
MEMORYATMEGA328P | 2KB SRAM, 32KB FLASH, 1KB EEPROM |
WEIGHT | 8.05 g |
WIDTH | 26.70 mm |
LENGTH | 34.20 mm |
CPUは、なんとATmega328Pそのまま。ライブラリの互換性を重視したのかも知れません。ボードの色は黒。SPRESENSEが出てから黒いボードが流行になってきましたね。ただ、つや消し黒じゃないので、SPRESENSEやPortenta H7よりもちょっと安っぽいかも。
それ以上に目を引くのが、このパッケージ。やはりイタリアの会社はオシャレですね。物欲をそそるパッケージです。内容もすごいオシャレ。化粧品でも入っているのか思うような箱の作りです。外装にお金かけるんだったら、基板もつや消し黒にしてほしかったなぁ。
色々と開発ボードは出てきていますが、老舗のArduinoからもまだまだ目が離せませんね。
(^^)/~
"Arduino Portenta H7" に "Vison Shield" が登場!! [Arduino]
今一度、仕様をおさらいしてみたいと思います。
Microcontroller | STM32H747XI dual Cortex®-M7+M4 32bit low power ARM MCU (datasheet) |
Radio module | Murata 1DX dual WiFi 802.11b/g/n 65 Mbps and Bluetooth 5.1 BR/EDR/LE (datasheet) |
Secure Element (default) | NXP SE0502 (datasheet) |
Board Power Supply (USB/VIN) | 5V |
Supported Battery | Li-Po Single Cell, 3.7V, 700mAh Minimum (integrated charger) |
Circuit Operating Voltage | 3.3V |
Current Consumption | 2.95 μA in Standby mode (Backup SRAM OFF, RTC/LSE ON) |
Display Connector | MIPI DSI host & MIPI D-PHY to interface with low-pin count large display |
GPU | Chrom-ART graphical hardware Accelerator |
Timers | 22x timers and watchdogs |
UART | 4x ports (2 with flow control) |
Ethernet PHY | 10 / 100 Mbps (through expansion port only) |
SD Card | Interface for SD Card connector (through expansion port only) |
Operational Temperature | -40 °C to +85 °C |
MKR Headers | Use any of the existing industrial MKR shields on it |
High-density Connectors | Two 80 pin connectors will expose all of the board's peripherals to other devices |
Camera Interface | 8-bit, up to 80 MHz |
ADC | 3× ADCs with 16-bit max. resolution (up to 36 channels, up to 3.6 MSPS) |
DAC | 2× 12-bit DAC (1 MHz) |
USB-C | Host / Device, DisplayPort out, High / Full Speed, Power delivery |
CPUは"STM32H747XI"で Cortex M7 と Cortex M4 のデュアルコアです。STマイクロのホームページで仕様を確認してみると、両方あわせて1,327 DMIPS なのでSPRESENSEにはやや及ばないレベル。(SPRESENSEはM4@300DMIPS x 6 = 1,800DMIS)
RAMとROMの情報がないので、こちらのサイトで確認しました。
SDRAM:64MB / Flash128M-bit で、かなりリッチです。ただSDRAMを使っているので低消費電力はあまり期待できないですね。残念ながら。
おさらいはここまでで、そのシールドが初登場しました。その名も "ARDUINO PORTENTA VISION SHIELD"。名前がかっこいいですね。カメラとEther、ステレオマイクが搭載されています。
Camera | Himax HM-01B0 camera module (manufacturer site) |
Resolution | 324 x 324 active pixel resolution with support for QVGA |
Image sensor | High sensitivity 3.6μ BrightSense pixel technology |
Microphone | 2 x MP34DT05 (datasheet) |
Length | 66 mm |
Width | 25 mm |
Weight | 11 gr |
イメージセンサーの解像度が想像以上に小さい。データシートを確認すると、QVGA 30FPS で 2mW 以下なのでかなり省電力ですが、本体の消費電力がイマイチなのでここで頑張ってもなぁという感じはします。
ひょっとして Global Shutter? とも思ったのですが、残念ながら Rolling Shutter。HDRというわけでもないし。うーん、なんのために使うのかよくわからない。
マイクのほうも確認をしてみました。データシートを確認すると、これはデジタルMEMSマイクですね。となると他のアナログセンサーや指向性マイクの接続は無理そうです。
マイクは設置する場所が大事なので嵌め殺しにせずに、コネクタで接続できるようにしてほしかったなぁ。かなり残念。
価格は"Portenta H7"と"Vision Shield"両方あわせて、約 $150 (15,750円:105円/ドル)、SPRESENSEのメインボード、カメラ、LTEボードをあわせると 16,980 円。これぐらいのクラスになるとこれくらいの値段が妥当なのかな。
Arduino Portenta H7は Ether, WiFi/BLE を使えるけど消費電力がもう致命的。一方、SPRESENSEは5百万画素カメラ、アナログマイク4チャンネル、GPS付きで超低消費電力だけど、LTEボードにSIMがいるってことを考えると、どっこいどっこいかな。
プロ向けにとっては、最後は消費電力が決め手になりそう。SPRESENSEのWiFI/BLEやEtherは必要に応じて追加できるし。
ということで和洋のソックリさん対決は、ホビー向けにはArduino Portenta に軍配。低消費電力を追い求めるIoT向けにはSPRESENSEに軍配。という痛み分けの結果となりました。
(○ ̄ ー  ̄○ )
Arduinoをはじめよう 第3版 (Make:PROJECTS)
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/11/28
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
久しぶりにESP-WROOM-02のファームをアップデートしたらクラッシュしたでござる [Arduino]
もともとは、SPRESENSE用のWi-Fi Add-onボードのファームをアップデートしようとしたのですが、ファームがアップデートできない仕様になっていました。残念。
なんで、手持ちのESP-WROOM-02を引っ張り出してきてハンダを駆使して最新ファームに更新したのですが、ATコマンドを打ってもウンスン…。
書き込むファーム間違えたかなと思い、もう一度 Flash Downloader を立ち上げファームを更新しようとしたら、無情にも次のようなメッセージが出てきて必ず失敗するようになりました。Σ(゚д゚lll)
====== CONNECT BAUD: 115200 ============ .Uploading stub... Running stub... Stub running... [2020-04-03 00:52:07,584][ESP8266Loader_spi[1]][espDownloader.py][line:468][ERROR]: ESP8266 Chip efuse check error esp_check_mac_and_efuse.
書き込んだのは "ESP8266_NONOS_SDK-3.0.3"。リリース日はなんと"2020/03/24"。いやな予感しかしません。(;・∀・)
Google大先生に問い合わせしてみたものの、いまだ解決策はない様子…。まじかー。OTZ…
"efuse check error" とか出てるから書き込んではいけない領域に書き込みをしてしまったのかなぁ。ここまで苦労したのに、オレの時間を返してくれよ~!!。・゚・(ノД`)・゚・。
誰か解決策知りませんか?(°д°)
ESP-WROOM-02 32メガビット 品 4MB 技適取得済み お得な2個セット
- 出版社/メーカー:
- メディア:
ESP-WROOM-02ピッチ変換済みモジュール《シンプル版》
- 出版社/メーカー: スイッチサイエンス(Switch Science)
- メディア: おもちゃ&ホビー
Arduino 新作ボードがまるで SPRESENSE?その違いとは!? [Arduino]
https://store.arduino.cc/usa/portenta-h7
あれぇ?これなんてSPRESENSE???
そう、SPRESENSEそっくりなんですよ。でもプロセッサーのシルクがイタリア製らしくおしゃれですね。大きさは、67.64 × 25 mm で SPRESENSE よりも一回り大きな感じですね。少しスリムなRaspberry Pi Zero という感じです。
裏面を見ると、なんとB2Bコネクタが!そこまでSPRESENSEと同じなの!?
ということは拡張ボードが出るのかな?
仕様を詳しく見てみましょう。
Microcontroller | STM32H747XI dual Cortex-M7+M4 32bit low power ARM MCU |
Radio module | Murata 1DX dual WiFi 802.11b/g/n 65 Mbps and Bluetooth 5.1 BR/EDR/LE |
Secure Element | NXP SE0502 |
Board Power Supply (USB/VIN) | 5V |
Supported Battery | Li-Po Single Cell, 3.7V, 700mAh Minimum (integrated charger) |
Circuit Operating Voltage | 3.3V |
Current Consumption | 2.95 μA in Standby mode (Backup SRAM OFF, RTC/LSE ON) |
Display Connector | MIPI DSI host & MIPI D-PHY to interface with low-pin count large display |
GPU | Chrom-ART graphical hardware Accelerator |
Timers | 22x timers and watchdogs |
UART | 4x ports (2 with flow control) |
Ethernet PHY | 10 / 100 Mbps (through expansion port only) |
SD Card | Interface for SD Card connector (through expansion port only) |
Operational Temperature | -40 °C to +85 °C (excl. Wireless module) / -10 °C to +55 °C (incl. Wireless module) |
MKR Headers | Use any of the existing industrial MKR shields on it |
High-density Connectors | Two 80 pin connectors will expose all of the board's peripherals to other devices |
Camera Interface | 8-bit, up to 80 MHz |
ADC | 3× ADCs with 16-bit max. resolution (up to 36 channels, up to 3.6 MSPS) |
DAC | 2× 12-bit DAC (1 MHz) |
USB-C | Host / Device, DisplayPort out, High / Full Speed, Power delivery |
カメラをサポートしているのですが、8ビットパラレルっぽいですね。あとSDカードも裏面のB2Bコネクタ (expantion port) 経由で扱えるようです。そこらへんもSPRESENSEと同じかー。完全にベンチマークしてますね。
メモリはデフォルトでRAMが2MB、Flashが16MBといったところで、少しSPRESENSEよりも大きい。ただメモリはオプションで 64MB まで拡張できるようです。TensorFlow Liteも使えるのでメモリが拡張できるのはいいですね。(でも、PoPになると思われるので、かなりお高くなるかと)
ADCが3.6MSPSまでいけるのと、スリープモードで 3μAというのはいいですね。でも回路を見る限り、動かしているときはかなり電力食うと思います。
SPRESENSEとARDUINO PORTENTA H7の違いを簡単にまとめてみました。
SPRESENSEの良いところ
・測位(GPS)機能
・音声出力、音声入力
・LPWA(LTE-M)をサポート
・動作時も省電力
ARDUINO PORTENTA H7の良いところ
・オンボードWiFi/BT
・バッテリチャージャー内蔵
・Sleep時の消費電力が低い
・メモリ拡張可能
どっちも一長一短ですね。PORTENTA H7も拡張ボードでLPWAをサポートしてくるだろうし、一方でSPRESENSEはWiFi/BLEのボードもサポートしているし。この2つの良いところを一緒にするとちょうどいいエッジソリューションなんですけどねぇ。
でも、日本発のボードデザインが本家に参考されるなんてちょっと誇らしいですね。この分野も今まで欧米(主にArduino, Raspberry Pi, Intel ですが)が主導してきましたが、ここにきて日本勢が巻き返してきた感じです。ソニーだけでなくルネサスもがんばってほしいなぁ。
(^^)
Arduinoをはじめよう 第3版 (Make:PROJECTS)
- 作者: Massimo Banzi
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/11/28
- メディア: 単行本(ソフトカバー)
アイデア実現のための Raspberry Piデザインパターン: 電子回路からMathematicaによるArduinoコラボまで
- 作者: 小林 博和
- 出版社/メーカー: オーム社
- 発売日: 2019/10/25
- メディア: 単行本
電子部品ごとの制御を学べる! Arduino 電子工作 実践講座
- 作者: 福田 和宏
- 出版社/メーカー: ソーテック社
- 発売日: 2018/09/21
- メディア: 単行本
ESP32 の機能を再確認してみた! [Arduino]
ESP32 Datasheet
http://espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
Wi-Fi は、2.4GHz帯 のみですね。まぁ値段がこれですから当然か。でも n に対応しているのはポイント高いです。
1.2 Wi-Fi Key Features
802.11 b/g/n/e/i
802.11 n (2.4 GHz), up to 150 Mbps
802.11 e: QoS for wireless multimedia technology
WMM-PS, UAPSD
A-MPDU and A-MSDU aggregation
Block ACK
Fragmentation and defragmentation
Automatic Beacon monitoring/scanning
802.11 i security features: pre-authentication and TSN
Wi-Fi Protected Access (WPA)/WPA2/WPA2-Enterprise/Wi-Fi Protected Setup (WPS)
Infrastructure BSS Station mode/SoftAP mode
Wi-Fi Direct (P2P), P2P Discovery, P2P Group Owner mode and P2P Power Management
UMA compliant and certified
Antenna diversity and selection
Bluetooth は、v4.2 対応です。BLEならびにGATTもサポートしています。HeartRateブロファイルで確認清みですね。A2DP/HSP/HFP もサポートしているので、ヘッドセットみたいなのもできそうですね。
1.3 BT Key Features
Compliant with Bluetooth v4.2 BR/EDR and BLE specification
Class-1, class-2 and class-3 transmitter without external power amplifier
Enhanced power control
+12 dBm transmitting power
NZIF receiver with -97 dBm sensitivity
Adaptive Frequency Hopping (AFH)
Standard HCI based on SDIO/SPI/UART
High-speed UART HCI, up to 4 Mbps
BT 4.2 controller and host stack
Service Discover Protocol (SDP)
General Access Profile (GAP)
Security Manage Protocol (SMP)
ATT/GATT
HID
All GATT-based profile supported
SPP-like GATT-based profile
BLE Beacon
A2DP/AVRCP/SPP, HSP/HFP, RFCOMM
CVSD and SBC for audio codec
Bluetooth Piconet and Scatternet
CPUはデュアルコアですね。メモリは520kB+15kB。外付けの Quad SPI Flash がつくようです。駆動電圧は 3.3V。
1.4.1 CPU and Memory
Xtensa single-/dual-core 32-bit LX6 microprocessor(s), up to 600 DMIPS (200 DMIPS for single-core microprocessor)
448 kB ROM
520 kB SRAM
16 kB SRAM in RTC
QSPI flash/SRAM, up to 4 x 16 MB
Power supply: 2.3V to 3.6V
タイマーは2系統のようです。RTCは当然ありますね。Wi-Fiと相性が良いので助かります。
1.4.2 Clocks and Timers
Internal 8 MHz oscillator with calibration
Internal RC oscillator with calibration
External 2 MHz to 60 MHz crystal oscillator (40 MHz only for Wi-Fi/BT functionality)
External 32 kHz crystal oscillator for RTC with calibration
Two timer groups, including 2 x 64-bit timers and 1 x main watchdog in each group
RTC timer with sub-second accuracy
RTC watchdog
12-bit ADCが18チャンネルもあるってスゴイですね。ただサンプリングレートが明記されていません。遅いのかな。CANもサポートとしているので車関連のアプリも作れますね。プリアンプも載っているんですね。DACが2系統あるし、音を出すためかな。すごいなぁ。
1.4.3 Advanced Peripheral Interfaces
12-bit SAR ADC up to 18 channels
2 × 8-bit DAC
10 × touch sensors
Temperature sensor
4 × SPI
2 × I2S
2 × I2C
3 × UART
1 host (SD/eMMC/SDIO)
1 slave (SDIO/SPI)
Ethernet MAC interface with dedicated DMA and IEEE 1588 support
CAN 2.0
IR (TX/RX)
Motor PWM
LED PWM up to 16 channels
Hall sensor
Ultra-low-noise analog pre-amplifier
セキュアブートをサポートしていて、ハードウェアの暗号化エンジンもあります。フラッシュ暗号化もサポートしているので、セキュリティはばっちりですね。
1.4.4 Security
IEEE 802.11 standard security features are all supported, including WFA, WPA/WPA2 and WAPI
Secure boot
Flash encryption
1024-bit OTP, up to 768-bit for customers
Cryptographic hardware acceleration:
> AES
> HASH (SHA-2) library
> RSA
> ECC
> Random Number Generator (RNG)
スマホのSoCに比べると、計算能力やワークメモリの容量はとても及びませんが、基本機能はほぼ匹敵するといってもいいくらいです。ESP32の全機能が使いこなせるようになったら面白そうです。
(^^)/~
waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送
- 出版社/メーカー: waves(ウェイブス)
- メディア: エレクトロニクス
waves ESP32 ESPDuino-32 (ESP-WROOM-32) 技適取得品
- 出版社/メーカー: waves(ウェイブス)
- メディア: おもちゃ&ホビー
MicroPython for ESP32 Development Workshop (English Edition)
- 出版社/メーカー:
- 発売日: 2017/08/19
- メディア: Kindle版
ESP32 で高周波クロックをPWMで生成してみた! [Arduino]
最初に試してみたのは、サーボモーターをコントロールした方法。スケッチを以下に示します。
#include "esp_system.h" void setup() { ledcSetup(0, 1000000, 8); // 0ch, 1MHz, 8bit ledcAttachPin(15, 0); // 15pin 0ch ledcWrite(0, 128); // 0ch, duty 50% (128/256) } void loop() { }
観測した波形がこちら。
1MHz にはほど遠いだいたい310kHz程度の上、波形をよくよく見るとパルス幅が不揃いで、安定した波形が得られていません。
で、いろいろと調べてみたら、ESP32 の PWM で高周波クロックを出力するには、HIGH_SPEED モードに設定する必要があるようです。スケッチを以下に示します。
#include "esp_system.h" #include "driver/ledc.h" void setup() { ledc_timer_bit_t bit_num = (ledc_timer_bit_t) 3; // duty range = 2^3 = 8 // Enable LEDC PWM peripheral periph_module_enable(PERIPH_LEDC_MODULE); // Set Duty int duty = pow(2, (int) bit_num) / 2; // (2^3) /2 = 4 : 50% // setup the timer ledc_timer_config_t clk_timer; clk_timer.bit_num = bit_num; // 3 clk_timer.freq_hz = 1000000; // 1MHz clk_timer.speed_mode = LEDC_HIGH_SPEED_MODE; clk_timer.timer_num = LEDC_TIMER_0; ledc_timer_config(&clk_timer); // setup the pwm channel ledc_channel_config_t pwm_channel; pwm_channel.channel = LEDC_CHANNEL_0; pwm_channel.duty = duty; pwm_channel.gpio_num = GPIO_NUM_15; pwm_channel.intr_type = LEDC_INTR_DISABLE; pwm_channel.speed_mode = LEDC_HIGH_SPEED_MODE; pwm_channel.timer_sel = LEDC_TIMER_0; ledc_channel_config(&pwm_channel); // Set the PWM to the duty specified ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, duty); ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); } void loop() { }
波形を確認してみます。
比較的安定した波形が得られました。オシロスコープが MHz の信号を観測するには性能不足なので、少し誤差が出ていますが、ほぼ 1MHz のクロックが得られたようです。
ESP32はまだ出たばかりなので、使いこなすのはちょっと面倒ですね。
σ(^_^;
waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送
- 出版社/メーカー: waves(ウェイブス)
- メディア: エレクトロニクス
waves ESP32 ESPDuino-32 (ESP-WROOM-32) 技適取得品
- 出版社/メーカー: waves(ウェイブス)
- メディア: おもちゃ&ホビー
SparkFun ESP32 Thing Development Workshop (English Edition)
- 出版社/メーカー: PE Press
- 発売日: 2017/04/09
- メディア: Kindle版
ESP32 の BLEデバイス化にチャレンジ!(4)ついに完結! [Arduino]
。・゚・(ノД`)・゚・。
まずは成功した証。スマホアプリ LAPIS Semconductor さん謹製の BLE TOOL の画面を披露します。
ばっちり心拍データを受信しています。実際に心拍を測定しているわけでなく、ランダムデータなんですけどね。^^; いろんな試行錯誤をした後、完成したスケッチがこちらです。
#pragma GCC diagnostic push #pragma GCC diagnostic warning "-fpermissive" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "bt.h" #include "bta_api.h" #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" #define HRPS_HT_MEAS_MAX_LEN 13 ///Attributes State Machine enum { HRS_IDX_SVC, HRS_IDX_HR_MEAS_CHAR, HRS_IDX_HR_MEAS_VAL, HRS_IDX_HR_MEAS_NTF_CFG, HRS_IDX_NB, }; #define ESP_HEART_RATE_APP_ID 0x55 #define SAMPLE_DEVICE_NAME "ESP_HEART_RATE" #define SAMPLE_MANUFACTURER_DATA_LEN 17 #define HEART_RATE_SVC_INST_ID 0 static uint8_t heart_rate_service_uuid[16] = { /* LSB <--------------------------------------------------------------------------------> MSB */ // first uuid, 16bit, [12],[13] is the value 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x18, 0x0D, 0x00, 0x00, }; static esp_ble_adv_data_t heart_rate_adv_config = { .set_scan_rsp = false, .include_name = true, .include_txpower = true, .min_interval = 0x20, .max_interval = 0x40, .appearance = 0x00, .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, .p_manufacturer_data = NULL, //&test_manufacturer[0], .service_data_len = 0, .p_service_data = NULL, .service_uuid_len = sizeof(heart_rate_service_uuid), .p_service_uuid = heart_rate_service_uuid, .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), }; static esp_ble_adv_params_t heart_rate_adv_params; struct gatts_profile_inst { esp_gatts_cb_t gatts_cb; uint16_t gatts_if; uint16_t app_id; uint16_t conn_id; uint16_t service_handle; esp_gatt_srvc_id_t service_id; uint16_t char_handle; esp_bt_uuid_t char_uuid; esp_gatt_perm_t perm; esp_gatt_char_prop_t property; uint16_t descr_handle; esp_bt_uuid_t descr_uuid; }; static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); /* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ static struct gatts_profile_inst heart_rate_profile = { .gatts_cb = gatts_profile_event_handler, .gatts_if = ESP_GATT_IF_NONE, }; /// Heart Rate Sensor Service static const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC; #define CHAR_DECLARATION_SIZE (sizeof(uint8_t)) static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY; static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ; static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_READ; /// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify static const uint16_t heart_rate_meas_uuid = ESP_GATT_HEART_RATE_MEAS; static const uint8_t heart_measurement_ccc[2] ={ 0x00, 0x00}; /// Full HRS Database Description - Used to add attributes into the database static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = { // Heart Rate Service Declaration [HRS_IDX_SVC] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}}, // Heart Rate Measurement Characteristic Declaration [HRS_IDX_HR_MEAS_CHAR] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}}, // Heart Rate Measurement Characteristic Value [HRS_IDX_HR_MEAS_VAL] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ, HRPS_HT_MEAS_MAX_LEN, 0, NULL}}, // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor [HRS_IDX_HR_MEAS_NTF_CFG] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}}, }; static uint16_t heart_rate_handle_table[HRS_IDX_NB]; ////////////////////////////////////////////////////// static void ble_indicate() { if (heart_rate_profile.gatts_if == ESP_GATT_IF_NONE) { Serial.println("cannot indicate: gatts_if_for_indicate is NONE"); return; } uint8_t hrm_value = random(30, 220); uint8_t hrm_value_arr[] = {0x00, hrm_value}; Serial.println(hrm_value); esp_ble_gatts_send_indicate(heart_rate_profile.gatts_if, heart_rate_profile.conn_id, heart_rate_handle_table[HRS_IDX_HR_MEAS_VAL], sizeof(hrm_value_arr), hrm_value_arr, false); } ////////////////////////////////////////////////////// static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: Serial.println("gap start advertising...."); esp_ble_gap_start_advertising(&heart_rate_adv_params); break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: //advertising start complete event to indicate advertising start successfully or failed if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { Serial.println("gap advertising failed"); } Serial.println("gap advertising success"); break; default: break; } } static bool bConnected = false; static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { switch (event) { case ESP_GATTS_REG_EVT: Serial.println("gatt server register event"); esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); esp_ble_gap_config_adv_data(&heart_rate_adv_config); esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if, HRS_IDX_NB, HEART_RATE_SVC_INST_ID); break; case ESP_GATTS_READ_EVT: Serial.println("gatt server read event"); break; case ESP_GATTS_WRITE_EVT: Serial.println("gatt server write event"); break; case ESP_GATTS_EXEC_WRITE_EVT: Serial.println("gatt server exec write event"); break; case ESP_GATTS_MTU_EVT: Serial.println("gatt server mtu event?"); break; case ESP_GATTS_CONF_EVT: Serial.println("gatt server configuration event"); heart_rate_profile.gatts_if = gatts_if; heart_rate_profile.conn_id = param->connect.conn_id; break; case ESP_GATTS_UNREG_EVT: Serial.println("gatt server unregister event"); break; case ESP_GATTS_DELETE_EVT: Serial.println("gatt server delete event"); break; case ESP_GATTS_START_EVT: Serial.println("gatt server start event"); break; case ESP_GATTS_STOP_EVT: Serial.println("gatt server stop event"); break; case ESP_GATTS_CONNECT_EVT: Serial.println("gatt server connect event"); heart_rate_profile.gatts_if = gatts_if; heart_rate_profile.conn_id = param->connect.conn_id; bConnected = true; break; case ESP_GATTS_CREATE_EVT: Serial.println("gatt server create event"); break; case ESP_GATTS_ADD_CHAR_EVT: Serial.println("gatt server add char event"); break; case ESP_GATTS_ADD_CHAR_DESCR_EVT: Serial.println("gatt server add char descriptor event"); break; case ESP_GATTS_DISCONNECT_EVT: Serial.println("gatt server disconnect event"); esp_ble_gap_start_advertising(&heart_rate_adv_params); bConnected = false; break; case ESP_GATTS_OPEN_EVT: Serial.println("gatt server open event"); break; case ESP_GATTS_CANCEL_OPEN_EVT: Serial.println("gatt server cancel open event"); break; case ESP_GATTS_CLOSE_EVT: Serial.println("gatt server close event"); break; case ESP_GATTS_LISTEN_EVT: Serial.println("gatt server listen event"); break; case ESP_GATTS_CONGEST_EVT: Serial.println("gatt server congest event?"); break; case ESP_GATTS_CREAT_ATTR_TAB_EVT: { if (param->add_attr_tab.status != ESP_GATT_OK){ Serial.println("create attribute table failed"); } else if (param->add_attr_tab.num_handle != HRS_IDX_NB){ Serial.println("create attribute table abnormally"); } else { memcpy(heart_rate_handle_table, param->add_attr_tab.handles, sizeof(heart_rate_handle_table)); esp_ble_gatts_start_service(heart_rate_handle_table[HRS_IDX_SVC]); Serial.println("gatt server create attribute tab successfully"); } } break; default: break; } } static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { /* If event is register event, store the gatts_if for each profile */ if (event == ESP_GATTS_REG_EVT) { if (param->reg.status == ESP_GATT_OK) { Serial.println("gatt server register accepted"); heart_rate_profile.gatts_if = gatts_if; } else { Serial.println("gatt server register app failed"); return; } } do { int idx; if (gatts_if == ESP_GATT_IF_NONE || gatts_if == heart_rate_profile.gatts_if) { if (heart_rate_profile.gatts_cb) { heart_rate_profile.gatts_cb(event, gatts_if, param); } } } while (0); } void setup() { Serial.begin(115200); /* initialize advertising info */ heart_rate_adv_params.adv_int_min = 0x20; heart_rate_adv_params.adv_int_max = 0x40; heart_rate_adv_params.adv_type = ADV_TYPE_IND; heart_rate_adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; heart_rate_adv_params.channel_map = ADV_CHNL_ALL; heart_rate_adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; btStart(); esp_err_t ret; Serial.println("Init bluetooth"); ret = esp_bluedroid_init(); if (ret) { Serial.println("esp_bluedroid_init failed"); return; } ret = esp_bluedroid_enable(); if (ret) { Serial.println("esp_bluedroid_enable failed"); return; } esp_ble_gatts_register_callback(gatts_event_handler); esp_ble_gap_register_callback(gap_event_handler); esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID); } void loop() { if (bConnected) { ble_indicate(); } delay(1000); }
ポイントは二つ。ひとつは esp_ble_gatts_send_indicate の handle を CREATE_ATTR_TAB で取得したものを使ったのと、もう一つは、心拍データを16bit で送る必要があったことの2点です。この心拍データが 16bit 必要というのが気が付かずに、かなり時間をかけてしまいました。
全般的に、ESP32 SDK の BLE は mbed の Nordic のものよりも分かり難いですねぇ。でも、使い方のコツは分かったので、これから ESP32 を BLEデバイスとして活用できそうです!
(^_^)/~
waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送
- 出版社/メーカー: waves(ウェイブス)
- メディア: エレクトロニクス
waves ESP32 ESPDuino-32 (ESP-WROOM-32) 技適取得品
- 出版社/メーカー: waves(ウェイブス)
- メディア: おもちゃ&ホビー
MicroPython for ESP32 Development Workshop (English Edition)
- 出版社/メーカー:
- 発売日: 2017/08/19
- メディア: Kindle版
ESP32 の BLEデバイス化にチャレンジ!(3) [Arduino]
なんとなく構造が分かってきたので、少しスケッチを整理してみました。見通しをよくするために "Body Sensor Location" や "Heart Rate Control Point" など、余計な "Characteristics" は省いてしまいました。
#pragma GCC diagnostic push #pragma GCC diagnostic warning "-fpermissive" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" #include "bt.h" #include "bta_api.h" #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" #include "esp_bt_main.h" #define HRPS_HT_MEAS_MAX_LEN 13 ///Attributes State Machine enum { HRS_IDX_SVC, HRS_IDX_HR_MEAS_CHAR, HRS_IDX_HR_MEAS_VAL, HRS_IDX_HR_MEAS_NTF_CFG, HRS_IDX_NB, }; #define ESP_HEART_RATE_APP_ID 0x55 #define SAMPLE_DEVICE_NAME "ESP_HEART_RATE" #define SAMPLE_MANUFACTURER_DATA_LEN 17 #define HEART_RATE_SVC_INST_ID 0 uint16_t heart_rate_handle_table[HRS_IDX_NB]; static uint8_t heart_rate_service_uuid[16] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x18, 0x0D, 0x00, 0x00, }; static esp_ble_adv_data_t heart_rate_adv_config = { .set_scan_rsp = false, .include_name = true, .include_txpower = true, .min_interval = 0x20, .max_interval = 0x40, .appearance = 0x00, .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, .p_manufacturer_data = NULL, //&test_manufacturer[0], .service_data_len = 0, .p_service_data = NULL, .service_uuid_len = sizeof(heart_rate_service_uuid), .p_service_uuid = heart_rate_service_uuid, .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), }; static esp_ble_adv_params_t heart_rate_adv_params; struct gatts_profile_inst { esp_gatts_cb_t gatts_cb; uint16_t gatts_if; uint16_t app_id; uint16_t conn_id; uint16_t service_handle; esp_gatt_srvc_id_t service_id; uint16_t char_handle; esp_bt_uuid_t char_uuid; esp_gatt_perm_t perm; esp_gatt_char_prop_t property; uint16_t descr_handle; esp_bt_uuid_t descr_uuid; }; static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); /* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ static struct gatts_profile_inst heart_rate_profile = { .gatts_cb = gatts_profile_event_handler, .gatts_if = ESP_GATT_IF_NONE, }; /// Heart Rate Sensor Service static const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC; #define CHAR_DECLARATION_SIZE (sizeof(uint8_t)) static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY; static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ; static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_READ; /// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify static const uint16_t heart_rate_meas_uuid = ESP_GATT_HEART_RATE_MEAS; static const uint8_t heart_measurement_ccc[2] ={ 0x00, 0x00}; static uint8_t hrm_value = 0x00; /// Full HRS Database Description - Used to add attributes into the database static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = { // Heart Rate Service Declaration [HRS_IDX_SVC] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}}, // Heart Rate Measurement Characteristic Declaration [HRS_IDX_HR_MEAS_CHAR] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}}, // Heart Rate Measurement Characteristic Value [HRS_IDX_HR_MEAS_VAL] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ, HRPS_HT_MEAS_MAX_LEN, sizeof(hrm_value), (uint8_t *)&hrm_value}}, // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor [HRS_IDX_HR_MEAS_NTF_CFG] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}}, }; ////////////////////////////////////////////////////// static void ble_indicate() { if (heart_rate_profile.gatts_if == ESP_GATT_IF_NONE) { Serial.println("cannot indicate: gatts_if_for_indicate is NONE"); return; } hrm_value = random(100); Serial.println(hrm_value); esp_ble_gatts_send_indicate(heart_rate_profile.gatts_if, heart_rate_profile.conn_id, heart_rate_profile.char_handle, sizeof(hrm_value), (uint8_t *)&hrm_value, true); } ////////////////////////////////////////////////////// static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: Serial.println("gap start advertising...."); esp_ble_gap_start_advertising(&heart_rate_adv_params); break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: //advertising start complete event to indicate advertising start successfully or failed if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { Serial.println("gap advertising failed"); } Serial.println("gap advertising success"); break; default: break; } } static bool bConnected = false; static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { switch (event) { case ESP_GATTS_REG_EVT: Serial.println("gatt server register event"); esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); esp_ble_gap_config_adv_data(&heart_rate_adv_config); esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if, HRS_IDX_NB, HEART_RATE_SVC_INST_ID); break; case ESP_GATTS_READ_EVT: Serial.println("gatt server read event"); break; case ESP_GATTS_WRITE_EVT: Serial.println("gatt server write event"); break; case ESP_GATTS_EXEC_WRITE_EVT: Serial.println("gatt server exec write event"); break; case ESP_GATTS_MTU_EVT: Serial.println("gatt server mtu event?"); break; case ESP_GATTS_CONF_EVT: Serial.println("gatt server configuration event"); break; case ESP_GATTS_UNREG_EVT: Serial.println("gatt server unregister event"); break; case ESP_GATTS_DELETE_EVT: Serial.println("gatt server delete event"); break; case ESP_GATTS_START_EVT: Serial.println("gatt server start event"); break; case ESP_GATTS_STOP_EVT: Serial.println("gatt server stop event"); break; case ESP_GATTS_CONNECT_EVT: heart_rate_profile.gatts_if = gatts_if; heart_rate_profile.conn_id = param->connect.conn_id; heart_rate_profile.char_handle = param->add_char.attr_handle; bConnected = true; break; case ESP_GATTS_DISCONNECT_EVT: Serial.println("gatt server disconnect event"); heart_rate_profile.gatts_if = ESP_GATT_IF_NONE; bConnected = false; break; case ESP_GATTS_OPEN_EVT: Serial.println("gatt server open event"); break; case ESP_GATTS_CANCEL_OPEN_EVT: Serial.println("gatt server cancel open event"); break; case ESP_GATTS_CLOSE_EVT: Serial.println("gatt server close event"); break; case ESP_GATTS_LISTEN_EVT: Serial.println("gatt server listen event"); break; case ESP_GATTS_CONGEST_EVT: Serial.println("gatt server congest event?"); break; case ESP_GATTS_CREAT_ATTR_TAB_EVT: { if (param->add_attr_tab.status != ESP_GATT_OK){ Serial.println("create attribute table failed"); } else if (param->add_attr_tab.num_handle != HRS_IDX_NB){ Serial.println("create attribute table abnormally"); } else { Serial.println("gatt server create attribute tab successfully"); memcpy(heart_rate_handle_table, param->add_attr_tab.handles, sizeof(heart_rate_handle_table)); esp_ble_gatts_start_service(heart_rate_handle_table[HRS_IDX_SVC]); } } break; default: break; } } static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { /* If event is register event, store the gatts_if for each profile */ if (event == ESP_GATTS_REG_EVT) { if (param->reg.status == ESP_GATT_OK) { Serial.println("gatt server register accepted"); heart_rate_profile.gatts_if = gatts_if; } else { Serial.println("gatt server register app failed"); return; } } do { int idx; if (gatts_if == ESP_GATT_IF_NONE || gatts_if == heart_rate_profile.gatts_if) { if (heart_rate_profile.gatts_cb) { heart_rate_profile.gatts_cb(event, gatts_if, param); } } } while (0); } void setup() { Serial.begin(115200); /* initialize advertising info */ heart_rate_adv_params.adv_int_min = 0x20; heart_rate_adv_params.adv_int_max = 0x40; heart_rate_adv_params.adv_type = ADV_TYPE_IND; heart_rate_adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; heart_rate_adv_params.channel_map = ADV_CHNL_ALL; heart_rate_adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; esp_err_t ret; btStart(); Serial.println("Init bluetooth"); ret = esp_bluedroid_init(); if (ret) { Serial.println("esp_bluedroid_init failed"); return; } ret = esp_bluedroid_enable(); if (ret) { Serial.println("esp_bluedroid_enable failed"); return; } esp_ble_gatts_register_callback(gatts_event_handler); esp_ble_gap_register_callback(gap_event_handler); esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID); } void loop() { if (bConnected) { ble_indicate(); } delay(1000); }
LAPIS Semicondocutor の BLE TOOL で Android スマホから接続してみたら、HRM として認識させることに成功しました!やった。
でも、値を受け取ることはできませんでした。NOTIFY をうまく伝えることができていないようです。あと一息なんだけどなぁ。 orz...
こちらがログになります。
Init bluetooth gatt server register accepted gatt server register event gatt server create attribute tab successfully gap start advertising.... gatt server start event gap advertising success 90 85 71 gatt server write event 69 10 97 32 91 62 19 gatt server write event 78 47 83 gatt server disconnect event
どうも Attribute の handle 値が正しく設定されていないようなのですが、どう値を持ってくればよいのかよく分かりません。ESP32 の BLE 関連の情報がほとんどなくしばらく苦戦しそうです。うーむ。
(。-`ω´-)
waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送
- 出版社/メーカー: waves(ウェイブス)
- メディア: エレクトロニクス
waves ESP32 ESP-WROOM-32 WiFi/Bluetoothモジュール(技適取得済み) ブレイクアウトボード付属
- 出版社/メーカー: waves(ウェイブス)
- メディア: おもちゃ&ホビー
SparkFun ESP32 Thing Development Workshop (English Edition)
- 出版社/メーカー: PE Press
- 発売日: 2017/04/09
- メディア: Kindle版
ESP32 の BLEデバイス化にチャレンジ!(2) [Arduino]
なにかよい資料がないかなとあら探しをしていたら、ESP32 による Heart Rate Profile のサンプルを見つけました!
esp-idf/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c
Heart Rate Profile なら mbed HRM1017 を使った開発経験があります。
mbed HRM1017 で Heart Rate Profile をサポートしてみる
http://makers-with-myson.blog.so-net.ne.jp/archive/c2306056631-1
これをヒントに、ESP32のBLEの使い方を習熟していきたいと思います。とりあえずダメもとで ESP-IDF のコードを Arduino のスケッチに移植してしみました。
#pragma GCC diagnostic push #pragma GCC diagnostic warning "-fpermissive" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" #include "bt.h" #include "bta_api.h" #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" #include "esp_bt_main.h" #define HRPS_HT_MEAS_MAX_LEN (13) #define HRPS_MANDATORY_MASK (0x0F) #define HRPS_BODY_SENSOR_LOC_MASK (0x30) #define HRPS_HR_CTNL_PT_MASK (0xC0) ///Attributes State Machine enum { HRS_IDX_SVC, HRS_IDX_HR_MEAS_CHAR, HRS_IDX_HR_MEAS_VAL, HRS_IDX_HR_MEAS_NTF_CFG, HRS_IDX_BOBY_SENSOR_LOC_CHAR, HRS_IDX_BOBY_SENSOR_LOC_VAL, HRS_IDX_HR_CTNL_PT_CHAR, HRS_IDX_HR_CTNL_PT_VAL, HRS_IDX_NB, }; #define GATTS_TABLE_TAG "GATTS_TABLE_DEMO" #define HEART_PROFILE_NUM 1 #define HEART_PROFILE_APP_IDX 0 #define ESP_HEART_RATE_APP_ID 0x55 #define SAMPLE_DEVICE_NAME "ESP_HEART_RATE" #define SAMPLE_MANUFACTURER_DATA_LEN 17 #define HEART_RATE_SVC_INST_ID 0 #define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 uint8_t char1_str[] ={0x11,0x22,0x33}; uint16_t heart_rate_handle_table[HRS_IDX_NB]; esp_attr_value_t gatts_demo_char1_val = { .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX, .attr_len = sizeof(char1_str), .attr_value = char1_str, }; static uint8_t heart_rate_service_uuid[16] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x18, 0x0D, 0x00, 0x00, }; static esp_ble_adv_data_t heart_rate_adv_config = { .set_scan_rsp = false, .include_name = true, .include_txpower = true, .min_interval = 0x20, .max_interval = 0x40, .appearance = 0x00, .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, .p_manufacturer_data = NULL, //&test_manufacturer[0], .service_data_len = 0, .p_service_data = NULL, .service_uuid_len = sizeof(heart_rate_service_uuid), .p_service_uuid = heart_rate_service_uuid, .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), }; static esp_ble_adv_params_t heart_rate_adv_params; struct gatts_profile_inst { esp_gatts_cb_t gatts_cb; uint16_t gatts_if; uint16_t app_id; uint16_t conn_id; uint16_t service_handle; esp_gatt_srvc_id_t service_id; uint16_t char_handle; esp_bt_uuid_t char_uuid; esp_gatt_perm_t perm; esp_gatt_char_prop_t property; uint16_t descr_handle; esp_bt_uuid_t descr_uuid; }; static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); /* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ static struct gatts_profile_inst heart_rate_profile_tab[HEART_PROFILE_NUM] = { [HEART_PROFILE_APP_IDX] = { .gatts_cb = gatts_profile_event_handler, .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ }, }; /* * HTPT PROFILE ATTRIBUTES **************************************************************************************** */ /* * Heart Rate PROFILE ATTRIBUTES **************************************************************************************** */ /// Heart Rate Sensor Service static const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC; #define CHAR_DECLARATION_SIZE (sizeof(uint8_t)) static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY; static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ; static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_READ; /// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify static const uint16_t heart_rate_meas_uuid = ESP_GATT_HEART_RATE_MEAS; static const uint8_t heart_measurement_ccc[2] ={ 0x00, 0x00}; /// Heart Rate Sensor Service -Body Sensor Location characteristic, read static const uint16_t body_sensor_location_uuid = ESP_GATT_BODY_SENSOR_LOCATION; static const uint8_t body_sensor_loc_val[1] = {0x00}; /// Heart Rate Sensor Service - Heart Rate Control Point characteristic, write&read static const uint16_t heart_rate_ctrl_point = ESP_GATT_HEART_RATE_CNTL_POINT; static const uint8_t heart_ctrl_point[1] = {0x00}; /// Full HRS Database Description - Used to add attributes into the database static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = { // Heart Rate Service Declaration [HRS_IDX_SVC] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}}, // Heart Rate Measurement Characteristic Declaration [HRS_IDX_HR_MEAS_CHAR] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}}, // Heart Rate Measurement Characteristic Value [HRS_IDX_HR_MEAS_VAL] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ, HRPS_HT_MEAS_MAX_LEN,0, NULL}}, // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor [HRS_IDX_HR_MEAS_NTF_CFG] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}}, // Body Sensor Location Characteristic Declaration [HRS_IDX_BOBY_SENSOR_LOC_CHAR] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read}}, // Body Sensor Location Characteristic Value [HRS_IDX_BOBY_SENSOR_LOC_VAL] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&body_sensor_location_uuid, ESP_GATT_PERM_READ, sizeof(uint8_t), sizeof(body_sensor_loc_val), (uint8_t *)body_sensor_loc_val}}, // Heart Rate Control Point Characteristic Declaration [HRS_IDX_HR_CTNL_PT_CHAR] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}}, // Heart Rate Control Point Characteristic Value [HRS_IDX_HR_CTNL_PT_VAL] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_ctrl_point, ESP_GATT_PERM_WRITE|ESP_GATT_PERM_READ, sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}}, }; static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { Serial.println("gap_event_handler"); switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: Serial.println(" ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT"); esp_ble_gap_start_advertising(&heart_rate_adv_params); break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: //advertising start complete event to indicate advertising start successfully or failed Serial.println(" ESP_GAP_BLE_ADV_START_COMPLETE_EVT"); if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { Serial.println("Advertising start failed"); } break; default: break; } } static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { Serial.println("gatts_profile_event_handler:"); switch (event) { case ESP_GATTS_REG_EVT: Serial.println(" ESP_GATTS_REG_EVT"); esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); esp_ble_gap_config_adv_data(&heart_rate_adv_config); esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if, HRS_IDX_NB, HEART_RATE_SVC_INST_ID); break; case ESP_GATTS_READ_EVT: Serial.println(" ESP_GATTS_READ_EVT"); char hrp1 = 0x1E; // dummy data char hrp2 = 0x00; // dummy data esp_gatt_rsp_t rsp; memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); rsp.attr_value.handle = param->read.handle; rsp.attr_value.len = 2; rsp.attr_value.value[0] = hrp1; rsp.attr_value.value[1] = hrp2; esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); break; case ESP_GATTS_WRITE_EVT: Serial.println(" ESP_GATTS_WRITE_EVT"); break; case ESP_GATTS_EXEC_WRITE_EVT: Serial.println(" ESP_GATTS_EXEC_WRITE_EVT"); break; case ESP_GATTS_MTU_EVT: Serial.println(" ESP_GATTS_MTU_EVT"); break; case ESP_GATTS_CONF_EVT: Serial.println(" ESP_GATTS_CONF_EVT"); break; case ESP_GATTS_UNREG_EVT: Serial.println(" ESP_GATTS_UREG_EVT"); break; case ESP_GATTS_DELETE_EVT: Serial.println(" ESP_GATTS_DELETE_EVT"); break; case ESP_GATTS_START_EVT: Serial.println(" ESP_GATTS_START_EVT"); break; case ESP_GATTS_STOP_EVT: Serial.println(" ESP_GATTS_STOP_EVT"); break; case ESP_GATTS_CONNECT_EVT: Serial.println(" ESP_GATTS_CONNECT_EVT"); break; case ESP_GATTS_DISCONNECT_EVT: Serial.println(" ESP_GATTS_DISCONNECT_EVT"); break; case ESP_GATTS_OPEN_EVT: Serial.println(" ESP_GATTS_OPEN_EVT"); break; case ESP_GATTS_CANCEL_OPEN_EVT: Serial.println(" ESP_GATTS_CANCEL_OPEN_EVT"); break; case ESP_GATTS_CLOSE_EVT: Serial.println(" ESP_GATTS_CLOSE_EVT"); break; case ESP_GATTS_LISTEN_EVT: Serial.println(" ESP_GATTS_LISTEN_EVT"); break; case ESP_GATTS_CONGEST_EVT: Serial.println(" ESP_GATTS_CONGEST_EVT"); break; case ESP_GATTS_CREAT_ATTR_TAB_EVT:{ Serial.println("ESP_GATTS_CREAT_ATTR_TAB_EVT"); if (param->add_attr_tab.status != ESP_GATT_OK){ Serial.println("Create attribute table failed"); } else if (param->add_attr_tab.num_handle != HRS_IDX_NB){ Serial.println("Create attribute table abnormally"); } else { memcpy(heart_rate_handle_table, param->add_attr_tab.handles, sizeof(heart_rate_handle_table)); esp_ble_gatts_start_service(heart_rate_handle_table[HRS_IDX_SVC]); } } break; default: break; } } static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { Serial.println("gatts_event_handler:"); /* If event is register event, store the gatts_if for each profile */ if (event == ESP_GATTS_REG_EVT) { if (param->reg.status == ESP_GATT_OK) { Serial.println(" Reg app success"); heart_rate_profile_tab[HEART_PROFILE_APP_IDX].gatts_if = gatts_if; } else { Serial.println(" Reg app failed"); return; } } do { int idx; for (idx = 0; idx < HEART_PROFILE_NUM; idx++) { if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ gatts_if == heart_rate_profile_tab[idx].gatts_if) { if (heart_rate_profile_tab[idx].gatts_cb) { heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param); } } } } while (0); } void setup() { Serial.begin(115200); /* initialize advertising info */ heart_rate_adv_params.adv_int_min = 0x20; heart_rate_adv_params.adv_int_max = 0x40; heart_rate_adv_params.adv_type = ADV_TYPE_IND; heart_rate_adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; heart_rate_adv_params.channel_map = ADV_CHNL_ALL; heart_rate_adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; esp_err_t ret; esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); ret = esp_bt_controller_init(&bt_cfg); if (ret) { Serial.println("esp_bt_controller_init failed"); return; } ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM); if (ret) { Serial.println("esp_bt_controller_enable enable controller"); return; } Serial.println("Init bluetooth"); ret = esp_bluedroid_init(); if (ret) { Serial.println("esp_bluedroid_init failed"); return; } ret = esp_bluedroid_enable(); if (ret) { Serial.println("esp_bluedroid_enable failed"); return; } esp_ble_gatts_register_callback(gatts_event_handler); esp_ble_gap_register_callback(gap_event_handler); esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID); } void loop() { // put your main code here, to run repeatedly: }
何も考えずに移植したコードなので、うまく動かないだろうなぁと思いつつ試してみました。
Init bluetooth gatts_event_handler: Reg app success gatts_profile_event_handler: ESP_GATTS_REG_EVT gatts_event_handler: gatts_profile_event_handler: gap_event_handler ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT gap_event_handler ESP_GAP_BLE_ADV_START_COMPLETE_EVT gatts_event_handler: gatts_profile_event_handler: gap_event_handler
とりあえずログは出ましたがそれらしい動きは一切なし。やっぱダメですね。ぼちぼち調べていくかぁ。
(´・ω・`)
waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送
- 出版社/メーカー: waves(ウェイブス)
- メディア: エレクトロニクス
SparkFun ESP32 Thing Development Workshop (English Edition)
- 出版社/メーカー: PE Press
- 発売日: 2017/04/09
- メディア: Kindle版
MicroPython for ESP32 Development Workshop (English Edition)
- 出版社/メーカー:
- 発売日: 2017/08/19
- メディア: Kindle版