Compare commits
@@ -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
|
||||
|
||||
@@ -9,6 +9,9 @@ This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosys
|
||||
# Documentation
|
||||
See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for more detailed usage information.
|
||||
|
||||
# Contributing
|
||||
Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blob/master/docs/contrib.rst) for more information on submitting pull requests and porting libraries or sketches to this core.
|
||||
|
||||
# Supported Boards
|
||||
* Raspberry Pi Pico
|
||||
* Raspberry Pi Pico W
|
||||
@@ -37,11 +40,14 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* 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
|
||||
* Seeed Indicator RP2040
|
||||
* Seeed XIAO RP2040
|
||||
* Solder Party RP2040 Stamp
|
||||
* SparkFun ProMicro RP2040
|
||||
@@ -60,13 +66,33 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* 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:
|
||||
@@ -84,6 +110,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
|
||||
@@ -160,7 +192,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
|
||||
@@ -191,6 +224,9 @@ Here are some links to coverage and additional tutorials for using `arduino-pico
|
||||
* Adafruit Feather RP2040 running LCD + TMP117 - https://www.youtube.com/watch?v=fKDeqZiIwHg
|
||||
* Demonstration of Servos and I2C in Korean - https://cafe.naver.com/arduinoshield/1201
|
||||
* Home Assistant Pico W integration starter project using Arduino - https://github.com/daniloc/PicoW_HomeAssistant_Starter
|
||||
* Tutorials for the Raspberry Pi Pico / uPesy RP2040 DevKit board
|
||||
- English version: https://www.upesy.com/blogs/tutorials/best-tutorials-for-rpi-pi-pico-with-arduino-code
|
||||
- French version: https://www.upesy.fr/blogs/tutorials/best-tutorials-for-rpi-pi-pico-with-arduino-code
|
||||
|
||||
# Contributing
|
||||
If you want to contribute or have bugfixes, drop me a note at <earlephilhower@yahoo.com> or open an issue/PR here.
|
||||
|
||||
+3878
-562
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
// Padded and checksummed version of: generated\Winbond\W25Q128JVxQ\boot2.bin by @maxgerhardt
|
||||
|
||||
.cpu cortex-m0plus
|
||||
.thumb
|
||||
|
||||
.section .boot2, "ax"
|
||||
|
||||
.byte 0xf7, 0xb5, 0x73, 0x46, 0x21, 0x22, 0x02, 0x26, 0x01, 0x93, 0x29, 0x4b, 0xc0, 0x24, 0x5a, 0x60
|
||||
.byte 0x9a, 0x68, 0x00, 0x27, 0xb2, 0x43, 0xda, 0x60, 0x9a, 0x60, 0x1a, 0x61, 0x5a, 0x61, 0x04, 0x23
|
||||
.byte 0x01, 0x25, 0x64, 0x05, 0xa7, 0x60, 0x63, 0x61, 0x22, 0x4b, 0x30, 0x00, 0x1d, 0x60, 0xe0, 0x23
|
||||
.byte 0xdb, 0x02, 0x23, 0x60, 0x35, 0x23, 0xa5, 0x60, 0x23, 0x66, 0x23, 0x66, 0x00, 0xf0, 0x4a, 0xf8
|
||||
.byte 0xc0, 0xb2, 0xb0, 0x42, 0x12, 0xd0, 0x06, 0x23, 0x28, 0x00, 0x23, 0x66, 0x00, 0xf0, 0x42, 0xf8
|
||||
.byte 0x25, 0x66, 0x03, 0x20, 0x27, 0x66, 0x26, 0x66, 0x00, 0xf0, 0x3c, 0xf8, 0x03, 0x36, 0x26, 0x66
|
||||
.byte 0x02, 0x20, 0x26, 0x66, 0x00, 0xf0, 0x36, 0xf8, 0x28, 0x42, 0xf8, 0xd1, 0x00, 0x25, 0x12, 0x4b
|
||||
.byte 0xa5, 0x60, 0x12, 0x4f, 0x23, 0x60, 0x12, 0x4b, 0x65, 0x60, 0x01, 0x26, 0x3b, 0x60, 0xeb, 0x23
|
||||
.byte 0xa6, 0x60, 0x23, 0x66, 0x4b, 0x3b, 0x23, 0x66, 0x02, 0x20, 0x00, 0xf0, 0x23, 0xf8, 0x0d, 0x4b
|
||||
.byte 0xa5, 0x60, 0x3b, 0x60, 0xa6, 0x60, 0x01, 0x9b, 0xab, 0x42, 0x08, 0xd1, 0x0a, 0x4b, 0x0b, 0x4a
|
||||
.byte 0x13, 0x60, 0x1b, 0x68, 0x83, 0xf3, 0x08, 0x88, 0x09, 0x4b, 0x1b, 0x68, 0x18, 0x47, 0xf7, 0xbd
|
||||
.byte 0x00, 0x00, 0x02, 0x40, 0xf0, 0x00, 0x00, 0x18, 0x00, 0x03, 0x5f, 0x00, 0xf4, 0x00, 0x00, 0x18
|
||||
.byte 0x21, 0x22, 0x00, 0x00, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
|
||||
.byte 0x04, 0x01, 0x00, 0x10, 0xc0, 0x22, 0x03, 0x00, 0x04, 0x21, 0x52, 0x05, 0x90, 0x6a, 0x08, 0x42
|
||||
.byte 0xfc, 0xd0, 0x01, 0x21, 0x90, 0x6a, 0x08, 0x42, 0xfc, 0xd1, 0x01, 0x3b, 0xdb, 0xb2, 0x10, 0x6e
|
||||
.byte 0x00, 0x2b, 0xfa, 0xd1, 0x70, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xa0, 0xf0, 0x2f
|
||||
+18
-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
|
||||
@@ -107,6 +107,9 @@ extern bool __isFreeRTOS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
// emptyString is an ESP-ism, a constant string with ""
|
||||
extern const String emptyString;
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
// Needed for declaring Serial
|
||||
#include "Adafruit_USBD_CDC.h"
|
||||
@@ -126,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;
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <hardware/structs/rosc.h>
|
||||
#include <hardware/structs/systick.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <pico/rand.h>
|
||||
#include <pico/util/queue.h>
|
||||
#include "CoreMutex.h"
|
||||
#include "ccount.pio.h"
|
||||
@@ -90,15 +91,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 +108,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.
|
||||
@@ -246,6 +244,11 @@ public:
|
||||
return clock_get_hz(clk_sys);
|
||||
}
|
||||
|
||||
// Get current CPU core number
|
||||
static int cpuid() {
|
||||
return sio_hw->cpuid;
|
||||
}
|
||||
|
||||
// Get CPU cycle count. Needs to do magic to extens 24b HW to something longer
|
||||
volatile uint64_t _epoch = 0;
|
||||
inline uint32_t getCycleCount() {
|
||||
@@ -336,28 +339,20 @@ 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() {
|
||||
#if !defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
return false;
|
||||
#else
|
||||
extern bool __isPicoW;
|
||||
return __isPicoW;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static void _SystickHandler() {
|
||||
rp2040._epoch += 1LL << 24;
|
||||
|
||||
+95
-27
@@ -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
|
||||
@@ -46,12 +46,11 @@ mutex_t __usb_mutex;
|
||||
#define USB_TASK_INTERVAL 1000
|
||||
static int __usb_task_irq;
|
||||
|
||||
// USB VID/PID (note that PID can change depending on the add'l interfaces)
|
||||
#ifndef USBD_VID
|
||||
#define USBD_VID (0x2E8A) // Raspberry Pi
|
||||
#endif
|
||||
|
||||
#ifdef SERIALUSB_PID
|
||||
#define USBD_PID (SERIALUSB_PID)
|
||||
#else
|
||||
#ifndef USBD_PID
|
||||
#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC
|
||||
#endif
|
||||
|
||||
@@ -101,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;
|
||||
}
|
||||
@@ -109,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) {
|
||||
@@ -130,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;
|
||||
@@ -157,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
|
||||
@@ -167,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);
|
||||
@@ -195,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
|
||||
@@ -207,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));
|
||||
@@ -236,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);
|
||||
|
||||
@@ -302,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
|
||||
@@ -384,6 +397,7 @@ void __USBStart() {
|
||||
// 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;
|
||||
@@ -397,6 +411,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;
|
||||
@@ -534,6 +549,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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 7
|
||||
#define ARDUINO_PICO_REVISION 1
|
||||
#define ARDUINO_PICO_VERSION_STR "2.7.1"
|
||||
#define ARDUINO_PICO_MAJOR 3
|
||||
#define ARDUINO_PICO_MINOR 2
|
||||
#define ARDUINO_PICO_REVISION 2
|
||||
#define ARDUINO_PICO_VERSION_STR "3.2.2"
|
||||
|
||||
+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;
|
||||
|
||||
+53
-18
@@ -134,6 +134,20 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
_overflow = false;
|
||||
_queue = new uint8_t[_fifoSize];
|
||||
_baud = baud;
|
||||
|
||||
_fcnTx = gpio_get_function(_tx);
|
||||
_fcnRx = gpio_get_function(_rx);
|
||||
gpio_set_function(_tx, GPIO_FUNC_UART);
|
||||
gpio_set_function(_rx, GPIO_FUNC_UART);
|
||||
if (_rts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnRts = gpio_get_function(_rts);
|
||||
gpio_set_function(_rts, GPIO_FUNC_UART);
|
||||
}
|
||||
if (_cts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnCts = gpio_get_function(_cts);
|
||||
gpio_set_function(_cts, GPIO_FUNC_UART);
|
||||
}
|
||||
|
||||
uart_init(_uart, baud);
|
||||
int bits, stop;
|
||||
uart_parity_t parity;
|
||||
@@ -171,18 +185,6 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
break;
|
||||
}
|
||||
uart_set_format(_uart, bits, stop, parity);
|
||||
_fcnTx = gpio_get_function(_tx);
|
||||
_fcnRx = gpio_get_function(_rx);
|
||||
gpio_set_function(_tx, GPIO_FUNC_UART);
|
||||
gpio_set_function(_rx, GPIO_FUNC_UART);
|
||||
if (_rts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnRts = gpio_get_function(_rts);
|
||||
gpio_set_function(_rts, GPIO_FUNC_UART);
|
||||
}
|
||||
if (_cts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnCts = gpio_get_function(_cts);
|
||||
gpio_set_function(_cts, GPIO_FUNC_UART);
|
||||
}
|
||||
uart_set_hw_flow(_uart, _rts != UART_PIN_NOT_DEFINED, _cts != UART_PIN_NOT_DEFINED);
|
||||
_writer = 0;
|
||||
_reader = 0;
|
||||
@@ -200,6 +202,7 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
} else {
|
||||
// Polling mode has no IRQs used
|
||||
}
|
||||
_break = false;
|
||||
_running = true;
|
||||
}
|
||||
|
||||
@@ -288,13 +291,22 @@ int SerialUART::read() {
|
||||
}
|
||||
|
||||
bool SerialUART::overflow() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m) {
|
||||
if (!_running) {
|
||||
return false;
|
||||
}
|
||||
bool hold = _overflow;
|
||||
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
|
||||
mutex_enter_blocking(&_fifoMutex);
|
||||
bool ovf = _overflow;
|
||||
_overflow = false;
|
||||
return hold;
|
||||
mutex_exit(&_fifoMutex);
|
||||
|
||||
return ovf;
|
||||
}
|
||||
|
||||
int SerialUART::available() {
|
||||
@@ -365,6 +377,25 @@ SerialUART::operator bool() {
|
||||
return _running;
|
||||
}
|
||||
|
||||
bool SerialUART::getBreakReceived() {
|
||||
if (!_running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
|
||||
mutex_enter_blocking(&_fifoMutex);
|
||||
bool break_received = _break;
|
||||
_break = false;
|
||||
mutex_exit(&_fifoMutex);
|
||||
|
||||
return break_received;
|
||||
}
|
||||
|
||||
void arduino::serialEvent1Run(void) {
|
||||
if (serialEvent1 && Serial1.available()) {
|
||||
serialEvent1();
|
||||
@@ -391,8 +422,12 @@ void __not_in_flash_func(SerialUART::_handleIRQ)(bool inIRQ) {
|
||||
uart_get_hw(_uart)->icr = UART_UARTICR_RTIC_BITS | UART_UARTICR_RXIC_BITS;
|
||||
while (uart_is_readable(_uart)) {
|
||||
uint32_t raw = uart_get_hw(_uart)->dr;
|
||||
if (raw & 0x700) {
|
||||
// Framing, Parity, or Break. Ignore this bad char
|
||||
if (raw & 0x400) {
|
||||
// break!
|
||||
_break = true;
|
||||
continue;
|
||||
} else if (raw & 0x300) {
|
||||
// Framing, Parity Error. Ignore this bad char
|
||||
continue;
|
||||
}
|
||||
uint8_t val = raw & 0xff;
|
||||
|
||||
@@ -63,9 +63,18 @@ public:
|
||||
bool overflow();
|
||||
operator bool() override;
|
||||
|
||||
// ESP8266 compat
|
||||
void setDebugOutput(bool unused) {
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
// Not to be called by users, only from the IRQ handler. In public so that the C-language IQR callback can access it
|
||||
void _handleIRQ(bool inIRQ = true);
|
||||
|
||||
// Allows the user to sleep until a break is received (self-clears the flag
|
||||
// on read)
|
||||
bool getBreakReceived();
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
uart_inst_t *_uart;
|
||||
@@ -76,6 +85,7 @@ private:
|
||||
mutex_t _mutex;
|
||||
bool _polling = false;
|
||||
bool _overflow;
|
||||
bool _break;
|
||||
|
||||
// Lockless, IRQ-handled circular queue
|
||||
uint32_t _writer;
|
||||
|
||||
@@ -25,14 +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 <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
|
||||
@@ -174,8 +175,20 @@ SerialUSB::operator bool() {
|
||||
static bool _dtr = false;
|
||||
static bool _rts = false;
|
||||
static int _bps = 115200;
|
||||
static bool _rebooting = false;
|
||||
static void CheckSerialReset() {
|
||||
if ((_bps == 1200) && (!_dtr)) {
|
||||
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);
|
||||
// Reset the whole USB hardware block
|
||||
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.
|
||||
busy_wait_ms(3);
|
||||
reset_usb_boot(0, 0);
|
||||
while (1); // WDT will fire here
|
||||
}
|
||||
|
||||
@@ -44,6 +44,11 @@ public:
|
||||
using Print::write;
|
||||
operator bool() override;
|
||||
|
||||
// ESP8266 compat
|
||||
void setDebugOutput(bool unused) {
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -28,7 +29,7 @@ typedef struct {
|
||||
} FMMap;
|
||||
|
||||
static FMMap *_map = nullptr;
|
||||
extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
|
||||
SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive) {
|
||||
if (!_map) {
|
||||
_map = (FMMap *)calloc(sizeof(FMMap), 16);
|
||||
}
|
||||
@@ -42,7 +43,12 @@ extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (_map[i].src == nullptr) {
|
||||
// Make a new mutex
|
||||
SemaphoreHandle_t fm = __freertos_mutex_create();
|
||||
SemaphoreHandle_t fm;
|
||||
if (recursive) {
|
||||
fm = _freertos_recursive_mutex_create();
|
||||
} else {
|
||||
fm = __freertos_mutex_create();
|
||||
}
|
||||
if (fm == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -54,3 +60,45 @@ extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-16
@@ -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 SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m);
|
||||
extern void __freertos_idle_other_core() __attribute__((weak));
|
||||
extern void __freertos_resume_other_core() __attribute__((weak));
|
||||
}
|
||||
|
||||
// Halt the FreeRTOS PendSV task switching magic
|
||||
extern "C" int __holdUpPendSV;
|
||||
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive = false);
|
||||
|
||||
@@ -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
|
||||
@@ -18,6 +18,8 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(DEBUG_RP2040_PORT)
|
||||
#define DEBUGV(...) do { } while(0)
|
||||
#define DEBUGCORE(...) do { } while(0)
|
||||
@@ -44,3 +46,7 @@
|
||||
#define DEBUGSPI(...) do { } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern void hexdump(const void* mem, uint32_t len, uint8_t cols = 16);
|
||||
#endif
|
||||
|
||||
+16
-4
@@ -80,7 +80,7 @@ static SemaphoreHandle_t __getFreeRTOSMutex(_LOCK_T lock) {
|
||||
} else if (l == &__lock___arc4random_mutex) {
|
||||
return __lock___arc4random_mutex_freertos;
|
||||
}
|
||||
return nullptr;
|
||||
return __get_freertos_mutex_for_ptr(l, false);
|
||||
}
|
||||
|
||||
static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
|
||||
@@ -96,12 +96,18 @@ static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
|
||||
} else if (l == &__lock___env_recursive_mutex) {
|
||||
return __lock___env_recursive_mutex_freertos;
|
||||
}
|
||||
return nullptr;
|
||||
return __get_freertos_mutex_for_ptr((mutex_t *)l, true);
|
||||
}
|
||||
|
||||
void __retarget_lock_init(_LOCK_T *lock) {
|
||||
if (__freeRTOSinitted) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
mutex_t *l = (mutex_t *)lock;
|
||||
if ((l == &__lock___at_quick_exit_mutex) || (l == &__lock___tz_mutex) || (l == &__lock___dd_hash_mutex) || (l == &__lock___arc4random_mutex)) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
} else {
|
||||
// Will init the mutex as well
|
||||
__get_freertos_mutex_for_ptr(l, false);
|
||||
}
|
||||
} else {
|
||||
mutex_init((mutex_t*) lock);
|
||||
}
|
||||
@@ -109,7 +115,13 @@ void __retarget_lock_init(_LOCK_T *lock) {
|
||||
|
||||
void __retarget_lock_init_recursive(_LOCK_T *lock) {
|
||||
if (__freeRTOSinitted) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
recursive_mutex_t *l = (recursive_mutex_t *)lock;
|
||||
if ((l == &__lock___sinit_recursive_mutex) || (l == &__lock___sfp_recursive_mutex) || (l == &__lock___atexit_recursive_mutex) || (l == &__lock___malloc_recursive_mutex) || (l == &__lock___env_recursive_mutex)) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
} else {
|
||||
// Will init the mutex as well
|
||||
__get_freertos_mutex_for_ptr((mutex_t *)l, true);
|
||||
}
|
||||
} else {
|
||||
recursive_mutex_init((recursive_mutex_t*) lock);
|
||||
}
|
||||
|
||||
+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();
|
||||
|
||||
+28
-1
@@ -27,7 +27,6 @@
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
int __holdUpPendSV = 0;
|
||||
};
|
||||
|
||||
mutex_t _pioMutex;
|
||||
@@ -163,6 +162,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;
|
||||
@@ -170,3 +170,30 @@ extern "C" struct _reent *__wrap___getreent() {
|
||||
return _impure_ptr1;
|
||||
}
|
||||
}
|
||||
|
||||
// ESP8266 internal debug routine
|
||||
extern void hexdump(const void* mem, uint32_t len, uint8_t cols) __attribute__((weak));
|
||||
void hexdump(const void* mem, uint32_t len, uint8_t cols) {
|
||||
const char* src = (const char*)mem;
|
||||
printf("\n[HEXDUMP] Address: %p len: 0x%lX (%ld)", src, len, len);
|
||||
while (len > 0) {
|
||||
uint32_t linesize = cols > len ? len : cols;
|
||||
printf("\n[%p] 0x%04x: ", src, (int)(src - (const char*)mem));
|
||||
for (uint32_t i = 0; i < linesize; i++) {
|
||||
printf("%02x ", *(src + i));
|
||||
}
|
||||
printf(" ");
|
||||
for (uint32_t i = linesize; i < cols; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
for (uint32_t i = 0; i < linesize; i++) {
|
||||
unsigned char c = *(src + i);
|
||||
putc(isprint(c) ? c : '.', stdout);
|
||||
}
|
||||
src += linesize;
|
||||
len -= linesize;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
const String emptyString = "";
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@ Returns a value from 0...4095 correspionding to the ADC reading
|
||||
of the specific pin.
|
||||
|
||||
void analogReadResolution(int bits)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Determines the resolution (in bits) of the value returned by the analogRead() function.
|
||||
Default resolution is 10bit.
|
||||
|
||||
float analogReadTemp(float vref = 3.3f)
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the temperature, in Celsius, of the onboard thermal sensor.
|
||||
If you have a custom Vref for the ADC on your RP2040 board, you can pass it in as a parameter. Calling with no parameters assumes the normal, 3.3V Vref.
|
||||
This reading is not exceedingly accurate and of relatively low
|
||||
|
||||
@@ -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.1'
|
||||
version = u'3.2.2'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.7.1'
|
||||
release = u'3.2.2'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
Contributing and Porting to the Core
|
||||
====================================
|
||||
|
||||
First of all, thank you for contributing to the project. It's a lot of work
|
||||
keeping up with all the different uses of the RP2040, so the more people
|
||||
working on the code, the better. Your assistance can help the project
|
||||
succeed.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
no spelling or style errors.
|
||||
|
||||
Tips for a Good Pull Request (PR)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
All code in the core and libraries, except for INO sketches, uses a 4-space
|
||||
indent with cuddled brackets. When in doubt, copy your formatting from the
|
||||
surrounding code. You should install ``astyle`` and run ``tests/restyle.sh``
|
||||
on your machine before committing and pushing any pull requests to ensure
|
||||
the formatting is correct.
|
||||
|
||||
Describe the change you're proposing and why it's important in your
|
||||
``git commit`` message. If it fixes an open issue, place ``Fixes #xxxx``
|
||||
(where xxxx is the issue number) in the message to link the two.
|
||||
|
||||
Try and only change one thing per pull request. That makes it easier to
|
||||
review and prioritize. Opening up a separate PR per change also helps keep
|
||||
track of them when release messages are generated.
|
||||
|
||||
Adding a New Board
|
||||
------------------
|
||||
|
||||
Adding a new board requires:
|
||||
|
||||
* Updated ``tools/makeboards.py`` script
|
||||
* Updated ``boards.txt`` file, generated by ``makeboard.py``
|
||||
* Updated ``package_pico_index.template.json`` file, generated by ``makeboard.py``
|
||||
* New ``tools/json/BOARD_NAME.json`` board file for Platform.IO
|
||||
* New ``variants/BOARD_NAME/pins_arduino.h`` header defining the I/O pins
|
||||
|
||||
To add a new RP2040 board you will need to update the ``tools/makeboards.py``
|
||||
script. Do *NOT* manually edit ``boards.txt``, that file is machine generated.
|
||||
You will need to add a ``MakeBoard`` call at the end of the file. Please be sure
|
||||
to add your board so that it sorts alphabetically, starting with the company name
|
||||
and then the board name. Otherwise it is hard to find a specific board in the menu.
|
||||
|
||||
Run ``python3 tools/makeboards.py`` to update the ``boards.txt`` file and generate
|
||||
a Platform.IO JSON file in the ``tools/json`` directory.
|
||||
|
||||
Create a folder called ``variants/BOARD_NAME`` and place in a ``pins_arduino.h``
|
||||
file in it that contains your default pin name mapping (i.e. SPI0/1 pins, UART
|
||||
pins, LED_DEFAULT, etc.). Copying one of the existing ones as a template can
|
||||
make this task much simpler.
|
||||
|
||||
In your ``git commit`` be sure to add the newly generated ``tools/json/XXX.json``
|
||||
file as well as the modified ``makeboards`` script and ``boards.txt``, the new
|
||||
``pins_arduino.h`` header you generated, and the Arduino packaging JSON
|
||||
``package/package_pico_index.template.json``. You should also add a note in
|
||||
the ``README.md`` file listing your new board.
|
||||
|
||||
Submit the updated commit as a PR and, if all goes well, your board will be in
|
||||
on the next core release.
|
||||
|
||||
|
||||
Porting Libraries and Applications to the Core
|
||||
----------------------------------------------
|
||||
|
||||
We try and follow Arduino standards so, with luck, porting to this core should
|
||||
be relatively straightforward. The ``WiFi`` library and associates support
|
||||
libraries like ``WebServer`` are modeled after the ESP32 and ESP8266 versions
|
||||
of those libraries, combined with the "standard" Arduino ``WiFi`` one.
|
||||
|
||||
Compiler Defines for Porting
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you are adding RP2040 support to an existing library and need to isolate
|
||||
code that only runs on this core, use the following define.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#if defined(ARDUINO_ARCH_RP2040) && !defined(__MBED__)
|
||||
~~~ your changes ~~~
|
||||
#endif
|
||||
|
||||
Library Architectures
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After adding support in the code, libraries need their ``library.properties``
|
||||
and ``library.json`` files updated to indicate support, or the IDE will
|
||||
not know your new code is compatible here.
|
||||
|
||||
Add ``rp2040`` to ``architectures`` (in ``library.properties``) and
|
||||
``"rp2040"`` to ``platforms[]`` (in ``library.json``) to let the tools know.
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
Getting Help and Contributing
|
||||
=============================
|
||||
Getting Help
|
||||
============
|
||||
|
||||
This is a community supported project and has multiple ways to get assistance.
|
||||
Posting complete details, in a polite and organized way will get the best
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
HTTPClient Library
|
||||
==================
|
||||
|
||||
A simple HTTP requestor that can handle both HTTP and HTTP requests is
|
||||
A simple HTTP requestor 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,
|
||||
|
||||
+13
-1
@@ -64,6 +64,13 @@ 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 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 +84,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 +117,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 |
+5
-1
@@ -16,7 +16,9 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
Getting Help and Contributing <help>
|
||||
Getting Help <help>
|
||||
Contributing <contrib>
|
||||
|
||||
Installation <install>
|
||||
IDE Menus <ide>
|
||||
|
||||
@@ -41,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::
|
||||
|
||||
|
||||
+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!
|
||||
+18
-2
@@ -13,6 +13,10 @@ Returns the current frequency of the core clock. This is read at runtime,
|
||||
versus the constant ``F_CPU`` macro that is also available. This is useful
|
||||
in cases where your code changes the core clock (i.e. low power modes, etc.)
|
||||
|
||||
int rp2040.cpuid()
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Returns the current core ID (0 or 1) of the executing task.
|
||||
|
||||
uint32_t rp2040.getCycleCount()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns a 32-bit cycle count from then the core started running. Because it
|
||||
@@ -41,13 +45,16 @@ Hardware Watchdog
|
||||
-----------------
|
||||
|
||||
void rp2040.wdt_begin(uint32_t delay_ms)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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.
|
||||
|
||||
|
||||
@@ -71,6 +78,15 @@ Returns the total heap that was available to this program at compile time (i.e.
|
||||
the Pico RAM size minus things like the ``.data`` and ``.bss`` sections and other
|
||||
overhead).
|
||||
|
||||
Hardware Identification
|
||||
-----------------------
|
||||
|
||||
bool 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.
|
||||
|
||||
Bootloader
|
||||
----------
|
||||
|
||||
|
||||
@@ -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_MINOR 5
|
||||
#define PICO_SDK_VERSION_REVISION 0
|
||||
#define PICO_SDK_VERSION_STRING "1.4.0"
|
||||
#define PICO_SDK_VERSION_STRING "1.5.0"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,6 +44,8 @@ restartCore1 KEYWORD2
|
||||
reboot KEYWORD2
|
||||
restart KEYWORD2
|
||||
|
||||
isPicoW KEYWORD2
|
||||
|
||||
getChipID KEYWORD2
|
||||
|
||||
hwrand32 KEYWORD2
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10
-6
@@ -191,11 +191,6 @@ SECTIONS
|
||||
__data_end__ = .;
|
||||
} > RAM AT> FLASH
|
||||
|
||||
.uninitialized_data (COPY): {
|
||||
. = ALIGN(4);
|
||||
*(.uninitialized_data*)
|
||||
} > RAM
|
||||
|
||||
/* Start and end symbols must be word-aligned */
|
||||
.scratch_x : {
|
||||
__scratch_x_start__ = .;
|
||||
@@ -213,7 +208,7 @@ SECTIONS
|
||||
} > SCRATCH_Y AT > FLASH
|
||||
__scratch_y_source__ = LOADADDR(.scratch_y);
|
||||
|
||||
.bss : {
|
||||
.bss : {
|
||||
. = ALIGN(4);
|
||||
__bss_start__ = .;
|
||||
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
|
||||
@@ -222,6 +217,15 @@ SECTIONS
|
||||
__bss_end__ = .;
|
||||
} > RAM
|
||||
|
||||
/* At most one of the following two will be engaged, depending on the SDK version */
|
||||
.uninitialized_ram MAX(0x20003000, .) (NOLOAD) : {
|
||||
*(.uninitialized_ram*)
|
||||
} > RAM
|
||||
|
||||
.uninitialized_data MAX(0x20003000, .) (NOLOAD) : {
|
||||
*(.uninitialized_data*)
|
||||
} > RAM
|
||||
|
||||
.heap (COPY):
|
||||
{
|
||||
__end__ = .;
|
||||
|
||||
@@ -0,0 +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
|
||||
|
||||
Submodule libraries/Adafruit_TinyUSB_Arduino updated: e1e922d2ca...b17ab37a0e
@@ -69,6 +69,7 @@ AudioBufferManager::AudioBufferManager(size_t bufferCount, size_t bufferWords, i
|
||||
}
|
||||
|
||||
AudioBufferManager::~AudioBufferManager() {
|
||||
noInterrupts();
|
||||
if (_running) {
|
||||
_running = false;
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
@@ -76,6 +77,7 @@ AudioBufferManager::~AudioBufferManager() {
|
||||
__channelMap[_channelDMA[i]] = nullptr;
|
||||
dma_channel_abort(_channelDMA[i]);
|
||||
dma_channel_unclaim(_channelDMA[i]);
|
||||
dma_channel_acknowledge_irq0(_channelDMA[i]);
|
||||
}
|
||||
__channelCount--;
|
||||
if (!__channelCount) {
|
||||
@@ -84,6 +86,7 @@ AudioBufferManager::~AudioBufferManager() {
|
||||
irq_remove_handler(DMA_IRQ_0, _irq);
|
||||
}
|
||||
}
|
||||
interrupts();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (_active[i] != _silence) {
|
||||
_deleteAudioBuffer(_active[i]);
|
||||
@@ -111,7 +114,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]);
|
||||
@@ -254,7 +257,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]);
|
||||
@@ -263,7 +266,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);
|
||||
|
||||
@@ -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
Submodule
+1
Submodule libraries/HID_Mouse added at 8bd9d96bd9
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
simple demo to show sha1 calculation
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <Hash.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// usage as String
|
||||
// SHA1:a9993e364706816aba3e25717850c26c9cd0d89d
|
||||
|
||||
Serial.print("SHA1:");
|
||||
Serial.println(sha1("abc"));
|
||||
|
||||
// usage as ptr
|
||||
// SHA1:a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
|
||||
uint8_t hash[20];
|
||||
sha1("test", &hash[0]);
|
||||
|
||||
Serial.print("SHA1:");
|
||||
for (uint16_t i = 0; i < 20; i++) { Serial.printf("%02x", hash[i]); }
|
||||
Serial.println();
|
||||
|
||||
delay(1000);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Hash
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
Hash KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
sha1 KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,10 @@
|
||||
name=Hash
|
||||
version=1.0
|
||||
author=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
sentence=Generate Hash from data
|
||||
paragraph=
|
||||
category=Data Processing
|
||||
url=
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @file Hash.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the esp8266 core for Arduino environment.
|
||||
*
|
||||
* 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 <bearssl/bearssl_hash.h>
|
||||
|
||||
#include "Hash.h"
|
||||
|
||||
/**
|
||||
* create a sha1 hash from data
|
||||
* @param data uint8_t *
|
||||
* @param size uint32_t
|
||||
* @param hash uint8_t[20]
|
||||
*/
|
||||
void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]) {
|
||||
br_sha1_context ctx;
|
||||
|
||||
#ifdef DEBUG_SHA1
|
||||
os_printf("DATA:");
|
||||
for(uint16_t i = 0; i < size; i++) {
|
||||
os_printf("%02X", data[i]);
|
||||
}
|
||||
os_printf("\n");
|
||||
os_printf("DATA:");
|
||||
for(uint16_t i = 0; i < size; i++) {
|
||||
os_printf("%c", data[i]);
|
||||
}
|
||||
os_printf("\n");
|
||||
#endif
|
||||
|
||||
br_sha1_init(&ctx);
|
||||
br_sha1_update(&ctx, data, size);
|
||||
br_sha1_out(&ctx, hash);
|
||||
|
||||
#ifdef DEBUG_SHA1
|
||||
os_printf("SHA1:");
|
||||
for(uint16_t i = 0; i < 20; i++) {
|
||||
os_printf("%02X", hash[i]);
|
||||
}
|
||||
os_printf("\n\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void sha1(const char* data, uint32_t size, uint8_t hash[20]) {
|
||||
sha1((const uint8_t *) data, size, hash);
|
||||
}
|
||||
|
||||
void sha1(const String& data, uint8_t hash[20]) {
|
||||
sha1(data.c_str(), data.length(), hash);
|
||||
}
|
||||
|
||||
String sha1(const uint8_t* data, uint32_t size) {
|
||||
uint8_t hash[20];
|
||||
String hashStr((const char*)nullptr);
|
||||
hashStr.reserve(20 * 2 + 1);
|
||||
|
||||
sha1(&data[0], size, &hash[0]);
|
||||
|
||||
for(uint16_t i = 0; i < 20; i++) {
|
||||
char hex[3];
|
||||
snprintf(hex, sizeof(hex), "%02x", hash[i]);
|
||||
hashStr += hex;
|
||||
}
|
||||
|
||||
return hashStr;
|
||||
}
|
||||
|
||||
String sha1(const char* data, uint32_t size) {
|
||||
return sha1((const uint8_t*) data, size);
|
||||
}
|
||||
|
||||
String sha1(const String& data) {
|
||||
return sha1(data.c_str(), data.length());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file Hash.h
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the esp8266 core for Arduino environment.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HASH_H_
|
||||
#define HASH_H_
|
||||
|
||||
//#define DEBUG_SHA1
|
||||
|
||||
void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]);
|
||||
void sha1(const char* data, uint32_t size, uint8_t hash[20]);
|
||||
void sha1(const String& data, uint8_t hash[20]);
|
||||
|
||||
String sha1(const uint8_t* data, uint32_t size);
|
||||
String sha1(const char* data, uint32_t size);
|
||||
String sha1(const String& data);
|
||||
|
||||
#endif /* HASH_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user