Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99b4aac48b | ||
|
|
2bd64b566c | ||
|
|
3c93d14b33 | ||
|
|
5639edefee | ||
|
|
585c31ef39 | ||
|
|
a385a4c1e5 | ||
|
|
ef257c32b4 | ||
|
|
88cd4b5288 | ||
|
|
ca7ec56f0d | ||
|
|
74b1156d5e | ||
|
|
6ac7ee1a9d | ||
|
|
0b56452c35 | ||
|
|
b9c66ca0fd | ||
|
|
faf06a3b70 | ||
|
|
d0ac7f06b1 | ||
|
|
e9daaa3589 | ||
|
|
1393811525 | ||
|
|
2b640669a7 | ||
|
|
9d0f0a8d7a | ||
|
|
21d2fb4afa | ||
|
|
ade74986ee | ||
|
|
73722b5c87 | ||
|
|
456b474a48 | ||
|
|
f11cc4db69 | ||
|
|
8c2901da13 | ||
|
|
ca4637d14d | ||
|
|
313caf406e | ||
|
|
25052fedd6 | ||
|
|
678cd2c4b9 | ||
|
|
1231317c9c | ||
|
|
cd76f030cb | ||
|
|
36839cb190 | ||
|
|
23e68973c0 | ||
|
|
70af32eead | ||
|
|
3cc5ac14ff | ||
|
|
4fd8e41db1 | ||
|
|
5fd4736d8f | ||
|
|
67a07edccc | ||
|
|
1dc0872818 | ||
|
|
ead0728a57 | ||
|
|
14eb8d3906 | ||
|
|
4c90b295b2 | ||
|
|
43aa0427ea | ||
|
|
cc5d1779a3 | ||
|
|
0f437e4db2 | ||
|
|
3778fbb833 | ||
|
|
4b6f3d05e1 | ||
|
|
9e89dda900 | ||
|
|
91e69e2a1a | ||
|
|
21d1a285dc | ||
|
|
0e579792d1 | ||
|
|
20cabe824f | ||
|
|
0847d3dbb0 | ||
|
|
fa7c287f07 | ||
|
|
9b3032cd58 | ||
|
|
50646b9e70 | ||
|
|
3d6a1c7b41 | ||
|
|
5b76b0668b | ||
|
|
d18f8dce2f | ||
|
|
c64cdc14b6 | ||
|
|
cc800713bd | ||
|
|
c6a0d6ecfe | ||
|
|
c6426ae461 | ||
|
|
7b04a033b3 | ||
|
|
8e4008bf12 | ||
|
|
7eb176c0b4 | ||
|
|
3f475ac68c | ||
|
|
5204dab99b | ||
|
|
273fb84dc5 | ||
|
|
fe3af4d98b | ||
|
|
9aade5bb24 | ||
|
|
3c408dab7c | ||
|
|
2888f4d03d | ||
|
|
f57b5bc762 | ||
|
|
35a4d57360 | ||
|
|
579e366bcf | ||
|
|
db4f79448d | ||
|
|
45bbcca207 | ||
|
|
8e961a5667 | ||
|
|
64ad69c247 | ||
|
|
4def2f219c | ||
|
|
b2b4b2d71d | ||
|
|
e93bd14ef5 | ||
|
|
652f9f9eda | ||
|
|
cac9eb0cd7 | ||
|
|
3b4fb295ea | ||
|
|
a1ae61b5b1 | ||
|
|
355abd8c57 | ||
|
|
b57ac66815 | ||
|
|
a851a928d2 | ||
|
|
16c2e8e454 | ||
|
|
ae908c8e9b | ||
|
|
723c81470a | ||
|
|
a7905fba3e | ||
|
|
6e52b72523 | ||
|
|
ef4ec4185a | ||
|
|
3dee0a276b | ||
|
|
e95248a787 | ||
|
|
414ff23141 | ||
|
|
2c0ce6f416 | ||
|
|
89fe754a9f | ||
|
|
01ee673dc2 | ||
|
|
5a949443a5 | ||
|
|
a916695155 | ||
|
|
611547ee97 | ||
|
|
ff9f228e1b | ||
|
|
f33dfd2313 | ||
|
|
7ef00c8761 | ||
|
|
437d03583a | ||
|
|
0322a6871c | ||
|
|
4f643d27d9 | ||
|
|
3dcb4bbf8b | ||
|
|
59981aed7f | ||
|
|
07781e9cf5 | ||
|
|
c276a36c9b | ||
|
|
ff0d794c50 | ||
|
|
369c878711 | ||
|
|
5bf570c27d | ||
|
|
0963611fc7 | ||
|
|
00644db9b1 | ||
|
|
f8ba72aa15 | ||
|
|
371b2c87b1 | ||
|
|
387655606e | ||
|
|
e1b881a688 | ||
|
|
869ea4c16b | ||
|
|
71238ccd74 | ||
|
|
1dfd9ebac7 | ||
|
|
3dbe5cf930 | ||
|
|
8e8fea4b7d | ||
|
|
ebe5bfbc07 | ||
|
|
d67930eeef | ||
|
|
084d5b0b87 | ||
|
|
8dc44b5e0d | ||
|
|
7851dc8cb7 | ||
|
|
75c553c391 | ||
|
|
6bff270402 | ||
|
|
72f1e53106 | ||
|
|
e6c7b97b5e | ||
|
|
54f9d3c414 | ||
|
|
9e272733cf | ||
|
|
51afd3440f | ||
|
|
f67bc43584 | ||
|
|
79e1af7bde | ||
|
|
173c44417c | ||
|
|
7aa1c08d17 | ||
|
|
0be1d9c3ea | ||
|
|
305e194993 | ||
|
|
719ab019a4 | ||
|
|
e9df18f910 | ||
|
|
06a1fdac50 | ||
|
|
7308ef4117 | ||
|
|
67c1db9957 | ||
|
|
f5a621935d | ||
|
|
354edb30d3 | ||
|
|
96113fe848 | ||
|
|
fd1596b6c9 | ||
|
|
9760efbca3 | ||
|
|
d92c1025ba | ||
|
|
de55db12f1 | ||
|
|
264b9efb36 | ||
|
|
9fd5cdf1ba | ||
|
|
1b33cbe591 | ||
|
|
a97d5eab22 | ||
|
|
83e790851f |
@@ -217,3 +217,5 @@ jobs:
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/WiFi/examples/ScanNetworks/ScanNetworks.ino
|
||||
- name: Build Signed OTA Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino
|
||||
- name: Build Bluetooth Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH" libraries/MouseBLE/examples/BLECircle/BLECircle.ino
|
||||
|
||||
@@ -19,18 +19,18 @@ jobs:
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
python-version: '3.x'
|
||||
# - name: Cache PlatformIO
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ~/.platformio
|
||||
# key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
# - name: Install PlatformIO
|
||||
# run: |
|
||||
# python -m pip install --upgrade pip
|
||||
# pip install --upgrade platformio
|
||||
- name: Deploy updated JSON
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
@@ -43,5 +43,5 @@ jobs:
|
||||
curl -L -o package_rp2040_index.json "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/package_rp2040_index.json"
|
||||
./package/update_release.py --token ${CI_GITHUB_API_KEY} --repo "$GITHUB_REPOSITORY" --tag global package_rp2040_index.json
|
||||
# Upload to Platform.IO
|
||||
curl -LO $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/rp2040-$TAG.zip
|
||||
pio package publish rp2040-$TAG.zip --non-interactive
|
||||
# curl -LO $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/rp2040-$TAG.zip
|
||||
# pio package publish rp2040-$TAG.zip --non-interactive
|
||||
|
||||
+7
-7
@@ -3,7 +3,7 @@
|
||||
url = https://github.com/earlephilhower/ArduinoCore-API.git
|
||||
[submodule "pico-sdk"]
|
||||
path = pico-sdk
|
||||
url = https://github.com/earlephilhower/pico-sdk.git
|
||||
url = https://github.com/raspberrypi/pico-sdk.git
|
||||
[submodule "system/pyserial"]
|
||||
path = tools/pyserial
|
||||
url = https://github.com/pyserial/pyserial.git
|
||||
@@ -14,14 +14,14 @@
|
||||
path = libraries/ESP8266SdFat
|
||||
url = https://github.com/earlephilhower/ESP8266SdFat.git
|
||||
[submodule "libraries/Keyboard"]
|
||||
path = libraries/Keyboard
|
||||
url = https://github.com/earlephilhower/Keyboard
|
||||
path = libraries/HID_Keyboard
|
||||
url = https://github.com/earlephilhower/Keyboard.git
|
||||
[submodule "libraries/Mouse"]
|
||||
path = libraries/Mouse
|
||||
url = https://github.com/earlephilhower/Mouse
|
||||
path = libraries/HID_Mouse
|
||||
url = https://github.com/earlephilhower/Mouse.git
|
||||
[submodule "libraries/Joystick"]
|
||||
path = libraries/Joystick
|
||||
url = https://github.com/benjaminaigner/Joystick
|
||||
path = libraries/HID_Joystick
|
||||
url = https://github.com/earlephilhower/Joystick.git
|
||||
[submodule "libraries/Adafruit_TinyUSB_Arduino"]
|
||||
path = libraries/Adafruit_TinyUSB_Arduino
|
||||
url = https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git
|
||||
|
||||
+1
-1
Submodule ArduinoCore-API updated: e37df85425...ece6e68f29
@@ -4,7 +4,7 @@
|
||||
|
||||
Raspberry Pi Pico Arduino core, for all RP2040 boards
|
||||
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 10.3/Newlib 4.0 toolchain.
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 12.3/Newlib 4.0 toolchain.
|
||||
|
||||
# Documentation
|
||||
See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for more detailed usage information.
|
||||
@@ -21,10 +21,12 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* Adafruit ItsyBitsy RP2040
|
||||
* Adafruit KB2040
|
||||
* Adafruit Macropad RP2040
|
||||
* Adafruit Metro RP2040
|
||||
* Adafruit QTPy RP2040
|
||||
* Adafruit STEMMA Friend RP2040
|
||||
* Adafruit Trinkey RP2040 QT
|
||||
* Arduino Nano RP2040 Connect
|
||||
* ArtronShop RP2 Nano
|
||||
* BridgeTek IDM2040-7A
|
||||
* Cytron Maker Pi RP2040
|
||||
* Cytron Maker Nano RP2040
|
||||
@@ -35,17 +37,24 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* ExtremeElectronics RC2040
|
||||
* Invector Labs Challenger RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 WiFi/BLE
|
||||
* Invector Labs Challenger RP2040 WiFi6/BLE
|
||||
* Invector Labs Challenger NB RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 LTE
|
||||
* Invector Labs Challenger RP2040 LoRa
|
||||
* Invector Labs Challenger RP2040 SubGHz
|
||||
* Invector Labs Challenger RP2040 SD/RTC
|
||||
* Invector Labs Challenger RP2040 UWB
|
||||
* Invector Labs RPICO32
|
||||
* Melopero Cookie RP2040
|
||||
* Melopero Shake RP2040
|
||||
* Neko Systems BL2040 Mini
|
||||
* nullbits Bit-C PRO
|
||||
* Pimoroni PGA2040
|
||||
* Pimoroni Plasma2040
|
||||
* Pimoroni Tiny2040
|
||||
* Seeed Indicator RP2040
|
||||
* Seeed XIAO RP2040
|
||||
* Silicognition RP2040-Shim
|
||||
* Solder Party RP2040 Stamp
|
||||
* SparkFun ProMicro RP2040
|
||||
* SparkFun Thing Plus RP2040
|
||||
@@ -60,16 +69,37 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* WIZnet W5100S-EVB-Pico
|
||||
* WIZnet W5500-EVB-Pico
|
||||
* WIZnet WizFi360-EVB-Pico
|
||||
* Redscorp RP2040-ProMini
|
||||
* Generic (configurable flash, I/O pins)
|
||||
|
||||
# Installing via Arduino Boards Manager
|
||||
**Windows Users**: Please do not use the Windows Store version of the actual Arduino application
|
||||
## Windows-specific Notes
|
||||
Please do not use the Windows Store version of the actual Arduino application
|
||||
because it has issues detecting attached Pico boards. Use the "Windows ZIP" or plain "Windows"
|
||||
executable (EXE) download direct from https://arduino.cc. and allow it to install any device
|
||||
drivers it suggests. Otherwise the Pico board may not be detected. Also, if trying out the
|
||||
2.0 beta Arduino please install the release 1.8 version beforehand to ensure needed device drivers
|
||||
are present. (See #20 for more details.)
|
||||
|
||||
## Linux-specific Notes
|
||||
Installing Arduino using flatpak (often used by "App Stores" in various Linux
|
||||
distributions) will mean it has restricted access to the host. This might cause uploads to fail
|
||||
with error messages such as the following:
|
||||
|
||||
```
|
||||
Scanning for RP2040 devices
|
||||
...
|
||||
No drive to deploy.
|
||||
```
|
||||
|
||||
If you encounter this, you will need to either install Arduino in a different manner, or override
|
||||
the flatpak sandboxing feature using the following command, then restarting Arduino.
|
||||
|
||||
```
|
||||
flatpak override --user --filesystem=host:ro cc.arduino.IDE2
|
||||
```
|
||||
|
||||
## Installation
|
||||
Open up the Arduino IDE and go to File->Preferences.
|
||||
|
||||
In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field:
|
||||
@@ -87,6 +117,12 @@ Type "pico" in the search box and select "Add":
|
||||

|
||||
|
||||
# Installing via GIT
|
||||
|
||||
**Windows Users:** Before installing via `git` on Windows, please read and follow the directions in
|
||||
[this link](https://arduino-pico.readthedocs.io/en/latest/platformio.html#important-steps-for-windows-users-before-installing).
|
||||
If Win32 long paths are not enabled, and `git` not configured to use them then there
|
||||
may be errors when attempting to clone the submodules.
|
||||
|
||||
To install via GIT (for latest and greatest versions):
|
||||
````
|
||||
mkdir -p ~/Arduino/hardware/pico
|
||||
@@ -163,7 +199,8 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
|
||||
# Features
|
||||
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
|
||||
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
|
||||
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
|
||||
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
|
||||
* WiFi (Pico W)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
|
||||
+5134
-761
File diff suppressed because it is too large
Load Diff
+15
-4
@@ -28,7 +28,7 @@
|
||||
#include "api/ArduinoAPI.h"
|
||||
#include "api/itoa.h" // ARM toolchain doesn't provide itoa etc, provide them
|
||||
#include <pins_arduino.h>
|
||||
#include "hardware/gpio.h" // Required for the port*Register macros
|
||||
#include <hardware/gpio.h> // Required for the port*Register macros
|
||||
#include "debug_internal.h"
|
||||
#include <RP2040.h> // CMSIS
|
||||
|
||||
@@ -84,13 +84,13 @@ void analogWriteFreq(uint32_t freq);
|
||||
void analogWriteRange(uint32_t range);
|
||||
void analogWriteResolution(int res);
|
||||
|
||||
// FreeRTOS potential calls
|
||||
extern bool __isFreeRTOS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
// FreeRTOS potential calls
|
||||
extern bool __isFreeRTOS;
|
||||
|
||||
// Ancient AVR defines
|
||||
#define HAVE_HWSERIAL0
|
||||
#define HAVE_HWSERIAL1
|
||||
@@ -129,3 +129,14 @@ constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
|
||||
return i < N ? (1L << a[i]) | __bitset(a, i + 1) : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Warn users trying to use Pico SDK's STDIO implementation
|
||||
#include <pico/stdio.h> // Ensure it won't be re-included elsewhere
|
||||
#undef stdio_uart_init
|
||||
#define stdio_uart_init(...) static_assert(0, "stdio_uart_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
|
||||
#undef stdio_init_all
|
||||
#define stdio_init_all(...) static_assert(0, "stdio_init_all is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
|
||||
#undef stdio_usb_init
|
||||
#define stdio_usb_init(...) static_assert(0, "stdio_usb_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
|
||||
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/structs/ioqspi.h"
|
||||
#include "hardware/structs/sio.h"
|
||||
#include <pico/stdlib.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <hardware/structs/ioqspi.h>
|
||||
#include <hardware/structs/sio.h>
|
||||
|
||||
// This example blinks the Pico LED when the BOOTSEL button is pressed.
|
||||
//
|
||||
|
||||
@@ -28,11 +28,17 @@ CoreMutex::CoreMutex(mutex_t *mutex, uint8_t option) {
|
||||
_mutex = mutex;
|
||||
_acquired = false;
|
||||
_option = option;
|
||||
_pxHigherPriorityTaskWoken = 0; // pdFALSE
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(mutex);
|
||||
if (_option & FromISR) {
|
||||
__freertos_mutex_take_from_isr(m);
|
||||
|
||||
if (__freertos_check_if_in_isr()) {
|
||||
if (!__freertos_mutex_take_from_isr(m, &_pxHigherPriorityTaskWoken)) {
|
||||
return;
|
||||
}
|
||||
// At this point we have the mutex in ISR
|
||||
} else {
|
||||
// Grab the mutex normally, possibly waking other tasks to get it
|
||||
__freertos_mutex_take(m);
|
||||
}
|
||||
} else {
|
||||
@@ -54,8 +60,8 @@ CoreMutex::~CoreMutex() {
|
||||
if (_acquired) {
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(_mutex);
|
||||
if (_option & FromISR) {
|
||||
__freertos_mutex_give_from_isr(m);
|
||||
if (__freertos_check_if_in_isr()) {
|
||||
__freertos_mutex_give_from_isr(m, &_pxHigherPriorityTaskWoken);
|
||||
} else {
|
||||
__freertos_mutex_give(m);
|
||||
}
|
||||
|
||||
@@ -23,12 +23,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
#include "_freertos.h"
|
||||
|
||||
enum {
|
||||
DebugEnable = 1,
|
||||
FromISR = 1 << 1,
|
||||
DebugEnable = 1
|
||||
};
|
||||
|
||||
class CoreMutex {
|
||||
@@ -43,5 +42,6 @@ public:
|
||||
private:
|
||||
mutex_t *_mutex;
|
||||
bool _acquired;
|
||||
u_int8_t _option;
|
||||
uint8_t _option;
|
||||
BaseType_t _pxHigherPriorityTaskWoken;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
RP2040 PIO utility class
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "PIOProgram.h"
|
||||
#include <map>
|
||||
|
||||
static std::map<const pio_program_t *, int> __pioMap[2];
|
||||
auto_init_mutex(_pioMutex);
|
||||
|
||||
|
||||
PIOProgram::PIOProgram(const pio_program_t *pgm) {
|
||||
_pgm = pgm;
|
||||
_pio = nullptr;
|
||||
_sm = -1;
|
||||
}
|
||||
|
||||
// We leave the INSN loaded in INSN RAM
|
||||
PIOProgram::~PIOProgram() {
|
||||
if (_pio) {
|
||||
pio_sm_unclaim(_pio, _sm);
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly load into a PIO and allocate a SM
|
||||
bool PIOProgram::prepare(PIO *pio, int *sm, int *offset) {
|
||||
CoreMutex m(&_pioMutex);
|
||||
PIO pi[2] = { pio0, pio1 };
|
||||
|
||||
// If it's already loaded into PIO IRAM, try and allocate in that specific PIO
|
||||
for (int o = 0; o < 2; o++) {
|
||||
auto p = __pioMap[o].find(_pgm);
|
||||
if (p != __pioMap[o].end()) {
|
||||
int idx = pio_claim_unused_sm(pi[o], false);
|
||||
if (idx >= 0) {
|
||||
_pio = pi[o];
|
||||
_sm = idx;
|
||||
*pio = pi[o];
|
||||
*sm = idx;
|
||||
*offset = p->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not in any PIO IRAM, so try and add
|
||||
for (int o = 0; o < 2; o++) {
|
||||
if (pio_can_add_program(pi[o], _pgm)) {
|
||||
int idx = pio_claim_unused_sm(pi[o], false);
|
||||
if (idx >= 0) {
|
||||
int off = pio_add_program(pi[o], _pgm);
|
||||
__pioMap[o].insert({_pgm, off});
|
||||
_pio = pi[o];
|
||||
_sm = idx;
|
||||
*pio = pi[o];
|
||||
*sm = idx;
|
||||
*offset = off;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nope, no room either for SMs or INSNs
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
RP2040 PIO utility class
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hardware/pio.h>
|
||||
|
||||
// Wrapper class for PIO programs, abstracting common operations out
|
||||
class PIOProgram {
|
||||
public:
|
||||
PIOProgram(const pio_program_t *pgm);
|
||||
~PIOProgram();
|
||||
// Possibly load into a PIO and allocate a SM
|
||||
bool prepare(PIO *pio, int *sm, int *offset);
|
||||
|
||||
private:
|
||||
const pio_program_t *_pgm;
|
||||
PIO _pio;
|
||||
int _sm;
|
||||
};
|
||||
@@ -27,8 +27,11 @@
|
||||
#include <hardware/structs/rosc.h>
|
||||
#include <hardware/structs/systick.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <pico/rand.h>
|
||||
#include <pico/util/queue.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include "CoreMutex.h"
|
||||
#include "PIOProgram.h"
|
||||
#include "ccount.pio.h"
|
||||
#include <malloc.h>
|
||||
|
||||
@@ -90,15 +93,14 @@ public:
|
||||
if (!_multicore) {
|
||||
return;
|
||||
}
|
||||
__holdUpPendSV = 1;
|
||||
if (__isFreeRTOS) {
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
vTaskSuspendAll();
|
||||
__freertos_idle_other_core();
|
||||
} else {
|
||||
mutex_enter_blocking(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
multicore_fifo_push_blocking(_GOTOSLEEP);
|
||||
while (!__otherCoreIdled) { /* noop */ }
|
||||
}
|
||||
mutex_enter_blocking(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
multicore_fifo_push_blocking(_GOTOSLEEP);
|
||||
while (!__otherCoreIdled) { /* noop */ }
|
||||
}
|
||||
|
||||
void resumeOtherCore() {
|
||||
@@ -108,10 +110,8 @@ public:
|
||||
mutex_exit(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
if (__isFreeRTOS) {
|
||||
xTaskResumeAll();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
__freertos_resume_other_core();
|
||||
}
|
||||
__holdUpPendSV = 0;
|
||||
|
||||
// Other core will exit busy-loop and return to operation
|
||||
// once __otherCoreIdled == false.
|
||||
@@ -155,65 +155,9 @@ private:
|
||||
class RP2040;
|
||||
extern RP2040 rp2040;
|
||||
extern "C" void main1();
|
||||
class PIOProgram;
|
||||
|
||||
extern "C" char __StackLimit;
|
||||
extern "C" char __bss_end__;
|
||||
|
||||
// Wrapper class for PIO programs, abstracting common operations out
|
||||
// TODO - Add unload/destructor
|
||||
class PIOProgram {
|
||||
public:
|
||||
PIOProgram(const pio_program_t *pgm) {
|
||||
_pgm = pgm;
|
||||
}
|
||||
|
||||
// Possibly load into a PIO and allocate a SM
|
||||
bool prepare(PIO *pio, int *sm, int *offset) {
|
||||
extern mutex_t _pioMutex;
|
||||
CoreMutex m(&_pioMutex);
|
||||
// Is there an open slot to run in, first?
|
||||
if (!_findFreeSM(pio, sm)) {
|
||||
return false;
|
||||
}
|
||||
// Is it loaded on that PIO?
|
||||
if (_offset[pio_get_index(*pio)] < 0) {
|
||||
// Nope, need to load it
|
||||
if (!pio_can_add_program(*pio, _pgm)) {
|
||||
return false;
|
||||
}
|
||||
_offset[pio_get_index(*pio)] = pio_add_program(*pio, _pgm);
|
||||
}
|
||||
// Here it's guaranteed loaded, return values
|
||||
// PIO and SM already set
|
||||
*offset = _offset[pio_get_index(*pio)];
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Find an unused PIO state machine to grab, returns false when none available
|
||||
static bool _findFreeSM(PIO *pio, int *sm) {
|
||||
int idx = pio_claim_unused_sm(pio0, false);
|
||||
if (idx >= 0) {
|
||||
*pio = pio0;
|
||||
*sm = idx;
|
||||
return true;
|
||||
}
|
||||
idx = pio_claim_unused_sm(pio1, false);
|
||||
if (idx >= 0) {
|
||||
*pio = pio1;
|
||||
*sm = idx;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
int _offset[2] = { -1, -1 };
|
||||
const pio_program_t *_pgm;
|
||||
};
|
||||
|
||||
class RP2040 {
|
||||
public:
|
||||
RP2040() { /* noop */ }
|
||||
@@ -319,6 +263,13 @@ public:
|
||||
reboot();
|
||||
}
|
||||
|
||||
inline void rebootToBootloader() {
|
||||
reset_usb_boot(0, 0);
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
static void enableDoubleResetBootloader();
|
||||
|
||||
void wdt_begin(uint32_t delay_ms) {
|
||||
@@ -341,26 +292,8 @@ public:
|
||||
_MFIFO fifo;
|
||||
|
||||
|
||||
// TODO - Not so great HW random generator. 32-bits wide. Cryptographers somewhere are crying
|
||||
uint32_t hwrand32() {
|
||||
// Try and whiten the HW ROSC bit
|
||||
uint32_t r = 0;
|
||||
for (int k = 0; k < 32; k++) {
|
||||
unsigned long int b;
|
||||
do {
|
||||
b = rosc_hw->randombit & 1;
|
||||
if (b != (rosc_hw->randombit & 1)) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
r <<= 1;
|
||||
r |= b;
|
||||
}
|
||||
// Stir using the cycle count LSBs. In any WiFi use case this will be a random # since the connection time is not cycle-accurate
|
||||
uint64_t rr = (((uint64_t)~r) << 32LL) | r;
|
||||
rr >>= rp2040.getCycleCount() & 32LL;
|
||||
|
||||
return (uint32_t)rr;
|
||||
return get_rand_32();
|
||||
}
|
||||
|
||||
bool isPicoW() {
|
||||
|
||||
+104
-23
@@ -25,17 +25,17 @@
|
||||
#include "CoreMutex.h"
|
||||
#include "RP2040USB.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
#include "class/audio/audio.h"
|
||||
#include "class/midi/midi.h"
|
||||
#include "pico/time.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/unique_id.h"
|
||||
#include "pico/usb_reset_interface.h"
|
||||
#include "hardware/watchdog.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include <tusb.h>
|
||||
#include <class/hid/hid_device.h>
|
||||
#include <class/audio/audio.h>
|
||||
#include <pico/time.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <pico/unique_id.h>
|
||||
#include <pico/usb_reset_interface.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include "sdkoverride/tusb_absmouse.h"
|
||||
#include <device/usbd_pvt.h>
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
@@ -100,7 +100,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
.iSerialNumber = USBD_STR_SERIAL,
|
||||
.bNumConfigurations = 1
|
||||
};
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallAbsoluteMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
|
||||
// Can use as-is, this is the default USB case
|
||||
return (const uint8_t *)&usbd_desc_device;
|
||||
}
|
||||
@@ -108,7 +108,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
if (__USBInstallKeyboard) {
|
||||
usbd_desc_device.idProduct |= 0x8000;
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
|
||||
usbd_desc_device.idProduct |= 0x4000;
|
||||
}
|
||||
if (__USBInstallJoystick) {
|
||||
@@ -129,15 +129,15 @@ int __USBGetKeyboardReportID() {
|
||||
}
|
||||
|
||||
int __USBGetMouseReportID() {
|
||||
return __USBInstallKeyboard ? 2 : 1;
|
||||
return __USBInstallKeyboard ? 3 : 1;
|
||||
}
|
||||
|
||||
int __USBGetJoystickReportID() {
|
||||
int i = 1;
|
||||
if (__USBInstallKeyboard) {
|
||||
i++;
|
||||
i += 2;
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
@@ -156,8 +156,9 @@ static uint8_t *GetDescHIDReport(int *len) {
|
||||
void __SetupDescHIDReport() {
|
||||
//allocate memory for the HID report descriptors. We don't use them, but need the size here.
|
||||
uint8_t desc_hid_report_mouse[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_absmouse[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_joystick[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(2)) };
|
||||
int size = 0;
|
||||
|
||||
//accumulate the size of all used HID report descriptors
|
||||
@@ -166,6 +167,8 @@ void __SetupDescHIDReport() {
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
size += sizeof(desc_hid_report_mouse);
|
||||
} else if (__USBInstallAbsoluteMouse) {
|
||||
size += sizeof(desc_hid_report_absmouse);
|
||||
}
|
||||
if (__USBInstallJoystick) {
|
||||
size += sizeof(desc_hid_report_joystick);
|
||||
@@ -194,11 +197,19 @@ void __SetupDescHIDReport() {
|
||||
if (__USBInstallMouse) {
|
||||
//determine if we need an offset (USB keyboard is installed)
|
||||
if (__USBInstallKeyboard) {
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2)) };
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(3)) };
|
||||
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
|
||||
} else {
|
||||
memcpy(__hid_report, desc_hid_report_mouse, sizeof(desc_hid_report_mouse));
|
||||
}
|
||||
} else if (__USBInstallAbsoluteMouse) {
|
||||
//determine if we need an offset (USB keyboard is installed)
|
||||
if (__USBInstallKeyboard) {
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(3)) };
|
||||
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
|
||||
} else {
|
||||
memcpy(__hid_report, desc_hid_report_absmouse, sizeof(desc_hid_report_absmouse));
|
||||
}
|
||||
}
|
||||
|
||||
//3.) joystick descriptor. 2 additional checks are necessary for mouse and/or keyboard
|
||||
@@ -206,12 +217,15 @@ void __SetupDescHIDReport() {
|
||||
uint8_t reportid = 1;
|
||||
int offset = 0;
|
||||
if (__USBInstallKeyboard) {
|
||||
reportid++;
|
||||
reportid += 2;
|
||||
offset += sizeof(desc_hid_report_keyboard);
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_mouse);
|
||||
} else if (__USBInstallAbsoluteMouse) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_absmouse);
|
||||
}
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(reportid)) };
|
||||
memcpy(__hid_report + offset, desc_local, sizeof(desc_local));
|
||||
@@ -235,7 +249,7 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
|
||||
void __SetupUSBDescriptor() {
|
||||
if (!usbd_desc_cfg) {
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallAbsoluteMouse || __USBInstallJoystick;
|
||||
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMassStorage ? 1 : 0);
|
||||
|
||||
@@ -301,15 +315,15 @@ void __SetupUSBDescriptor() {
|
||||
|
||||
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
(void) langid;
|
||||
#define DESC_STR_MAX (20)
|
||||
#define DESC_STR_MAX (32)
|
||||
static uint16_t desc_str[DESC_STR_MAX];
|
||||
|
||||
static char idString[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1];
|
||||
|
||||
static const char *const usbd_desc_str[] = {
|
||||
[USBD_STR_0] = "",
|
||||
[USBD_STR_MANUF] = "Raspberry Pi",
|
||||
[USBD_STR_PRODUCT] = "PicoArduino",
|
||||
[USBD_STR_MANUF] = USB_MANUFACTURER,
|
||||
[USBD_STR_PRODUCT] = USB_PRODUCT,
|
||||
[USBD_STR_SERIAL] = idString,
|
||||
[USBD_STR_CDC] = "Board CDC",
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
@@ -380,9 +394,22 @@ void __USBStart() {
|
||||
}
|
||||
|
||||
|
||||
bool __USBHIDReady() {
|
||||
uint32_t start = millis();
|
||||
const uint32_t timeout = 500;
|
||||
|
||||
while (((millis() - start) < timeout) && tud_ready() && !tud_hid_ready()) {
|
||||
tud_task();
|
||||
delay(1);
|
||||
}
|
||||
return tud_hid_ready();
|
||||
}
|
||||
|
||||
|
||||
// Invoked when received GET_REPORT control request
|
||||
// Application must fill buffer report's content and return its length.
|
||||
// Return zero will cause the stack to STALL request
|
||||
extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) __attribute__((weak));
|
||||
extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
|
||||
// TODO not implemented
|
||||
(void) instance;
|
||||
@@ -396,6 +423,7 @@ extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, h
|
||||
|
||||
// Invoked when received SET_REPORT control request or
|
||||
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||
extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) __attribute__((weak));
|
||||
extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
|
||||
// TODO set LED based on CAPLOCK, NUMLOCK etc...
|
||||
(void) instance;
|
||||
@@ -533,6 +561,59 @@ usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
|
||||
*driver_count = 1;
|
||||
return &_resetd_driver;
|
||||
}
|
||||
|
||||
#elif defined NO_USB
|
||||
|
||||
// will ensure backward compatibility with existing code when using pico-debug
|
||||
|
||||
#warning "NO_USB selected. No output to Serial will occur!"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void SerialUSB::begin(unsigned long baud) {
|
||||
}
|
||||
|
||||
void SerialUSB::end() {
|
||||
|
||||
}
|
||||
|
||||
int SerialUSB::peek() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SerialUSB::read() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SerialUSB::available() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SerialUSB::availableForWrite() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SerialUSB::flush() {
|
||||
|
||||
}
|
||||
|
||||
size_t SerialUSB::write(uint8_t c) {
|
||||
(void) c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
(void) buf;
|
||||
(void) length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SerialUSB::operator bool() {
|
||||
return false;
|
||||
}
|
||||
|
||||
SerialUSB Serial;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -19,13 +19,19 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
|
||||
// Weak function definitions for each type of endpoint
|
||||
extern void __USBInstallSerial() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallKeyboard() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallJoystick() __attribute__((weak));
|
||||
|
||||
// One or the other allowed, not both
|
||||
extern void __USBInstallMouse() __attribute__((weak));
|
||||
extern void __USBInstallAbsoluteMouse() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallMassStorage() __attribute__((weak));
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
@@ -39,3 +45,6 @@ int __USBGetJoystickReportID();
|
||||
|
||||
// Called by main() to init the USB HW/SW.
|
||||
void __USBStart();
|
||||
|
||||
// Helper class for HID report sending with wait and timeout
|
||||
bool __USBHIDReady();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 7
|
||||
#define ARDUINO_PICO_REVISION 3
|
||||
#define ARDUINO_PICO_VERSION_STR "2.7.3"
|
||||
#define ARDUINO_PICO_MAJOR 3
|
||||
#define ARDUINO_PICO_MINOR 5
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "3.5.0"
|
||||
|
||||
+21
-14
@@ -46,22 +46,24 @@ static pio_program_t *pio_make_uart_prog(int repl, const pio_program_t *pg) {
|
||||
return p;
|
||||
}
|
||||
|
||||
static PIOProgram *_getTxProgram(int bits) {
|
||||
auto f = _txMap.find(bits);
|
||||
static PIOProgram *_getTxProgram(int bits, bool inverted) {
|
||||
int key = inverted ? -bits : bits;
|
||||
auto f = _txMap.find(key);
|
||||
if (f == _txMap.end()) {
|
||||
pio_program_t * p = pio_make_uart_prog(bits, &pio_tx_program);
|
||||
_txMap.insert({bits, new PIOProgram(p)});
|
||||
f = _txMap.find(bits);
|
||||
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_tx_inv_program : &pio_tx_program);
|
||||
_txMap.insert({key, new PIOProgram(p)});
|
||||
f = _txMap.find(key);
|
||||
}
|
||||
return f->second;
|
||||
}
|
||||
|
||||
static PIOProgram *_getRxProgram(int bits) {
|
||||
auto f = _rxMap.find(bits);
|
||||
static PIOProgram *_getRxProgram(int bits, bool inverted) {
|
||||
int key = inverted ? -bits : bits;
|
||||
auto f = _rxMap.find(key);
|
||||
if (f == _rxMap.end()) {
|
||||
pio_program_t * p = pio_make_uart_prog(bits, &pio_rx_program);
|
||||
_rxMap.insert({bits, new PIOProgram(p)});
|
||||
f = _rxMap.find(bits);
|
||||
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_rx_inv_program : &pio_rx_program);
|
||||
_rxMap.insert({key, new PIOProgram(p)});
|
||||
f = _rxMap.find(key);
|
||||
}
|
||||
return f->second;
|
||||
}
|
||||
@@ -96,7 +98,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
|
||||
return;
|
||||
}
|
||||
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
|
||||
uint32_t decode = _rxPIO->rxf[_rxSM];
|
||||
uint32_t decode = _rxPIO->rxf[_rxSM] ^ (_rxInverted ? 0xffffffff : 0);
|
||||
decode >>= 33 - _rxBits;
|
||||
uint32_t val = 0;
|
||||
for (int b = 0; b < _bits + 1; b++) {
|
||||
@@ -189,7 +191,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
|
||||
if (_tx != NOPIN) {
|
||||
_txBits = _bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1/*start bit*/;
|
||||
_txPgm = _getTxProgram(_txBits);
|
||||
_txPgm = _getTxProgram(_txBits, _txInverted);
|
||||
int off;
|
||||
if (!_txPgm->prepare(&_txPIO, &_txSM, &off)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO TX UART, out of PIO resources\n");
|
||||
@@ -216,7 +218,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
_reader = 0;
|
||||
|
||||
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
|
||||
_rxPgm = _getRxProgram(_rxBits);
|
||||
_rxPgm = _getRxProgram(_rxBits, _rxInverted);
|
||||
int off;
|
||||
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO RX UART, out of PIO resources\n");
|
||||
@@ -346,6 +348,11 @@ void SerialPIO::flush() {
|
||||
delay((1000 * (_txBits + 1)) / _baud);
|
||||
}
|
||||
|
||||
void SerialPIO::setInverted(bool invTx, bool invRx) {
|
||||
_txInverted = invTx;
|
||||
_rxInverted = invRx;
|
||||
}
|
||||
|
||||
size_t SerialPIO::write(uint8_t c) {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_tx == NOPIN)) {
|
||||
@@ -364,7 +371,7 @@ size_t SerialPIO::write(uint8_t c) {
|
||||
}
|
||||
val <<= 1; // Start bit = low
|
||||
|
||||
pio_sm_put_blocking(_txPIO, _txSM, val);
|
||||
pio_sm_put_blocking(_txPIO, _txSM, _txInverted ? ~val : val);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
void begin(unsigned long baud, uint16_t config) override;
|
||||
void end() override;
|
||||
|
||||
void setInverted(bool invTx = true, bool invRx = true);
|
||||
|
||||
virtual int peek() override;
|
||||
virtual int read() override;
|
||||
virtual int available() override;
|
||||
@@ -63,6 +65,8 @@ protected:
|
||||
int _stop;
|
||||
bool _overflow;
|
||||
mutex_t _mutex;
|
||||
bool _txInverted = false;
|
||||
bool _rxInverted = false;
|
||||
|
||||
PIOProgram *_txPgm;
|
||||
PIO _txPIO;
|
||||
|
||||
@@ -40,6 +40,10 @@ bool SerialUART::setRX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_rx == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -57,6 +61,10 @@ bool SerialUART::setTX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_tx == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.TX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -74,6 +82,10 @@ bool SerialUART::setRTS(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_rts == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -91,6 +103,10 @@ bool SerialUART::setCTS(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_cts == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.CTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
|
||||
+17
-13
@@ -25,15 +25,15 @@
|
||||
#include <Arduino.h>
|
||||
#include "CoreMutex.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "pico/time.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "hardware/watchdog.h"
|
||||
#include "pico/unique_id.h"
|
||||
#include "hardware/resets.h"
|
||||
#include <tusb.h>
|
||||
#include <pico/time.h>
|
||||
#include <pico/binary_info.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <pico/unique_id.h>
|
||||
#include <hardware/resets.h>
|
||||
|
||||
#ifndef DISABLE_USB_SERIAL
|
||||
// Ensure we are installed in the USB chain
|
||||
@@ -130,7 +130,7 @@ size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
|
||||
static uint64_t last_avail_time;
|
||||
int written = 0;
|
||||
if (tud_cdc_connected()) {
|
||||
if (tud_cdc_connected() || _ignoreFlowControl) {
|
||||
for (size_t i = 0; i < length;) {
|
||||
int n = length - i;
|
||||
int avail = tud_cdc_write_available();
|
||||
@@ -171,14 +171,19 @@ SerialUSB::operator bool() {
|
||||
return tud_cdc_connected();
|
||||
}
|
||||
|
||||
void SerialUSB::ignoreFlowControl(bool ignore) {
|
||||
_ignoreFlowControl = ignore;
|
||||
}
|
||||
|
||||
static bool _dtr = false;
|
||||
static bool _rts = false;
|
||||
static int _bps = 115200;
|
||||
static bool _rebooting = false;
|
||||
static void CheckSerialReset() {
|
||||
__holdUpPendSV = 1; // Ensure we don't get swapped out by FreeRTOS
|
||||
if (!_rebooting && (_bps == 1200) && (!_dtr)) {
|
||||
if (__isFreeRTOS) {
|
||||
__freertos_idle_other_core();
|
||||
}
|
||||
_rebooting = true;
|
||||
// Disable NVIC IRQ, so that we don't get bothered anymore
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
@@ -186,11 +191,10 @@ static void CheckSerialReset() {
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
// Delay a bit, so the PC can figure out that we have disconnected.
|
||||
sleep_ms(3);
|
||||
busy_wait_ms(3);
|
||||
reset_usb_boot(0, 0);
|
||||
while (1); // WDT will fire here
|
||||
}
|
||||
__holdUpPendSV = 0;
|
||||
}
|
||||
|
||||
extern "C" void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
|
||||
|
||||
@@ -44,6 +44,8 @@ public:
|
||||
using Print::write;
|
||||
operator bool() override;
|
||||
|
||||
void ignoreFlowControl(bool ignore = true);
|
||||
|
||||
// ESP8266 compat
|
||||
void setDebugOutput(bool unused) {
|
||||
(void) unused;
|
||||
@@ -51,6 +53,7 @@ public:
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
bool _ignoreFlowControl = false;
|
||||
};
|
||||
|
||||
extern SerialUSB Serial;
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "stdint.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void randomSeed(uint32_t dwSeed) {
|
||||
if (dwSeed != 0) {
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
*/
|
||||
|
||||
#include "_freertos.h"
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
#include <stdlib.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
typedef struct {
|
||||
mutex_t *src;
|
||||
@@ -59,3 +60,45 @@ SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive) {
|
||||
}
|
||||
return nullptr; // Need to make space for more mutex maps!
|
||||
}
|
||||
|
||||
|
||||
// The HW Random code needs a precise sleep_until() in order to assure it
|
||||
// grabs the ROSC bit when it wants. Unfortunately, under FreeRTOS the
|
||||
// sleep_until() becomes imprecise and the "did I get the bit when I wanted"
|
||||
// check in the pico_rand code always fails and you get an infinite loop.
|
||||
|
||||
// This block wraps the 2 get_rand calls to set a flag to convert
|
||||
// sleep_until() (which can do a task swap and cause bad timing) into a
|
||||
// busy wait.
|
||||
|
||||
extern "C" {
|
||||
static bool __inRand = false;
|
||||
|
||||
extern uint64_t __real_get_rand_64();
|
||||
uint64_t __wrap_get_rand_64() {
|
||||
if (__isFreeRTOS) {
|
||||
rp2040.idleOtherCore();
|
||||
__inRand = true;
|
||||
auto r = __real_get_rand_64();
|
||||
__inRand = false;
|
||||
rp2040.resumeOtherCore();
|
||||
return r;
|
||||
} else {
|
||||
return __real_get_rand_64();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t __wrap_get_rand_32() {
|
||||
return (uint32_t) __wrap_get_rand_64();
|
||||
}
|
||||
|
||||
extern void __real_sleep_until(absolute_time_t t);
|
||||
void __wrap_sleep_until(absolute_time_t t) {
|
||||
if (__inRand) {
|
||||
busy_wait_until(t);
|
||||
} else {
|
||||
__real_sleep_until(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
|
||||
// Cannot include refs to FreeRTOS's actual semaphore calls because they
|
||||
// are implemented as macros, so we have a wrapper in our variant hook
|
||||
@@ -35,32 +35,26 @@ extern "C" {
|
||||
struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */
|
||||
typedef struct QueueDefinition * QueueHandle_t;
|
||||
typedef QueueHandle_t SemaphoreHandle_t;
|
||||
typedef int32_t BaseType_t;
|
||||
#endif
|
||||
|
||||
extern bool __freertos_check_if_in_isr() __attribute__((weak));
|
||||
|
||||
extern SemaphoreHandle_t __freertos_mutex_create() __attribute__((weak));
|
||||
extern SemaphoreHandle_t _freertos_recursive_mutex_create() __attribute__((weak));
|
||||
|
||||
extern void __freertos_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern void __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
|
||||
extern int __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
|
||||
extern int __freertos_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern void __freertos_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
|
||||
|
||||
extern void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
extern void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
extern void vTaskSuspendAll() __attribute__((weak));
|
||||
extern int32_t xTaskResumeAll() __attribute__((weak));
|
||||
|
||||
typedef struct tskTaskControlBlock * TaskHandle_t;
|
||||
extern void vTaskPreemptionDisable(TaskHandle_t p) __attribute__((weak));
|
||||
extern void vTaskPreemptionEnable(TaskHandle_t p) __attribute__((weak));
|
||||
#endif
|
||||
|
||||
extern void __freertos_idle_other_core() __attribute__((weak));
|
||||
extern void __freertos_resume_other_core() __attribute__((weak));
|
||||
}
|
||||
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive = false);
|
||||
|
||||
// Halt the FreeRTOS PendSV task switching magic
|
||||
extern "C" int __holdUpPendSV;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Simple helper header to ensure pico libs support ?BT
|
||||
|
||||
#ifndef ENABLE_CLASSIC
|
||||
#define ENABLE_CLASSIC 0
|
||||
#endif
|
||||
|
||||
static_assert(ENABLE_CLASSIC, "This library needs Bluetooth enabled. Use the 'Tools->IP/Bluetooth Stack' menu in the IDE to enable it.");
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
CYW43 TCP/Ethernet wrappers
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
|
||||
#include <lwip/netif.h>
|
||||
extern "C" {
|
||||
#include <cyw43.h>
|
||||
#include <cyw43_stats.h>
|
||||
}
|
||||
#include <pico/cyw43_arch.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
// From cyw43_ctrl.c
|
||||
#define WIFI_JOIN_STATE_KIND_MASK (0x000f)
|
||||
#define WIFI_JOIN_STATE_ACTIVE (0x0001)
|
||||
#define WIFI_JOIN_STATE_FAIL (0x0002)
|
||||
#define WIFI_JOIN_STATE_NONET (0x0003)
|
||||
#define WIFI_JOIN_STATE_BADAUTH (0x0004)
|
||||
#define WIFI_JOIN_STATE_AUTH (0x0200)
|
||||
#define WIFI_JOIN_STATE_LINK (0x0400)
|
||||
#define WIFI_JOIN_STATE_KEYED (0x0800)
|
||||
#define WIFI_JOIN_STATE_ALL (0x0e01)
|
||||
|
||||
// The core can't directly call a library, so put in a dummy weak one to be overridden by one in lwip_cyw43 library
|
||||
extern struct netif *__getCYW43Netif() __attribute__((weak));
|
||||
struct netif *__getCYW43Netif() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// CB from the cyw43 driver
|
||||
extern "C" void __wrap_cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
|
||||
(void) cb_data;
|
||||
(void) itf;
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
if (netif && (netif->flags & NETIF_FLAG_LINK_UP)) {
|
||||
struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
||||
if (p != nullptr) {
|
||||
pbuf_take(p, buf, len);
|
||||
if ((netif->input(p, netif) != ERR_OK)) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
CYW43_STAT_INC(PACKET_IN_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
if (netif) {
|
||||
netif_set_link_up(netif);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
if (netif) {
|
||||
netif_set_link_down(netif);
|
||||
}
|
||||
self->wifi_join_state &= ~WIFI_JOIN_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
extern "C" int __wrap_cyw43_tcpip_link_status(cyw43_t *self, int itf) {
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
//if ((CYW43::_netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) == (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP))
|
||||
// Fake this since it's only used in the SDK
|
||||
if (netif && ((netif->flags & (NETIF_FLAG_LINK_UP)) == (NETIF_FLAG_LINK_UP))) {
|
||||
return CYW43_LINK_UP;
|
||||
} else {
|
||||
return cyw43_wifi_link_status(self, itf);
|
||||
}
|
||||
}
|
||||
|
||||
// CBs from the SDK, not needed here as we do TCP later in the game
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
|
||||
#endif
|
||||
+13
-32
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
LWIP wrappers to protect against timer-based re-entrancy
|
||||
|
||||
Copyright (c) 202s Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
@@ -18,45 +18,25 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "pico/mutex.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/timeouts.h"
|
||||
|
||||
// Global indicator that we're inside an LWIP block
|
||||
extern "C" {
|
||||
volatile bool __inLWIP = false;
|
||||
}
|
||||
|
||||
auto_init_recursive_mutex(__mtxLWIP);
|
||||
#include <pico/mutex.h>
|
||||
#include <lwip/pbuf.h>
|
||||
#include <lwip/udp.h>
|
||||
#include <lwip/tcp.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <lwip/raw.h>
|
||||
#include <lwip/timeouts.h>
|
||||
#include <pico/cyw43_arch.h>
|
||||
|
||||
class LWIPMutex {
|
||||
public:
|
||||
LWIPMutex() {
|
||||
noInterrupts();
|
||||
recursive_mutex_enter_blocking(&__mtxLWIP);
|
||||
__inLWIP = true;
|
||||
_ref++;
|
||||
interrupts();
|
||||
cyw43_arch_lwip_begin();
|
||||
}
|
||||
|
||||
~LWIPMutex() {
|
||||
noInterrupts();
|
||||
if (0 == --_ref) {
|
||||
__inLWIP = false;
|
||||
}
|
||||
recursive_mutex_exit(&__mtxLWIP);
|
||||
interrupts();
|
||||
cyw43_arch_lwip_end();
|
||||
}
|
||||
|
||||
private:
|
||||
static int _ref;
|
||||
};
|
||||
int LWIPMutex::_ref = 0;
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -275,7 +255,8 @@ extern "C" {
|
||||
return __real_udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
|
||||
}
|
||||
|
||||
extern void __real_sys_check_timeouts(void);
|
||||
// sys_check_timeouts is special case because the async process will call it. If we're already in a timeout check, just do a noop
|
||||
extern void __real_sys_check_timeouts();
|
||||
void __wrap_sys_check_timeouts(void) {
|
||||
LWIPMutex m;
|
||||
__real_sys_check_timeouts();
|
||||
|
||||
@@ -27,11 +27,8 @@
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
int __holdUpPendSV = 0;
|
||||
};
|
||||
|
||||
mutex_t _pioMutex;
|
||||
|
||||
extern void setup();
|
||||
extern void loop();
|
||||
|
||||
@@ -92,8 +89,6 @@ extern "C" int main() {
|
||||
_REENT_INIT_PTR(_impure_ptr1);
|
||||
}
|
||||
|
||||
mutex_init(&_pioMutex);
|
||||
|
||||
rp2040.begin();
|
||||
|
||||
initVariant();
|
||||
@@ -163,6 +158,7 @@ extern "C" void __register_impure_ptr(struct _reent *p) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" struct _reent *__wrap___getreent() __attribute__((weak));
|
||||
extern "C" struct _reent *__wrap___getreent() {
|
||||
if (get_core_num() == 0) {
|
||||
return _impure_ptr;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
; We shift out the start and stop bit as part of the FIFO
|
||||
set x, 9
|
||||
|
||||
pull side 1 ; Force stop bit
|
||||
pull side 1 ; Force stop bit high
|
||||
|
||||
; Send the bits
|
||||
bitloop:
|
||||
@@ -41,6 +41,26 @@ wait_bit:
|
||||
|
||||
|
||||
|
||||
; inverted-logic version (inverts the stop bit)
|
||||
|
||||
.program pio_tx_inv
|
||||
.side_set 1 opt
|
||||
|
||||
|
||||
; We shift out the start and stop bit as part of the FIFO
|
||||
set x, 9
|
||||
|
||||
pull side 0 ; Force stop bit low
|
||||
|
||||
; Send the bits
|
||||
bitloop:
|
||||
out pins, 1
|
||||
mov y, isr ; ISR is loaded by the setup routine with the period-1 count
|
||||
wait_bit:
|
||||
jmp y-- wait_bit
|
||||
jmp x-- bitloop
|
||||
|
||||
|
||||
% c-sdk {
|
||||
|
||||
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
|
||||
@@ -68,7 +88,7 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
|
||||
|
||||
.program pio_rx
|
||||
|
||||
@@ -91,6 +111,27 @@ wait_half:
|
||||
push ; Stuff it and wait for next start
|
||||
|
||||
|
||||
.program pio_rx_inv
|
||||
|
||||
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
|
||||
|
||||
start:
|
||||
set x, 18 ; Preload bit counter...we'll shift in the start bit and stop bit, and each bit will be double-recorded (to be fixed by RP2040 code)
|
||||
wait 1 pin 0 ; Stall until start bit is asserted
|
||||
|
||||
bitloop:
|
||||
; Delay until 1/2 way into the bit time
|
||||
mov y, osr
|
||||
wait_half:
|
||||
jmp y-- wait_half
|
||||
|
||||
; Read in the bit
|
||||
in pins, 1 ; Shift data bit into ISR
|
||||
jmp x-- bitloop ; Loop all bits
|
||||
|
||||
push ; Stuff it and wait for next start
|
||||
|
||||
|
||||
% c-sdk {
|
||||
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
@@ -37,6 +39,39 @@ static inline pio_sm_config pio_tx_program_get_default_config(uint offset) {
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------- //
|
||||
// pio_tx_inv //
|
||||
// ---------- //
|
||||
|
||||
#define pio_tx_inv_wrap_target 0
|
||||
#define pio_tx_inv_wrap 5
|
||||
|
||||
static const uint16_t pio_tx_inv_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe029, // 0: set x, 9
|
||||
0x90a0, // 1: pull block side 0
|
||||
0x6001, // 2: out pins, 1
|
||||
0xa046, // 3: mov y, isr
|
||||
0x0084, // 4: jmp y--, 4
|
||||
0x0042, // 5: jmp x--, 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_tx_inv_program = {
|
||||
.instructions = pio_tx_inv_program_instructions,
|
||||
.length = 6,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_tx_inv_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_tx_inv_wrap_target, offset + pio_tx_inv_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
|
||||
// Tell PIO to initially drive output-high on the selected pin, then map PIO
|
||||
@@ -90,6 +125,39 @@ static inline pio_sm_config pio_rx_program_get_default_config(uint offset) {
|
||||
sm_config_set_wrap(&c, offset + pio_rx_wrap_target, offset + pio_rx_wrap);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------- //
|
||||
// pio_rx_inv //
|
||||
// ---------- //
|
||||
|
||||
#define pio_rx_inv_wrap_target 0
|
||||
#define pio_rx_inv_wrap 6
|
||||
|
||||
static const uint16_t pio_rx_inv_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe032, // 0: set x, 18
|
||||
0x20a0, // 1: wait 1 pin, 0
|
||||
0xa047, // 2: mov y, osr
|
||||
0x0083, // 3: jmp y--, 3
|
||||
0x4001, // 4: in pins, 1
|
||||
0x0042, // 5: jmp x--, 2
|
||||
0x8020, // 6: push block
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_rx_inv_program = {
|
||||
.instructions = pio_rx_inv_program_instructions,
|
||||
.length = 7,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_rx_inv_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_rx_inv_wrap_target, offset + pio_rx_inv_wrap);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#include <btstack.h>
|
||||
#include <pico/btstack_flash_bank.h>
|
||||
#include <hardware/flash.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <string.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
const uint8_t __bluetooth_tlv[8192] __attribute__((aligned(4096))) = { 0 };
|
||||
extern const uint8_t __flash_binary_start;
|
||||
|
||||
#undef PICO_FLASH_BANK_TOTAL_SIZE
|
||||
#undef PICO_FLASH_BANK_STORAGE_OFFSET
|
||||
#define PICO_FLASH_BANK_TOTAL_SIZE sizeof(__bluetooth_tlv)
|
||||
#define PICO_FLASH_BANK_STORAGE_OFFSET ((unsigned int)(__bluetooth_tlv - &__flash_binary_start))
|
||||
|
||||
#if 0
|
||||
// Check sizes
|
||||
static_assert(PICO_FLASH_BANK_TOTAL_SIZE % (FLASH_SECTOR_SIZE * 2) == 0, "PICO_FLASH_BANK_TOTAL_SIZE invalid");
|
||||
static_assert(PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
|
||||
static_assert(PICO_FLASH_BANK_STORAGE_OFFSET + PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
|
||||
#endif
|
||||
|
||||
// Size of one bank
|
||||
#define PICO_FLASH_BANK_SIZE (PICO_FLASH_BANK_TOTAL_SIZE / 2)
|
||||
|
||||
#if 0
|
||||
#define DEBUG_PRINT(format,args...) printf(format, ## args)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#endif
|
||||
|
||||
static uint32_t pico_flash_bank_get_size(void * context) {
|
||||
(void)(context);
|
||||
return PICO_FLASH_BANK_SIZE;
|
||||
}
|
||||
|
||||
static uint32_t pico_flash_bank_get_alignment(void * context) {
|
||||
(void)(context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pico_flash_bank_erase(void * context, int bank) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("erase: bank %d\n", bank);
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
flash_range_erase(PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank), PICO_FLASH_BANK_SIZE);
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
}
|
||||
|
||||
static void pico_flash_bank_read(void *context, int bank, uint32_t offset, uint8_t *buffer, uint32_t size) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("read: bank %d offset %u size %u\n", bank, offset, size);
|
||||
|
||||
assert(bank <= 1);
|
||||
if (bank > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(offset < PICO_FLASH_BANK_SIZE);
|
||||
if (offset >= PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
|
||||
if ((offset + size) > PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Flash is xip
|
||||
memcpy(buffer, (void *)(XIP_BASE + PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank) + offset), size);
|
||||
}
|
||||
|
||||
static void pico_flash_bank_write(void * context, int bank, uint32_t offset, const uint8_t *data, uint32_t size) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("write: bank %d offset %u size %u\n", bank, offset, size);
|
||||
|
||||
assert(bank <= 1);
|
||||
if (bank > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(offset < PICO_FLASH_BANK_SIZE);
|
||||
if (offset >= PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
|
||||
if ((offset + size) > PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calc bank start position
|
||||
const uint32_t bank_start_pos = PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank);
|
||||
|
||||
// Calculate first and last page in the bank
|
||||
const uint32_t first_page = offset / FLASH_PAGE_SIZE;
|
||||
const uint32_t last_page = (offset + size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
|
||||
|
||||
// Now we only care about the offset in the first page
|
||||
offset %= FLASH_PAGE_SIZE;
|
||||
|
||||
// Amount of data we've copied
|
||||
uint32_t data_pos = 0;
|
||||
uint32_t size_left = size;
|
||||
|
||||
// Write all the pages required
|
||||
for (uint32_t page = first_page; page < last_page; page++) {
|
||||
uint8_t page_data[FLASH_PAGE_SIZE];
|
||||
|
||||
assert(data_pos < size && size_left <= size);
|
||||
|
||||
// Copy data we're not going to overwrite in the first page
|
||||
if (page == first_page && offset > 0) {
|
||||
memcpy(page_data,
|
||||
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE)),
|
||||
offset);
|
||||
}
|
||||
|
||||
// Copy the data we're not going to overwrite in the last page
|
||||
if (page == last_page - 1 && (offset + size_left) < FLASH_PAGE_SIZE) {
|
||||
memcpy(page_data + offset + size_left,
|
||||
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE) + offset + size_left),
|
||||
FLASH_PAGE_SIZE - offset - size_left);
|
||||
}
|
||||
|
||||
// Now copy the new data into the page
|
||||
const uint32_t size_to_copy = MIN(size_left, FLASH_PAGE_SIZE - offset);
|
||||
memcpy(page_data + offset, data + data_pos, size_to_copy);
|
||||
|
||||
data_pos += size_to_copy;
|
||||
size_left -= size_to_copy;
|
||||
|
||||
// zero offset for the following pages
|
||||
offset = 0;
|
||||
|
||||
// Now program the entire page
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
flash_range_program(bank_start_pos + (page * FLASH_PAGE_SIZE), page_data, FLASH_PAGE_SIZE);
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
static const hal_flash_bank_t pico_flash_bank_instance_obj = {
|
||||
/* uint32_t (*get_size)(..) */ &pico_flash_bank_get_size,
|
||||
/* uint32_t (*get_alignment)(..); */ &pico_flash_bank_get_alignment,
|
||||
/* void (*erase)(..); */ &pico_flash_bank_erase,
|
||||
/* void (*read)(..); */ &pico_flash_bank_read,
|
||||
/* void (*write)(..); */ &pico_flash_bank_write,
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
const hal_flash_bank_t *pico_flash_bank_instance(void) {
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check we're not overlapping the binary in flash
|
||||
//extern char __flash_binary_end;
|
||||
// assert((uintptr_t)&__flash_binary_end - XIP_BASE <= PICO_FLASH_BANK_STORAGE_OFFSET);
|
||||
#endif
|
||||
|
||||
return &pico_flash_bank_instance_obj;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Taken from the Pico-SDK v1.4.0 and hacked by EFP3 to allow overriding the LWIP setup
|
||||
|
||||
//#include <stdio.h>
|
||||
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/sem.h"
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
#include "cyw43_stats.h"
|
||||
|
||||
#include <lwip/init.h>
|
||||
#include "lwip/timeouts.h"
|
||||
|
||||
#ifndef CYW43_PIN_WL_HOST_WAKE
|
||||
#define CYW43_PIN_WL_HOST_WAKE 24
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_PIN_WL_REG_ON
|
||||
#define CYW43_PIN_WL_REG_ON 23
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_WL_GPIO_COUNT
|
||||
#define CYW43_WL_GPIO_COUNT 3
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_WL_GPIO_LED_PIN
|
||||
#define CYW43_WL_GPIO_LED_PIN 0
|
||||
#endif
|
||||
|
||||
extern volatile bool __inLWIP;
|
||||
|
||||
// note same code
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && !NO_SYS
|
||||
#error PICO_CYW43_ARCH_THREADSAFE_BACKGROUND requires lwIP NO_SYS=1
|
||||
#endif
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && MEM_LIBC_MALLOC
|
||||
#error MEM_LIBC_MALLOC is incompatible with PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
#endif
|
||||
// todo right now we are now always doing a cyw43_dispatch along with a lwip one when hopping cores in low_prio_irq_schedule_dispatch
|
||||
|
||||
#ifndef CYW43_SLEEP_CHECK_MS
|
||||
#define CYW43_SLEEP_CHECK_MS 50 // How often to run lwip callback
|
||||
#endif
|
||||
static alarm_id_t periodic_alarm = -1;
|
||||
|
||||
static inline uint recursive_mutex_enter_count(recursive_mutex_t *mutex) {
|
||||
return mutex->enter_count;
|
||||
}
|
||||
|
||||
static inline lock_owner_id_t recursive_mutex_owner(recursive_mutex_t *mutex) {
|
||||
return mutex->owner;
|
||||
}
|
||||
|
||||
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
|
||||
|
||||
enum {
|
||||
CYW43_DISPATCH_SLOT_CYW43 = 0,
|
||||
CYW43_DISPATCH_SLOT_ADAPTER,
|
||||
CYW43_DISPATCH_SLOT_ENUM_COUNT
|
||||
};
|
||||
#ifndef CYW43_DISPATCH_SLOT_COUNT
|
||||
#define CYW43_DISPATCH_SLOT_COUNT CYW43_DISPATCH_SLOT_ENUM_COUNT
|
||||
#endif
|
||||
|
||||
typedef void (*low_prio_irq_dispatch_t)(void);
|
||||
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f);
|
||||
|
||||
static uint8_t cyw43_core_num;
|
||||
#ifndef NDEBUG
|
||||
static bool in_low_priority_irq;
|
||||
#endif
|
||||
static uint8_t low_priority_irq_num;
|
||||
static bool low_priority_irq_missed;
|
||||
static low_prio_irq_dispatch_t low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_COUNT];
|
||||
static recursive_mutex_t cyw43_mutex;
|
||||
semaphore_t cyw43_irq_sem;
|
||||
|
||||
// Called in low priority pendsv interrupt only to do lwip processing and check cyw43 sleep
|
||||
static void periodic_worker(void) {
|
||||
#if CYW43_USE_STATS && LWIP_SYS_CHECK_MS
|
||||
static uint32_t counter;
|
||||
if (counter++ % (30000 / LWIP_SYS_CHECK_MS) == 0) {
|
||||
cyw43_dump_stats();
|
||||
}
|
||||
#endif
|
||||
|
||||
CYW43_STAT_INC(LWIP_RUN_COUNT);
|
||||
//#if CYW43_LWIP
|
||||
if (!__inLWIP) {
|
||||
sys_check_timeouts();
|
||||
}
|
||||
//#endif
|
||||
if (cyw43_poll) {
|
||||
if (cyw43_sleep > 0) {
|
||||
if (--cyw43_sleep == 0) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular callback to get lwip to check for timeouts
|
||||
static int64_t periodic_alarm_handler(__unused alarm_id_t id, __unused void *user_data) {
|
||||
// Do lwip processing in low priority pendsv interrupt
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_ADAPTER, periodic_worker);
|
||||
return CYW43_SLEEP_CHECK_MS * 1000;
|
||||
}
|
||||
|
||||
void cyw43_await_background_or_timeout_us(uint32_t timeout_us) {
|
||||
// if we are called from within an IRQ, then don't wait (we are only ever called in a polling loop)
|
||||
if (!__get_current_exception()) {
|
||||
sem_acquire_timeout_us(&cyw43_irq_sem, timeout_us);
|
||||
}
|
||||
}
|
||||
|
||||
// GPIO interrupt handler to tell us there's cyw43 has work to do
|
||||
static void gpio_irq_handler(void) {
|
||||
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
|
||||
if (events & GPIO_IRQ_LEVEL_HIGH) {
|
||||
// As we use a high level interrupt, it will go off forever until it's serviced
|
||||
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
|
||||
// which is called at the end of cyw43_poll_func
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
|
||||
// also clear the force bit which we use to progratically cause this handler to fire (on the right core)
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
|
||||
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
hw_clear_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
CYW43_STAT_INC(IRQ_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
// Low priority interrupt handler to perform background processing
|
||||
static void low_priority_irq_handler(void) {
|
||||
assert(cyw43_core_num == get_core_num());
|
||||
if (recursive_mutex_try_enter(&cyw43_mutex, NULL)) {
|
||||
if (__inLWIP || (recursive_mutex_enter_count(&cyw43_mutex) != 1)) {
|
||||
low_priority_irq_missed = true;
|
||||
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
|
||||
} else {
|
||||
CYW43_STAT_INC(PENDSV_RUN_COUNT);
|
||||
#ifndef NDEBUG
|
||||
in_low_priority_irq = true;
|
||||
#endif
|
||||
for (size_t i = 0; i < count_of(low_priority_irq_dispatch_slots); i++) {
|
||||
if (low_priority_irq_dispatch_slots[i] != NULL) {
|
||||
low_prio_irq_dispatch_t f = low_priority_irq_dispatch_slots[i];
|
||||
low_priority_irq_dispatch_slots[i] = NULL;
|
||||
f();
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
in_low_priority_irq = false;
|
||||
#endif
|
||||
}
|
||||
recursive_mutex_exit(&cyw43_mutex);
|
||||
} else {
|
||||
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
|
||||
low_priority_irq_missed = true;
|
||||
}
|
||||
sem_release(&cyw43_irq_sem);
|
||||
}
|
||||
|
||||
static bool low_prio_irq_init(uint8_t priority) {
|
||||
assert(get_core_num() == cyw43_core_num);
|
||||
int irq = user_irq_claim_unused(false);
|
||||
if (irq < 0) {
|
||||
return false;
|
||||
}
|
||||
low_priority_irq_num = (uint8_t) irq;
|
||||
irq_set_exclusive_handler(low_priority_irq_num, low_priority_irq_handler);
|
||||
irq_set_enabled(low_priority_irq_num, true);
|
||||
irq_set_priority(low_priority_irq_num, priority);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void low_prio_irq_deinit(void) {
|
||||
if (low_priority_irq_num > 0) {
|
||||
irq_set_enabled(low_priority_irq_num, false);
|
||||
irq_remove_handler(low_priority_irq_num, low_priority_irq_handler);
|
||||
user_irq_unclaim(low_priority_irq_num);
|
||||
low_priority_irq_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cyw43_arch_init(void) {
|
||||
cyw43_core_num = get_core_num();
|
||||
recursive_mutex_init(&cyw43_mutex);
|
||||
cyw43_init(&cyw43_state);
|
||||
sem_init(&cyw43_irq_sem, 0, 1);
|
||||
|
||||
// Start regular lwip callback to handle timeouts
|
||||
periodic_alarm = add_alarm_in_us(CYW43_SLEEP_CHECK_MS * 1000, periodic_alarm_handler, NULL, true);
|
||||
if (periodic_alarm < 0) {
|
||||
return PICO_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
|
||||
irq_set_enabled(IO_IRQ_BANK0, true);
|
||||
|
||||
lwip_init();
|
||||
|
||||
// start low priority handler (no background work is done before this)
|
||||
bool ok = low_prio_irq_init(PICO_LOWEST_IRQ_PRIORITY);
|
||||
if (!ok) {
|
||||
cyw43_arch_deinit();
|
||||
return PICO_ERROR_GENERIC;
|
||||
}
|
||||
return PICO_OK;
|
||||
}
|
||||
|
||||
void cyw43_arch_deinit(void) {
|
||||
if (periodic_alarm >= 0) {
|
||||
cancel_alarm(periodic_alarm);
|
||||
periodic_alarm = -1;
|
||||
}
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
|
||||
gpio_remove_raw_irq_handler(IO_IRQ_BANK0, gpio_irq_handler);
|
||||
low_prio_irq_deinit();
|
||||
}
|
||||
|
||||
void cyw43_post_poll_hook(void) {
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
|
||||
}
|
||||
|
||||
// This is called in the gpio and low_prio_irq interrupts and on either core
|
||||
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f) {
|
||||
assert(slot < count_of(low_priority_irq_dispatch_slots));
|
||||
low_priority_irq_dispatch_slots[slot] = f;
|
||||
if (cyw43_core_num == get_core_num()) {
|
||||
//on same core, can dispatch directly
|
||||
irq_set_pending(low_priority_irq_num);
|
||||
} else {
|
||||
// on wrong core, so force via GPIO IRQ which itself calls this method for the CYW43 slot.
|
||||
// since the CYW43 slot always uses the same function, this is fine with the addition of an
|
||||
// extra (but harmless) CYW43 slot call when another SLOT is invoked.
|
||||
// We could do better, but would have to track why the IRQ was called.
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = cyw43_core_num ?
|
||||
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
hw_set_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_schedule_internal_poll_dispatch(void (*func)(void)) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, func);
|
||||
}
|
||||
|
||||
// Prevent background processing in pensv and access by the other core
|
||||
// These methods are called in pensv context and on either core
|
||||
// They can be called recursively
|
||||
void cyw43_thread_enter(void) {
|
||||
// Lock the other core and stop low_prio_irq running
|
||||
recursive_mutex_enter_blocking(&cyw43_mutex);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void cyw43_thread_lock_check(void) {
|
||||
// Lock the other core and stop low_prio_irq running
|
||||
if (recursive_mutex_enter_count(&cyw43_mutex) < 1 || recursive_mutex_owner(&cyw43_mutex) != lock_get_caller_owner_id()) {
|
||||
panic("cyw43_thread_lock_check failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Re-enable background processing
|
||||
void cyw43_thread_exit(void) {
|
||||
// Run low_prio_irq if needed
|
||||
if (1 == recursive_mutex_enter_count(&cyw43_mutex)) {
|
||||
// note the outer release of the mutex is not via cyw43_exit in the low_priority_irq case (it is a direct mutex exit)
|
||||
assert(!in_low_priority_irq);
|
||||
// if (low_priority_irq_missed) {
|
||||
// low_priority_irq_missed = false;
|
||||
if (low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_CYW43]) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
}
|
||||
// }
|
||||
}
|
||||
recursive_mutex_exit(&cyw43_mutex);
|
||||
}
|
||||
|
||||
|
||||
static void cyw43_delay_until(absolute_time_t until) {
|
||||
// sleep can be called in IRQs, so there's not much we can do there
|
||||
if (__get_current_exception()) {
|
||||
busy_wait_until(until);
|
||||
} else {
|
||||
sleep_until(until);
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_delay_ms(uint32_t ms) {
|
||||
cyw43_delay_until(make_timeout_time_ms(ms));
|
||||
}
|
||||
|
||||
void cyw43_delay_us(uint32_t us) {
|
||||
cyw43_delay_until(make_timeout_time_us(us));
|
||||
}
|
||||
|
||||
void cyw43_arch_poll() {
|
||||
// should not be necessary
|
||||
// if (cyw43_poll) {
|
||||
// low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
// }
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_init(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_deinit(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf);
|
||||
void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
|
||||
(void) cb_data;
|
||||
(void) itf;
|
||||
(void) len;
|
||||
(void) buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -6,10 +6,9 @@
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico.h"
|
||||
#include "pico/time.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include <pico/time.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include <pico/binary_info.h>
|
||||
|
||||
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS, Window of opportunity for a second press of a reset button to enter BOOTSEL mode (milliseconds), type=int, default=200, group=pico_bootsel_via_double_reset
|
||||
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Expost absolute mouse HID descriptor and report
|
||||
Taken from @tobozo PR https://github.com/hathach/tinyusb/pull/1363
|
||||
TODO - remove once that PR merged with TinyUSB
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
|
||||
// Absolute Mouse: same as the Standard (relative) Mouse Report but
|
||||
// with int16_t instead of int8_t for X and Y coordinates.
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
|
||||
int16_t x; /**< Current x position of the mouse. */
|
||||
int16_t y; /**< Current y position of the mouse. */
|
||||
int8_t wheel; /**< Current delta wheel movement on the mouse. */
|
||||
int8_t pan; // using AC Pan
|
||||
} hid_abs_mouse_report_t;
|
||||
|
||||
|
||||
// Absolute Mouse Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 5 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
/* Left, Right, Middle, Backward, Forward buttons */ \
|
||||
HID_REPORT_COUNT( 5 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 3 bit padding */ \
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 3 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
/* X, Y absolute position [0, 32767] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||
HID_LOGICAL_MIN ( 0x00 ) ,\
|
||||
HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
|
||||
HID_REPORT_SIZE ( 16 ) ,\
|
||||
HID_REPORT_COUNT ( 2 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* Vertical wheel scroll [-127, 127] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
|
||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
|
||||
/* Horizontal wheel scroll [-127, 127] */ \
|
||||
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
|
||||
HID_LOGICAL_MIN ( 0x81 ), \
|
||||
HID_LOGICAL_MAX ( 0x7f ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_REPORT_SIZE ( 8 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
|
||||
static inline bool tud_hid_abs_mouse_report(uint8_t report_id,
|
||||
uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
|
||||
hid_abs_mouse_report_t report = {
|
||||
.buttons = buttons,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.wheel = vertical,
|
||||
.pan = horizontal
|
||||
};
|
||||
|
||||
return tud_hid_n_report(0, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ static std::map<pin_size_t, CBInfo> _map;
|
||||
void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
|
||||
(void) events;
|
||||
// Only need to lock around the std::map check, not the whole IRQ callback
|
||||
CoreMutex m(&_irqMutex, (FromISR | DebugEnable));
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (m) {
|
||||
auto irq = _map.find(gpio);
|
||||
if (irq != _map.end()) {
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ need to be periodically sampled to be read by applications, easily, such as:
|
||||
* Light dependent resistors (LDR), etc.
|
||||
|
||||
|
||||
Up to 4 analog samples can be recorded by the hardware (``A0`` ... ``A4``), and all
|
||||
Up to 4 analog samples can be recorded by the hardware (``A0`` ... ``A3``), and all
|
||||
recording is done at 16-bit levels (but be aware that the ADC in the Pico will only
|
||||
ever return values between 0...4095).
|
||||
|
||||
@@ -29,7 +29,7 @@ ADC Input API
|
||||
ADCInput(pin0 [, pin1, pin2, pin3])
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Creates an ADC input object which will record the pins specified in the code.
|
||||
Only pins ``A0`` ... ``A4`` can be used, and they must be specified in increasing
|
||||
Only pins ``A0`` ... ``A3`` can be used, and they must be specified in increasing
|
||||
order (i.e. ``ADCInput(A0, A1);`` is valid, but ``ADCInput(A1, A0)`` is not.
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords)
|
||||
@@ -47,7 +47,7 @@ Adjusts the pin to record. Only legal before ``ADCInput::begin()``.
|
||||
bool setFrequency(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the ADC sampling frequency, but does not start recording (however if the
|
||||
device was already running, it will wontinue to run at the new frequency). Note
|
||||
device was already running, it will continue to run at the new frequency). Note
|
||||
that every pin requested will be sampled at this frequency, one after the other.
|
||||
That is, if you have code like this:
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
Bluetooth on PicoW Support
|
||||
==========================
|
||||
|
||||
As of the Pico-SDK version 1.5.0, the PicoW has **BETA** Bluetooth support.
|
||||
So, since this core builds off the SDK the best that can be suggested it
|
||||
that we have **ALPHA** Bluetooth support. As such, bug reports are welcome,
|
||||
but Pull Requests fixing problems you find are seriously appreciated.
|
||||
|
||||
Enabling Bluetooth
|
||||
------------------
|
||||
To enable Bluetooth (BT), use the ``Tools->IP/Bluetooth Stack`` menu. It
|
||||
requires around 80KB of flash and 20KB of RAM when enabled.
|
||||
|
||||
Both Bluetooth Classic and BluetoothBLE are enabled in ``btstack_config.h``.
|
||||
|
||||
Included Bluetooth Libraries
|
||||
----------------------------
|
||||
You may use the ``KeyboardBT``, ``MouseBT``, or ``JoystickBT`` to emulate a
|
||||
Bluetooth Classic HID device using the same API as their USB versions.
|
||||
|
||||
You may use the ``KeyboardBLE``, ``MouseBLE``, or ``JoystickBLE`` to emulate a
|
||||
Bluetooth Low Energy (BLE) HID device using the same API as their USB versions.
|
||||
|
||||
The ``SerialBT`` library implements a very simple SPP (Serial Port Profile)
|
||||
Serial-compatible port.
|
||||
|
||||
Writing Custom Bluetooth Applications
|
||||
-------------------------------------
|
||||
You may also write full applications using the ``BTStack`` standard callback
|
||||
method, but please be aware that the Raspberry Pi team has built an
|
||||
interrupt-driven version of the BT execute loop, so there is no need
|
||||
to actually call ``btstack_run_loop_execute`` because the ``async_context``
|
||||
handler will do it for you.
|
||||
|
||||
There is no need to call ``cyw43_arch_init`` in your code, either, as that
|
||||
is part of the PicoW variant booting process.
|
||||
|
||||
For many BTStack examples, you simply need call the included
|
||||
``btstack_main()`` and make sure that ``hci_power_control(HCI_POWER_ON);`` is
|
||||
called afterwards to start processing (in the background).
|
||||
|
||||
You will also need to acquire the BT ``async_context`` system lock before
|
||||
calling any BTStack APIs. See the ``libraries/PicoBluetoothHID`` helper
|
||||
class for an example of how to do this.
|
||||
|
||||
Note that if you need to modify the system ``btstack_config.h`` file, do so
|
||||
in the ``tools/libpico`` directory and rebuild the Pico SDK static library.
|
||||
Otherwise the change will not take effect in the precompiled code, leading
|
||||
to really bad behavior.
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'2.7.3'
|
||||
version = u'3.5.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.7.3'
|
||||
release = u'3.5.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
+3
-2
@@ -10,7 +10,8 @@ Contributing to the Core (Pull Requests)
|
||||
----------------------------------------
|
||||
|
||||
We use the standard GitHub Pull Request model. If you're unfamiliar with it,
|
||||
this `guide <https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/>` gives a simple overview of the process.
|
||||
this `guide <https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/>`__
|
||||
gives a simple overview of the process.
|
||||
|
||||
All pull requests have to pass a set of Continuous Integration (CI) checks
|
||||
which help make sure the code compiles under different configurations and has
|
||||
@@ -84,7 +85,7 @@ code that only runs on this core, use the following define.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#if defined(ARDUINO_ARCH_RP2040)
|
||||
#if defined(ARDUINO_ARCH_RP2040) && !defined(__MBED__)
|
||||
~~~ your changes ~~~
|
||||
#endif
|
||||
|
||||
|
||||
+2
-2
@@ -325,8 +325,8 @@ info64
|
||||
.. code:: cpp
|
||||
|
||||
FSInfo64 fsinfo;
|
||||
SDFS.info(fsinfo);
|
||||
or LittleFS(fsinfo);
|
||||
SDFS.info64(fsinfo);
|
||||
or LittleFS.info64(fsinfo);
|
||||
|
||||
Performs the same operation as ``info`` but allows for reporting greater than
|
||||
4GB for filesystem size/used/etc. Should be used with the SD and SDFS
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
HTTPClient Library
|
||||
==================
|
||||
|
||||
A simple HTTP requestor that can handle both HTTP and HTTP requests is
|
||||
A simple HTTP requester that can handle both HTTP and HTTPS requests is
|
||||
included as the ``HTTPClient`` library.
|
||||
|
||||
Check the examples for use under HTTP and HTTPS configurations. In general,
|
||||
|
||||
+39
-1
@@ -40,6 +40,16 @@ bool setDATA(pin_size_t pin)
|
||||
Sets the DOUT or DIN pin of the I2S device. Any pin may be used.
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool setMCLK(pin_size_t pin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the MCLK pin of the I2S device and enables MCLK output. Any pin may be used.
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool setMCLKmult(int mult)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the sample rate to MCLK multiplier value. Only multiples of 64 are valid.
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool setBitsPerSample(int bits)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Specify how many bits per audio sample to read or write. Note that
|
||||
@@ -58,12 +68,35 @@ Sets the word clock frequency, but does not start the I2S device if not
|
||||
already running. May be called after ``I2S::begin()`` to change the
|
||||
sample rate on-the-fly.
|
||||
|
||||
bool setSysClk(int samplerate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Changes the PICO system clock to optimise for the desired samplerate.
|
||||
The clock changes to 147.6 MHz for samplerates that are a multiple of 8 kHz, and 135.6 MHz for multiples of 11.025 kHz.
|
||||
Note that using ``setSysClk()`` may affect the timing of other sysclk-dependent functions.
|
||||
Should be called before any I2S functions and any other sysclk dependent initialisations.
|
||||
|
||||
bool setLSBJFormat()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Enables LSB-J format for I2S output. In this mode the MSB comes out at the
|
||||
same time as the LRCLK changes, and not the normal 1-cycle delay. Useful for
|
||||
DAC chips like the PT8211.
|
||||
|
||||
bool setTDMFormat()
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Enabled TDM formatted multi-channel output. Be sure to set the number of channels to
|
||||
the expected value (8 normally) and the bits per sample to 32.
|
||||
|
||||
bool setTDMChannels(int channels)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the number of TDM channels between frame syncs. Generally should be set to 8.
|
||||
|
||||
bool swapClocks()
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Certain boards are hardwired with the WCLK before the BCLK, instead of the normal
|
||||
way around. This call swaps the WCLK and BCLK pins. Note that you still call
|
||||
``setBCLK(x)`` with ``x`` being the lowest pin ID (i.e. in swapClocks mode the
|
||||
``setBCLK`` call actually sets LRCLK).
|
||||
|
||||
bool begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the I2S device up with the given sample rate, or with the value set
|
||||
@@ -77,6 +110,11 @@ void flush()
|
||||
~~~~~~~~~~~~
|
||||
Waits until all the I2S buffers have been output.
|
||||
|
||||
void getOverUnderflow()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns a flag indicating if the I2S system ran our of data to send on output,
|
||||
or had to throw away data on input.
|
||||
|
||||
size_t write(uint8_t/int8_t/int16_t/int32_t)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a single sample of ``bitsPerSample`` to the buffer. It is up to the
|
||||
@@ -105,7 +143,7 @@ many bytes were actually written.
|
||||
|
||||
int availableForWrite()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of L/R samples that can be written without
|
||||
Returns the amount of bytes that can be written without
|
||||
potentially blocking.
|
||||
|
||||
int read()
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
@@ -43,6 +43,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
USB (Arduino and Adafruit_TinyUSB) <usb>
|
||||
Multicore Processing <multicore>
|
||||
|
||||
Bluetooth (Alpha/Beta) <bluetooth>
|
||||
|
||||
Single File USB Drive <singlefile>
|
||||
|
||||
FreeRTOS SMP (multicore) <freertos>
|
||||
|
||||
+23
-1
@@ -38,6 +38,22 @@ prevent the Arduino IDE from accessing. For Arduino IDE V2, override the filesys
|
||||
restriction using ``flatpak override --user --filesystem=host cc.arduino.IDE2`` . For
|
||||
For Arduino IDE < V2, use ``flatpak override --user --filesystem=host cc.arduino.arduinoide``.
|
||||
|
||||
Installing via Arduino CLI
|
||||
--------------------------
|
||||
To install using the Arduino command line tool (arduino-cli):
|
||||
|
||||
.. code:: bash
|
||||
|
||||
arduino-cli config add board_manager.additional_urls https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
|
||||
arduino-cli core update-index
|
||||
arduino-cli core install rp2040:rp2040
|
||||
|
||||
To list the supported boards:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
arduino-cli board listall | grep rp2040
|
||||
|
||||
Installing via GIT
|
||||
------------------
|
||||
To install via GIT (for latest and greatest versions):
|
||||
@@ -143,7 +159,13 @@ So for the first sketch you will need to rebuild (with the ``Upload Method->Pico
|
||||
|
||||
After the initial upload, as long as the running binary was built using the ``Picotool`` upload method, then the normal upload process should work.
|
||||
|
||||
Note that for Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
|
||||
Under MacOS, it may be necessary to install the USB support libraries from a command terminal before the ``Picotool`` upload method can be used:
|
||||
|
||||
.. code::
|
||||
|
||||
brew install libusb
|
||||
|
||||
For Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
|
||||
|
||||
.. code::
|
||||
|
||||
|
||||
+1
-1
@@ -10,5 +10,5 @@ In addition, it contains code from additional open source projects:
|
||||
* The `Pico-SDK <https://github.com/raspberrypi/pico-sdk>`_ and `Pico-Extras <https://github.com/raspberrypi/pico-extras>`_ are by Raspberry Pi (Trading) Ltd. and licensed under the BSD 3-Clause license.
|
||||
* `Arduino-Pico <https://github.com/earlephilhower/arduino-pico>`_ core files are licenses under the LGPL.
|
||||
* `LittleFS <https://github.com/ARMmbed/littlefs>`_ library written by ARM Limited and released under the `BSD 3-clause license <https://github.com/ARMmbed/littlefs/blob/master/LICENSE.md>`_ .
|
||||
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporatio and licensed under the MIT license.
|
||||
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporation and licensed under the MIT license.
|
||||
* Some filesystem code taken from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`_ and licensed under the LGPL.
|
||||
|
||||
+12
-12
@@ -90,7 +90,7 @@ Signing requires the generation of an RSA-2048 key (other bit lengths are suppor
|
||||
Automatic Signing
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable sigining verification with a given public key, and signs binaries as part of the standard build process using a given public key.
|
||||
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable signing verification with a given public key, and signs binaries as part of the standard build process using a given public key.
|
||||
|
||||
To enable this mode, just include `private.key` and `public.key` in the sketch `.ino` directory. The IDE will call a helper script (`tools/signing.py`) before the build begins to create a header to enable key validation using the given public key, and to actually do the signing after the build process, generating a `sketch.bin.signed` file. When OTA is enabled (ArduinoOTA, Web, or HTTP), the binary will automatically only accept signed updates.
|
||||
|
||||
@@ -140,11 +140,11 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using
|
||||
Compression
|
||||
-----------
|
||||
|
||||
The eboot bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent. For uploading compressed filesystems, the application must be built with `ATOMIC_FS_UPDATE` defined because, otherwise, eboot will not be involved in writing the filesystem.
|
||||
The bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent.
|
||||
|
||||
No changes to the application are required. The `Updater` class and `eboot` bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
|
||||
No changes to the application are required. The `Updater` class and bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
|
||||
|
||||
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compressino level). For example:
|
||||
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compression level). For example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
@@ -185,11 +185,11 @@ Uploading modules wirelessly from Arduino IDE is intended for the following typi
|
||||
|
||||
- Only if modules are accessible on the same network as the computer with the Arduino IDE.
|
||||
|
||||
- For all IDE uploads,m the Pico W and the computer must be connected to the same network.
|
||||
- For all IDE uploads, the Pico W and the computer must be connected to the same network.
|
||||
|
||||
To upload wirelessly from the IDE:
|
||||
|
||||
1. Build a sketch starts ``WiFi`` and includes the appropriare calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
|
||||
1. Build a sketch starts ``WiFi`` and includes the appropriate calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
|
||||
|
||||
2. Upload using standard USB connection the first time.
|
||||
|
||||
@@ -200,7 +200,7 @@ To upload wirelessly from the IDE:
|
||||
Password Protection
|
||||
-------------------
|
||||
|
||||
Protecting your OTA uploads with password is really straightforward. All you need to do, is to include the following statement in your code:
|
||||
Protecting your OTA uploads with a password is really straightforward. All you need to do, is to include the following statement in your code:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
@@ -208,11 +208,11 @@ Protecting your OTA uploads with password is really straightforward. All you nee
|
||||
|
||||
Where ``123`` is a sample password that you should replace with your own.
|
||||
|
||||
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This is including original simple ``123`` OTA password. Then attempt to upload sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see prompt for password.
|
||||
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This includes the original simple ``123`` OTA password. Then attempt to upload a sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see a prompt for password.
|
||||
|
||||
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in upload log.
|
||||
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in the upload log.
|
||||
|
||||
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
|
||||
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see a prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
|
||||
|
||||
Please note, it is possible to reveal password entered previously in Arduino IDE, if IDE has not been closed since last upload. This can be done by enabling *Show verbose output during: upload* in *File > Preferences* and attempting to upload the module.
|
||||
|
||||
@@ -289,7 +289,7 @@ Simple updater downloads the file every time the function is called.
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Its possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
|
||||
It's possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
|
||||
|
||||
The server-side script can respond as follows: - response code 200, and send the firmware image, - or response code 304 to notify Pico that no update is required.
|
||||
|
||||
@@ -305,7 +305,7 @@ The server-side script can respond as follows: - response code 200, and send the
|
||||
Serial.println("[update] Update no Update.");
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("[update] Update ok."); // may not be called since we reboot the RP2040
|
||||
Serial.println("[update] Update ok.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+85
-1
@@ -23,6 +23,46 @@ Especially useful is the `Getting started with VSCode + PlatformIO <https://docs
|
||||
|
||||
Hereafter it is assumed that you have a basic understanding of PlatformIO in regards to project creation, project file structure and building and uploading PlatformIO projects, through reading the above pages.
|
||||
|
||||
Important steps for Windows users, before installing
|
||||
----------------------------------------------------
|
||||
|
||||
By default, Windows has a limited path length that is not long enough to fully clone the ``Pico-SDK``'s ``tinyusb`` repository, resulting in error messages like the one below while attempting to fetch the repository.
|
||||
|
||||
.. code::
|
||||
|
||||
error: unable to create file '.....' : Filename too long
|
||||
|
||||
To work around this requires performing two steps and rebooting Windows once. These steps will enable longer file paths at the Windows OS and the ``git`` level.
|
||||
|
||||
Step 1: Enabling long paths in git
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Open up a Windows ``cmd`` or ``terminal`` window and execute the following command
|
||||
|
||||
.. code::
|
||||
|
||||
git config --system core.longpaths true
|
||||
|
||||
Step 2: Enabling long paths in the Windows OS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
(taken from https://www.microfocus.com/documentation/filr/filr-4/filr-desktop/t47bx2ogpfz7.html)
|
||||
|
||||
1. Click Window key and type gpedit.msc, then press the Enter key. This launches the Local Group Policy Editor.
|
||||
|
||||
2. Navigate to Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem.
|
||||
|
||||
3. Double click Enable NTFS/Win32 long paths and close the dialog.
|
||||
|
||||
.. image:: images/longpath.png
|
||||
|
||||
|
||||
Step 3: Reboot the computer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Once the two prior stages are complete, please do a full reboot or power cycle so that the new settings will take effect.
|
||||
|
||||
|
||||
Current state of development
|
||||
----------------------------
|
||||
|
||||
@@ -234,6 +274,17 @@ The lwIP stack can be configured to support only IPv4 (default) or additionally
|
||||
|
||||
to the ``platformio.ini``.
|
||||
|
||||
Bluetooth Stack
|
||||
---------------
|
||||
|
||||
The Bluetooth Classic (BTC) and Bluetooth Low Energy (BLE) stack can be activated by adding
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; BTC and BLE
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH
|
||||
|
||||
to the ``platformio.ini``.
|
||||
|
||||
Selecting a different core version
|
||||
----------------------------------
|
||||
@@ -290,10 +341,15 @@ To specify the debugging adapter, use ``debug_tool`` (`documentation <https://do
|
||||
* ``cmsis-dap``
|
||||
* ``jlink``
|
||||
* ``raspberrypi-swd``
|
||||
* ``blackmagic``
|
||||
* ``pico-debug``
|
||||
|
||||
These values can also be used in ``upload_protocol`` if you want PlatformIO to upload the regular firmware through this method, which you likely want.
|
||||
|
||||
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`_) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`_ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
|
||||
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`__) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`__ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
|
||||
|
||||
.. note::
|
||||
Newer PicoProbe firmware versions have dropped the proprietary "PicoProbe" USB communication protocol and emulate a **CMSIS-DAP** instead. Meaning, you have to use ``debug_tool = cmsis-dap`` for these newer firmwares, such as those obtained from `raspberrypi/picoprobe <https://github.com/raspberrypi/picoprobe/releases>`__
|
||||
|
||||
With that set up, debugging can be started via the left debugging sidebar and works nicely: Setup breakpoints, inspect the value of variables in the code, step through the code line by line. When a breakpoint is hit or execution is halted, you can even see the execution state both Cortex-M0+ cores of the RP2040.
|
||||
|
||||
@@ -301,6 +357,30 @@ With that set up, debugging can be started via the left debugging sidebar and wo
|
||||
|
||||
For further information on customizing debug options, like the initial breakpoint or debugging / SWD speed, consult `the documentation <https://docs.platformio.org/en/latest/projectconf/section_env_debug.html>`_.
|
||||
|
||||
.. note::
|
||||
For the BlackMagicProbe debugging probe (as can be e.g., created by simply flashing a STM32F103C8 "Bluepill" board), you currently have to use the branch ``fix/rp2040-flash-reliability`` (or at least commit ``1d001bc``) **and** use the `official ARM provided toolchain <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503393266>`_.
|
||||
|
||||
You can obtain precompiled binaries from `here <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503372723>`__. A flashing guide is available `here <https://primalcortex.wordpress.com/2017/06/13/building-a-black-magic-debug-probe/>`__. You then have to configure the target serial port ("GDB port") in your project per `documentation <https://docs.platformio.org/en/latest/plus/debug-tools/blackmagic.html#debugging-tool-blackmagic>`__.
|
||||
|
||||
.. note::
|
||||
For the pico-debug (`download <https://github.com/majbthrd/pico-debug/releases>`__) debugging way, *which needs to no additional debug probe*, add this snippet to your ``platformio.ini`` and follow the given procedure:
|
||||
|
||||
.. code:: ini
|
||||
|
||||
upload_protocol = pico-debug
|
||||
debug_tool = pico-debug
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_NO_USB
|
||||
|
||||
1. Build your firmware normally
|
||||
2. Plug in the Pico in BOOTSEL mode
|
||||
3. Drag and drop your ``.pio/build/<env>/firmware.uf2`` onto the boot drive
|
||||
4. Unplug and replug your Pico back into BOOTSEL mode for the second time
|
||||
5. Drag and drop the downloaded ``pico-debug-gimmecache.uf2`` file onto the boot drive
|
||||
6. A CMSIS-DAP device should now appear on your computer
|
||||
7. Start debugging via the debug sidebar as normal
|
||||
|
||||
Note the restrictions: The second core cannot be used, the USB port cannot be used (no USB serial, only UART serial), 16KB less RAM is available.
|
||||
|
||||
Filesystem Uploading
|
||||
--------------------
|
||||
|
||||
@@ -315,3 +395,7 @@ The files you want to upload should be placed in a folder called ``data`` inside
|
||||
The task "Build Filesystem Image" will take all files in the data directory and create a ``littlefs.bin`` file from it using the ``mklittlefs`` tool.
|
||||
|
||||
The task "Upload Filesystem Image" will upload the filesystem image to the Pico via the specified ``upload_protocol``.
|
||||
|
||||
.. note::
|
||||
Set the space available for the filesystem in the ``platformio.ini`` using e.g., ``board_build.filesystem_size = 0.5m``, or filesystem
|
||||
creation will fail!
|
||||
+9
-2
@@ -50,6 +50,9 @@ Enables the hardware watchdog timer with a delay value of delay_ms
|
||||
milliseconds. Note that on the RP2040, once this function has called, the
|
||||
hardware watchdog can _not_ be disabled.
|
||||
|
||||
The maximum ``delay_ms`` allowed in this call is ``8300``, corresponding
|
||||
to 8.3 seconds. Any higher values will be truncated by the hardware.
|
||||
|
||||
void rp2040.wdt_reset()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reloads the watchdog's counter with the amount of time set by wdt_begin.
|
||||
@@ -78,8 +81,8 @@ overhead).
|
||||
Hardware Identification
|
||||
-----------------------
|
||||
|
||||
bool isPicoW()
|
||||
~~~~~~~~~~~~~~
|
||||
bool rp2040.isPicoW()
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the core's best guess if this code is running on a Raspberry Pi Pico W.
|
||||
This would let you, possibly, use the same UF2 for Pico and PicoW by simply not
|
||||
doing any WiFi calls.
|
||||
@@ -92,3 +95,7 @@ void rp2040.enableDoubleResetBootloader()
|
||||
Add a call anywhere in the sketch to ``rp2040.enableDoubleResetBootloader()`` and
|
||||
the core will check for a double-tap on reset, and if found will start the USB
|
||||
bootloader.
|
||||
|
||||
void rp2040.rebootToBootloader()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Will reboot the RP2040 into USB UF2 upload mode.
|
||||
|
||||
@@ -45,3 +45,13 @@ For applications where an IRQ driven serial port is not appropriate, use
|
||||
|
||||
For detailed information about the Serial ports, see the
|
||||
Arduino `Serial Reference <https://www.arduino.cc/reference/en/language/functions/communication/serial/>`_ .
|
||||
|
||||
|
||||
RP2040 Specific SerialUSB methods
|
||||
---------------------------------
|
||||
|
||||
void Serial.ignoreFlowControl(bool ignore)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
In some cases, the target application will not assert the DTR virtual line, thus preventing writing operations to succeed.
|
||||
|
||||
For this reason, the SerialUSB::ignoreFlowControl() method disables the connection's state verification, enabling the program to write on the port, even though the data might be lost.
|
||||
|
||||
+2
-2
@@ -40,9 +40,9 @@ Please note that WiFi on the Pico W is a work-in-progress and there are some imp
|
||||
|
||||
* Combined STA/AP mode is not supported
|
||||
|
||||
* Multicore is supported, but only one core may run ``WiFi`` code.
|
||||
* Multicore is supported, but only core 0 may run ``WiFi`` related code.
|
||||
|
||||
* FreeRTOS is not yet supported due to the requirement for a very different LWIP implementation. PRs always appreciated!
|
||||
* FreeRTOS is supported only on core 0 and from within ``setup`` and ``loop`, not tasks, due to the requirement for a very different LWIP implementation. PRs always appreciated!
|
||||
|
||||
The WiFi library borrows much work from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`__ , especially the ``WiFiClient`` and ``WiFiServer`` classes.
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
// BTstack features that can be enabled
|
||||
#define ENABLE_LOG_INFO
|
||||
#define ENABLE_LOG_ERROR
|
||||
#define ENABLE_PRINTF_HEXDUMP
|
||||
#define ENABLE_SCO_OVER_HCI
|
||||
|
||||
#ifdef ENABLE_CLASSIC
|
||||
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BLE
|
||||
#define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
|
||||
#define ENABLE_LE_PERIPHERAL
|
||||
#define ENABLE_LE_CENTRAL
|
||||
#endif
|
||||
|
||||
// BTstack configuration. buffers, sizes, ...
|
||||
#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
|
||||
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
|
||||
#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
|
||||
#define MAX_NR_AVDTP_CONNECTIONS 1
|
||||
#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
|
||||
#define MAX_NR_AVRCP_CONNECTIONS 2
|
||||
#define MAX_NR_BNEP_CHANNELS 1
|
||||
#define MAX_NR_BNEP_SERVICES 1
|
||||
#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
|
||||
#define MAX_NR_GATT_CLIENTS 1
|
||||
#define MAX_NR_HCI_CONNECTIONS 2
|
||||
#define MAX_NR_HID_HOST_CONNECTIONS 1
|
||||
#define MAX_NR_HIDS_CLIENTS 1
|
||||
#define MAX_NR_HFP_CONNECTIONS 1
|
||||
#define MAX_NR_L2CAP_CHANNELS 4
|
||||
#define MAX_NR_L2CAP_SERVICES 3
|
||||
#define MAX_NR_RFCOMM_CHANNELS 1
|
||||
#define MAX_NR_RFCOMM_MULTIPLEXERS 1
|
||||
#define MAX_NR_RFCOMM_SERVICES 1
|
||||
#define MAX_NR_SERVICE_RECORD_ITEMS 4
|
||||
#define MAX_NR_SM_LOOKUP_ENTRIES 3
|
||||
#define MAX_NR_WHITELIST_ENTRIES 16
|
||||
#define MAX_NR_LE_DEVICE_DB_ENTRIES 16
|
||||
|
||||
// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
|
||||
#define MAX_NR_CONTROLLER_ACL_BUFFERS 3
|
||||
#define MAX_NR_CONTROLLER_SCO_PACKETS 3
|
||||
|
||||
// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
|
||||
#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
|
||||
#define HCI_HOST_ACL_PACKET_LEN 1024
|
||||
#define HCI_HOST_ACL_PACKET_NUM 3
|
||||
#define HCI_HOST_SCO_PACKET_LEN 120
|
||||
#define HCI_HOST_SCO_PACKET_NUM 3
|
||||
|
||||
// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
|
||||
#define NVM_NUM_DEVICE_DB_ENTRIES 16
|
||||
#define NVM_NUM_LINK_KEYS 16
|
||||
|
||||
// We don't give btstack a malloc, so use a fixed-size ATT DB.
|
||||
#define MAX_ATT_DB_SIZE 512
|
||||
|
||||
// BTstack HAL configuration
|
||||
#define HAVE_EMBEDDED_TIME_MS
|
||||
|
||||
// map btstack_assert onto Pico SDK assert()
|
||||
#define HAVE_ASSERT
|
||||
|
||||
// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
|
||||
#define HCI_RESET_RESEND_TIMEOUT_MS 1000
|
||||
|
||||
#define ENABLE_SOFTWARE_AES128
|
||||
#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
|
||||
|
||||
#undef HAVE_BTSTACK_STDIN
|
||||
+7
-1
@@ -13,6 +13,9 @@ extern void interrupts();
|
||||
#define SYS_ARCH_PROTECT(lev) noInterrupts
|
||||
#define SYS_ARCH_UNPROTECT(lev) interrupts
|
||||
|
||||
extern unsigned long get_rand_32(void);
|
||||
#define LWIP_RAND() get_rand_32()
|
||||
|
||||
// Common settings used in most of the pico_w examples
|
||||
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
|
||||
|
||||
@@ -25,7 +28,7 @@ extern void interrupts();
|
||||
#define MEMP_NUM_TCP_SEG 32
|
||||
#define MEMP_NUM_ARP_QUEUE 10
|
||||
#define PBUF_POOL_SIZE 24
|
||||
#define LWIP_ARP 1
|
||||
#define LWIP_ARP 2
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_ICMP 1
|
||||
#define LWIP_RAW 1
|
||||
@@ -53,6 +56,9 @@ extern void interrupts();
|
||||
#define DHCP_DOES_ARP_CHECK 0
|
||||
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||
|
||||
// See #1285
|
||||
#define MEMP_NUM_UDP_PCB 6
|
||||
|
||||
#if LWIP_IPV6
|
||||
#define LWIP_IPV6_DHCP6 1
|
||||
#endif
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#define _PICO_VERSION_H
|
||||
|
||||
#define PICO_SDK_VERSION_MAJOR 1
|
||||
#define PICO_SDK_VERSION_MINOR 4
|
||||
#define PICO_SDK_VERSION_REVISION 0
|
||||
#define PICO_SDK_VERSION_STRING "1.4.0"
|
||||
#define PICO_SDK_VERSION_MINOR 5
|
||||
#define PICO_SDK_VERSION_REVISION 1
|
||||
#define PICO_SDK_VERSION_STRING "1.5.1"
|
||||
|
||||
#endif
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -94,7 +94,9 @@ SECTIONS
|
||||
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
|
||||
* FLASH ... we will include any thing excluded here in .data below by default */
|
||||
*(.init)
|
||||
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
|
||||
/* Some of these excludes required for PicoDVI library, won't affect most code */
|
||||
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.c.obj *divider.S.obj *PicoDVI.cpp.o *dvi.c.o) .text*)
|
||||
|
||||
*(.fini)
|
||||
/* Pull all c'tors into .text */
|
||||
*crtbegin.o(.ctors)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
source [find interface/picoprobe.cfg]
|
||||
source [find target/rp2040.cfg]
|
||||
@@ -1,2 +1,3 @@
|
||||
source [find interface/cmsis-dap.cfg]
|
||||
adapter speed 5000
|
||||
source [find target/rp2040.cfg]
|
||||
|
||||
@@ -40,8 +40,11 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_vreg/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_watchdog/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_xosc/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_async_context/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_bootrom/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_btstack/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_arch/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_driver/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_double/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_float/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_int64_ops/include
|
||||
@@ -50,8 +53,11 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_platform/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_printf/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_runtime/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_rand/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio_uart/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_unique_id/include
|
||||
-iwithprefixbefore/pico-sdk/lib/cyw43-driver/src
|
||||
-iwithprefixbefore/pico-sdk/lib/lwip/src/include
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/src
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/platform/embedded
|
||||
|
||||
@@ -200,3 +200,14 @@
|
||||
-Wl,--wrap=raw_bind
|
||||
-Wl,--wrap=raw_sendto
|
||||
-Wl,--wrap=raw_remove
|
||||
|
||||
-Wl,--wrap=cyw43_cb_process_ethernet
|
||||
-Wl,--wrap=cyw43_cb_tcpip_set_link_up
|
||||
-Wl,--wrap=cyw43_cb_tcpip_set_link_down
|
||||
-Wl,--wrap=cyw43_tcpip_link_status
|
||||
-Wl,--wrap=cyw43_cb_tcpip_init
|
||||
-Wl,--wrap=cyw43_cb_tcpip_deinit
|
||||
|
||||
-Wl,--wrap=get_rand_64
|
||||
-Wl,--wrap=get_rand_32
|
||||
-Wl,--wrap=sleep_until
|
||||
|
||||
@@ -79,6 +79,10 @@ void ADCInput::onReceive(void(*fn)(void)) {
|
||||
}
|
||||
|
||||
bool ADCInput::begin() {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_running = true;
|
||||
|
||||
_isHolding = 0;
|
||||
@@ -105,7 +109,11 @@ bool ADCInput::begin() {
|
||||
setFrequency(_freq);
|
||||
|
||||
_arb = new AudioBufferManager(_buffers, _bufferWords, 0, INPUT, DMA_SIZE_16);
|
||||
_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo);
|
||||
if (!_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo)) {
|
||||
delete _arb;
|
||||
_arb = nullptr;
|
||||
return false;
|
||||
}
|
||||
_arb->setCallback(_cb);
|
||||
|
||||
adc_fifo_drain();
|
||||
|
||||
Submodule libraries/Adafruit_TinyUSB_Arduino updated: e1e922d2ca...b17ab37a0e
@@ -20,8 +20,8 @@
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/irq.h>
|
||||
#include "AudioBufferManager.h"
|
||||
|
||||
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
|
||||
@@ -73,13 +73,11 @@ AudioBufferManager::~AudioBufferManager() {
|
||||
if (_running) {
|
||||
_running = false;
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
dma_channel_set_irq0_enabled(_channelDMA[i], false);
|
||||
dma_channel_cleanup(_channelDMA[i]);
|
||||
__channelMap[_channelDMA[i]] = nullptr;
|
||||
dma_channel_abort(_channelDMA[i]);
|
||||
dma_channel_unclaim(_channelDMA[i]);
|
||||
dma_channel_acknowledge_irq0(_channelDMA[i]);
|
||||
__channelCount--;
|
||||
}
|
||||
__channelCount--;
|
||||
if (!__channelCount) {
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
// TODO - how can we know if there are no other parts of the core using DMA0 IRQ??
|
||||
@@ -114,7 +112,7 @@ bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) {
|
||||
|
||||
// Get ping and pong DMA channels
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
_channelDMA[i] = dma_claim_unused_channel(true);
|
||||
_channelDMA[i] = dma_claim_unused_channel(false);
|
||||
if (_channelDMA[i] == -1) {
|
||||
if (i == 1) {
|
||||
dma_channel_unclaim(_channelDMA[0]);
|
||||
@@ -257,7 +255,7 @@ void __not_in_flash_func(AudioBufferManager::_dmaIRQ)(int channel) {
|
||||
_active[1] = _takeFromList(&_filled);
|
||||
}
|
||||
_overunderflow = _overunderflow | (_active[1] == _silence);
|
||||
dma_channel_set_read_addr(channel, _active[0]->buff, false);
|
||||
dma_channel_set_read_addr(channel, _active[1]->buff, false);
|
||||
} else {
|
||||
if (_empty) {
|
||||
_addToList(&_filled, _active[0]);
|
||||
@@ -266,7 +264,7 @@ void __not_in_flash_func(AudioBufferManager::_dmaIRQ)(int channel) {
|
||||
} else {
|
||||
_overunderflow = true;
|
||||
}
|
||||
dma_channel_set_write_addr(channel, _active[0]->buff, false);
|
||||
dma_channel_set_write_addr(channel, _active[1]->buff, false);
|
||||
}
|
||||
dma_channel_set_trans_count(channel, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
|
||||
dma_channel_acknowledge_irq0(channel);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "hardware/dma.h"
|
||||
#include <hardware/dma.h>
|
||||
|
||||
class AudioBufferManager {
|
||||
public:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
This is the BTstack ports/arduino library, with compile errors fixed only.
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <BTstackLib.h>
|
||||
#include <stdio.h>
|
||||
#include "ble/att_server.h"
|
||||
#include "ble/gatt_client.h"
|
||||
#include "ble/gatt-service/ancs_client.h"
|
||||
#include "ble/sm.h"
|
||||
#include "btstack_event.h"
|
||||
#include <SPI.h>
|
||||
|
||||
/*
|
||||
EXAMPLE_START(ANCS): ANCS Client
|
||||
*/
|
||||
|
||||
/*
|
||||
@section Advertisement
|
||||
@text An ANCS Client needs to include the ANCS UUID in its advertisement to
|
||||
get recognized by iOS
|
||||
*/
|
||||
|
||||
/* LISTING_START(ANCSAdvertisement): ANCS Advertisement */
|
||||
const uint8_t adv_data[] = {
|
||||
// Flags general discoverable
|
||||
0x02, 0x01, 0x02,
|
||||
// Name
|
||||
0x05, 0x09, 'A', 'N', 'C', 'S',
|
||||
// Service Solicitation, 128-bit UUIDs - ANCS (little endian)
|
||||
0x11, 0x15, 0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79
|
||||
};
|
||||
/* LISTING_END(ANCSAdvertisement): ANCS Advertisement */
|
||||
|
||||
/*
|
||||
@section Setup
|
||||
|
||||
@text In the setup, the LE Security Manager is configured to accept pairing requests.
|
||||
Then, the ANCS Client library is initialized and and ancs_callback registered.
|
||||
Finally, the Advertisement data is set and Advertisements are started.
|
||||
*/
|
||||
|
||||
/* LISTING_START(ANCSSetup): ANCS Setup */
|
||||
void setup(void) {
|
||||
|
||||
Serial.begin(9600);
|
||||
Serial.println("BTstack ANCS Client starting up...");
|
||||
|
||||
// startup BTstack and configure log_info/log_error
|
||||
BTstack.setup();
|
||||
|
||||
sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
|
||||
sm_set_authentication_requirements(SM_AUTHREQ_BONDING);
|
||||
|
||||
// setup ANCS Client
|
||||
ancs_client_init();
|
||||
ancs_client_register_callback(&ancs_callback);
|
||||
|
||||
// enable advertisements
|
||||
BTstack.setAdvData(sizeof(adv_data), adv_data);
|
||||
BTstack.startAdvertising();
|
||||
}
|
||||
/* LISTING_END(ANCSSetup): ANCS Setup */
|
||||
|
||||
void loop(void) {
|
||||
BTstack.loop();
|
||||
}
|
||||
|
||||
/*
|
||||
@section ANCS Callback
|
||||
@text In the ANCS Callback, connect and disconnect events are received.
|
||||
For actual notifications, ancs_client_attribute_name_for_id allows to
|
||||
look up the name. To get the notification body, e.g., the actual message,
|
||||
the GATT Client needs to be used directly.
|
||||
*/
|
||||
|
||||
|
||||
/* LISTING_START(ANCSCallback): ANCS Callback */
|
||||
void ancs_callback(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
||||
(void) packet_type;
|
||||
(void) channel;
|
||||
(void) size;
|
||||
const char * attribute_name;
|
||||
if (hci_event_packet_get_type(packet) != HCI_EVENT_ANCS_META) {
|
||||
return;
|
||||
}
|
||||
switch (hci_event_ancs_meta_get_subevent_code(packet)) {
|
||||
case ANCS_SUBEVENT_CLIENT_CONNECTED:
|
||||
Serial.println("ANCS Client: Connected");
|
||||
break;
|
||||
case ANCS_SUBEVENT_CLIENT_DISCONNECTED:
|
||||
Serial.println("ANCS Client: Disconnected");
|
||||
break;
|
||||
case ANCS_SUBEVENT_CLIENT_NOTIFICATION:
|
||||
attribute_name = ancs_client_attribute_name_for_id(ancs_subevent_client_notification_get_attribute_id(packet));
|
||||
if (!attribute_name) {
|
||||
break;
|
||||
}
|
||||
Serial.print("Notification: ");
|
||||
Serial.print(attribute_name);
|
||||
Serial.print(" - ");
|
||||
Serial.println(ancs_subevent_client_notification_get_text(packet));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* LISTING_END(ANCSCallback): ANCS Callback */
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
#include <BTstackLib.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/*
|
||||
EXAMPLE_START(LECentral): LE Central
|
||||
|
||||
@text Compared with the other examples, the LE Central is
|
||||
a bit more complex. This is because it performs multiple
|
||||
steps in sequence as it is common with GATT Client APIs.
|
||||
|
||||
It shows how to first scan for other
|
||||
devices and then connect to one. When connected, a series of
|
||||
GATT Client operations are performed: first the list of
|
||||
GATT Services is queried. If a particular service is found,
|
||||
the list of its GATT Characteristics is retrieved and a set
|
||||
of known Characteristics are cached for later access.
|
||||
*/
|
||||
|
||||
/*
|
||||
@section Characteristic Summary
|
||||
@text As multiple Characteristics need to be found, a custom
|
||||
struct is used to collect all information about it. This allows
|
||||
to define the list of necessary characteristics in the
|
||||
characteristics[] array
|
||||
*/
|
||||
/* LISTING_START(LECentralSummary): Characteristic Summary */
|
||||
|
||||
// BLE Shield Service V2 incl. used Characteristics
|
||||
UUID bleShieldServiceV2UUID("B8E06067-62AD-41BA-9231-206AE80AB550");
|
||||
|
||||
typedef struct characteristic_summary {
|
||||
UUID uuid;
|
||||
const char * name;
|
||||
bool found;
|
||||
BLECharacteristic characteristic;
|
||||
} characteristic_summary_t;
|
||||
|
||||
typedef enum characteristicIDs {
|
||||
charRX = 0,
|
||||
charTX,
|
||||
charBaud,
|
||||
charBdAddr,
|
||||
numCharacteristics /* last one */
|
||||
} characteristicIDs_t;
|
||||
|
||||
characteristic_summary characteristics[] = {
|
||||
{ UUID("f897177b-aee8-4767-8ecc-cc694fd5fcee"), "RX", false, BLECharacteristic() },
|
||||
{ UUID("bf45e40a-de2a-4bc8-bba0-e5d6065f1b4b"), "TX", false, BLECharacteristic() },
|
||||
{ UUID("2fbc0f31-726a-4014-b9fe-c8be0652e982"), "Baudrate", false, BLECharacteristic() },
|
||||
{ UUID("65c228da-bad1-4f41-b55f-3d177f4e2196"), "BD ADDR", false, BLECharacteristic() }
|
||||
};
|
||||
|
||||
/* LISTING_END(LECentralSummary): Characteristic Summary */
|
||||
|
||||
// Application state
|
||||
BLEDevice myBLEDevice;
|
||||
BLEService myBLEService;
|
||||
bool serviceFound;
|
||||
bool sendCounter = false;
|
||||
|
||||
int counter = 0;
|
||||
char counterString[20];
|
||||
|
||||
// static btstack_timer_source_t heartbeat;
|
||||
|
||||
/*
|
||||
@section Setup
|
||||
@text In the setup, various callbacks are registered. After that
|
||||
we start scanning for other devices
|
||||
*/
|
||||
/* LISTING_START(LECentralSetup): LE Central Setup */
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
BTstack.setBLEAdvertisementCallback(advertisementCallback);
|
||||
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
|
||||
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
|
||||
BTstack.setGATTServiceDiscoveredCallback(gattServiceDiscovered);
|
||||
BTstack.setGATTCharacteristicDiscoveredCallback(gattCharacteristicDiscovered);
|
||||
BTstack.setGATTCharacteristicNotificationCallback(gattCharacteristicNotification);
|
||||
BTstack.setGATTCharacteristicReadCallback(gattReadCallback);
|
||||
BTstack.setGATTCharacteristicWrittenCallback(gattWrittenCallback);
|
||||
BTstack.setGATTCharacteristicSubscribedCallback(gattSubscribedCallback);
|
||||
BTstack.setup();
|
||||
BTstack.bleStartScanning();
|
||||
}
|
||||
/* LISTING_END(LECentralSetup): LE Central Setup */
|
||||
|
||||
/*
|
||||
@section Loop
|
||||
|
||||
@text In the standard Arduino loop() function, BTstack's loop() is called first
|
||||
If we're connected, we send the string "BTstack" plus a counter as fast as possible.
|
||||
As the Bluetooth module might be busy, it's important to check the result of the
|
||||
writeCharacteristicWithoutResponse() call. If it's not ok, we just try again in the
|
||||
next loop iteration.
|
||||
*/
|
||||
/* LISTING_START(LECentralLoop): Loop */
|
||||
void loop(void) {
|
||||
BTstack.loop();
|
||||
|
||||
// send counter as fast as possible
|
||||
if (sendCounter) {
|
||||
sprintf(counterString, "BTstack %u\n", counter);
|
||||
int result = myBLEDevice.writeCharacteristicWithoutResponse(&characteristics[charTX].characteristic, (uint8_t*) counterString, strlen(counterString));
|
||||
if (result == 0) {
|
||||
Serial.print("Wrote without response: ");
|
||||
Serial.println(counterString);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* LISTING_END(LECentralLoop): Loop */
|
||||
|
||||
/*
|
||||
@section Advertisement Callback
|
||||
|
||||
@text When an Advertisement is received, we check if it contains
|
||||
the UUID of the service we're interested in. Only a single service
|
||||
with a 128-bit UUID can be contained in and Advertisement and not
|
||||
all BLE devices provides this. Other options are to match on the
|
||||
reported device name or the BD ADDR prefix.
|
||||
|
||||
If we found an interesting device, we try to connect to it.
|
||||
*/
|
||||
/* LISTING_START(LECentralAdvertisementCallback): Advertisement Callback */
|
||||
void advertisementCallback(BLEAdvertisement *bleAdvertisement) {
|
||||
Serial.print("Device discovered: ");
|
||||
Serial.print(bleAdvertisement->getBdAddr()->getAddressString());
|
||||
Serial.print(", RSSI: ");
|
||||
Serial.println(bleAdvertisement->getRssi());
|
||||
if (bleAdvertisement->containsService(&bleShieldServiceV2UUID)) {
|
||||
Serial.println("\nBLE ShieldService V2 found!\n");
|
||||
BTstack.bleStopScanning();
|
||||
BTstack.bleConnect(bleAdvertisement, 10000); // 10 s
|
||||
}
|
||||
}
|
||||
/* LISTING_END(LECentralAdvertisementCallback): Advertisement Callback */
|
||||
|
||||
/*
|
||||
@section Device Connected Callback
|
||||
|
||||
@text At the end of bleConnect(), the device connected callback is callec.
|
||||
The status argument tells if the connection timed out, or if the connection
|
||||
was established successfully.
|
||||
|
||||
On a successful connection, a GATT Service Discovery is started.
|
||||
*/
|
||||
/* LISTING_START(LECentralDeviceConnectedCallback): Device Connected Callback */
|
||||
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
|
||||
switch (status) {
|
||||
case BLE_STATUS_OK:
|
||||
Serial.println("Device connected!");
|
||||
myBLEDevice = *device;
|
||||
counter = 0;
|
||||
myBLEDevice.discoverGATTServices();
|
||||
break;
|
||||
case BLE_STATUS_CONNECTION_TIMEOUT:
|
||||
Serial.println("Error while Connecting the Peripheral");
|
||||
BTstack.bleStartScanning();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* LISTING_END(LECentralDeviceConnectedCallback): Device Connected Callback */
|
||||
|
||||
/*
|
||||
@section Device Disconnected Callback
|
||||
|
||||
@text If the connection to a device breaks, the device disconnected callback
|
||||
is called. Here, we start scanning for new devices again.
|
||||
*/
|
||||
/* LISTING_START(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
|
||||
void deviceDisconnectedCallback(BLEDevice * device) {
|
||||
(void) device;
|
||||
Serial.println("Disconnected, starting over..");
|
||||
sendCounter = false;
|
||||
BTstack.bleStartScanning();
|
||||
}
|
||||
/* LISTING_END(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
|
||||
|
||||
/*
|
||||
@section Service Discovered Callback
|
||||
|
||||
@text The service discovered callback is called for each service and after the
|
||||
service discovery is complete. The status argument is provided for this.
|
||||
|
||||
The main information about a discovered Service is its UUID.
|
||||
If we find our service, we store the reference to this service.
|
||||
This allows to discover the Characteristics for our service after
|
||||
the service discovery is complete.
|
||||
*/
|
||||
/* LISTING_START(LECentralServiceDiscoveredCallback): Service Discovered Callback */
|
||||
void gattServiceDiscovered(BLEStatus status, BLEDevice *device, BLEService *bleService) {
|
||||
switch (status) {
|
||||
case BLE_STATUS_OK:
|
||||
Serial.print("Service Discovered: :");
|
||||
Serial.println(bleService->getUUID()->getUuidString());
|
||||
if (bleService->matches(&bleShieldServiceV2UUID)) {
|
||||
serviceFound = true;
|
||||
Serial.println("Our service located!");
|
||||
myBLEService = *bleService;
|
||||
}
|
||||
break;
|
||||
case BLE_STATUS_DONE:
|
||||
Serial.println("Service discovery finished");
|
||||
if (serviceFound) {
|
||||
device->discoverCharacteristicsForService(&myBLEService);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Serial.println("Service discovery error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* LISTING_END(LECentralServiceDiscoveredCallback): Service Discovered Callback */
|
||||
|
||||
/*
|
||||
@section Characteristic Discovered Callback
|
||||
|
||||
@text Similar to the Service Discovered callback, the Characteristic Discovered
|
||||
callback is called for each Characteristic found and after the discovery is complete.
|
||||
|
||||
The main information is again its UUID. If we find a Characteristic that we're
|
||||
interested in, it's name is printed and a reference stored for later.
|
||||
|
||||
On discovery complete, we subscribe to a particular Characteristic to receive
|
||||
Characteristic Value updates in the Notificaation Callback.
|
||||
*/
|
||||
/* LISTING_START(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
|
||||
void gattCharacteristicDiscovered(BLEStatus status, BLEDevice *device, BLECharacteristic *characteristic) {
|
||||
switch (status) {
|
||||
case BLE_STATUS_OK:
|
||||
Serial.print("Characteristic Discovered: ");
|
||||
Serial.print(characteristic->getUUID()->getUuidString());
|
||||
Serial.print(", handle 0x");
|
||||
Serial.println(characteristic->getCharacteristic()->value_handle, HEX);
|
||||
int i;
|
||||
for (i = 0; i < numCharacteristics; i++) {
|
||||
if (characteristic->matches(&characteristics[i].uuid)) {
|
||||
Serial.print("Characteristic found: ");
|
||||
Serial.println(characteristics[i].name);
|
||||
characteristics[i].found = 1;
|
||||
characteristics[i].characteristic = *characteristic;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BLE_STATUS_DONE:
|
||||
Serial.print("Characteristic discovery finished, status ");
|
||||
Serial.println(status, HEX);
|
||||
if (characteristics[charRX].found) {
|
||||
device->subscribeForNotifications(&characteristics[charRX].characteristic);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Serial.println("Characteristics discovery error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* LISTING_END(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
|
||||
|
||||
/*
|
||||
@section Subscribed Callback
|
||||
|
||||
@text After the subscribe operation is complete, we get notified if it was
|
||||
successful. In this example, we read the Characteristic that contains the
|
||||
BD ADDR of the other device. This isn't strictly necessary as we already
|
||||
know the device address from the Advertisement, but it's a common pattern
|
||||
with iOS as the device address is hidden from applications.
|
||||
*/
|
||||
/* LISTING_START(LECentralSubscribedCallback): Subscribed Callback */
|
||||
void gattSubscribedCallback(BLEStatus status, BLEDevice * device) {
|
||||
(void) status;
|
||||
device->readCharacteristic(&characteristics[charBdAddr].characteristic);
|
||||
}
|
||||
/* LISTING_END(LECentralSubscribedCallback): Subscribed Callback */
|
||||
|
||||
/*
|
||||
@section Read Callback
|
||||
|
||||
@text The Read callback is called with the result from a read operation.
|
||||
Here, we write to the TX Characteristic next.
|
||||
*/
|
||||
/* LISTING_START(LECentralReadCallback): Read Callback */
|
||||
void gattReadCallback(BLEStatus status, BLEDevice *device, uint8_t *value, uint16_t length) {
|
||||
(void) status;
|
||||
(void) length;
|
||||
Serial.print("Read callback: ");
|
||||
Serial.println((const char *)value);
|
||||
device->writeCharacteristic(&characteristics[charTX].characteristic, (uint8_t*) "Hello!", 6);
|
||||
}
|
||||
/* LISTING_END(LECentralReadCallback): Read Callback */
|
||||
|
||||
/*
|
||||
@section Written Callback
|
||||
|
||||
@text After the write operation is complete, the Written Callback is callbed with
|
||||
the result in the status argument. As we're done with the initial setup of the remote
|
||||
device, we set the flag to write the test string as fast as possible.
|
||||
*/
|
||||
/* LISTING_START(LECentralWrittenCallback): Written Callback */
|
||||
void gattWrittenCallback(BLEStatus status, BLEDevice *device) {
|
||||
(void) status;
|
||||
(void) device;
|
||||
sendCounter = true;
|
||||
}
|
||||
/* LISTING_END(LECentralWrittenCallback): Written Callback */
|
||||
|
||||
/*
|
||||
@section Notification Callback
|
||||
|
||||
@text Notifications for Characteristic Value Updates are delivered via the
|
||||
Notification Callback. When more than one Characteristic is subscribed,
|
||||
the value handle can be used to distinguish between them. The
|
||||
BLECharacteristic.isValueHandle(int handle) allows to test if a value handle
|
||||
belongs to a particular Characteristic.
|
||||
*/
|
||||
/* LISTING_START(LECentralNotificationCallback): Notification Callback */
|
||||
void gattCharacteristicNotification(BLEDevice *device, uint16_t value_handle, uint8_t *value, uint16_t length) {
|
||||
(void) device;
|
||||
(void) value_handle;
|
||||
(void) length;
|
||||
Serial.print("Notification: ");
|
||||
Serial.println((const char *)value);
|
||||
}
|
||||
/* LISTING_END(LECentralNotificationCallback): Notification Callback */
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <BTstackLib.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/*
|
||||
EXAMPLE_START(LEDeviceScanner): LE Device Scanner
|
||||
|
||||
@text The LE Device Scanner monitors BLE device advertisements,
|
||||
keeping track of one or more known devices as they appear.
|
||||
*/
|
||||
|
||||
// Application state
|
||||
int counter[2] = {0, 0};
|
||||
BD_ADDR known_devices[2] = {BD_ADDR("DB:88:B6:70:9E:EB"),
|
||||
BD_ADDR("C9:60:BD:F2:B4:9D")
|
||||
};
|
||||
|
||||
/*
|
||||
@section Setup
|
||||
|
||||
@text After BTstack.setup(), BTstack is configured to call
|
||||
advertisementCallback whenever an Advertisement was received.
|
||||
*/
|
||||
/* LISTING_START(LEDeviceScannerSetup): LE Device Scanner Setup */
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
BTstack.setBLEAdvertisementCallback(advertisementCallback);
|
||||
BTstack.setup();
|
||||
BTstack.bleStartScanning();
|
||||
}
|
||||
/* LISTING_END(LEDeviceScannerSetup): LE Device Scanner Setup */
|
||||
|
||||
/*
|
||||
@section Loop
|
||||
|
||||
@text In the standard Arduino loop() function, BTstack's loop() is called.
|
||||
*/
|
||||
/* LISTING_START(LEDeviceScannerLoop): Loop */
|
||||
void loop(void) {
|
||||
BTstack.loop();
|
||||
}
|
||||
/* LISTING_END(LEDeviceScannerLoop): Loop */
|
||||
|
||||
/*
|
||||
@section Advertisement Callback
|
||||
|
||||
@text Whenever an Advertisement is received, isIBeacon() checks if
|
||||
it contains an iBeacon.
|
||||
|
||||
If it's not an iBeacon, the BD_ADDR is compared to the address we are
|
||||
looking for and the counter is incremented.
|
||||
*/
|
||||
/* LISTING_START(LEDeviceScannerAdvertisementCallback): Advertisement Callback
|
||||
*/
|
||||
void advertisementCallback(BLEAdvertisement *bleAdvertisement) {
|
||||
if (!(bleAdvertisement->isIBeacon())) {
|
||||
Serial.print("Device discovered: ");
|
||||
Serial.println(bleAdvertisement->getBdAddr()->getAddressString());
|
||||
for (size_t i = 0; i < sizeof(counter) / sizeof(int); i++) {
|
||||
if (memcmp(bleAdvertisement->getBdAddr()->getAddress(),
|
||||
known_devices[i].getAddress(), sizeof(known_devices[i])) == 0) {
|
||||
counter[i]++;
|
||||
Serial.printf("Known device: %s, has been discovered %d times.\n",
|
||||
known_devices[i].getAddressString(), counter[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* LISTING_END(LEDeviceScannerAdvertisementCallback): Advertisement Callback */
|
||||
@@ -0,0 +1,120 @@
|
||||
// LE Peripheral Example - not working yet
|
||||
#include <BTstackLib.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/*
|
||||
EXAMPLE_START(LEPeripheral): LE Peripheral
|
||||
|
||||
@text BTstack allows to setup a GATT Services and Characteristics directly
|
||||
from the setup function without using other tools outside of the Arduino IDE.
|
||||
|
||||
@section Setup
|
||||
|
||||
@text First, a number of callbacks are set. Then, a Service with a Read-only
|
||||
Characteristic and a dynamic Characteristic is added to the GATT database.
|
||||
In BTstack, a dynamic Characteristic is a Characteristic where reads and writes
|
||||
are forwarded to the Sketch. In this example, the dynamic Characteristic is
|
||||
provided by the single byte variable characteristic_data.
|
||||
*/
|
||||
|
||||
/* LISTING_START(LEPeripheralSetup): Setup */
|
||||
static char characteristic_data = 'H';
|
||||
|
||||
void setup(void) {
|
||||
|
||||
Serial.begin(9600);
|
||||
|
||||
// set callbacks
|
||||
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
|
||||
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
|
||||
BTstack.setGATTCharacteristicRead(gattReadCallback);
|
||||
BTstack.setGATTCharacteristicWrite(gattWriteCallback);
|
||||
|
||||
// setup GATT Database
|
||||
BTstack.addGATTService(new UUID("B8E06067-62AD-41BA-9231-206AE80AB551"));
|
||||
BTstack.addGATTCharacteristic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fcef"), ATT_PROPERTY_READ, "This is a String!");
|
||||
BTstack.addGATTCharacteristicDynamic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fce0"), ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_NOTIFY, 0);
|
||||
|
||||
// startup Bluetooth and activate advertisements
|
||||
BTstack.setup();
|
||||
BTstack.startAdvertising();
|
||||
}
|
||||
/* LISTING_END(LEPeripheralSetup): Setup */
|
||||
|
||||
void loop(void) {
|
||||
BTstack.loop();
|
||||
}
|
||||
|
||||
/*
|
||||
@section Device Connected Callback
|
||||
|
||||
@text When a remove device connects, device connected callback is callec.
|
||||
*/
|
||||
/* LISTING_START(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
|
||||
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
|
||||
(void) device;
|
||||
switch (status) {
|
||||
case BLE_STATUS_OK:
|
||||
Serial.println("Device connected!");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* LISTING_END(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
|
||||
|
||||
/*
|
||||
@section Device Disconnected Callback
|
||||
|
||||
@text If the connection to a device breaks, the device disconnected callback
|
||||
is called.
|
||||
*/
|
||||
/* LISTING_START(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
|
||||
void deviceDisconnectedCallback(BLEDevice * device) {
|
||||
(void) device;
|
||||
Serial.println("Disconnected.");
|
||||
}
|
||||
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
|
||||
|
||||
/*
|
||||
@section Read Callback
|
||||
|
||||
@text In BTstack, the Read Callback is first called to query the size of the
|
||||
Characteristic Value, before it is called to provide the data.
|
||||
Both times, the size has to be returned. The data is only stored in the provided
|
||||
buffer, if the buffer argeument is not NULL.
|
||||
If more than one dynamic Characteristics is used, the value handle is used
|
||||
to distinguish them.
|
||||
*/
|
||||
/* LISTING_START(LEPeripheralReadCallback): Read Callback */
|
||||
uint16_t gattReadCallback(uint16_t value_handle, uint8_t * buffer, uint16_t buffer_size) {
|
||||
(void) value_handle;
|
||||
(void) buffer_size;
|
||||
if (buffer) {
|
||||
Serial.print("gattReadCallback, value: ");
|
||||
Serial.println(characteristic_data, HEX);
|
||||
buffer[0] = characteristic_data;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Read Callback */
|
||||
|
||||
/*
|
||||
@section Write Callback
|
||||
|
||||
@text When the remove device writes a Characteristic Value, the Write callback
|
||||
is called. The buffer arguments points to the data of size size/
|
||||
If more than one dynamic Characteristics is used, the value handle is used
|
||||
to distinguish them.
|
||||
*/
|
||||
/* LISTING_START(LEPeripheralWriteCallback): Write Callback */
|
||||
int gattWriteCallback(uint16_t value_handle, uint8_t *buffer, uint16_t size) {
|
||||
(void) value_handle;
|
||||
(void) size;
|
||||
characteristic_data = buffer[0];
|
||||
Serial.print("gattWriteCallback , value ");
|
||||
Serial.println(characteristic_data, HEX);
|
||||
return 0;
|
||||
}
|
||||
/* LISTING_END(LEPeripheralWriteCallback): Write Callback */
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <BTstackLib.h>
|
||||
#include <stdio.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/* EXAMPLE_START(iBeacon): iBeacon Simulator
|
||||
|
||||
@section Setup
|
||||
|
||||
@text After BTstack.setup(), iBeaconConfigure() configures BTstack
|
||||
to send out iBeacons Advertisements with the provided Major ID,
|
||||
Minor ID and UUID.
|
||||
*/
|
||||
/* LISTING_START(iBeaconSetup): iBeacon Setup */
|
||||
UUID uuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0");
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
BTstack.setup();
|
||||
BTstack.iBeaconConfigure(&uuid, 4711, 2);
|
||||
BTstack.startAdvertising();
|
||||
}
|
||||
/* LISTING_END(iBeaconSetup) */
|
||||
|
||||
void loop(void) {
|
||||
BTstack.loop();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <BTstackLib.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/* EXAMPLE_START(iBeaconScanner): iBeacon Scanner
|
||||
|
||||
@section Setup
|
||||
|
||||
@text After BTstack.setup(), BTstack is configured to call
|
||||
advertisementCallback whenever an Advertisement was received.
|
||||
Then, a device discovery is started
|
||||
*/
|
||||
|
||||
/* LISTING_START(iBeaconSetup): iBeacon Scanner Setup */
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
BTstack.setup();
|
||||
BTstack.setBLEAdvertisementCallback(advertisementCallback);
|
||||
BTstack.bleStartScanning();
|
||||
}
|
||||
/* LISTING_END(iBeaconSetup): iBeacon Scanner Setup */
|
||||
|
||||
void loop(void) {
|
||||
BTstack.loop();
|
||||
}
|
||||
|
||||
/*
|
||||
@section Advertisement Callback
|
||||
|
||||
@text Whenever an Advertisement is received, isIBeacon() checks if
|
||||
it contains an iBeacon. If yes, the Major ID, Minor ID, and UUID
|
||||
is printed.
|
||||
If it's not an iBeacon, only the BD_ADDR and the received signal strength
|
||||
(RSSI) is shown.
|
||||
*/
|
||||
/* LISTING_START(iBeaconCallback): iBeacon Scanner Callback */
|
||||
void advertisementCallback(BLEAdvertisement *adv) {
|
||||
if (adv->isIBeacon()) {
|
||||
Serial.print("iBeacon found ");
|
||||
Serial.print(adv->getBdAddr()->getAddressString());
|
||||
Serial.print(", RSSI ");
|
||||
Serial.print(adv->getRssi());
|
||||
Serial.print(", UUID ");
|
||||
Serial.print(adv->getIBeaconUUID()->getUuidString());
|
||||
Serial.print(", MajorID ");
|
||||
Serial.print(adv->getIBeaconMajorID());
|
||||
Serial.print(", MinorID ");
|
||||
Serial.print(adv->getIBecaonMinorID());
|
||||
Serial.print(", Measured Power ");
|
||||
Serial.println(adv->getiBeaconMeasuredPower());
|
||||
} else {
|
||||
Serial.print("Device discovered: ");
|
||||
Serial.print(adv->getBdAddr()->getAddressString());
|
||||
Serial.print(", RSSI ");
|
||||
Serial.println(adv->getRssi());
|
||||
}
|
||||
}
|
||||
/* LISTING_END(iBeaconCallback): iBeacon Scanner Callback */
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
BTstack KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
BLEStatus KEYWORD2
|
||||
UUID KEYWORD2
|
||||
BD_ADDR_TYPE KEYWORD2
|
||||
BD_ADDR KEYWORD2
|
||||
BLEAdvertisement KEYWORD2
|
||||
BLECharacteristic KEYWORD2
|
||||
BLEService KEYWORD2
|
||||
BLEDevice KEYWORD2
|
||||
BTstackManager KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
getUuidString KEYWORD2
|
||||
getUuid128String KEYWORD2
|
||||
getUuid KEYWORD2
|
||||
matches KEYWORD2
|
||||
getAddress KEYWORD2
|
||||
getAddressString KEYWORD2
|
||||
getAddressType KEYWORD2
|
||||
getBdAddr KEYWORD2
|
||||
getBdAddrType KEYWORD2
|
||||
getRssi KEYWORD2
|
||||
containsService KEYWORD2
|
||||
nameHasPrefix KEYWORD2
|
||||
getAdvData KEYWORD2
|
||||
isIBeacon KEYWORD2
|
||||
getIBeaconUUID KEYWORD2
|
||||
getIBeaconMajorID KEYWORD2
|
||||
getIBecaonMinorID KEYWORD2
|
||||
getiBeaconMeasuredPower KEYWORD2
|
||||
isValueHandle KEYWORD2
|
||||
getCharacteristic KEYWORD2
|
||||
getUUID KEYWORD2
|
||||
getService KEYWORD2
|
||||
getHandle KEYWORD2
|
||||
discoverGATTServices KEYWORD2
|
||||
discoverCharacteristicsForService KEYWORD2
|
||||
readCharacteristic KEYWORD2
|
||||
writeCharacteristic KEYWORD2
|
||||
writeCharacteristicWithoutResponse KEYWORD2
|
||||
subscribeForNotifications KEYWORD2
|
||||
unsubscribeFromNotifications KEYWORD2
|
||||
subscribeForIndications KEYWORD2
|
||||
unsubscribeFromIndications KEYWORD2
|
||||
setPublicBdAddr KEYWORD2
|
||||
enablePacketLogger KEYWORD2
|
||||
enableDebugLogger KEYWORD2
|
||||
setAdvData KEYWORD2
|
||||
iBeaconConfigure KEYWORD2
|
||||
startAdvertising KEYWORD2
|
||||
stopAdvertising KEYWORD2
|
||||
bleStartScanning KEYWORD2
|
||||
bleStopScanning KEYWORD2
|
||||
bleConnect KEYWORD2
|
||||
bleDisconnect KEYWORD2
|
||||
setBLEAdvertisementCallback KEYWORD2
|
||||
setBLEDeviceConnectedCallback KEYWORD2
|
||||
setBLEDeviceDisconnectedCallback KEYWORD2
|
||||
setGATTServiceDiscoveredCallback KEYWORD2
|
||||
setGATTCharacteristicDiscoveredCallback KEYWORD2
|
||||
setGATTCharacteristicReadCallback KEYWORD2
|
||||
setGATTCharacteristicNotificationCallback KEYWORD2
|
||||
setGATTCharacteristicIndicationCallback KEYWORD2
|
||||
setGATTDoneCallback KEYWORD2
|
||||
setGATTCharacteristicWrittenCallback KEYWORD2
|
||||
setGATTCharacteristicSubscribedCallback KEYWORD2
|
||||
setGATTCharacteristicUnsubscribedCallback KEYWORD2
|
||||
setGATTCharacteristicRead KEYWORD2
|
||||
setGATTCharacteristicWrite KEYWORD2
|
||||
addGATTService KEYWORD2
|
||||
addGATTCharacteristic KEYWORD2
|
||||
addGATTCharacteristicDynamic KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
BLE_STATUS_OK LITERAL1
|
||||
BLE_STATUS_DONE LITERAL1
|
||||
BLE_STATUS_CONNECTION_TIMEOUT LITERAL1
|
||||
BLE_STATUS_CONNECTION_ERROR LITERAL1
|
||||
BLE_STATUS_OTHER_ERROR LITERAL1
|
||||
PUBLIC_ADDRESS LITERAL1
|
||||
PRIVAT_ADDRESS LITERAL1
|
||||
@@ -0,0 +1,10 @@
|
||||
name=BTstack
|
||||
version=1.0
|
||||
author=bluekitchen-gmbh
|
||||
maintainer=Earle Philhower
|
||||
sentence=BTstack Arduino library distributed with BTstack
|
||||
paragraph=
|
||||
category=Data Processing
|
||||
url=
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,914 @@
|
||||
/**
|
||||
Arduino Wrapper for BTstack
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#ifdef __AVR__
|
||||
#include <avr/wdt.h>
|
||||
#endif
|
||||
|
||||
#if __arm__
|
||||
//#include <Reset.h> // also provides NVIC_SystemReset
|
||||
#endif
|
||||
|
||||
#include "BTstackLib.h"
|
||||
|
||||
#include "btstack_memory.h"
|
||||
#include "hal_tick.h"
|
||||
#include "hal_cpu.h"
|
||||
#include "hci_cmd.h"
|
||||
#include "btstack_util.h"
|
||||
#include "btstack_run_loop.h"
|
||||
#include "btstack_event.h"
|
||||
#include "btstack_run_loop_embedded.h"
|
||||
#include "hci_transport.h"
|
||||
#include "hci_transport_h4.h"
|
||||
|
||||
#include "ad_parser.h"
|
||||
//#include "btstack_chipset_em9301.h"
|
||||
#include "btstack_debug.h"
|
||||
#include "gap.h"
|
||||
#include "hci.h"
|
||||
#include "hci_dump.h"
|
||||
#include "hci_dump_embedded_stdout.h"
|
||||
#include "l2cap.h"
|
||||
#include "ble/att_db.h"
|
||||
#include "ble/att_server.h"
|
||||
#include "ble/att_db_util.h"
|
||||
#include "ble/le_device_db.h"
|
||||
#include "ble/sm.h"
|
||||
|
||||
// Pin 13 has an LED connected on most Arduino boards.
|
||||
//#define PIN_LED 13
|
||||
|
||||
// prototypes
|
||||
extern "C" void hal_uart_dma_process(void);
|
||||
|
||||
enum {
|
||||
SET_ADVERTISEMENT_PARAMS = 1 << 0,
|
||||
SET_ADVERTISEMENT_DATA = 1 << 1,
|
||||
SET_ADVERTISEMENT_ENABLED = 1 << 2,
|
||||
};
|
||||
|
||||
typedef enum gattAction {
|
||||
gattActionWrite,
|
||||
gattActionSubscribe,
|
||||
gattActionUnsubscribe,
|
||||
gattActionServiceQuery,
|
||||
gattActionCharacteristicQuery,
|
||||
gattActionRead,
|
||||
} gattAction_t;
|
||||
|
||||
static gattAction_t gattAction;
|
||||
|
||||
// btstack state
|
||||
static int btstack_state;
|
||||
|
||||
static const uint8_t iBeaconAdvertisement01[] = { 0x02, 0x01 };
|
||||
static const uint8_t iBeaconAdvertisement38[] = { 0x1a, 0xff, 0x4c, 0x00, 0x02, 0x15 };
|
||||
static uint8_t adv_data[31];
|
||||
static uint16_t adv_data_len = 0;
|
||||
//static int gatt_is_characteristics_query;
|
||||
|
||||
static uint16_t le_peripheral_todos = 0;
|
||||
static bool have_custom_addr;
|
||||
static bd_addr_t public_bd_addr;
|
||||
|
||||
static btstack_timer_source_t connection_timer;
|
||||
|
||||
static void (*bleAdvertismentCallback)(BLEAdvertisement * bleAdvertisement) = NULL;
|
||||
static void (*bleDeviceConnectedCallback)(BLEStatus status, BLEDevice * device) = NULL;
|
||||
static void (*bleDeviceDisconnectedCallback)(BLEDevice * device) = NULL;
|
||||
static void (*gattServiceDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLEService * bleService) = NULL;
|
||||
static void (*gattCharacteristicDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic) = NULL;
|
||||
static void (*gattCharacteristicNotificationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
|
||||
static void (*gattCharacteristicIndicationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
|
||||
static void (*gattCharacteristicReadCallback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length) = NULL;
|
||||
static void (*gattCharacteristicWrittenCallback)(BLEStatus status, BLEDevice * device) = NULL;
|
||||
static void (*gattCharacteristicSubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
|
||||
static void (*gattCharacteristicUnsubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
|
||||
|
||||
|
||||
// retarget printf to Serial
|
||||
#ifdef ENERGIA
|
||||
extern "C" int putchar(int c) {
|
||||
Serial.write((uint8_t)c);
|
||||
return c;
|
||||
}
|
||||
#else
|
||||
#ifdef __AVR__
|
||||
static FILE uartout = {0} ;
|
||||
static int uart_putchar(char c, FILE *stream) {
|
||||
Serial.write(c);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
// added for Arduino Zero. Arduino Due already has tis own _write(..) implementation
|
||||
// in /Users/mringwal/Library/Arduino15/packages/arduino/hardware/sam/1.6.4/cores/arduino/syscalls_sam3.c
|
||||
#if defined(__SAMD21G18A__)
|
||||
// #ifdef __arm__
|
||||
extern "C" int _write(int file, char *ptr, int len) {
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (ptr[i] == '\n') {
|
||||
Serial.write((uint8_t)'\r');
|
||||
}
|
||||
Serial.write((uint8_t)ptr[i]);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// HAL CPU Implementation
|
||||
extern "C" void hal_cpu_disable_irqs(void) { }
|
||||
extern "C" void hal_cpu_enable_irqs(void) { }
|
||||
extern "C" void hal_cpu_enable_irqs_and_sleep(void) { }
|
||||
|
||||
//
|
||||
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
||||
(void) channel;
|
||||
(void) size;
|
||||
hci_con_handle_t con_handle;
|
||||
|
||||
switch (packet_type) {
|
||||
|
||||
case HCI_EVENT_PACKET:
|
||||
switch (packet[0]) {
|
||||
|
||||
case BTSTACK_EVENT_STATE:
|
||||
btstack_state = packet[2];
|
||||
// bt stack activated, get started
|
||||
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
|
||||
le_peripheral_todos |= SET_ADVERTISEMENT_PARAMS
|
||||
| SET_ADVERTISEMENT_DATA
|
||||
| SET_ADVERTISEMENT_ENABLED;
|
||||
bd_addr_t addr;
|
||||
gap_local_bd_addr(addr);
|
||||
printf("BTstack up and running at %s\n", bd_addr_to_str(addr));
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_EVENT_DISCONNECTION_COMPLETE:
|
||||
if (bleDeviceDisconnectedCallback) {
|
||||
con_handle = little_endian_read_16(packet, 3);
|
||||
BLEDevice device(con_handle);
|
||||
(*bleDeviceDisconnectedCallback)(&device);
|
||||
}
|
||||
le_peripheral_todos |= SET_ADVERTISEMENT_ENABLED;
|
||||
break;
|
||||
|
||||
case GAP_EVENT_ADVERTISING_REPORT: {
|
||||
if (bleAdvertismentCallback) {
|
||||
BLEAdvertisement advertisement(packet);
|
||||
(*bleAdvertismentCallback)(&advertisement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case HCI_EVENT_LE_META:
|
||||
switch (packet[2]) {
|
||||
case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
|
||||
con_handle = little_endian_read_16(packet, 4);
|
||||
printf("Connection complete, con_handle 0x%04x\n", con_handle);
|
||||
btstack_run_loop_remove_timer(&connection_timer);
|
||||
if (!bleDeviceConnectedCallback) {
|
||||
break;
|
||||
}
|
||||
if (packet[3]) {
|
||||
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_ERROR, NULL);
|
||||
} else {
|
||||
BLEDevice device(con_handle);
|
||||
(*bleDeviceConnectedCallback)(BLE_STATUS_OK, &device);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_service(gatt_client_service_t * service, uint8_t * packet) {
|
||||
service->start_group_handle = little_endian_read_16(packet, 4);
|
||||
service->end_group_handle = little_endian_read_16(packet, 6);
|
||||
service->uuid16 = 0;
|
||||
reverse_128(&packet[8], service->uuid128);
|
||||
if (uuid_has_bluetooth_prefix(service->uuid128)) {
|
||||
service->uuid16 = big_endian_read_32(service->uuid128, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_characteristic(gatt_client_characteristic_t * characteristic, uint8_t * packet) {
|
||||
characteristic->start_handle = little_endian_read_16(packet, 4);
|
||||
characteristic->value_handle = little_endian_read_16(packet, 6);
|
||||
characteristic->end_handle = little_endian_read_16(packet, 8);
|
||||
characteristic->properties = little_endian_read_16(packet, 10);
|
||||
characteristic->uuid16 = 0;
|
||||
reverse_128(&packet[12], characteristic->uuid128);
|
||||
if (uuid_has_bluetooth_prefix(characteristic->uuid128)) {
|
||||
characteristic->uuid16 = big_endian_read_32(characteristic->uuid128, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void gatt_client_callback(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t size) {
|
||||
(void) channel;
|
||||
(void) packet_type;
|
||||
(void) size;
|
||||
// if (hci) event is not 4-byte aligned, event->handle causes crash
|
||||
// workaround: check event type, assuming GATT event types are contagious
|
||||
if (packet[0] < GATT_EVENT_QUERY_COMPLETE) {
|
||||
return;
|
||||
}
|
||||
if (packet[0] > GATT_EVENT_MTU) {
|
||||
return;
|
||||
}
|
||||
|
||||
hci_con_handle_t con_handle = little_endian_read_16(packet, 2);
|
||||
uint8_t status;
|
||||
uint8_t * value;
|
||||
uint16_t value_handle;
|
||||
uint16_t value_length;
|
||||
|
||||
BLEDevice device(con_handle);
|
||||
switch (hci_event_packet_get_type(packet)) {
|
||||
case GATT_EVENT_SERVICE_QUERY_RESULT:
|
||||
if (gattServiceDiscoveredCallback) {
|
||||
gatt_client_service_t service;
|
||||
extract_service(&service, packet);
|
||||
BLEService bleService(service);
|
||||
(*gattServiceDiscoveredCallback)(BLE_STATUS_OK, &device, &bleService);
|
||||
}
|
||||
break;
|
||||
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
|
||||
if (gattCharacteristicDiscoveredCallback) {
|
||||
gatt_client_characteristic_t characteristic;
|
||||
extract_characteristic(&characteristic, packet);
|
||||
BLECharacteristic bleCharacteristic(characteristic);
|
||||
(*gattCharacteristicDiscoveredCallback)(BLE_STATUS_OK, &device, &bleCharacteristic);
|
||||
}
|
||||
break;
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
status = little_endian_read_16(packet, 4);
|
||||
switch (gattAction) {
|
||||
case gattActionWrite:
|
||||
if (gattCharacteristicWrittenCallback) {
|
||||
gattCharacteristicWrittenCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
|
||||
}
|
||||
break;
|
||||
case gattActionSubscribe:
|
||||
if (gattCharacteristicSubscribedCallback) {
|
||||
gattCharacteristicSubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
|
||||
}
|
||||
break;
|
||||
case gattActionUnsubscribe:
|
||||
if (gattCharacteristicUnsubscribedCallback) {
|
||||
gattCharacteristicUnsubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
|
||||
}
|
||||
break;
|
||||
case gattActionServiceQuery:
|
||||
if (gattServiceDiscoveredCallback) {
|
||||
gattServiceDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
|
||||
}
|
||||
break;
|
||||
case gattActionCharacteristicQuery:
|
||||
if (gattCharacteristicDiscoveredCallback) {
|
||||
gattCharacteristicDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case GATT_EVENT_NOTIFICATION:
|
||||
if (gattCharacteristicNotificationCallback) {
|
||||
value_handle = little_endian_read_16(packet, 4);
|
||||
value_length = little_endian_read_16(packet, 6);
|
||||
value = &packet[8];
|
||||
(*gattCharacteristicNotificationCallback)(&device, value_handle, value, value_length);
|
||||
}
|
||||
break;
|
||||
case GATT_EVENT_INDICATION:
|
||||
if (gattCharacteristicIndicationCallback) {
|
||||
value_handle = little_endian_read_16(packet, 4);
|
||||
value_length = little_endian_read_16(packet, 6);
|
||||
value = &packet[8];
|
||||
(*gattCharacteristicIndicationCallback)(&device, value_handle, value, value_length);
|
||||
}
|
||||
break;
|
||||
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
|
||||
if (gattCharacteristicReadCallback) {
|
||||
value_handle = little_endian_read_16(packet, 4);
|
||||
value_length = little_endian_read_16(packet, 6);
|
||||
value = &packet[8];
|
||||
(*gattCharacteristicReadCallback)(BLE_STATUS_OK, &device, value, value_length);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void connection_timeout_handler(btstack_timer_source_t * timer) {
|
||||
(void) timer;
|
||||
// log_info("Cancel outgoing connection");
|
||||
gap_connect_cancel();
|
||||
if (!bleDeviceConnectedCallback) {
|
||||
return;
|
||||
}
|
||||
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_TIMEOUT, NULL); // page timeout 0x04
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/// UUID class
|
||||
UUID::UUID(void) {
|
||||
memset(uuid, 0, 16);
|
||||
}
|
||||
|
||||
UUID::UUID(const uint8_t uuid[16]) {
|
||||
memcpy(this->uuid, uuid, 16);
|
||||
}
|
||||
|
||||
UUID::UUID(const char * uuidStr) {
|
||||
memset(uuid, 0, 16);
|
||||
int len = strlen(uuidStr);
|
||||
if (len <= 4) {
|
||||
// Handle 4 Bytes HEX
|
||||
unsigned int uuid16;
|
||||
int result = sscanf((char *) uuidStr, "%x", &uuid16);
|
||||
if (result == 1) {
|
||||
uuid_add_bluetooth_prefix(uuid, uuid16);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// quick UUID parser, ignoring dashes
|
||||
int i = 0;
|
||||
int data = 0;
|
||||
int have_nibble = 0;
|
||||
while (*uuidStr && i < 16) {
|
||||
const char c = *uuidStr++;
|
||||
if (c == '-') {
|
||||
continue;
|
||||
}
|
||||
data = data << 4 | nibble_for_char(c);
|
||||
if (!have_nibble) {
|
||||
have_nibble = 1;
|
||||
continue;
|
||||
}
|
||||
uuid[i++] = data;
|
||||
data = 0;
|
||||
have_nibble = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t * UUID::getUuid(void) const {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
static char uuid16_buffer[5];
|
||||
const char * UUID::getUuidString() const {
|
||||
// TODO: fix uuid_has_bluetooth_prefix call to use const
|
||||
if (uuid_has_bluetooth_prefix((uint8_t*)uuid)) {
|
||||
sprintf(uuid16_buffer, "%04x", (uint16_t) big_endian_read_32(uuid, 0));
|
||||
return uuid16_buffer;
|
||||
} else {
|
||||
// TODO: fix uuid128_to_str
|
||||
return uuid128_to_str((uint8_t*)uuid);
|
||||
}
|
||||
}
|
||||
|
||||
const char * UUID::getUuid128String() const {
|
||||
return uuid128_to_str((uint8_t*)uuid);
|
||||
}
|
||||
|
||||
bool UUID::matches(UUID *other) const {
|
||||
return memcmp(this->uuid, other->uuid, 16) == 0;
|
||||
}
|
||||
|
||||
|
||||
// BD_ADDR class
|
||||
BD_ADDR::BD_ADDR(void) {
|
||||
}
|
||||
|
||||
BD_ADDR::BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type) : address_type(address_type) {
|
||||
int processed = sscanf(address_string, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", address, address + 1,
|
||||
address + 2, address + 3, address + 4, address + 5);
|
||||
if (processed != 6) { // Set address to zeroes if we did not get six bytes back.
|
||||
for (int i = 0; i < 6; i++) {
|
||||
address[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BD_ADDR::BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type) : address_type(address_type) {
|
||||
memcpy(this->address, address, 6);
|
||||
}
|
||||
|
||||
const uint8_t * BD_ADDR::getAddress(void) {
|
||||
return address;
|
||||
}
|
||||
|
||||
const char * BD_ADDR::getAddressString(void) {
|
||||
return bd_addr_to_str(address);
|
||||
}
|
||||
|
||||
BD_ADDR_TYPE BD_ADDR::getAddressType(void) {
|
||||
return address_type;
|
||||
}
|
||||
|
||||
|
||||
BLEAdvertisement::BLEAdvertisement(uint8_t * event_packet) :
|
||||
advertising_event_type(event_packet[2]),
|
||||
rssi(event_packet[10]),
|
||||
data_length(event_packet[11]),
|
||||
iBeacon_UUID(NULL) {
|
||||
bd_addr_t addr;
|
||||
reverse_bd_addr(&event_packet[4], addr);
|
||||
bd_addr = BD_ADDR(addr, (BD_ADDR_TYPE)event_packet[3]);
|
||||
memcpy(data, &event_packet[12], LE_ADVERTISING_DATA_SIZE);
|
||||
}
|
||||
|
||||
BLEAdvertisement::~BLEAdvertisement() {
|
||||
if (iBeacon_UUID) {
|
||||
delete (iBeacon_UUID);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t * BLEAdvertisement::getAdvData(void) {
|
||||
return data;
|
||||
}
|
||||
|
||||
BD_ADDR * BLEAdvertisement::getBdAddr(void) {
|
||||
return &bd_addr;
|
||||
}
|
||||
|
||||
int BLEAdvertisement::getRssi(void) {
|
||||
return rssi > 127 ? rssi - 256 : rssi;
|
||||
}
|
||||
|
||||
|
||||
bool BLEAdvertisement::containsService(UUID * service) {
|
||||
return ad_data_contains_uuid128(data_length, data, (uint8_t*) service->getUuid());
|
||||
}
|
||||
|
||||
bool BLEAdvertisement::nameHasPrefix(const char * name_prefix) {
|
||||
ad_context_t context;
|
||||
int name_prefix_len = strlen(name_prefix);
|
||||
for (ad_iterator_init(&context, data_length, data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) {
|
||||
uint8_t data_type = ad_iterator_get_data_type(&context);
|
||||
uint8_t data_len = ad_iterator_get_data_len(&context);
|
||||
const uint8_t * data = ad_iterator_get_data(&context);
|
||||
int compare_len = name_prefix_len;
|
||||
switch (data_type) {
|
||||
case 8: // shortented local name
|
||||
case 9: // complete local name
|
||||
if (compare_len > data_len) {
|
||||
compare_len = data_len;
|
||||
}
|
||||
if (strncmp(name_prefix, (const char*) data, compare_len) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool BLEAdvertisement::isIBeacon(void) {
|
||||
return ((memcmp(iBeaconAdvertisement01, data, sizeof(iBeaconAdvertisement01)) == 0)
|
||||
&& (memcmp(iBeaconAdvertisement38, &data[3], sizeof(iBeaconAdvertisement38)) == 0));
|
||||
}
|
||||
|
||||
const UUID * BLEAdvertisement::getIBeaconUUID(void) {
|
||||
if (!iBeacon_UUID) {
|
||||
iBeacon_UUID = new UUID(&data[9]);
|
||||
}
|
||||
return iBeacon_UUID;
|
||||
};
|
||||
uint16_t BLEAdvertisement::getIBeaconMajorID(void) {
|
||||
return big_endian_read_16(data, 25);
|
||||
};
|
||||
uint16_t BLEAdvertisement::getIBecaonMinorID(void) {
|
||||
return big_endian_read_16(data, 27);
|
||||
};
|
||||
uint8_t BLEAdvertisement::getiBeaconMeasuredPower(void) {
|
||||
return data[29];
|
||||
}
|
||||
|
||||
|
||||
BLECharacteristic::BLECharacteristic(void) {
|
||||
}
|
||||
|
||||
BLECharacteristic::BLECharacteristic(gatt_client_characteristic_t characteristic)
|
||||
: characteristic(characteristic), uuid(characteristic.uuid128) {
|
||||
}
|
||||
|
||||
const UUID * BLECharacteristic::getUUID(void) {
|
||||
return &uuid;
|
||||
}
|
||||
|
||||
bool BLECharacteristic::matches(UUID * uuid) {
|
||||
return this->uuid.matches(uuid);
|
||||
}
|
||||
|
||||
bool BLECharacteristic::isValueHandle(uint16_t value_handle) {
|
||||
return characteristic.value_handle == value_handle;
|
||||
}
|
||||
|
||||
const gatt_client_characteristic_t * BLECharacteristic::getCharacteristic(void) {
|
||||
return &characteristic;
|
||||
}
|
||||
|
||||
|
||||
BLEService::BLEService(void) {
|
||||
}
|
||||
|
||||
BLEService::BLEService(gatt_client_service_t service)
|
||||
: service(service), uuid(service.uuid128) {
|
||||
}
|
||||
|
||||
const UUID * BLEService::getUUID(void) {
|
||||
return &uuid;
|
||||
}
|
||||
|
||||
bool BLEService::matches(UUID * uuid) {
|
||||
return this->uuid.matches(uuid);
|
||||
}
|
||||
|
||||
const gatt_client_service_t * BLEService::getService(void) {
|
||||
return &service;
|
||||
}
|
||||
|
||||
// discovery of services and characteristics
|
||||
BLEDevice::BLEDevice(void) {
|
||||
}
|
||||
BLEDevice::BLEDevice(hci_con_handle_t handle)
|
||||
: handle(handle) {
|
||||
}
|
||||
uint16_t BLEDevice::getHandle(void) {
|
||||
return handle;
|
||||
}
|
||||
int BLEDevice::discoverGATTServices(void) {
|
||||
return BTstack.discoverGATTServices(this);
|
||||
}
|
||||
int BLEDevice::discoverCharacteristicsForService(BLEService * service) {
|
||||
return BTstack.discoverCharacteristicsForService(this, service);
|
||||
}
|
||||
int BLEDevice::readCharacteristic(BLECharacteristic * characteristic) {
|
||||
return BTstack.readCharacteristic(this, characteristic);
|
||||
}
|
||||
int BLEDevice::writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
|
||||
return BTstack.writeCharacteristic(this, characteristic, data, size);
|
||||
}
|
||||
int BLEDevice::writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
|
||||
return BTstack.writeCharacteristicWithoutResponse(this, characteristic, data, size);
|
||||
}
|
||||
int BLEDevice::subscribeForNotifications(BLECharacteristic * characteristic) {
|
||||
return BTstack.subscribeForNotifications(this, characteristic);
|
||||
}
|
||||
int BLEDevice::unsubscribeFromNotifications(BLECharacteristic * characteristic) {
|
||||
return BTstack.unsubscribeFromNotifications(this, characteristic);
|
||||
}
|
||||
int BLEDevice::subscribeForIndications(BLECharacteristic * characteristic) {
|
||||
return BTstack.subscribeForIndications(this, characteristic);
|
||||
}
|
||||
int BLEDevice::unsubscribeFromIndications(BLECharacteristic * characteristic) {
|
||||
return BTstack.unsubscribeFromIndications(this, characteristic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static uint16_t (*gattReadCallback)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size);
|
||||
static int (*gattWriteCallback)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size);
|
||||
|
||||
// ATT Client Read Callback for Dynamic Data
|
||||
// - if buffer == NULL, don't copy data, just return size of value
|
||||
// - if buffer != NULL, copy data and return number bytes copied
|
||||
// @param offset defines start of attribute value
|
||||
static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size) {
|
||||
(void) con_handle;
|
||||
(void) offset;
|
||||
if (gattReadCallback) {
|
||||
return gattReadCallback(att_handle, buffer, buffer_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* LISTING_END */
|
||||
|
||||
|
||||
/*
|
||||
@section ATT Write
|
||||
|
||||
@text The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification
|
||||
and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored and used
|
||||
in the heartbeat handler to decide if a new value should be sent. See Listing attWrite.
|
||||
*/
|
||||
|
||||
/* LISTING_START(attWrite): ATT Write */
|
||||
static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) {
|
||||
(void) con_handle;
|
||||
(void) transaction_mode;
|
||||
(void) offset;
|
||||
if (gattWriteCallback) {
|
||||
gattWriteCallback(att_handle, buffer, buffer_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BTstackManager::BTstackManager(void) {
|
||||
// client_packet_handler = NULL;
|
||||
have_custom_addr = false;
|
||||
// reset handler
|
||||
bleAdvertismentCallback = NULL;
|
||||
bleDeviceConnectedCallback = NULL;
|
||||
bleDeviceDisconnectedCallback = NULL;
|
||||
gattServiceDiscoveredCallback = NULL;
|
||||
gattCharacteristicDiscoveredCallback = NULL;
|
||||
gattCharacteristicNotificationCallback = NULL;
|
||||
|
||||
att_db_util_init();
|
||||
|
||||
// disable LOG_INFO messages
|
||||
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 0);
|
||||
|
||||
#ifdef __AVR__
|
||||
// configure stdout to go via Serial
|
||||
fdev_setup_stream(&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
stdout = &uartout;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BTstackManager::setBLEAdvertisementCallback(void (*callback)(BLEAdvertisement * bleAdvertisement)) {
|
||||
bleAdvertismentCallback = callback;
|
||||
}
|
||||
void BTstackManager::setBLEDeviceConnectedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
|
||||
bleDeviceConnectedCallback = callback;
|
||||
}
|
||||
void BTstackManager::setBLEDeviceDisconnectedCallback(void (*callback)(BLEDevice * device)) {
|
||||
bleDeviceDisconnectedCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTServiceDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLEService * bleService)) {
|
||||
gattServiceDiscoveredCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic)) {
|
||||
gattCharacteristicDiscoveredCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicNotificationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
|
||||
gattCharacteristicNotificationCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicIndicationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
|
||||
gattCharacteristicIndicationCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicReadCallback(void (*callback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length)) {
|
||||
gattCharacteristicReadCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicWrittenCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
|
||||
gattCharacteristicWrittenCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicSubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
|
||||
gattCharacteristicSubscribedCallback = callback;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicUnsubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
|
||||
gattCharacteristicUnsubscribedCallback = callback;
|
||||
}
|
||||
|
||||
int BTstackManager::discoverGATTServices(BLEDevice * device) {
|
||||
gattAction = gattActionServiceQuery;
|
||||
return gatt_client_discover_primary_services(gatt_client_callback, device->getHandle());
|
||||
}
|
||||
int BTstackManager::discoverCharacteristicsForService(BLEDevice * device, BLEService * service) {
|
||||
gattAction = gattActionCharacteristicQuery;
|
||||
return gatt_client_discover_characteristics_for_service(gatt_client_callback, device->getHandle(), (gatt_client_service_t*) service->getService());
|
||||
}
|
||||
int BTstackManager::readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic) {
|
||||
return gatt_client_read_value_of_characteristic(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic());
|
||||
}
|
||||
int BTstackManager::writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
|
||||
gattAction = gattActionWrite;
|
||||
return gatt_client_write_value_of_characteristic(gatt_client_callback, device->getHandle(), characteristic->getCharacteristic()->value_handle,
|
||||
size, data);
|
||||
}
|
||||
int BTstackManager::writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
|
||||
return gatt_client_write_value_of_characteristic_without_response(device->getHandle(), characteristic->getCharacteristic()->value_handle,
|
||||
size, data);
|
||||
}
|
||||
int BTstackManager::subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
|
||||
gattAction = gattActionSubscribe;
|
||||
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
|
||||
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
|
||||
}
|
||||
int BTstackManager::subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic) {
|
||||
gattAction = gattActionSubscribe;
|
||||
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
|
||||
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION);
|
||||
}
|
||||
int BTstackManager::unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
|
||||
gattAction = gattActionUnsubscribe;
|
||||
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
|
||||
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
|
||||
}
|
||||
int BTstackManager::unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic) {
|
||||
gattAction = gattActionUnsubscribe;
|
||||
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
|
||||
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
|
||||
}
|
||||
void BTstackManager::bleConnect(BLEAdvertisement * advertisement, int timeout_ms) {
|
||||
bleConnect(advertisement->getBdAddr(), timeout_ms);
|
||||
}
|
||||
void BTstackManager::bleConnect(BD_ADDR * address, int timeout_ms) {
|
||||
bleConnect(address->getAddressType(), address->getAddress(), timeout_ms);
|
||||
}
|
||||
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) {
|
||||
(void) address_type;
|
||||
(void) address;
|
||||
(void) timeout_ms;
|
||||
// TODO: implement
|
||||
// log_error("BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) not implemented");
|
||||
}
|
||||
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms) {
|
||||
gap_connect((uint8_t*)address, (bd_addr_type_t) address_type);
|
||||
if (!timeout_ms) {
|
||||
return;
|
||||
}
|
||||
btstack_run_loop_set_timer(&connection_timer, timeout_ms);
|
||||
btstack_run_loop_set_timer_handler(&connection_timer, connection_timeout_handler);
|
||||
btstack_run_loop_add_timer(&connection_timer);
|
||||
}
|
||||
|
||||
void BTstackManager::bleDisconnect(BLEDevice * device) {
|
||||
btstack_run_loop_remove_timer(&connection_timer);
|
||||
gap_disconnect(device->getHandle());
|
||||
}
|
||||
|
||||
void BTstackManager::setPublicBdAddr(bd_addr_t addr) {
|
||||
have_custom_addr = true;
|
||||
memcpy(public_bd_addr, addr, 6);
|
||||
}
|
||||
|
||||
void bluetooth_hardware_error(uint8_t error) {
|
||||
printf("Bluetooth Hardware Error event 0x%02x. Restarting...\n\n\n", error);
|
||||
#ifdef __AVR__
|
||||
wdt_enable(WDTO_15MS);
|
||||
// wait for watchdog to trigger
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
// NVIC_SystemReset();
|
||||
#endif
|
||||
while (1);
|
||||
}
|
||||
#if 0
|
||||
static hci_transport_config_uart_t config = {
|
||||
HCI_TRANSPORT_CONFIG_UART,
|
||||
115200,
|
||||
0, // main baudrate
|
||||
1, // flow control
|
||||
NULL,
|
||||
};
|
||||
#endif
|
||||
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
|
||||
void BTstackManager::setup(void) {
|
||||
setup("BTstack LE Shield");
|
||||
}
|
||||
|
||||
void BTstackManager::setup(const char * name) {
|
||||
|
||||
//#ifdef PIN_LED
|
||||
// pinMode(PIN_LED, OUTPUT);
|
||||
//#endif
|
||||
|
||||
// printf("BTstackManager::setup()\n");
|
||||
#if 0
|
||||
btstack_memory_init();
|
||||
btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
|
||||
|
||||
const hci_transport_t * transport = hci_transport_h4_instance(btstack_uart_block_embedded_instance());
|
||||
hci_init(transport, (void*) &config);
|
||||
hci_set_chipset(btstack_chipset_em9301_instance());
|
||||
|
||||
if (have_custom_addr) {
|
||||
hci_set_bd_addr(public_bd_addr);
|
||||
}
|
||||
|
||||
hci_set_hardware_error_callback(&bluetooth_hardware_error);
|
||||
|
||||
#endif
|
||||
// inform about BTstack state
|
||||
hci_event_callback_registration.callback = &packet_handler;
|
||||
hci_add_event_handler(&hci_event_callback_registration);
|
||||
l2cap_init();
|
||||
|
||||
// setup central device db
|
||||
le_device_db_init();
|
||||
|
||||
sm_init();
|
||||
|
||||
att_server_init(att_db_util_get_address(), att_read_callback, att_write_callback);
|
||||
|
||||
gatt_client_init();
|
||||
|
||||
// setup advertisements params
|
||||
uint16_t adv_int_min = 0x0030;
|
||||
uint16_t adv_int_max = 0x0030;
|
||||
uint8_t adv_type = 0;
|
||||
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);
|
||||
|
||||
// setup advertisements data
|
||||
int pos = 0;
|
||||
const uint8_t flags[] = { 0x02, 0x01, 0x02 };
|
||||
memcpy(&adv_data[pos], flags, sizeof(flags));
|
||||
pos += sizeof(flags);
|
||||
adv_data[pos++] = strlen(name) + 1;
|
||||
adv_data[pos++] = 0x09;
|
||||
memcpy(&adv_data[pos], name, strlen(name));
|
||||
pos += strlen(name);
|
||||
adv_data_len = pos;
|
||||
gap_advertisements_set_data(adv_data_len, adv_data);
|
||||
|
||||
// turn on!
|
||||
btstack_state = 0;
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
}
|
||||
|
||||
void BTstackManager::enablePacketLogger(void) {
|
||||
hci_dump_init(hci_dump_embedded_stdout_get_instance());
|
||||
}
|
||||
|
||||
void BTstackManager::enableDebugLogger() {
|
||||
// enable LOG_INFO messages
|
||||
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 1);
|
||||
}
|
||||
|
||||
|
||||
void BTstackManager::loop(void) {
|
||||
// process data from/to Bluetooth module
|
||||
// hal_uart_dma_process();
|
||||
// BTstack Run Loop
|
||||
// btstack_run_loop_embedded_execute_once();
|
||||
}
|
||||
|
||||
void BTstackManager::bleStartScanning(void) {
|
||||
// printf("Start scanning\n");
|
||||
gap_start_scan();
|
||||
}
|
||||
void BTstackManager::bleStopScanning(void) {
|
||||
gap_stop_scan();
|
||||
}
|
||||
|
||||
void BTstackManager::setGATTCharacteristicRead(uint16_t (*cb)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size)) {
|
||||
gattReadCallback = cb;
|
||||
}
|
||||
void BTstackManager::setGATTCharacteristicWrite(int (*cb)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size)) {
|
||||
gattWriteCallback = cb;
|
||||
}
|
||||
void BTstackManager::addGATTService(UUID * uuid) {
|
||||
att_db_util_add_service_uuid128((uint8_t*)uuid->getUuid());
|
||||
}
|
||||
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text) {
|
||||
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, (uint8_t*)text, strlen(text));
|
||||
}
|
||||
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len) {
|
||||
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, data, data_len);
|
||||
}
|
||||
uint16_t BTstackManager::addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id) {
|
||||
(void) characteristic_id;
|
||||
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0);
|
||||
}
|
||||
void BTstackManager::setAdvData(uint16_t adv_data_len, const uint8_t * adv_data) {
|
||||
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
|
||||
}
|
||||
void BTstackManager::startAdvertising() {
|
||||
gap_advertisements_enable(1);
|
||||
}
|
||||
void BTstackManager::stopAdvertising() {
|
||||
gap_advertisements_enable(0);
|
||||
}
|
||||
void BTstackManager::iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power) {
|
||||
memcpy(adv_data, iBeaconAdvertisement01, sizeof(iBeaconAdvertisement01));
|
||||
adv_data[2] = 0x06;
|
||||
memcpy(&adv_data[3], iBeaconAdvertisement38, sizeof(iBeaconAdvertisement38));
|
||||
memcpy(&adv_data[9], uuid->getUuid(), 16);
|
||||
big_endian_store_16(adv_data, 25, major_id);
|
||||
big_endian_store_16(adv_data, 27, minor_id);
|
||||
adv_data[29] = measured_power;
|
||||
adv_data_len = 30;
|
||||
gap_advertisements_set_data(adv_data_len, adv_data);
|
||||
}
|
||||
// 02 01 06 1A FF 4C 00 02 15 -- F8 97 17 7B AE E8 47 67 8E CC CC 69 4F D5 FC EE -- 12 67 00 02 00 00
|
||||
// 02 01 06 1a ff 4c 00 02 15 -- FB 0B 57 A2 82 28 44 CD 91 3A 94 A1 22 BA 12 06 -- 00 01 00 02 D1 00
|
||||
|
||||
|
||||
BTstackManager BTstack;
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
Arduino Wrapper for BTstack
|
||||
*/
|
||||
|
||||
#include <_needsbt.h>
|
||||
|
||||
#ifndef __ARDUINO_BTSTACK_H
|
||||
#define __ARDUINO_BTSTACK_H
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ble/att_db.h"
|
||||
#include "btstack_util.h"
|
||||
#include "ble/gatt_client.h"
|
||||
#include "hci.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum BLEStatus {
|
||||
BLE_STATUS_OK,
|
||||
BLE_STATUS_DONE, // e.g. for service or characteristic discovery done
|
||||
BLE_STATUS_CONNECTION_TIMEOUT,
|
||||
BLE_STATUS_CONNECTION_ERROR,
|
||||
BLE_STATUS_OTHER_ERROR
|
||||
} BLEStatus;
|
||||
|
||||
typedef void (*btstack_packet_handler_t)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
|
||||
class UUID {
|
||||
private:
|
||||
uint8_t uuid[16];
|
||||
public:
|
||||
UUID();
|
||||
UUID(const uint8_t uuid[16]);
|
||||
UUID(const char * uuidStr);
|
||||
const char * getUuidString() const;
|
||||
const char * getUuid128String() const;
|
||||
const uint8_t * getUuid(void) const;
|
||||
bool matches(UUID *uuid) const;
|
||||
};
|
||||
|
||||
typedef enum BD_ADDR_TYPE {
|
||||
PUBLIC_ADDRESS = 0,
|
||||
PRIVAT_ADDRESS
|
||||
} BD_ADDR_TYPE;
|
||||
|
||||
class BD_ADDR {
|
||||
private:
|
||||
uint8_t address[6];
|
||||
BD_ADDR_TYPE address_type;
|
||||
public:
|
||||
BD_ADDR();
|
||||
BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
|
||||
BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
|
||||
const uint8_t * getAddress();
|
||||
const char * getAddressString();
|
||||
BD_ADDR_TYPE getAddressType();
|
||||
};
|
||||
|
||||
class BLEAdvertisement {
|
||||
private:
|
||||
uint8_t advertising_event_type;
|
||||
uint8_t rssi;
|
||||
uint8_t data_length;
|
||||
uint8_t data[10 + LE_ADVERTISING_DATA_SIZE];
|
||||
BD_ADDR bd_addr;
|
||||
UUID * iBeacon_UUID;
|
||||
public:
|
||||
BLEAdvertisement(uint8_t * event_packet);
|
||||
~BLEAdvertisement();
|
||||
BD_ADDR * getBdAddr();
|
||||
BD_ADDR_TYPE getBdAddrType();
|
||||
int getRssi();
|
||||
bool containsService(UUID * service);
|
||||
bool nameHasPrefix(const char * namePrefix);
|
||||
const uint8_t * getAdvData();
|
||||
bool isIBeacon();
|
||||
const UUID * getIBeaconUUID();
|
||||
uint16_t getIBeaconMajorID();
|
||||
uint16_t getIBecaonMinorID();
|
||||
uint8_t getiBeaconMeasuredPower();
|
||||
};
|
||||
|
||||
class BLECharacteristic {
|
||||
private:
|
||||
gatt_client_characteristic_t characteristic;
|
||||
UUID uuid;
|
||||
public:
|
||||
BLECharacteristic();
|
||||
BLECharacteristic(gatt_client_characteristic_t characteristic);
|
||||
const UUID * getUUID();
|
||||
bool matches(UUID * uuid);
|
||||
bool isValueHandle(uint16_t value_handle);
|
||||
const gatt_client_characteristic_t * getCharacteristic();
|
||||
};
|
||||
|
||||
class BLEService {
|
||||
private:
|
||||
gatt_client_service_t service;
|
||||
UUID uuid;
|
||||
public:
|
||||
BLEService();
|
||||
BLEService(gatt_client_service_t service);
|
||||
const UUID * getUUID();
|
||||
bool matches(UUID * uuid);
|
||||
const gatt_client_service_t * getService();
|
||||
};
|
||||
|
||||
class BLEDevice {
|
||||
private:
|
||||
hci_con_handle_t handle;
|
||||
public:
|
||||
BLEDevice();
|
||||
BLEDevice(hci_con_handle_t handle);
|
||||
hci_con_handle_t getHandle();
|
||||
|
||||
// discovery of services and characteristics
|
||||
int discoverGATTServices();
|
||||
int discoverCharacteristicsForService(BLEService * service);
|
||||
|
||||
// read/write
|
||||
int readCharacteristic(BLECharacteristic * characteristic);
|
||||
int writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
|
||||
int writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
|
||||
|
||||
// subscribe/unsubscribe
|
||||
int subscribeForNotifications(BLECharacteristic * characteristic);
|
||||
int unsubscribeFromNotifications(BLECharacteristic * characteristic);
|
||||
int subscribeForIndications(BLECharacteristic * characteristic);
|
||||
int unsubscribeFromIndications(BLECharacteristic * characteristic);
|
||||
};
|
||||
|
||||
class BTstackManager {
|
||||
public:
|
||||
BTstackManager(void);
|
||||
void setup(void);
|
||||
void setup(const char * name);
|
||||
void loop(void);
|
||||
|
||||
void setPublicBdAddr(bd_addr_t addr);
|
||||
void enablePacketLogger();
|
||||
void enableDebugLogger();
|
||||
|
||||
void setAdvData(uint16_t size, const uint8_t * data);
|
||||
void iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power = 0xc6);
|
||||
void startAdvertising();
|
||||
void stopAdvertising();
|
||||
|
||||
void bleStartScanning();
|
||||
void bleStopScanning();
|
||||
|
||||
// connection management
|
||||
void bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms);
|
||||
void bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms);
|
||||
void bleConnect(BD_ADDR * address, int timeout_ms);
|
||||
void bleConnect(BLEAdvertisement * advertisement, int timeout_ms);
|
||||
void bleDisconnect(BLEDevice * device);
|
||||
|
||||
// discovery of services and characteristics
|
||||
int discoverGATTServices(BLEDevice * device);
|
||||
int discoverCharacteristicsForService(BLEDevice * peripheral, BLEService * service);
|
||||
|
||||
// read/write
|
||||
int readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic);
|
||||
int writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
|
||||
int writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
|
||||
|
||||
// subscribe/unsubscribe notification and indications
|
||||
int subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic);
|
||||
int unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic);
|
||||
int subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic);
|
||||
int unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic);
|
||||
|
||||
// Callbacks
|
||||
void setBLEAdvertisementCallback(void (*)(BLEAdvertisement * bleAdvertisement));
|
||||
void setBLEDeviceConnectedCallback(void (*)(BLEStatus status, BLEDevice * device));
|
||||
void setBLEDeviceDisconnectedCallback(void (*)(BLEDevice * device));
|
||||
void setGATTServiceDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLEService * bleService));
|
||||
void setGATTCharacteristicDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic));
|
||||
void setGATTCharacteristicReadCallback(void (*)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length));
|
||||
void setGATTCharacteristicNotificationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
|
||||
void setGATTCharacteristicIndicationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
|
||||
void setGATTDoneCallback(void (*)(BLEStatus status, BLEDevice * device));
|
||||
|
||||
void setGATTCharacteristicWrittenCallback(void (*)(BLEStatus status, BLEDevice * device));
|
||||
void setGATTCharacteristicSubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
|
||||
void setGATTCharacteristicUnsubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
|
||||
|
||||
void setGATTCharacteristicRead(uint16_t (*)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size));
|
||||
void setGATTCharacteristicWrite(int (*)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size));
|
||||
|
||||
void addGATTService(UUID * uuid);
|
||||
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text);
|
||||
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len);
|
||||
uint16_t addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id);
|
||||
};
|
||||
|
||||
extern BTstackManager BTstack;
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __ARDUINO_BTSTACK_H
|
||||
@@ -2,6 +2,8 @@
|
||||
# https://arduino.github.io/arduino-cli/library-specification/#keywords
|
||||
# Formatted by a single true tab (not spaces)
|
||||
|
||||
FreeRTOS KEYWORD1
|
||||
|
||||
# Datatypes (KEYWORD1)
|
||||
StackType_t KEYWORD1
|
||||
BaseType_t KEYWORD1
|
||||
@@ -34,6 +36,7 @@ pcTaskGetName KEYWORD2
|
||||
ulTaskNotifyTake KEYWORD2
|
||||
vTaskNotifyGiveFromISR KEYWORD2
|
||||
taskYIELD KEYWORD2
|
||||
vTaskCoreAffinitySet KEYWORD2
|
||||
vTaskSuspend KEYWORD2
|
||||
vTaskResume KEYWORD2
|
||||
xTaskResumeFromISR KEYWORD2
|
||||
@@ -41,6 +44,7 @@ xTaskGetTickCount KEYWORD2
|
||||
xTaskGetTickCountFromISR KEYWORD2
|
||||
uxTaskGetNumberOfTasks KEYWORD2
|
||||
uxTaskGetStackHighWaterMark KEYWORD2
|
||||
uxTaskGetSystemState KEYWORD2
|
||||
|
||||
# Instances (KEYWORD2)
|
||||
|
||||
|
||||
Submodule libraries/FreeRTOS/lib/FreeRTOS-Kernel updated: b5942ddd07...29fdcf092a
@@ -29,10 +29,7 @@
|
||||
#define configUSE_TASK_PREEMPTION_DISABLE 1
|
||||
|
||||
#define configUSE_NEWLIB_REENTRANT 1
|
||||
#define configNEWLIB_REENTRANT_IS_DYNAMIC 0 /* Note that we have a different config option, portSET_IMPURE_PTR */
|
||||
#include <reent.h>
|
||||
extern void __register_impure_ptr(struct _reent *p);
|
||||
#define portSET_IMPURE_PTR(x) __register_impure_ptr(x)
|
||||
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1
|
||||
|
||||
/* Run time stats related definitions. */
|
||||
void vMainConfigureTimerForRunTimeStats(void);
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
#include "../lib/FreeRTOS-Kernel/tasks.c"
|
||||
|
||||
//See https://github.com/FreeRTOS/FreeRTOS-Kernel/pull/496
|
||||
struct _reent* __wrap___getreent(void) {
|
||||
// No lock needed because if this changes, we won't be running anymore.
|
||||
TCB_t *pxCurTask = xTaskGetCurrentTaskHandle();
|
||||
if (pxCurTask == NULL) {
|
||||
// No task running. Return global struct.
|
||||
return _GLOBAL_REENT;
|
||||
} else {
|
||||
// We have a task; return its reentrant struct.
|
||||
return &pxCurTask->xNewLib_reent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,59 @@
|
||||
#include <pico.h>
|
||||
#include <pico/time.h>
|
||||
|
||||
#include "_freertos.h"
|
||||
#include <_freertos.h>
|
||||
|
||||
// Interfaces for the main core to use FreeRTOS mutexes
|
||||
extern "C" {
|
||||
extern volatile bool __otherCoreIdled;
|
||||
|
||||
SemaphoreHandle_t __freertos_mutex_create() {
|
||||
return xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
SemaphoreHandle_t _freertos_recursive_mutex_create() {
|
||||
return xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
|
||||
void __freertos_mutex_take(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreTake(mtx, portMAX_DELAY);
|
||||
}
|
||||
|
||||
int __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) {
|
||||
return xSemaphoreTakeFromISR(mtx, pxHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
int __freertos_mutex_try_take(SemaphoreHandle_t mtx) {
|
||||
return xSemaphoreTake(mtx, 0);
|
||||
}
|
||||
|
||||
void __freertos_mutex_give(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreGive(mtx);
|
||||
}
|
||||
|
||||
void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) {
|
||||
BaseType_t hiPrio = pxHigherPriorityTaskWoken ? *pxHigherPriorityTaskWoken : pdFALSE;
|
||||
xSemaphoreGiveFromISR(mtx, &hiPrio);
|
||||
portYIELD_FROM_ISR(hiPrio);
|
||||
}
|
||||
|
||||
void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
|
||||
}
|
||||
|
||||
int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) {
|
||||
return xSemaphoreTakeRecursive(mtx, 0);
|
||||
}
|
||||
|
||||
void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreGiveRecursive(mtx);
|
||||
}
|
||||
|
||||
bool __freertos_check_if_in_isr() {
|
||||
return portCHECK_IF_IN_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
@@ -108,6 +160,40 @@ extern "C" void yield() {
|
||||
taskYIELD();
|
||||
}
|
||||
|
||||
static TaskHandle_t __idleCoreTask[2];
|
||||
static void __no_inline_not_in_flash_func(IdleThisCore)(void *param) {
|
||||
(void) param;
|
||||
while (true) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
portDISABLE_INTERRUPTS();
|
||||
__otherCoreIdled = true;
|
||||
while (__otherCoreIdled) {
|
||||
/* noop */
|
||||
}
|
||||
portENABLE_INTERRUPTS();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __no_inline_not_in_flash_func(__freertos_idle_other_core)() {
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
xTaskNotifyGive(__idleCoreTask[ 1 ^ sio_hw->cpuid ]);
|
||||
while (!__otherCoreIdled) {
|
||||
/* noop */
|
||||
}
|
||||
portDISABLE_INTERRUPTS();
|
||||
vTaskSuspendAll();
|
||||
}
|
||||
|
||||
extern "C" void __no_inline_not_in_flash_func(__freertos_resume_other_core)() {
|
||||
__otherCoreIdled = false;
|
||||
portENABLE_INTERRUPTS();
|
||||
xTaskResumeAll();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
}
|
||||
|
||||
|
||||
extern mutex_t __usb_mutex;
|
||||
static TaskHandle_t __usbTask;
|
||||
static void __usb(void *param);
|
||||
@@ -124,6 +210,12 @@ void startFreeRTOS(void) {
|
||||
vTaskCoreAffinitySet(c1, 1 << 1);
|
||||
}
|
||||
|
||||
// Create the idle-other-core tasks (for when flash is being written)
|
||||
xTaskCreate(IdleThisCore, "IdleCore0", 128, 0, configMAX_PRIORITIES - 1, __idleCoreTask + 0);
|
||||
vTaskCoreAffinitySet(__idleCoreTask[0], 1 << 0);
|
||||
xTaskCreate(IdleThisCore, "IdleCore1", 128, 0, configMAX_PRIORITIES - 1, __idleCoreTask + 1);
|
||||
vTaskCoreAffinitySet(__idleCoreTask[1], 1 << 1);
|
||||
|
||||
// Initialise and run the freeRTOS scheduler. Execution should never return here.
|
||||
__freeRTOSinitted = true;
|
||||
vTaskStartScheduler();
|
||||
@@ -216,16 +308,20 @@ void vApplicationTickHook(void) {
|
||||
Private function to enable board led to use it in application hooks
|
||||
*/
|
||||
void prvSetMainLedOn(void) {
|
||||
#ifdef LED_BUILTIN
|
||||
gpio_init(LED_BUILTIN);
|
||||
gpio_set_dir(LED_BUILTIN, true);
|
||||
gpio_put(LED_BUILTIN, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
Private function to blink board led to use it in application hooks
|
||||
*/
|
||||
void prvBlinkMainLed(void) {
|
||||
#ifdef LED_BUILTIN
|
||||
gpio_put(LED_BUILTIN, !gpio_get(LED_BUILTIN));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -376,9 +472,10 @@ static void __usb(void *param) {
|
||||
__usbInitted = true;
|
||||
|
||||
while (true) {
|
||||
if (mutex_try_enter(&__usb_mutex, NULL)) {
|
||||
auto m = __get_freertos_mutex_for_ptr(&__usb_mutex);
|
||||
if (xSemaphoreTake(m, 0)) {
|
||||
tud_task();
|
||||
mutex_exit(&__usb_mutex);
|
||||
xSemaphoreGive(m);
|
||||
}
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
@@ -393,54 +490,7 @@ void __USBStart() {
|
||||
__SetupDescHIDReport();
|
||||
__SetupUSBDescriptor();
|
||||
|
||||
// Make highest prio and locked to core 0
|
||||
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 1, &__usbTask);
|
||||
// Make high prio and locked to core 0
|
||||
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 2, &__usbTask);
|
||||
vTaskCoreAffinitySet(__usbTask, 1 << 0);
|
||||
}
|
||||
|
||||
|
||||
// Interfaces for the main core to use FreeRTOS mutexes
|
||||
extern "C" {
|
||||
|
||||
SemaphoreHandle_t __freertos_mutex_create() {
|
||||
return xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
SemaphoreHandle_t _freertos_recursive_mutex_create() {
|
||||
return xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
|
||||
void __freertos_mutex_take(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreTake(mtx, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreTakeFromISR(mtx, NULL);
|
||||
}
|
||||
|
||||
int __freertos_mutex_try_take(SemaphoreHandle_t mtx) {
|
||||
return xSemaphoreTake(mtx, 0);
|
||||
}
|
||||
|
||||
void __freertos_mutex_give(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreGive(mtx);
|
||||
}
|
||||
|
||||
void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreGiveFromISR(mtx, NULL);
|
||||
}
|
||||
|
||||
void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
|
||||
}
|
||||
|
||||
int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) {
|
||||
return xSemaphoreTakeRecursive(mtx, 0);
|
||||
}
|
||||
|
||||
void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) {
|
||||
xSemaphoreGiveRecursive(mtx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
PicoBluetoothHID KEYWORD1
|
||||
PicoBluetoothBLEHID KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
setOpenedCB KEYWORD2
|
||||
setClosedCB KEYWORD2
|
||||
setCanSendNowCB KEYWORD2
|
||||
startHID KEYWORD2
|
||||
connected KEYWORD2
|
||||
send KEYWORD2
|
||||
lockBluetooth KEYWORD2
|
||||
unlockBluetooth KEYWORD2
|
||||
getCID KEYWORD2
|
||||
setBattery KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
name=HID_Bluetooth
|
||||
version=1.0.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Implements a HID device base class for PicoW Bluetooth
|
||||
paragraph=Implements a HID device base class for PicoW Bluetooth
|
||||
category=Device Control
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifdef ENABLE_CLASSIC
|
||||
#include "PicoBluetoothHID.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BLE
|
||||
#include "PicoBluetoothBLEHID.h"
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
PicoBluetoothBLEHID.h - Simple wrapper for BT-HID objects like
|
||||
keyboards, mice, gamepads.
|
||||
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "PicoBluetoothBLEHID.h"
|
||||
|
||||
PicoBluetoothBLEHID_ PicoBluetoothBLEHID;
|
||||
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
PicoBluetoothBLEHID.h - Simple wrapper for BT-HID objects like
|
||||
keyboards, mice, gamepads using Bluetooth LE mode
|
||||
Based off of the BTStack HID main loop
|
||||
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <_needsbt.h>
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
#include <pico/cyw43_arch.h>
|
||||
#include <class/hid/hid_device.h>
|
||||
|
||||
// The BTStack has redefinitions of this USB enum (same values, just redefined), so hide it to allow easy compilation
|
||||
#define HID_REPORT_TYPE_INPUT HID_REPORT_TYPE_INPUT_BT
|
||||
#define HID_REPORT_TYPE_OUTPUT HID_REPORT_TYPE_OUTPUT_BT
|
||||
#define HID_REPORT_TYPE_FEATURE HID_REPORT_TYPE_FEATURE_BT
|
||||
#define hid_report_type_t hid_report_type_t_bt
|
||||
#include <btstack.h>
|
||||
#undef hid_report_type_t
|
||||
#undef HID_REPORT_TYPE_FEATURE
|
||||
#undef HID_REPORT_TYPE_OUTPUT
|
||||
#undef HID_REPORT_TYPE_INPUT
|
||||
|
||||
#include <ble/sm.h>
|
||||
#include <ble/att_server.h>
|
||||
#include <btstack_event.h>
|
||||
#include <ble/gatt-service/battery_service_server.h>
|
||||
#include <ble/gatt-service/device_information_service_server.h>
|
||||
#include <ble/gatt-service/hids_device.h>
|
||||
|
||||
class PicoBluetoothBLEHID_;
|
||||
extern PicoBluetoothBLEHID_ PicoBluetoothBLEHID;
|
||||
|
||||
class PicoBluetoothBLEHID_ {
|
||||
public:
|
||||
PicoBluetoothBLEHID_() {
|
||||
}
|
||||
|
||||
~PicoBluetoothBLEHID_() {
|
||||
}
|
||||
|
||||
// Optional callback, not used presently
|
||||
// Usage: PicoBluetoothHID.setCanSendNowCB(std::bind(&kbd::onCanSendNow, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
typedef std::function<void(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size)> BTCallback;
|
||||
|
||||
void setOpenedCB(BTCallback cb) {
|
||||
_onOpened = cb;
|
||||
}
|
||||
|
||||
void setClosedCB(BTCallback cb) {
|
||||
_onClosed = cb;
|
||||
}
|
||||
|
||||
void setCanSendNowCB(BTCallback cb) {
|
||||
_onCanSendNow = cb;
|
||||
}
|
||||
|
||||
bool startHID(const char *localName, const char *hidName, uint16_t appearance, const uint8_t *hidDescriptor, uint16_t hidDescriptorSize, int battery = 100) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
_running = true;
|
||||
|
||||
_buildAdvData(localName, appearance);
|
||||
_buildAttdb(hidName);
|
||||
|
||||
_battery = battery;
|
||||
|
||||
// Setup L2CAP
|
||||
l2cap_init();
|
||||
|
||||
// Setup SM
|
||||
sm_init();
|
||||
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
|
||||
sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION | SM_AUTHREQ_BONDING);
|
||||
|
||||
// Setup ATT server
|
||||
att_server_init(_attdb, NULL, NULL);
|
||||
|
||||
// Setup battery service
|
||||
battery_service_server_init(battery);
|
||||
|
||||
// Setup device information service
|
||||
device_information_service_server_init();
|
||||
|
||||
// Setup HID Device service
|
||||
hids_device_init(0, hidDescriptor, hidDescriptorSize);
|
||||
|
||||
// Setup advertisements
|
||||
uint16_t adv_int_min = 0x0030;
|
||||
uint16_t adv_int_max = 0x0030;
|
||||
uint8_t adv_type = 0;
|
||||
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(_advDataLen, _advData);
|
||||
gap_advertisements_enable(1);
|
||||
|
||||
// Register for HCI events
|
||||
_hci_event_callback_registration.callback = PacketHandlerWrapper;
|
||||
hci_add_event_handler(&_hci_event_callback_registration);
|
||||
|
||||
// Register for SM events
|
||||
_sm_event_callback_registration.callback = PacketHandlerWrapper;
|
||||
sm_add_event_handler(&_sm_event_callback_registration);
|
||||
|
||||
// Register for HIDS events
|
||||
hids_device_register_packet_handler(PacketHandlerWrapper);
|
||||
|
||||
// GO!
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void PacketHandlerWrapper(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t packet_size) {
|
||||
PicoBluetoothBLEHID.packetHandler(packet_type, channel, packet, packet_size);
|
||||
}
|
||||
|
||||
void packetHandler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
||||
if (type != HCI_EVENT_PACKET) {
|
||||
return;
|
||||
}
|
||||
switch (hci_event_packet_get_type(packet)) {
|
||||
case HCI_EVENT_DISCONNECTION_COMPLETE:
|
||||
_con_handle = HCI_CON_HANDLE_INVALID;
|
||||
if (_onClosed) {
|
||||
_onClosed(type, channel, packet, size);
|
||||
}
|
||||
break;
|
||||
case SM_EVENT_JUST_WORKS_REQUEST:
|
||||
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
|
||||
break;
|
||||
case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
|
||||
// printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
|
||||
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
|
||||
break;
|
||||
case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
|
||||
//printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
|
||||
break;
|
||||
case HCI_EVENT_HIDS_META:
|
||||
switch (hci_event_hids_meta_get_subevent_code(packet)) {
|
||||
case HIDS_SUBEVENT_INPUT_REPORT_ENABLE:
|
||||
_con_handle = hids_subevent_input_report_enable_get_con_handle(packet);
|
||||
if (_onOpened) {
|
||||
_onOpened(type, channel, packet, size);
|
||||
}
|
||||
break;
|
||||
case HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE:
|
||||
_con_handle = hids_subevent_boot_keyboard_input_report_enable_get_con_handle(packet);
|
||||
if (_onOpened) {
|
||||
_onOpened(type, channel, packet, size);
|
||||
}
|
||||
break;
|
||||
case HIDS_SUBEVENT_PROTOCOL_MODE:
|
||||
_protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet);
|
||||
break;
|
||||
case HIDS_SUBEVENT_CAN_SEND_NOW:
|
||||
switch (_protocol_mode) {
|
||||
case 0:
|
||||
hids_device_send_boot_keyboard_input_report(_con_handle, (const uint8_t *)_sendReport, _sendReportLen);
|
||||
break;
|
||||
case 1:
|
||||
hids_device_send_input_report(_con_handle, (const uint8_t *)_sendReport, _sendReportLen);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_needToSend = false;
|
||||
if (_onCanSendNow) {
|
||||
_onCanSendNow(type, channel, packet, size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool end() {
|
||||
if (_running) {
|
||||
hci_power_control(HCI_POWER_OFF);
|
||||
}
|
||||
_running = false;
|
||||
_needToSend = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool connected() {
|
||||
return _con_handle != HCI_CON_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
bool send(void *rpt, int len) {
|
||||
_needToSend = true;
|
||||
_sendReport = rpt;
|
||||
_sendReportLen = len;
|
||||
lockBluetooth();
|
||||
hids_device_request_can_send_now_event(_con_handle);
|
||||
unlockBluetooth();
|
||||
while (connected() && _needToSend) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
return connected();
|
||||
}
|
||||
|
||||
bool setBattery(int level) {
|
||||
if (!_running || (level < 0) || (level > 100)) {
|
||||
return false;
|
||||
}
|
||||
battery_service_server_set_battery_value(level);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lockBluetooth() {
|
||||
async_context_acquire_lock_blocking(cyw43_arch_async_context());
|
||||
}
|
||||
|
||||
static void unlockBluetooth() {
|
||||
async_context_release_lock(cyw43_arch_async_context());
|
||||
}
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
|
||||
BTCallback _onOpened = nullptr;
|
||||
BTCallback _onClosed = nullptr;
|
||||
BTCallback _onCanSendNow = nullptr;
|
||||
|
||||
btstack_packet_callback_registration_t _hci_event_callback_registration;
|
||||
btstack_packet_callback_registration_t _sm_event_callback_registration;
|
||||
uint8_t _battery = 100;
|
||||
hci_con_handle_t _con_handle = HCI_CON_HANDLE_INVALID;
|
||||
uint8_t _protocol_mode = 1;
|
||||
|
||||
void _buildAdvData(const char *completeLocalName, uint16_t appearance) {
|
||||
free(_advData);
|
||||
_advDataLen = 13 + strlen(completeLocalName);
|
||||
_advData = (uint8_t*) malloc(_advDataLen);
|
||||
int i = 0;
|
||||
// Flags general discoverable, BR/EDR not supported
|
||||
// 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
|
||||
_advData[i++] = 0x02;
|
||||
_advData[i++] = BLUETOOTH_DATA_TYPE_FLAGS;
|
||||
_advData[i++] = 0x06;
|
||||
// Name
|
||||
// 0x0d, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME,
|
||||
_advData[i++] = 1 + strlen(completeLocalName);
|
||||
_advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME;
|
||||
memcpy(_advData + i, completeLocalName, strlen(completeLocalName));
|
||||
i += strlen(completeLocalName);
|
||||
// 16-bit Service UUIDs
|
||||
// 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE & 0xff, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE >> 8,
|
||||
_advData[i++] = 0x03;
|
||||
_advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS;
|
||||
_advData[i++] = ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE & 0xff;
|
||||
_advData[i++] = ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE >> 8;
|
||||
// Appearance HID - Keyboard (Category 15, Sub-Category 1)
|
||||
// 0x03, BLUETOOTH_DATA_TYPE_APPEARANCE, 0xC1, 0x03,
|
||||
_advData[i++] = 0x03;
|
||||
_advData[i++] = BLUETOOTH_DATA_TYPE_APPEARANCE;
|
||||
_advData[i++] = appearance & 0xff;
|
||||
_advData[i++] = appearance >> 8;
|
||||
}
|
||||
uint8_t *_advData = nullptr;
|
||||
uint8_t _advDataLen = 0;
|
||||
|
||||
void _buildAttdb(const char *hidName) {
|
||||
free(_attdb);
|
||||
_attdbLen = sizeof(_attdb_head) + 8 + strlen(hidName) + sizeof(_attdb_tail);
|
||||
_attdb = (uint8_t *) malloc(_attdbLen);
|
||||
memcpy(_attdb, _attdb_head, sizeof(_attdb_head));
|
||||
// 0x0003 VALUE CHARACTERISTIC-GAP_DEVICE_NAME - READ -'HID Mouse'
|
||||
// READ_ANYBODY
|
||||
// 0x11, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x48, 0x49, 0x44, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65,
|
||||
int i = sizeof(_attdb_head);
|
||||
_attdb[i++] = 8 + strlen(hidName);
|
||||
_attdb[i++] = 0x00;
|
||||
_attdb[i++] = 0x02;
|
||||
_attdb[i++] = 0x00;
|
||||
_attdb[i++] = 0x03;
|
||||
_attdb[i++] = 0x00;
|
||||
_attdb[i++] = 0x00;
|
||||
_attdb[i++] = 0x2a;
|
||||
memcpy(_attdb + i, hidName, strlen(hidName));
|
||||
i += strlen(hidName);
|
||||
memcpy(_attdb + i, _attdb_tail, sizeof(_attdb_tail));
|
||||
}
|
||||
uint8_t *_attdb = nullptr;
|
||||
int _attdbLen = 0;
|
||||
|
||||
static constexpr const uint8_t _attdb_head[] = {
|
||||
// ATT DB Version
|
||||
1,
|
||||
|
||||
// 0x0001 PRIMARY_SERVICE-GAP_SERVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18,
|
||||
// 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME - READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_tail[] = {
|
||||
// #import <battery_service.gatt> -- BEGIN
|
||||
// Specification Type org.bluetooth.service.battery_service
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.battery_service.xml
|
||||
// Battery Service 180F
|
||||
// 0x0004 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x28, 0x0f, 0x18,
|
||||
// 0x0005 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x28, 0x12, 0x06, 0x00, 0x19, 0x2a,
|
||||
// 0x0006 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x06, 0x00, 0x19, 0x2a,
|
||||
// 0x0007 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x07, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// #import <battery_service.gatt> -- END
|
||||
// add Device ID Service
|
||||
|
||||
// #import <device_information_service.gatt> -- BEGIN
|
||||
// Specification Type org.bluetooth.service.device_information
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.device_information.xml
|
||||
// Device Information 180A
|
||||
// 0x0008 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION
|
||||
0x0a, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x28, 0x0a, 0x18,
|
||||
// 0x0009 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x09, 0x00, 0x03, 0x28, 0x02, 0x0a, 0x00, 0x29, 0x2a,
|
||||
// 0x000a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x0a, 0x00, 0x29, 0x2a,
|
||||
// 0x000b CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x28, 0x02, 0x0c, 0x00, 0x24, 0x2a,
|
||||
// 0x000c VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x0c, 0x00, 0x24, 0x2a,
|
||||
// 0x000d CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x03, 0x28, 0x02, 0x0e, 0x00, 0x25, 0x2a,
|
||||
// 0x000e VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x0e, 0x00, 0x25, 0x2a,
|
||||
// 0x000f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x03, 0x28, 0x02, 0x10, 0x00, 0x27, 0x2a,
|
||||
// 0x0010 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x10, 0x00, 0x27, 0x2a,
|
||||
// 0x0011 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x11, 0x00, 0x03, 0x28, 0x02, 0x12, 0x00, 0x26, 0x2a,
|
||||
// 0x0012 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x26, 0x2a,
|
||||
// 0x0013 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x13, 0x00, 0x03, 0x28, 0x02, 0x14, 0x00, 0x28, 0x2a,
|
||||
// 0x0014 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x14, 0x00, 0x28, 0x2a,
|
||||
// 0x0015 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x15, 0x00, 0x03, 0x28, 0x02, 0x16, 0x00, 0x23, 0x2a,
|
||||
// 0x0016 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x16, 0x00, 0x23, 0x2a,
|
||||
// 0x0017 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x17, 0x00, 0x03, 0x28, 0x02, 0x18, 0x00, 0x2a, 0x2a,
|
||||
// 0x0018 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x18, 0x00, 0x2a, 0x2a,
|
||||
// 0x0019 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x19, 0x00, 0x03, 0x28, 0x02, 0x1a, 0x00, 0x50, 0x2a,
|
||||
// 0x001a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x1a, 0x00, 0x50, 0x2a,
|
||||
// #import <device_information_service.gatt> -- END
|
||||
|
||||
// #import <hids.gatt> -- BEGIN
|
||||
// Specification Type org.bluetooth.service.human_interface_device
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.human_interface_device.xml
|
||||
// Human Interface Device 1812
|
||||
// 0x001b PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x28, 0x12, 0x18,
|
||||
// 0x001c CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE - DYNAMIC | READ | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x03, 0x28, 0x06, 0x1d, 0x00, 0x4e, 0x2a,
|
||||
// 0x001d VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE - DYNAMIC | READ | WRITE_WITHOUT_RESPONSE
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x06, 0x01, 0x1d, 0x00, 0x4e, 0x2a,
|
||||
// 0x001e CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x03, 0x28, 0x1a, 0x1f, 0x00, 0x4d, 0x2a,
|
||||
// 0x001f VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x08, 0x00, 0x0b, 0xf5, 0x1f, 0x00, 0x4d, 0x2a,
|
||||
// 0x0020 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x0a, 0x00, 0x0f, 0xf1, 0x20, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// fixed report id = 1, type = Input (1)
|
||||
// 0x0021 REPORT_REFERENCE-READ-1-1
|
||||
0x0a, 0x00, 0x02, 0x00, 0x21, 0x00, 0x08, 0x29, 0x1, 0x1,
|
||||
// 0x0022 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x22, 0x00, 0x03, 0x28, 0x1a, 0x23, 0x00, 0x4d, 0x2a,
|
||||
// 0x0023 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x08, 0x00, 0x0b, 0xf5, 0x23, 0x00, 0x4d, 0x2a,
|
||||
// 0x0024 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x0a, 0x00, 0x0f, 0xf1, 0x24, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// fixed report id = 2, type = Output (2)
|
||||
// 0x0025 REPORT_REFERENCE-READ-2-2
|
||||
0x0a, 0x00, 0x02, 0x00, 0x25, 0x00, 0x08, 0x29, 0x2, 0x2,
|
||||
// 0x0026 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x26, 0x00, 0x03, 0x28, 0x1a, 0x27, 0x00, 0x4d, 0x2a,
|
||||
// 0x0027 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x08, 0x00, 0x0b, 0xf5, 0x27, 0x00, 0x4d, 0x2a,
|
||||
// 0x0028 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x0a, 0x00, 0x0f, 0xf1, 0x28, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// fixed report id = 3, type = Feature (3)
|
||||
// 0x0029 REPORT_REFERENCE-READ-3-3
|
||||
0x0a, 0x00, 0x02, 0x00, 0x29, 0x00, 0x08, 0x29, 0x3, 0x3,
|
||||
// 0x002a CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x03, 0x28, 0x02, 0x2b, 0x00, 0x4b, 0x2a,
|
||||
// 0x002b VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP - DYNAMIC | READ
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x2b, 0x00, 0x4b, 0x2a,
|
||||
// 0x002c CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2c, 0x00, 0x03, 0x28, 0x1a, 0x2d, 0x00, 0x22, 0x2a,
|
||||
// 0x002d VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x0a, 0x01, 0x2d, 0x00, 0x22, 0x2a,
|
||||
// 0x002e CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x2e, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// 0x002f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT - DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2f, 0x00, 0x03, 0x28, 0x0e, 0x30, 0x00, 0x32, 0x2a,
|
||||
// 0x0030 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT - DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x0e, 0x01, 0x30, 0x00, 0x32, 0x2a,
|
||||
// 0x0031 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x31, 0x00, 0x03, 0x28, 0x1a, 0x32, 0x00, 0x33, 0x2a,
|
||||
// 0x0032 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x0a, 0x01, 0x32, 0x00, 0x33, 0x2a,
|
||||
// 0x0033 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x33, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// bcdHID = 0x101 (v1.0.1), bCountryCode 0, remote wakeable = 0 | normally connectable 2
|
||||
// 0x0034 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION - READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x34, 0x00, 0x03, 0x28, 0x02, 0x35, 0x00, 0x4a, 0x2a,
|
||||
// 0x0035 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION - READ -'01 01 00 02'
|
||||
// READ_ANYBODY
|
||||
0x0c, 0x00, 0x02, 0x00, 0x35, 0x00, 0x4a, 0x2a, 0x01, 0x01, 0x00, 0x02,
|
||||
// 0x0036 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT - DYNAMIC | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x36, 0x00, 0x03, 0x28, 0x04, 0x37, 0x00, 0x4c, 0x2a,
|
||||
// 0x0037 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT - DYNAMIC | WRITE_WITHOUT_RESPONSE
|
||||
// WRITE_ANYBODY
|
||||
0x08, 0x00, 0x04, 0x01, 0x37, 0x00, 0x4c, 0x2a,
|
||||
// #import <hids.gatt> -- END
|
||||
// 0x0038 PRIMARY_SERVICE-GATT_SERVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x28, 0x01, 0x18,
|
||||
// 0x0039 CHARACTERISTIC-GATT_DATABASE_HASH - READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x39, 0x00, 0x03, 0x28, 0x02, 0x3a, 0x00, 0x2a, 0x2b,
|
||||
// 0x003a VALUE CHARACTERISTIC-GATT_DATABASE_HASH - READ -''
|
||||
// READ_ANYBODY
|
||||
0x18, 0x00, 0x02, 0x00, 0x3a, 0x00, 0x2a, 0x2b, 0xc2, 0x10, 0xf5, 0x75, 0xf3, 0x9f, 0x50, 0xb6, 0x83, 0xc7, 0xfa, 0xac, 0xa6, 0x1b, 0x7d, 0x32,
|
||||
// END
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
volatile bool _needToSend = false;
|
||||
void *_sendReport;
|
||||
int _sendReportLen;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
PicoBluetoothHID.h - Simple wrapper for BT-HID objects like
|
||||
keyboards, mice, gamepads.
|
||||
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "PicoBluetoothHID.h"
|
||||
|
||||
PicoBluetoothHID_ PicoBluetoothHID;
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
PicoBluetoothHID.h - Simple wrapper for BT-HID objects like
|
||||
keyboards, mice, gamepads.
|
||||
Based off of the BTStack HID main loop
|
||||
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <_needsbt.h>
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
#include <pico/cyw43_arch.h>
|
||||
#include <class/hid/hid_device.h>
|
||||
|
||||
// The BTStack has redefinitions of this USB enum (same values, just redefined), so hide it to allow easy compilation
|
||||
#define HID_REPORT_TYPE_INPUT HID_REPORT_TYPE_INPUT_BT
|
||||
#define HID_REPORT_TYPE_OUTPUT HID_REPORT_TYPE_OUTPUT_BT
|
||||
#define HID_REPORT_TYPE_FEATURE HID_REPORT_TYPE_FEATURE_BT
|
||||
#define hid_report_type_t hid_report_type_t_bt
|
||||
#include <btstack.h>
|
||||
#undef hid_report_type_t
|
||||
#undef HID_REPORT_TYPE_FEATURE
|
||||
#undef HID_REPORT_TYPE_OUTPUT
|
||||
#undef HID_REPORT_TYPE_INPUT
|
||||
|
||||
class PicoBluetoothHID_;
|
||||
extern PicoBluetoothHID_ PicoBluetoothHID;
|
||||
|
||||
class PicoBluetoothHID_ {
|
||||
public:
|
||||
PicoBluetoothHID_() {
|
||||
}
|
||||
|
||||
~PicoBluetoothHID_() {
|
||||
}
|
||||
|
||||
// Optional callback, not used presently
|
||||
// Usage: PicoBluetoothHID.setCanSendNowCB(std::bind(&kbd::onCanSendNow, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
typedef std::function<void(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size)> BTCallback;
|
||||
|
||||
void setOpenedCB(BTCallback cb) {
|
||||
_onOpened = cb;
|
||||
}
|
||||
|
||||
void setClosedCB(BTCallback cb) {
|
||||
_onClosed = cb;
|
||||
}
|
||||
|
||||
void setCanSendNowCB(BTCallback cb) {
|
||||
_onCanSendNow = cb;
|
||||
}
|
||||
|
||||
bool startHID(const char *localName, const char *hidName, uint16_t hidClass, uint8_t hidSubclass, const uint8_t *hidDescriptor, uint16_t hidDescriptorSize) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
_running = true;
|
||||
// Allow finding via inquiry
|
||||
gap_discoverable_control(1);
|
||||
// Use Limited Discoverable Mode; Peripheral; Keyboard as CoD
|
||||
gap_set_class_of_device(hidClass);
|
||||
// Set local name to be identified - zeroes will be replaced by actual BD ADDR
|
||||
gap_set_local_name(localName);
|
||||
// Allow for role switch in general and sniff mode
|
||||
gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE);
|
||||
// Allow for role switch on outgoing connections - this allow HID Host to become master when we re-connect to it
|
||||
gap_set_allow_role_switch(true);
|
||||
|
||||
// L2CAP
|
||||
l2cap_init();
|
||||
#ifdef ENABLE_BLE
|
||||
// Initialize LE Security Manager. Needed for cross-transport key derivation
|
||||
sm_init();
|
||||
#endif
|
||||
|
||||
// SDP Server
|
||||
sdp_init();
|
||||
bzero(_hid_service_buffer, sizeof(_hid_service_buffer));
|
||||
|
||||
const uint8_t hid_boot_device = 0;
|
||||
const uint8_t hid_virtual_cable = 0;
|
||||
const uint8_t hid_remote_wake = 1;
|
||||
const uint8_t hid_reconnect_initiate = 1;
|
||||
const uint8_t hid_normally_connectable = 1;
|
||||
// When not set to 0xffff, sniff and sniff subrating are enabled
|
||||
const uint16_t host_max_latency = 1600;
|
||||
const uint16_t host_min_timeout = 3200;
|
||||
|
||||
hid_sdp_record_t hid_params = {
|
||||
hidClass, hidSubclass,
|
||||
hid_virtual_cable, hid_remote_wake,
|
||||
hid_reconnect_initiate, (bool)hid_normally_connectable,
|
||||
(bool)hid_boot_device,
|
||||
host_max_latency, host_min_timeout,
|
||||
3200,
|
||||
hidDescriptor,
|
||||
hidDescriptorSize,
|
||||
hidName
|
||||
};
|
||||
|
||||
hid_create_sdp_record(_hid_service_buffer, 0x10001, &hid_params);
|
||||
sdp_register_service(_hid_service_buffer);
|
||||
|
||||
// See https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers if you don't have a USB Vendor ID and need a Bluetooth Vendor ID
|
||||
// device info: BlueKitchen GmbH, product 1, version 1
|
||||
device_id_create_sdp_record(_device_id_sdp_service_buffer, 0x10003, DEVICE_ID_VENDOR_ID_SOURCE_BLUETOOTH, BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH, 1, 1);
|
||||
sdp_register_service(_device_id_sdp_service_buffer);
|
||||
|
||||
// HID Device
|
||||
hid_device_init(hid_boot_device, hidDescriptorSize, hidDescriptor);
|
||||
|
||||
// register for HCI events
|
||||
_hci_event_callback_registration.callback = PacketHandlerWrapper;
|
||||
hci_add_event_handler(&_hci_event_callback_registration);
|
||||
|
||||
// register for HID events
|
||||
hid_device_register_packet_handler(PacketHandlerWrapper);
|
||||
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void PacketHandlerWrapper(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t packet_size) {
|
||||
PicoBluetoothHID.packetHandler(packet_type, channel, packet, packet_size);
|
||||
}
|
||||
|
||||
void packetHandler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
||||
uint8_t status;
|
||||
if (type != HCI_EVENT_PACKET) {
|
||||
return;
|
||||
}
|
||||
switch (hci_event_packet_get_type(packet)) {
|
||||
case BTSTACK_EVENT_STATE:
|
||||
if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) {
|
||||
return;
|
||||
}
|
||||
_appState = APP_NOT_CONNECTED;
|
||||
break;
|
||||
|
||||
case HCI_EVENT_USER_CONFIRMATION_REQUEST:
|
||||
// ssp: inform about user confirmation request
|
||||
//log_info("SSP User Confirmation Request with numeric value '%06" PRIu32 "'\n", hci_event_user_confirmation_request_get_numeric_value(packet));
|
||||
//log_info("SSP User Confirmation Auto accept\n");
|
||||
break;
|
||||
|
||||
case HCI_EVENT_HID_META:
|
||||
switch (hci_event_hid_meta_get_subevent_code(packet)) {
|
||||
case HID_SUBEVENT_CONNECTION_OPENED:
|
||||
status = hid_subevent_connection_opened_get_status(packet);
|
||||
if (status != ERROR_CODE_SUCCESS) {
|
||||
// outgoing connection failed
|
||||
_appState = APP_NOT_CONNECTED;
|
||||
_hid_cid = 0;
|
||||
return;
|
||||
}
|
||||
_appState = APP_CONNECTED;
|
||||
_hid_cid = hid_subevent_connection_opened_get_hid_cid(packet);
|
||||
if (_onOpened) {
|
||||
_onOpened(type, channel, packet, size);
|
||||
}
|
||||
break;
|
||||
case HID_SUBEVENT_CONNECTION_CLOSED:
|
||||
_appState = APP_NOT_CONNECTED;
|
||||
_hid_cid = 0;
|
||||
if (_onClosed) {
|
||||
_onClosed(type, channel, packet, size);
|
||||
}
|
||||
break;
|
||||
case HID_SUBEVENT_CAN_SEND_NOW:
|
||||
uint8_t report[2 + _sendReportLen];
|
||||
report[0] = 0xa1;
|
||||
report[1] = _sendReportID;
|
||||
memcpy(report + 2, _sendReport, _sendReportLen);
|
||||
hid_device_send_interrupt_message(getCID(), report, sizeof(report));
|
||||
_needToSend = false;
|
||||
if (_onCanSendNow) {
|
||||
_onCanSendNow(type, channel, packet, size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool end() {
|
||||
if (_running) {
|
||||
hci_power_control(HCI_POWER_OFF);
|
||||
}
|
||||
_running = false;
|
||||
_needToSend = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool connected() {
|
||||
return _appState == APP_CONNECTED;
|
||||
}
|
||||
|
||||
bool send(int id, void *rpt, int len) {
|
||||
_needToSend = true;
|
||||
_sendReportID = id;
|
||||
_sendReport = rpt;
|
||||
_sendReportLen = len;
|
||||
lockBluetooth();
|
||||
hid_device_request_can_send_now_event(getCID());
|
||||
unlockBluetooth();
|
||||
while (connected() && _needToSend) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
return connected();
|
||||
}
|
||||
|
||||
static void lockBluetooth() {
|
||||
async_context_acquire_lock_blocking(cyw43_arch_async_context());
|
||||
}
|
||||
|
||||
static void unlockBluetooth() {
|
||||
async_context_release_lock(cyw43_arch_async_context());
|
||||
}
|
||||
|
||||
uint16_t getCID() {
|
||||
return _hid_cid;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
|
||||
enum {APP_BOOTING, APP_NOT_CONNECTED, APP_CONNECTING, APP_CONNECTED} _appState = APP_BOOTING;
|
||||
|
||||
BTCallback _onOpened = nullptr;
|
||||
BTCallback _onClosed = nullptr;
|
||||
BTCallback _onCanSendNow = nullptr;
|
||||
|
||||
uint8_t _hid_service_buffer[300];
|
||||
uint8_t _device_id_sdp_service_buffer[100];
|
||||
btstack_packet_callback_registration_t _hci_event_callback_registration;
|
||||
uint16_t _hid_cid;
|
||||
|
||||
volatile bool _needToSend = false;
|
||||
int _sendReportID;
|
||||
void *_sendReport;
|
||||
int _sendReportLen;
|
||||
};
|
||||
Submodule
+1
Submodule libraries/HID_Joystick added at 4eb3e28dc2
Submodule
+1
Submodule libraries/HID_Keyboard added at c0f474695f
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user