Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32e74d024e | ||
|
|
326697bbe1 | ||
|
|
afbba68549 | ||
|
|
042555206b | ||
|
|
24f6302612 | ||
|
|
15eb459df9 | ||
|
|
3160fde679 | ||
|
|
1160d7cd7c | ||
|
|
2aa85e3263 | ||
|
|
c4f36170d4 | ||
|
|
3068cd0af0 | ||
|
|
fa390f48af | ||
|
|
0c7454b6f7 | ||
|
|
500f197c02 | ||
|
|
2bf249ffe1 | ||
|
|
81070a0b3f | ||
|
|
d45a11f9e7 | ||
|
|
c3a3526aad | ||
|
|
280fc43731 | ||
|
|
762535faf9 | ||
|
|
0e4fd0587b | ||
|
|
d2461a14ad | ||
|
|
a1902b5f41 | ||
|
|
c2d60774af | ||
|
|
269c579846 | ||
|
|
91183ca22f | ||
|
|
971c235e8d | ||
|
|
abd3547711 | ||
|
|
9181ec055d | ||
|
|
58089b1f44 | ||
|
|
4f62e0a4ba | ||
|
|
dbab62c214 | ||
|
|
6a878cdee8 | ||
|
|
cebdb6c917 | ||
|
|
39a2bbd788 | ||
|
|
199314a463 | ||
|
|
1020023eab | ||
|
|
a475c444c5 | ||
|
|
633ac316b4 | ||
|
|
4625d2c8ab | ||
|
|
db7ba160ae | ||
|
|
ea936f00e1 | ||
|
|
8ac616e8db | ||
|
|
3ce902183e | ||
|
|
c34e602937 | ||
|
|
28e25293a9 | ||
|
|
319d36531e | ||
|
|
5bea328967 | ||
|
|
f5f7267f44 | ||
|
|
a4ad8ae0fd | ||
|
|
44abc19d46 | ||
|
|
2e93f1c9c8 | ||
|
|
ae6847cf78 | ||
|
|
d42f0ab4b0 | ||
|
|
10ddaee94d | ||
|
|
5678bb9904 | ||
|
|
5ecef160d4 | ||
|
|
52d50da56f | ||
|
|
47111a6eba | ||
|
|
f6a5ef0f85 | ||
|
|
7868ddee42 | ||
|
|
6a0cc90a4d | ||
|
|
21d212a2ff | ||
|
|
bd6b20dedd | ||
|
|
3cc68f82a2 | ||
|
|
4f945780e7 | ||
|
|
f08ef117b5 | ||
|
|
41b0686aec | ||
|
|
9178ed580e | ||
|
|
60e93f3e20 | ||
|
|
7e8fcc5afa | ||
|
|
39238a505d | ||
|
|
f60b7831c8 | ||
|
|
c48cdeeea0 | ||
|
|
0be7c98dff | ||
|
|
0ed1f3dce1 | ||
|
|
f5e8e5b325 | ||
|
|
1f3d5011b2 | ||
|
|
3950b94474 | ||
|
|
99b4aac48b | ||
|
|
2bd64b566c | ||
|
|
3c93d14b33 | ||
|
|
5639edefee | ||
|
|
585c31ef39 | ||
|
|
a385a4c1e5 | ||
|
|
ef257c32b4 | ||
|
|
88cd4b5288 | ||
|
|
ca7ec56f0d | ||
|
|
74b1156d5e | ||
|
|
6ac7ee1a9d | ||
|
|
0b56452c35 | ||
|
|
b9c66ca0fd | ||
|
|
faf06a3b70 | ||
|
|
d0ac7f06b1 | ||
|
|
e9daaa3589 | ||
|
|
1393811525 | ||
|
|
2b640669a7 | ||
|
|
9d0f0a8d7a | ||
|
|
21d2fb4afa | ||
|
|
ade74986ee | ||
|
|
73722b5c87 | ||
|
|
456b474a48 | ||
|
|
f11cc4db69 | ||
|
|
8c2901da13 | ||
|
|
ca4637d14d |
+1
-1
Submodule ArduinoCore-API updated: e37df85425...ece6e68f29
@@ -4,7 +4,7 @@
|
||||
|
||||
Raspberry Pi Pico Arduino core, for all RP2040 boards
|
||||
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 10.3/Newlib 4.0 toolchain.
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 12.3/Newlib 4.0 toolchain.
|
||||
|
||||
# Documentation
|
||||
See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for more detailed usage information.
|
||||
@@ -31,6 +31,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* Cytron Maker Pi RP2040
|
||||
* Cytron Maker Nano RP2040
|
||||
* DatanoiseTV PicoADK+
|
||||
* Degz Suibo RP2040
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* ElectronicCats Hunter Cat NFC
|
||||
@@ -50,6 +51,12 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* Neko Systems BL2040 Mini
|
||||
* nullbits Bit-C PRO
|
||||
* Pimoroni PGA2040
|
||||
* Pimoroni Plasma2040
|
||||
* Pimoroni Tiny2040
|
||||
* RAKwireless RAK11300
|
||||
* Redscorp RP2040-Eins
|
||||
* Redscorp RP2040-ProMini
|
||||
* Sea-Picro
|
||||
* Seeed Indicator RP2040
|
||||
* Seeed XIAO RP2040
|
||||
* Silicognition RP2040-Shim
|
||||
@@ -199,6 +206,7 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
* 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)
|
||||
* Ethernet (Wired W5500, W5100, ENC28J60)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
* Over-the-Air (OTA) upgrades
|
||||
@@ -210,7 +218,7 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
* Analog stereo audio in using DMA and the built-in ADC
|
||||
* Analog stereo audio out using PWM hardware
|
||||
* USB drive mode for data loggers (SingleFileDrive)
|
||||
* Peripherals: SPI master, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input, I2S audio output, Servo
|
||||
* Peripherals: SPI master/slave, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input/output, Servo
|
||||
* printf (i.e. debug) output over USB serial
|
||||
|
||||
The RP2040 PIO state machines (SMs) are used to generate jitter-free:
|
||||
@@ -244,14 +252,14 @@ If you want to contribute or have bugfixes, drop me a note at <earlephilhower@ya
|
||||
* [UF2CONV.PY](https://github.com/microsoft/uf2) is by Microsoft Corporation and licensed under the MIT license.
|
||||
* Networking and filesystem code taken from the [ESP8266 Arduino Core](https://github.com/esp8266/Arduino) and licensed under the LGPL.
|
||||
* DHCP server for AP host mode from the [Micropython Project](https://micropython.org), distributed under the MIT License.
|
||||
* [FreeRTOS](https://freertos.org) is Copyright Amazon.com, Inc. or its affiliates, and distributed under the MIT license.
|
||||
* [FreeRTOS](https://freertos.org) is copyright Amazon.com, Inc. or its affiliates, and distributed under the MIT license.
|
||||
* [lwIP](https://savannah.nongnu.org/projects/lwip/) is (c) the Swedish Institute of Computer Science and licenced under the BSD license.
|
||||
* [BearSSL](https://bearssl.org) library written by Thomas Pornin, is distributed under the [MIT License](https://bearssl.org/#legal-details).
|
||||
* [UZLib](https://github.com/pfalcon/uzlib) is copyright (c) 2003 Joergen Ibsen and distributed under the zlib license.
|
||||
* [LEAmDNS](https://github.com/LaborEtArs/ESP8266mDNS) is copyright multiple authors and distributed under the MIT license.
|
||||
* [http-parser](https://github.com/nodejs/http-parser) is copyright Joyent, Inc. and other Node contributors.
|
||||
* WebServer code modified from the [ESP32 WebServer](https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer) and is copyright (c) 2015 Ivan Grokhotkov and others
|
||||
|
||||
* WebServer code modified from the [ESP32 WebServer](https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer) and is copyright (c) 2015 Ivan Grokhotkov and others.
|
||||
* [Xoshiro-cpp](https://github.com/Reputeless/Xoshiro-cpp) is copyright (c) 2020 Ryo Suzuki and distributed under the MIT license.
|
||||
|
||||
-Earle F. Philhower, III
|
||||
earlephilhower@yahoo.com
|
||||
|
||||
+2082
-448
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Enable BTStack debugging to a Print-able object
|
||||
|
||||
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(ENABLE_CLASSIC) || defined(ENABLE_BLE)
|
||||
#include <Arduino.h>
|
||||
#include <btstack.h>
|
||||
#include <hci_dump.h>
|
||||
|
||||
static Print *_print;
|
||||
|
||||
static void _log_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
|
||||
if (!_print) {
|
||||
return;
|
||||
}
|
||||
_print->printf("[BT @%lu] ", millis());
|
||||
|
||||
switch (packet_type) {
|
||||
case HCI_COMMAND_DATA_PACKET:
|
||||
_print->printf("CMD => ");
|
||||
break;
|
||||
case HCI_EVENT_PACKET:
|
||||
_print->printf("EVT <= ");
|
||||
break;
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
_print->printf("ACL %s ", in ? "<=" : "=>");
|
||||
break;
|
||||
case HCI_SCO_DATA_PACKET:
|
||||
_print->printf("SCO %s ", in ? "<=" : "=>");
|
||||
break;
|
||||
case HCI_ISO_DATA_PACKET:
|
||||
_print->printf("ISO %s ", in ? "<=" : "=>");
|
||||
break;
|
||||
case LOG_MESSAGE_PACKET:
|
||||
_print->printf("LOG -- %s\n", (char*) packet);
|
||||
return;
|
||||
default:
|
||||
_print->printf("UNK(%x) %s ", packet_type, in ? "<=" : "=>");
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
_print->printf("%02X ", packet[i]);
|
||||
}
|
||||
_print->printf("\n");
|
||||
}
|
||||
|
||||
static void _log_message(int log_level, const char * format, va_list argptr) {
|
||||
(void)log_level;
|
||||
char log_message_buffer[HCI_DUMP_MAX_MESSAGE_LEN];
|
||||
if (!_print) {
|
||||
return;
|
||||
}
|
||||
vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
|
||||
_print->printf("[BT @%lu] LOG -- %s\n", millis(), log_message_buffer);
|
||||
}
|
||||
|
||||
|
||||
static const hci_dump_t hci_dump_instance = {
|
||||
NULL,
|
||||
_log_packet,
|
||||
_log_message
|
||||
};
|
||||
|
||||
|
||||
void __EnableBluetoothDebug(Print &print) {
|
||||
_print = &print;
|
||||
hci_dump_init(&hci_dump_instance);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -157,6 +157,10 @@ extern RP2040 rp2040;
|
||||
extern "C" void main1();
|
||||
extern "C" char __StackLimit;
|
||||
extern "C" char __bss_end__;
|
||||
extern "C" void setup1() __attribute__((weak));
|
||||
extern "C" void loop1() __attribute__((weak));
|
||||
extern "C" bool core1_separate_stack;
|
||||
extern "C" uint32_t* core1_separate_stack_address;
|
||||
|
||||
class RP2040 {
|
||||
public:
|
||||
@@ -238,6 +242,25 @@ public:
|
||||
return &__StackLimit - &__bss_end__;
|
||||
}
|
||||
|
||||
inline uint32_t getStackPointer() {
|
||||
uint32_t *sp;
|
||||
asm volatile("mov %0, sp" : "=r"(sp));
|
||||
return (uint32_t)sp;
|
||||
}
|
||||
|
||||
inline int getFreeStack() {
|
||||
const unsigned int sp = getStackPointer();
|
||||
uint32_t ref = 0x20040000;
|
||||
if (setup1 || loop1) {
|
||||
if (core1_separate_stack) {
|
||||
ref = cpuid() ? (unsigned int)core1_separate_stack_address : 0x20040000;
|
||||
} else {
|
||||
ref = cpuid() ? 0x20040000 : 0x20041000;
|
||||
}
|
||||
}
|
||||
return sp - ref;
|
||||
}
|
||||
|
||||
void idleOtherCore() {
|
||||
fifo.idleOtherCore();
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ static int __usb_task_irq;
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, RESET_INTERFACE_PROTOCOL, _stridx,
|
||||
|
||||
|
||||
int usb_hid_poll_interval __attribute__((weak)) = 10;
|
||||
|
||||
const uint8_t *tud_descriptor_device_cb(void) {
|
||||
static tusb_desc_device_t usbd_desc_device = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
@@ -263,7 +265,7 @@ void __SetupUSBDescriptor() {
|
||||
uint8_t hid_itf = __USBInstallSerial ? 2 : 0;
|
||||
uint8_t hid_desc[TUD_HID_DESC_LEN] = {
|
||||
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
|
||||
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, (uint8_t)usb_hid_poll_interval)
|
||||
};
|
||||
|
||||
uint8_t msd_itf = interface_count - 1;
|
||||
@@ -394,6 +396,18 @@ void __USBStart() {
|
||||
}
|
||||
|
||||
|
||||
bool __USBHIDReady() {
|
||||
uint32_t start = millis();
|
||||
const uint32_t timeout = 500;
|
||||
|
||||
while (((millis() - start) < timeout) && tud_ready() && !tud_hid_ready()) {
|
||||
tud_task();
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
return tud_hid_ready();
|
||||
}
|
||||
|
||||
|
||||
// Invoked when received GET_REPORT control request
|
||||
// Application must fill buffer report's content and return its length.
|
||||
// Return zero will cause the stack to STALL request
|
||||
|
||||
@@ -45,3 +45,6 @@ int __USBGetJoystickReportID();
|
||||
|
||||
// Called by main() to init the USB HW/SW.
|
||||
void __USBStart();
|
||||
|
||||
// Helper class for HID report sending with wait and timeout
|
||||
bool __USBHIDReady();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 3
|
||||
#define ARDUINO_PICO_MINOR 3
|
||||
#define ARDUINO_PICO_REVISION 2
|
||||
#define ARDUINO_PICO_VERSION_STR "3.3.2"
|
||||
#define ARDUINO_PICO_MINOR 6
|
||||
#define ARDUINO_PICO_REVISION 3
|
||||
#define ARDUINO_PICO_VERSION_STR "3.6.3"
|
||||
|
||||
@@ -40,6 +40,10 @@ bool SerialUART::setRX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_rx == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -57,6 +61,10 @@ bool SerialUART::setTX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_tx == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.TX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -74,6 +82,10 @@ bool SerialUART::setRTS(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_rts == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -91,6 +103,10 @@ bool SerialUART::setCTS(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_cts == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.CTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
|
||||
@@ -197,6 +197,14 @@ static void CheckSerialReset() {
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialUSB::dtr() {
|
||||
return _dtr;
|
||||
}
|
||||
|
||||
bool SerialUSB::rts() {
|
||||
return _rts;
|
||||
}
|
||||
|
||||
extern "C" void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
|
||||
(void) itf;
|
||||
_dtr = dtr ? true : false;
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
virtual size_t write(const uint8_t *p, size_t len) override;
|
||||
using Print::write;
|
||||
operator bool() override;
|
||||
bool dtr();
|
||||
bool rts();
|
||||
|
||||
void ignoreFlowControl(bool ignore = true);
|
||||
|
||||
|
||||
@@ -60,45 +60,3 @@ SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive) {
|
||||
}
|
||||
return nullptr; // Need to make space for more mutex maps!
|
||||
}
|
||||
|
||||
|
||||
// The HW Random code needs a precise sleep_until() in order to assure it
|
||||
// grabs the ROSC bit when it wants. Unfortunately, under FreeRTOS the
|
||||
// sleep_until() becomes imprecise and the "did I get the bit when I wanted"
|
||||
// check in the pico_rand code always fails and you get an infinite loop.
|
||||
|
||||
// This block wraps the 2 get_rand calls to set a flag to convert
|
||||
// sleep_until() (which can do a task swap and cause bad timing) into a
|
||||
// busy wait.
|
||||
|
||||
extern "C" {
|
||||
static bool __inRand = false;
|
||||
|
||||
extern uint64_t __real_get_rand_64();
|
||||
uint64_t __wrap_get_rand_64() {
|
||||
if (__isFreeRTOS) {
|
||||
rp2040.idleOtherCore();
|
||||
__inRand = true;
|
||||
auto r = __real_get_rand_64();
|
||||
__inRand = false;
|
||||
rp2040.resumeOtherCore();
|
||||
return r;
|
||||
} else {
|
||||
return __real_get_rand_64();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t __wrap_get_rand_32() {
|
||||
return (uint32_t) __wrap_get_rand_64();
|
||||
}
|
||||
|
||||
extern void __real_sleep_until(absolute_time_t t);
|
||||
void __wrap_sleep_until(absolute_time_t t) {
|
||||
if (__inRand) {
|
||||
busy_wait_until(t);
|
||||
} else {
|
||||
__real_sleep_until(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <lwip/pbuf.h>
|
||||
#include <lwip/udp.h>
|
||||
@@ -26,20 +27,63 @@
|
||||
#include <lwip/raw.h>
|
||||
#include <lwip/timeouts.h>
|
||||
#include <pico/cyw43_arch.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <sys/lock.h>
|
||||
#include "_xoshiro.h"
|
||||
|
||||
extern void ethernet_arch_lwip_begin() __attribute__((weak));
|
||||
extern void ethernet_arch_lwip_end() __attribute__((weak));
|
||||
|
||||
auto_init_recursive_mutex(__lwipMutex); // Only for case with no Ethernet or PicoW, but still doing LWIP (PPP?)
|
||||
|
||||
class LWIPMutex {
|
||||
public:
|
||||
LWIPMutex() {
|
||||
cyw43_arch_lwip_begin();
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_begin();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (ethernet_arch_lwip_begin) {
|
||||
ethernet_arch_lwip_begin();
|
||||
} else {
|
||||
recursive_mutex_enter_blocking(&__lwipMutex);
|
||||
}
|
||||
}
|
||||
|
||||
~LWIPMutex() {
|
||||
cyw43_arch_lwip_end();
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_end();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (ethernet_arch_lwip_end) {
|
||||
ethernet_arch_lwip_end();
|
||||
} else {
|
||||
recursive_mutex_exit(&__lwipMutex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
static XoshiroCpp::Xoshiro256PlusPlus *_lwip_rng = nullptr;
|
||||
// Random number generator for LWIP
|
||||
unsigned long __lwip_rand() {
|
||||
return (unsigned long)(*_lwip_rng)();
|
||||
}
|
||||
|
||||
// Avoid calling lwip_init multiple times
|
||||
extern void __real_lwip_init();
|
||||
void __wrap_lwip_init() {
|
||||
if (!_lwip_rng) {
|
||||
_lwip_rng = new XoshiroCpp::Xoshiro256PlusPlus(get_rand_64());
|
||||
__real_lwip_init();
|
||||
}
|
||||
}
|
||||
|
||||
extern u8_t __real_pbuf_header(struct pbuf *p, s16_t header_size);
|
||||
u8_t __wrap_pbuf_header(struct pbuf *p, s16_t header_size) {
|
||||
LWIPMutex m;
|
||||
|
||||
+12
-1
@@ -27,6 +27,7 @@
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
uint32_t* core1_separate_stack_address = nullptr;
|
||||
};
|
||||
|
||||
extern void setup();
|
||||
@@ -38,12 +39,14 @@ extern void startFreeRTOS() __attribute__((weak));
|
||||
bool __isFreeRTOS;
|
||||
volatile bool __freeRTOSinitted;
|
||||
|
||||
extern void __EnableBluetoothDebug(Print &);
|
||||
|
||||
// Weak empty variant initialization. May be redefined by variant files.
|
||||
void initVariant() __attribute__((weak));
|
||||
void initVariant() { }
|
||||
|
||||
// Optional 2nd core setup and loop
|
||||
bool core1_separate_stack __attribute__((weak)) = false;
|
||||
extern void setup1() __attribute__((weak));
|
||||
extern void loop1() __attribute__((weak));
|
||||
extern "C" void main1() {
|
||||
@@ -117,6 +120,9 @@ extern "C" int main() {
|
||||
#if defined DEBUG_RP2040_PORT
|
||||
if (!__isFreeRTOS) {
|
||||
DEBUG_RP2040_PORT.begin(115200);
|
||||
#if (defined(ENABLE_BLUETOOTH) || defined(ENABLE_BLE)) && defined(DEBUG_RP2040_BLUETOOTH)
|
||||
__EnableBluetoothDebug(DEBUG_RP2040_PORT);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -132,7 +138,12 @@ extern "C" int main() {
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
delay(1); // Needed to make Picoprobe upload start 2nd core
|
||||
multicore_launch_core1(main1);
|
||||
if (core1_separate_stack) {
|
||||
core1_separate_stack_address = (uint32_t*)malloc(0x2000);
|
||||
multicore_launch_core1_with_stack(main1, core1_separate_stack_address, 0x2000);
|
||||
} else {
|
||||
multicore_launch_core1(main1);
|
||||
}
|
||||
}
|
||||
setup();
|
||||
while (true) {
|
||||
|
||||
+39
-13
@@ -33,7 +33,9 @@
|
||||
|
||||
extern "C" int errno;
|
||||
|
||||
extern "C" ssize_t _write(int fd, const void *buf, size_t count) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
ssize_t _write(int fd, const void *buf, size_t count) {
|
||||
#if defined DEBUG_RP2040_PORT
|
||||
(void) fd;
|
||||
return DEBUG_RP2040_PORT.write((const char *)buf, count);
|
||||
@@ -45,7 +47,9 @@ extern "C" ssize_t _write(int fd, const void *buf, size_t count) {
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" int _chown(const char *path, uid_t owner, gid_t group) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _chown(const char *path, uid_t owner, gid_t group) {
|
||||
(void) path;
|
||||
(void) owner;
|
||||
(void) group;
|
||||
@@ -53,7 +57,9 @@ extern "C" int _chown(const char *path, uid_t owner, gid_t group) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _close(int fd) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _close(int fd) {
|
||||
(void) fd;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@@ -72,7 +78,9 @@ extern "C" int _fork(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _fstat(int fd, struct stat *st) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _fstat(int fd, struct stat *st) {
|
||||
(void) fd;
|
||||
(void) st;
|
||||
errno = ENOSYS;
|
||||
@@ -115,7 +123,9 @@ extern "C" void __setSystemTime(unsigned long long sec, unsigned long usec) {
|
||||
__timedelta_us = newnow_us - now_us;
|
||||
}
|
||||
|
||||
extern "C" int _isatty(int file) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _isatty(int file) {
|
||||
(void) file;
|
||||
errno = ENOSYS;
|
||||
return 0;
|
||||
@@ -128,14 +138,18 @@ extern "C" int _kill(int pid, int sig) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _link(char *existing, char *newlink) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _link(char *existing, char *newlink) {
|
||||
(void) existing;
|
||||
(void) newlink;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _lseek(int file, int ptr, int dir) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _lseek(int file, int ptr, int dir) {
|
||||
(void) file;
|
||||
(void) ptr;
|
||||
(void) dir;
|
||||
@@ -143,7 +157,9 @@ extern "C" int _lseek(int file, int ptr, int dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _open(char *file, int flags, int mode) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _open(char *file, int flags, int mode) {
|
||||
(void) file;
|
||||
(void) flags;
|
||||
(void) mode;
|
||||
@@ -151,7 +167,9 @@ extern "C" int _open(char *file, int flags, int mode) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _read(int file, char *ptr, int len) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _read(int file, char *ptr, int len) {
|
||||
(void) file;
|
||||
(void) ptr;
|
||||
(void) len;
|
||||
@@ -159,7 +177,9 @@ extern "C" int _read(int file, char *ptr, int len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _readlink(const char *path, char *buf, size_t bufsize) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _readlink(const char *path, char *buf, size_t bufsize) {
|
||||
(void) path;
|
||||
(void) buf;
|
||||
(void) bufsize;
|
||||
@@ -167,14 +187,18 @@ extern "C" int _readlink(const char *path, char *buf, size_t bufsize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _stat(const char *file, struct stat *st) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _stat(const char *file, struct stat *st) {
|
||||
(void) file;
|
||||
(void) st;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _symlink(const char *path1, const char *path2) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _symlink(const char *path1, const char *path2) {
|
||||
(void) path1;
|
||||
(void) path2;
|
||||
errno = ENOSYS;
|
||||
@@ -187,7 +211,9 @@ extern "C" clock_t _times(struct tms *buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _unlink(char *name) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _unlink(char *name) {
|
||||
(void) name;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
Copyright (C) 2014 BlueKitchen GmbH
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
4. Any redistribution, use, or modification is done solely for
|
||||
personal benefit and not for any commercial purpose or for
|
||||
monetary gain.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
|
||||
GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Please inquire about commercial licensing options at
|
||||
contact@bluekitchen-gmbh.com
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
@title ATT Database Engine
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ATT_DB_H
|
||||
#define ATT_DB_H
|
||||
|
||||
#if defined ENABLE_CLASSIC
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sdkoverride/bluetooth.h>
|
||||
#include "btstack_linked_list.h"
|
||||
#include "btstack_defines.h"
|
||||
#include "btstack_bool.h"
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// MARK: Attribute PDU Opcodes
|
||||
#define ATT_ERROR_RESPONSE 0x01u
|
||||
|
||||
#define ATT_EXCHANGE_MTU_REQUEST 0x02u
|
||||
#define ATT_EXCHANGE_MTU_RESPONSE 0x03u
|
||||
|
||||
#define ATT_FIND_INFORMATION_REQUEST 0x04u
|
||||
#define ATT_FIND_INFORMATION_REPLY 0x05u
|
||||
#define ATT_FIND_BY_TYPE_VALUE_REQUEST 0x06u
|
||||
#define ATT_FIND_BY_TYPE_VALUE_RESPONSE 0x07u
|
||||
|
||||
#define ATT_READ_BY_TYPE_REQUEST 0x08u
|
||||
#define ATT_READ_BY_TYPE_RESPONSE 0x09u
|
||||
#define ATT_READ_REQUEST 0x0au
|
||||
#define ATT_READ_RESPONSE 0x0bu
|
||||
#define ATT_READ_BLOB_REQUEST 0x0cu
|
||||
#define ATT_READ_BLOB_RESPONSE 0x0du
|
||||
#define ATT_READ_MULTIPLE_REQUEST 0x0eu
|
||||
#define ATT_READ_MULTIPLE_RESPONSE 0x0fu
|
||||
#define ATT_READ_BY_GROUP_TYPE_REQUEST 0x10u
|
||||
#define ATT_READ_BY_GROUP_TYPE_RESPONSE 0x11u
|
||||
|
||||
#define ATT_WRITE_REQUEST 0x12u
|
||||
#define ATT_WRITE_RESPONSE 0x13u
|
||||
|
||||
#define ATT_PREPARE_WRITE_REQUEST 0x16u
|
||||
#define ATT_PREPARE_WRITE_RESPONSE 0x17u
|
||||
#define ATT_EXECUTE_WRITE_REQUEST 0x18u
|
||||
#define ATT_EXECUTE_WRITE_RESPONSE 0x19u
|
||||
|
||||
#define ATT_HANDLE_VALUE_NOTIFICATION 0x1bu
|
||||
#define ATT_HANDLE_VALUE_INDICATION 0x1du
|
||||
#define ATT_HANDLE_VALUE_CONFIRMATION 0x1eu
|
||||
|
||||
#define ATT_READ_MULTIPLE_VARIABLE_REQ 0x20u
|
||||
#define ATT_READ_MULTIPLE_VARIABLE_RSP 0x21u
|
||||
#define ATT_MULTIPLE_HANDLE_VALUE_NTF 0x23u
|
||||
|
||||
#define ATT_WRITE_COMMAND 0x52u
|
||||
#define ATT_SIGNED_WRITE_COMMAND 0xD2u
|
||||
|
||||
|
||||
// internal additions
|
||||
// 128 bit UUID used
|
||||
#define ATT_PROPERTY_UUID128 0x200u
|
||||
// Read/Write Permission bits
|
||||
#define ATT_PROPERTY_READ_PERMISSION_BIT_0 0x0400u
|
||||
#define ATT_PROPERTY_READ_PERMISSION_BIT_1 0x0800u
|
||||
#define ATT_PROPERTY_WRITE_PERMISSION_BIT_0 0x0001u
|
||||
#define ATT_PROPERTY_WRITE_PERMISSION_BIT_1 0x0010u
|
||||
#define ATT_PROPERTY_READ_PERMISSION_SC 0x0020u
|
||||
#define ATT_PROPERTY_WRITE_PERMISSION_SC 0x0080u
|
||||
|
||||
|
||||
typedef struct att_connection {
|
||||
hci_con_handle_t con_handle;
|
||||
uint16_t mtu; // initialized to ATT_DEFAULT_MTU (23), negotiated during MTU exchange
|
||||
uint16_t max_mtu; // local maximal L2CAP_MTU, set to l2cap_max_le_mtu()
|
||||
bool mtu_exchanged;
|
||||
uint8_t encryption_key_size;
|
||||
uint8_t authenticated;
|
||||
uint8_t authorized;
|
||||
uint8_t secure_connection;
|
||||
} att_connection_t;
|
||||
|
||||
/* API_START */
|
||||
|
||||
// map ATT ERROR CODES on to att_read_callback length
|
||||
#define ATT_READ_ERROR_CODE_OFFSET 0xfe00u
|
||||
|
||||
// custom BTstack ATT Response Pending for att_read_callback
|
||||
#define ATT_READ_RESPONSE_PENDING 0xffffu
|
||||
|
||||
// internally used to signal write response pending
|
||||
#define ATT_INTERNAL_WRITE_RESPONSE_PENDING 0xfffeu
|
||||
|
||||
/**
|
||||
@brief 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
|
||||
If ENABLE_ATT_DELAYED_READ_RESPONSE is defined, you may return ATT_READ_RESPONSE_PENDING if data isn't available yet
|
||||
@param con_handle of hci le connection
|
||||
@param attribute_handle to be read
|
||||
@param offset defines start of attribute value
|
||||
@param buffer
|
||||
@param buffer_size
|
||||
@return size of value if buffer is NULL, otherwise number of bytes copied
|
||||
*/
|
||||
typedef uint16_t (*att_read_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
|
||||
|
||||
/**
|
||||
@brief ATT Client Write Callback for Dynamic Data
|
||||
Each Prepared Write Request triggers a callback with transaction mode ATT_TRANSACTION_MODE_ACTIVE.
|
||||
On Execute Write, the callback will be called with ATT_TRANSACTION_MODE_VALIDATE and allows to validate all queued writes and return an application error.
|
||||
If none of the registered callbacks return an error for ATT_TRANSACTION_MODE_VALIDATE and the callback will be called with ATT_TRANSACTION_MODE_EXECUTE.
|
||||
Otherwise, all callbacks will be called with ATT_TRANSACTION_MODE_CANCEL.
|
||||
|
||||
If the additional validation step is not needed, just return 0 for all callbacks with transaction mode ATT_TRANSACTION_MODE_VALIDATE.
|
||||
|
||||
@param con_handle of hci le connection
|
||||
@param attribute_handle to be written
|
||||
@param transaction - ATT_TRANSACTION_MODE_NONE for regular writes. For prepared writes: ATT_TRANSACTION_MODE_ACTIVE, ATT_TRANSACTION_MODE_VALIDATE, ATT_TRANSACTION_MODE_EXECUTE, ATT_TRANSACTION_MODE_CANCEL
|
||||
@param offset into the value - used for queued writes and long attributes
|
||||
@param buffer
|
||||
@param buffer_size
|
||||
@param signature used for signed write commands
|
||||
@return 0 if write was ok, ATT_ERROR_PREPARE_QUEUE_FULL if no space in queue, ATT_ERROR_INVALID_OFFSET if offset is larger than max buffer
|
||||
*/
|
||||
typedef int (*att_write_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size);
|
||||
|
||||
// Read & Write Callbacks for handle range
|
||||
typedef struct att_service_handler {
|
||||
btstack_linked_item_t * item;
|
||||
uint16_t start_handle;
|
||||
uint16_t end_handle;
|
||||
att_read_callback_t read_callback;
|
||||
att_write_callback_t write_callback;
|
||||
btstack_packet_handler_t packet_handler;
|
||||
} att_service_handler_t;
|
||||
|
||||
// MARK: ATT Operations
|
||||
|
||||
/**
|
||||
@brief setup ATT database
|
||||
@param db
|
||||
*/
|
||||
void att_set_db(uint8_t const * db);
|
||||
|
||||
/*
|
||||
@brief set callback for read of dynamic attributes
|
||||
@param callback
|
||||
*/
|
||||
void att_set_read_callback(att_read_callback_t callback);
|
||||
|
||||
/**
|
||||
@brief set callback for write of dynamic attributes
|
||||
@param callback
|
||||
*/
|
||||
void att_set_write_callback(att_write_callback_t callback);
|
||||
|
||||
/**
|
||||
@brief debug helper, dump ATT database to stdout using log_info
|
||||
*/
|
||||
void att_dump_attributes(void);
|
||||
|
||||
/**
|
||||
@brief process ATT request against database and put response into response buffer
|
||||
@param att_connection used for mtu and security properties
|
||||
@param request_buffer, request_len: ATT request from client
|
||||
@param response_buffer for result
|
||||
@return len of data in response buffer. 0 = no response,
|
||||
ATT_READ_RESPONSE_PENDING if it was returned at least once for dynamic data (requires ENABLE_ATT_DELAYED_READ_RESPONSE)
|
||||
*/
|
||||
uint16_t att_handle_request(att_connection_t * att_connection,
|
||||
uint8_t * request_buffer,
|
||||
uint16_t request_len,
|
||||
uint8_t * response_buffer);
|
||||
|
||||
/**
|
||||
@brief setup value notification in response buffer for a given handle and value
|
||||
@param att_connection
|
||||
@param attribute_handle
|
||||
@param value
|
||||
@param value_len
|
||||
@param response_buffer for notification
|
||||
*/
|
||||
uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection,
|
||||
uint16_t attribute_handle,
|
||||
const uint8_t *value,
|
||||
uint16_t value_len,
|
||||
uint8_t * response_buffer);
|
||||
|
||||
/**
|
||||
@brief setup value notification in response buffer for multiple handles and values
|
||||
@param att_connection
|
||||
@param attribute_handle
|
||||
@param value
|
||||
@param value_len
|
||||
@param response_buffer for notification
|
||||
*/
|
||||
uint16_t att_prepare_handle_value_multiple_notification(att_connection_t * att_connection,
|
||||
uint8_t num_attributes,
|
||||
const uint16_t * attribute_handles,
|
||||
const uint8_t ** values_data,
|
||||
const uint16_t * values_len,
|
||||
uint8_t * response_buffer);
|
||||
|
||||
/**
|
||||
@brief setup value indication in response buffer for a given handle and value
|
||||
@param att_connection
|
||||
@param attribute_handle
|
||||
@param value
|
||||
@param value_len
|
||||
@param response_buffer for indication
|
||||
*/
|
||||
uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection,
|
||||
uint16_t attribute_handle,
|
||||
const uint8_t *value,
|
||||
uint16_t value_len,
|
||||
uint8_t * response_buffer);
|
||||
|
||||
/**
|
||||
@brief transcation queue of prepared writes, e.g., after disconnect
|
||||
@return att_connection
|
||||
*/
|
||||
void att_clear_transaction_queue(att_connection_t * att_connection);
|
||||
|
||||
// att_read_callback helpers for a various data types
|
||||
|
||||
/**
|
||||
@brief Handle read of blob like data for att_read_callback
|
||||
@param blob of data
|
||||
@param blob_size of blob
|
||||
@param offset from att_read_callback
|
||||
@param buffer from att_read_callback
|
||||
@param buffer_size from att_read_callback
|
||||
@return value size for buffer == 0 and num bytes copied otherwise
|
||||
*/
|
||||
uint16_t att_read_callback_handle_blob(const uint8_t * blob, uint16_t blob_size, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
|
||||
|
||||
/**
|
||||
@brief Handle read of little endian unsigned 32 bit value for att_read_callback
|
||||
@param value
|
||||
@param offset from att_read_callback
|
||||
@param buffer from att_read_callback
|
||||
@param buffer_size from att_read_callback
|
||||
@return value size for buffer == 0 and num bytes copied otherwise
|
||||
*/
|
||||
uint16_t att_read_callback_handle_little_endian_32(uint32_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
|
||||
|
||||
/**
|
||||
@brief Handle read of little endian unsigned 16 bit value for att_read_callback
|
||||
@param value
|
||||
@param offset from att_read_callback
|
||||
@param buffer from att_read_callback
|
||||
@param buffer_size from att_read_callback
|
||||
@return value size for buffer == 0 and num bytes copied otherwise
|
||||
*/
|
||||
uint16_t att_read_callback_handle_little_endian_16(uint16_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
|
||||
|
||||
/**
|
||||
@brief Handle read of single byte for att_read_callback
|
||||
@param blob of data
|
||||
@param blob_size of blob
|
||||
@param offset from att_read_callback
|
||||
@param buffer from att_read_callback
|
||||
@param buffer_size from att_read_callback
|
||||
@return value size for buffer == 0 and num bytes copied otherwise
|
||||
*/
|
||||
uint16_t att_read_callback_handle_byte(uint8_t value, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
|
||||
|
||||
|
||||
// experimental client API
|
||||
/**
|
||||
@brief Get UUID for handle
|
||||
@param attribute_handle
|
||||
@return 0 if not found
|
||||
*/
|
||||
uint16_t att_uuid_for_handle(uint16_t attribute_handle);
|
||||
|
||||
/**
|
||||
@brief Get const value for handle
|
||||
@param attribute_handle
|
||||
@param out_value_len output variable that hold value len
|
||||
@return value
|
||||
*/
|
||||
|
||||
const uint8_t * gatt_server_get_const_value_for_handle(uint16_t attribute_handle, uint16_t * out_value_len);
|
||||
|
||||
// experimental GATT Server API
|
||||
|
||||
/**
|
||||
@brief Get handle range for primary service.
|
||||
@param uuid16
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@return false if not found
|
||||
*/
|
||||
bool gatt_server_get_handle_range_for_service_with_uuid16(uint16_t uuid16, uint16_t * start_handle, uint16_t * end_handle);
|
||||
|
||||
/**
|
||||
@brief Get handle range for included service.
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@param uuid16
|
||||
@param out_included_service_handle
|
||||
@param out_included_service_start_handle
|
||||
@param out_included_service_end_handle
|
||||
@return false if not found
|
||||
*/
|
||||
bool gatt_server_get_included_service_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16,
|
||||
uint16_t * out_included_service_handle, uint16_t * out_included_service_start_handle, uint16_t * out_included_service_end_handle);
|
||||
|
||||
/**
|
||||
@brief Get value handle for characteristic.
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@param uuid16
|
||||
@return 0 if not found
|
||||
*/
|
||||
uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16);
|
||||
|
||||
/**
|
||||
@brief Get descriptor handle for characteristic.
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@param characteristic_uuid16
|
||||
@param descriptor_uuid16
|
||||
@return 0 if not found
|
||||
*/
|
||||
uint16_t gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16, uint16_t descriptor_uuid16);
|
||||
|
||||
/**
|
||||
@brief Get client configuration handle for characteristic.
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@param characteristic_uuid16
|
||||
@return 0 if not found
|
||||
*/
|
||||
uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16);
|
||||
|
||||
/**
|
||||
@brief Get server configuration handle for characteristic.
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@param characteristic_uuid16
|
||||
@param descriptor_uuid16
|
||||
@return 0 if not found
|
||||
*/
|
||||
uint16_t gatt_server_get_server_configuration_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t characteristic_uuid16);
|
||||
|
||||
|
||||
/**
|
||||
@brief Get handle range for primary service.
|
||||
@param uuid128
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@return false if not found
|
||||
*/
|
||||
bool gatt_server_get_handle_range_for_service_with_uuid128(const uint8_t * uuid128, uint16_t * start_handle, uint16_t * end_handle);
|
||||
|
||||
/**
|
||||
@brief Get value handle.
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@param uuid128
|
||||
@return 0 if not found
|
||||
*/
|
||||
uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128);
|
||||
|
||||
/**
|
||||
@brief Get client configuration handle.
|
||||
@param start_handle
|
||||
@param end_handle
|
||||
@param uuid128
|
||||
@return 0 if not found
|
||||
*/
|
||||
uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128);
|
||||
|
||||
/* API_END */
|
||||
|
||||
// non-user functionality for att_server
|
||||
|
||||
/**
|
||||
@brief Check if writes to handle should be persistent
|
||||
@param handle
|
||||
@return 1 if persistent
|
||||
*/
|
||||
bool att_is_persistent_ccc(uint16_t handle);
|
||||
|
||||
|
||||
|
||||
// auto-pts testing, returns response size
|
||||
#ifdef ENABLE_BTP
|
||||
uint16_t btp_att_get_attributes_by_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16, uint8_t * response_buffer, uint16_t response_buffer_size);
|
||||
uint16_t btp_att_get_attributes_by_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128, uint8_t * response_buffer, uint16_t response_buffer_size);
|
||||
uint16_t btp_att_get_attribute_value(att_connection_t * att_connection, uint16_t attribute_handle, uint8_t * response_buffer, uint16_t response_buffer_size);
|
||||
#endif
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ATT_H
|
||||
@@ -0,0 +1,926 @@
|
||||
/*
|
||||
Copyright (C) 2015 BlueKitchen GmbH
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
4. Any redistribution, use, or modification is done solely for
|
||||
personal benefit and not for any commercial purpose or for
|
||||
monetary gain.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
|
||||
GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Please inquire about commercial licensing options at
|
||||
contact@bluekitchen-gmbh.com
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
bluetooth.h
|
||||
|
||||
Numbers defined or derived from the official Bluetooth specification
|
||||
*/
|
||||
|
||||
#ifndef BLUETOOTH_H
|
||||
#define BLUETOOTH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
@brief hci connection handle type
|
||||
*/
|
||||
typedef uint16_t hci_con_handle_t;
|
||||
|
||||
/**
|
||||
@brief Length of a bluetooth device address.
|
||||
*/
|
||||
#define BD_ADDR_LEN 6
|
||||
|
||||
/**
|
||||
@brief Bluetooth address
|
||||
*/
|
||||
typedef uint8_t bd_addr_t[BD_ADDR_LEN];
|
||||
|
||||
/**
|
||||
Address types
|
||||
*/
|
||||
typedef enum {
|
||||
// Public Device Address
|
||||
BD_ADDR_TYPE_LE_PUBLIC = 0,
|
||||
// Random Device Address
|
||||
BD_ADDR_TYPE_LE_RANDOM = 1,
|
||||
// Public Identity Address (Corresponds to Resolved Private Address)
|
||||
BD_ADDR_TYPE_LE_PUBLIC_IDENTITY = 2,
|
||||
// Random (static) Identity Address (Corresponds to Resolved Private Address)
|
||||
BD_ADDR_TYPE_LE_RANDOM_IDENTITY = 3,
|
||||
// internal BTstack addr types for Classic connections
|
||||
BD_ADDR_TYPE_SCO = 0xfc,
|
||||
BD_ADDR_TYPE_ACL = 0xfd,
|
||||
BD_ADDR_TYPE_UNKNOWN = 0xfe, // also used as 'invalid'
|
||||
} bd_addr_type_t;
|
||||
|
||||
/**
|
||||
Link types for BR/EDR Connections
|
||||
*/
|
||||
typedef enum {
|
||||
HCI_LINK_TYPE_SCO = 0,
|
||||
HCI_LINK_TYPE_ACL = 1,
|
||||
HCI_LINK_TYPE_ESCO = 2,
|
||||
} hci_link_type_t;
|
||||
|
||||
|
||||
/**
|
||||
@brief link key
|
||||
*/
|
||||
#define LINK_KEY_LEN 16
|
||||
#define LINK_KEY_STR_LEN (LINK_KEY_LEN*2)
|
||||
typedef uint8_t link_key_t[LINK_KEY_LEN];
|
||||
|
||||
/**
|
||||
@brief link key type
|
||||
*/
|
||||
typedef enum {
|
||||
INVALID_LINK_KEY = 0xffff,
|
||||
COMBINATION_KEY = 0, // standard pairing
|
||||
LOCAL_UNIT_KEY, // ?
|
||||
REMOTE_UNIT_KEY, // ?
|
||||
DEBUG_COMBINATION_KEY, // SSP with debug
|
||||
UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P192, // SSP Simple Pairing
|
||||
AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P192, // SSP Passkey, Number confirm, OOB
|
||||
CHANGED_COMBINATION_KEY, // Link key changed using Change Connection Lnk Key
|
||||
UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P256, // SSP Simpe Pairing
|
||||
AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P256, // SSP Passkey, Number confirm, OOB
|
||||
} link_key_type_t;
|
||||
|
||||
/**
|
||||
LE Privacy 1.2
|
||||
*/
|
||||
typedef enum {
|
||||
LE_PRIVACY_MODE_NETWORK = 0,
|
||||
LE_PRIVACY_MODE_DEVICE = 1,
|
||||
} le_privacy_mode_t;
|
||||
|
||||
/**
|
||||
@brief Extended Inquiry Response
|
||||
*/
|
||||
#define EXTENDED_INQUIRY_RESPONSE_DATA_LEN 240
|
||||
|
||||
/**
|
||||
@brief Inquiry modes
|
||||
*/
|
||||
typedef enum {
|
||||
INQUIRY_MODE_STANDARD = 0,
|
||||
INQUIRY_MODE_RSSI,
|
||||
INQUIRY_MODE_RSSI_AND_EIR,
|
||||
} inquiry_mode_t;
|
||||
|
||||
/**
|
||||
@brief Page Scan Types
|
||||
*/
|
||||
typedef enum {
|
||||
PAGE_SCAN_MODE_STANDARD = 0,
|
||||
PAGE_SCAN_MODE_INTERLACED,
|
||||
} page_scan_type_t;
|
||||
|
||||
/**
|
||||
@brief Inquiry Scan Types
|
||||
*/
|
||||
typedef enum {
|
||||
INQUIRY_SCAN_MODE_STANDARD = 0,
|
||||
INQUIRY_SCAN_MODE_INTERLACED,
|
||||
} inquiry_scan_type_t;
|
||||
|
||||
/**
|
||||
Link Supervision Timeout Default, 0x7d00 * 0.625ms = 20s
|
||||
*/
|
||||
#define HCI_LINK_SUPERVISION_TIMEOUT_DEFAULT 0x7D00
|
||||
|
||||
/**
|
||||
Service Type used for QoS Setup and Flow Specification
|
||||
*/
|
||||
typedef enum {
|
||||
HCI_SERVICE_TYPE_NO_TRAFFIC = 0,
|
||||
HCI_SERVICE_TYPE_BEST_EFFORT,
|
||||
HCI_SERVICE_TYPE_GUARANTEED,
|
||||
HCI_SERVICE_TYPE_INVALID,
|
||||
} hci_service_type_t;
|
||||
|
||||
/**
|
||||
HCI Transport
|
||||
*/
|
||||
|
||||
/**
|
||||
packet types - used in BTstack and over the H4 UART interface
|
||||
*/
|
||||
#define HCI_COMMAND_DATA_PACKET 0x01
|
||||
#define HCI_ACL_DATA_PACKET 0x02
|
||||
#define HCI_SCO_DATA_PACKET 0x03
|
||||
#define HCI_EVENT_PACKET 0x04
|
||||
#define HCI_ISO_DATA_PACKET 0x05
|
||||
|
||||
/**
|
||||
Other assigned numbers, Assigned_Numbers_Host Controller Interface.pdf
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
HCI_AUDIO_CODING_FORMAT_U_LAW_LOG = 0x00,
|
||||
HCI_AUDIO_CODING_FORMAT_A_LAW_LOG,
|
||||
HCI_AUDIO_CODING_FORMAT_CVSD,
|
||||
HCI_AUDIO_CODING_FORMAT_TRANSPARENT, // Indicates that the controller does not do any transcoding or resampling. This is also used for test mode.
|
||||
HCI_AUDIO_CODING_FORMAT_LINEAR_PCM,
|
||||
HCI_AUDIO_CODING_FORMAT_MSBC,
|
||||
HCI_AUDIO_CODING_FORMAT_LC3,
|
||||
HCI_AUDIO_CODING_FORMAT_G_729A,
|
||||
HCI_AUDIO_CODING_FORMAT_RFU,
|
||||
HCI_AUDIO_CODING_FORMAT_VENDOR_SPECIFIC = 0xFF
|
||||
} hci_audio_coding_format_t;
|
||||
|
||||
/**
|
||||
HCI Layer
|
||||
*/
|
||||
|
||||
//
|
||||
// Error Codes rfom Bluetooth Core Specification
|
||||
//
|
||||
|
||||
/* ENUM_START: BLUETOOTH_ERROR_CODE */
|
||||
#define ERROR_CODE_SUCCESS 0x00
|
||||
#define ERROR_CODE_UNKNOWN_HCI_COMMAND 0x01
|
||||
#define ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER 0x02
|
||||
#define ERROR_CODE_HARDWARE_FAILURE 0x03
|
||||
#define ERROR_CODE_PAGE_TIMEOUT 0x04
|
||||
#define ERROR_CODE_AUTHENTICATION_FAILURE 0x05
|
||||
#define ERROR_CODE_PIN_OR_KEY_MISSING 0x06
|
||||
#define ERROR_CODE_MEMORY_CAPACITY_EXCEEDED 0x07
|
||||
#define ERROR_CODE_CONNECTION_TIMEOUT 0x08
|
||||
#define ERROR_CODE_CONNECTION_LIMIT_EXCEEDED 0x09
|
||||
#define ERROR_CODE_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED 0x0A
|
||||
#define ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS 0x0B
|
||||
#define ERROR_CODE_COMMAND_DISALLOWED 0x0C
|
||||
#define ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES 0x0D
|
||||
#define ERROR_CODE_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS 0x0E
|
||||
#define ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR 0x0F
|
||||
#define ERROR_CODE_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED 0x10
|
||||
#define ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE 0x11
|
||||
#define ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS 0x12
|
||||
#define ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION 0x13
|
||||
#define ERROR_CODE_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES 0x14
|
||||
#define ERROR_CODE_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF 0x15
|
||||
#define ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST 0x16
|
||||
#define ERROR_CODE_REPEATED_ATTEMPTS 0x17
|
||||
#define ERROR_CODE_PAIRING_NOT_ALLOWED 0x18
|
||||
#define ERROR_CODE_UNKNOWN_LMP_PDU 0x19
|
||||
#define ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE_UNSUPPORTED_LMP_FEATURE 0x1A
|
||||
#define ERROR_CODE_SCO_OFFSET_REJECTED 0x1B
|
||||
#define ERROR_CODE_SCO_INTERVAL_REJECTED 0x1C
|
||||
#define ERROR_CODE_SCO_AIR_MODE_REJECTED 0x1D
|
||||
#define ERROR_CODE_INVALID_LMP_PARAMETERS_INVALID_LL_PARAMETERS 0x1E
|
||||
#define ERROR_CODE_UNSPECIFIED_ERROR 0x1F
|
||||
#define ERROR_CODE_UNSUPPORTED_LMP_PARAMETER_VALUE_UNSUPPORTED_LL_PARAMETER_VALUE 0x20
|
||||
#define ERROR_CODE_ROLE_CHANGE_NOT_ALLOWED 0x21
|
||||
#define ERROR_CODE_LMP_RESPONSE_TIMEOUT_LL_RESPONSE_TIMEOUT 0x22
|
||||
#define ERROR_CODE_LMP_ERROR_TRANSACTION_COLLISION 0x23
|
||||
#define ERROR_CODE_LMP_PDU_NOT_ALLOWED 0x24
|
||||
#define ERROR_CODE_ENCRYPTION_MODE_NOT_ACCEPTABLE 0x25
|
||||
#define ERROR_CODE_LINK_KEY_CANNOT_BE_CHANGED 0x26
|
||||
#define ERROR_CODE_REQUESTED_QOS_NOT_SUPPORTED 0x27
|
||||
#define ERROR_CODE_INSTANT_PASSED 0x28
|
||||
#define ERROR_CODE_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED 0x29
|
||||
#define ERROR_CODE_DIFFERENT_TRANSACTION_COLLISION 0x2A
|
||||
#define ERROR_CODE_RESERVED 0x2B
|
||||
#define ERROR_CODE_QOS_UNACCEPTABLE_PARAMETER 0x2C
|
||||
#define ERROR_CODE_QOS_REJECTED 0x2D
|
||||
#define ERROR_CODE_CHANNEL_CLASSIFICATION_NOT_SUPPORTED 0x2E
|
||||
#define ERROR_CODE_INSUFFICIENT_SECURITY 0x2F
|
||||
#define ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 0x30
|
||||
// #define ERROR_CODE_RESERVED
|
||||
#define ERROR_CODE_ROLE_SWITCH_PENDING 0x32
|
||||
// #define ERROR_CODE_RESERVED
|
||||
#define ERROR_CODE_RESERVED_SLOT_VIOLATION 0x34
|
||||
#define ERROR_CODE_ROLE_SWITCH_FAILED 0x35
|
||||
#define ERROR_CODE_EXTENDED_INQUIRY_RESPONSE_TOO_LARGE 0x36
|
||||
#define ERROR_CODE_SECURE_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST 0x37
|
||||
#define ERROR_CODE_HOST_BUSY_PAIRING 0x38
|
||||
#define ERROR_CODE_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND 0x39
|
||||
#define ERROR_CODE_CONTROLLER_BUSY 0x3A
|
||||
#define ERROR_CODE_UNACCEPTABLE_CONNECTION_PARAMETERS 0x3B
|
||||
#define ERROR_CODE_DIRECTED_ADVERTISING_TIMEOUT 0x3C
|
||||
#define ERROR_CODE_CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE 0x3D
|
||||
#define ERROR_CODE_CONNECTION_FAILED_TO_BE_ESTABLISHED 0x3E
|
||||
#define ERROR_CODE_MAC_CONNECTION_FAILED 0x3F
|
||||
#define ERROR_CODE_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING 0x40
|
||||
|
||||
// BTstack defined ERRORS, mapped into BLuetooth status code range
|
||||
|
||||
#define BTSTACK_CONNECTION_TO_BTDAEMON_FAILED 0x50
|
||||
#define BTSTACK_ACTIVATION_FAILED_SYSTEM_BLUETOOTH 0x51
|
||||
#define BTSTACK_ACTIVATION_POWERON_FAILED 0x52
|
||||
#define BTSTACK_ACTIVATION_FAILED_UNKNOWN 0x53
|
||||
#define BTSTACK_NOT_ACTIVATED 0x54
|
||||
#define BTSTACK_BUSY 0x55
|
||||
#define BTSTACK_MEMORY_ALLOC_FAILED 0x56
|
||||
#define BTSTACK_ACL_BUFFERS_FULL 0x57
|
||||
|
||||
// l2cap errors - enumeration by the command that created them
|
||||
#define L2CAP_COMMAND_REJECT_REASON_COMMAND_NOT_UNDERSTOOD 0x60
|
||||
#define L2CAP_COMMAND_REJECT_REASON_SIGNALING_MTU_EXCEEDED 0x61
|
||||
#define L2CAP_COMMAND_REJECT_REASON_INVALID_CID_IN_REQUEST 0x62
|
||||
|
||||
#define L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL 0x63
|
||||
#define L2CAP_CONNECTION_RESPONSE_RESULT_PENDING 0x64
|
||||
#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_PSM 0x65
|
||||
#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY 0x66
|
||||
#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_RESOURCES 0x67
|
||||
#define L2CAP_CONNECTION_RESPONSE_RESULT_ERTM_NOT_SUPPORTED 0x68
|
||||
|
||||
// should be L2CAP_CONNECTION_RTX_TIMEOUT
|
||||
#define L2CAP_CONNECTION_RESPONSE_RESULT_RTX_TIMEOUT 0x69
|
||||
#define L2CAP_CONNECTION_BASEBAND_DISCONNECT 0x6A
|
||||
#define L2CAP_SERVICE_ALREADY_REGISTERED 0x6B
|
||||
#define L2CAP_DATA_LEN_EXCEEDS_REMOTE_MTU 0x6C
|
||||
#define L2CAP_SERVICE_DOES_NOT_EXIST 0x6D
|
||||
#define L2CAP_LOCAL_CID_DOES_NOT_EXIST 0x6E
|
||||
#define L2CAP_CONNECTION_RESPONSE_UNKNOWN_ERROR 0x6F
|
||||
|
||||
#define RFCOMM_MULTIPLEXER_STOPPED 0x70
|
||||
#define RFCOMM_CHANNEL_ALREADY_REGISTERED 0x71
|
||||
#define RFCOMM_NO_OUTGOING_CREDITS 0x72
|
||||
#define RFCOMM_AGGREGATE_FLOW_OFF 0x73
|
||||
#define RFCOMM_DATA_LEN_EXCEEDS_MTU 0x74
|
||||
|
||||
#define HFP_REMOTE_REJECTS_AUDIO_CONNECTION 0x7F
|
||||
|
||||
#define SDP_HANDLE_ALREADY_REGISTERED 0x80
|
||||
#define SDP_QUERY_INCOMPLETE 0x81
|
||||
#define SDP_SERVICE_NOT_FOUND 0x82
|
||||
#define SDP_HANDLE_INVALID 0x83
|
||||
#define SDP_QUERY_BUSY 0x84
|
||||
|
||||
#define ATT_HANDLE_VALUE_INDICATION_IN_PROGRESS 0x90
|
||||
#define ATT_HANDLE_VALUE_INDICATION_TIMEOUT 0x91
|
||||
#define ATT_HANDLE_VALUE_INDICATION_DISCONNECT 0x92
|
||||
|
||||
#define GATT_CLIENT_NOT_CONNECTED 0x93
|
||||
#define GATT_CLIENT_BUSY 0x94
|
||||
#define GATT_CLIENT_IN_WRONG_STATE 0x95
|
||||
#define GATT_CLIENT_DIFFERENT_CONTEXT_FOR_ADDRESS_ALREADY_EXISTS 0x96
|
||||
#define GATT_CLIENT_VALUE_TOO_LONG 0x97
|
||||
#define GATT_CLIENT_CHARACTERISTIC_NOTIFICATION_NOT_SUPPORTED 0x98
|
||||
#define GATT_CLIENT_CHARACTERISTIC_INDICATION_NOT_SUPPORTED 0x99
|
||||
|
||||
#define BNEP_SERVICE_ALREADY_REGISTERED 0xA0
|
||||
#define BNEP_CHANNEL_NOT_CONNECTED 0xA1
|
||||
#define BNEP_DATA_LEN_EXCEEDS_MTU 0xA2
|
||||
|
||||
// OBEX ERRORS
|
||||
#define OBEX_UNKNOWN_ERROR 0xB0
|
||||
#define OBEX_CONNECT_FAILED 0xB1
|
||||
#define OBEX_DISCONNECTED 0xB2
|
||||
#define OBEX_NOT_FOUND 0xB3
|
||||
#define OBEX_NOT_ACCEPTABLE 0xB4
|
||||
#define OBEX_ABORTED 0xB5
|
||||
|
||||
#define MESH_ERROR_APPKEY_INDEX_INVALID 0xD0
|
||||
/* ENUM_END */
|
||||
|
||||
|
||||
/* ENUM_START: AVRCP_BROWSING_ERROR_CODE */
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND 0x00 // Sent if TG received a PDU that it did not understand. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_PARAMETER 0x01 // Sent if the TG received a PDU with a parameter ID that it did not understand. Sent if there is only one parameter ID in the PDU. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SPECIFIED_PARAMETER_NOT_FOUND 0x02 // Sent if the parameter ID is understood, but content is wrong or corrupted. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INTERNAL_ERROR 0x03 // Sent if there are error conditions not covered by a more specific error code. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SUCCESS 0x04 // This is the status that should be returned if the operation was successful. Valid for All except where the response CType is AV/C REJECTED.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_UID_CHANGED 0x05 // The UIDs on the device have changed. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_RESERVED_06 0x06 // Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_DIRECTION 0x07 // The Direction parameter is invalid. Valid for Change Path.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NOT_A_DIRECTORY 0x08 // The UID provided does not refer to a folder item. Valid for Change Path.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_DOES_NOT_EXIST 0x09 // The UID provided does not refer to any currently valid. Valid for Change Path, PlayItem, AddToNowPlaying, GetItemAttributes.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_SCOPE 0x0a // The scope parameter is invalid. Valid for GetFolderItems, PlayItem, AddToNowPlayer, GetItemAttributes,.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_RANGE_OUT_OF_BOUNDS 0x0b // The start of range provided is not valid. Valid for GetFolderItems.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_UID_IS_A_DIRECTORY 0x0c // The UID provided refers to a directory, which cannot be handled by this media player. Valid for PlayItem, AddToNowPlaying.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_MEDIA_IN_USES 0x0d // The media is not able to be used for this operation at this time. Valid for PlayItem, AddToNowPlaying.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NOW_PLAYING_LIST_FULL 0x0e // No more items can be added to the Now Playing List. Valid for AddToNowPlaying.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SEARCH_NOT_SUPPORTED 0x0f // The Browsed Media Player does not support search. Valid for Search.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SEARCH_IN_PROGRESS 0x10 // A search operation is already in progress. Valid for Search.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_PLAYER_ID 0x11 // The specified Player Id does not refer to a valid player. Valid for SetAddressedPlayer, SetBrowsedPlayer.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_PLAYER_NOT_BROWSABLE 0x12 // The Player Id supplied refers to a Media Player which does not support browsing. Valid for SetBrowsedPlayer.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_PLAYER_NOT_ADDRESSED 0x13 // The Player Id supplied refers to a player which is not currently addressed, and the command is not able to be performed if the player is not set as addressed. Valid for Search SetBrowsedPlayer.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NO_VALID_SEARCH_RESULTS 0x14 // The Search result list does not contain valid entries, e.g. after being invalidated due to change of browsed player. Valid for GetFolderItems.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NO_AVAILABLE_PLAYERS 0x15 // Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_ADDRESSED_PLAYER_CHANGED 0x16 // Valid for Register Notification.
|
||||
// 0x17-0xff Reserved
|
||||
/* ENUM_END */
|
||||
|
||||
// HCI roles
|
||||
typedef enum {
|
||||
HCI_ROLE_MASTER = 0,
|
||||
HCI_ROLE_SLAVE = 1,
|
||||
HCI_ROLE_INVALID = 0xff,
|
||||
} hci_role_t;
|
||||
|
||||
// packet sizes (max payload)
|
||||
#define HCI_ACL_DM1_SIZE 17
|
||||
#define HCI_ACL_DH1_SIZE 27
|
||||
#define HCI_ACL_2DH1_SIZE 54
|
||||
#define HCI_ACL_3DH1_SIZE 83
|
||||
#define HCI_ACL_DM3_SIZE 121
|
||||
#define HCI_ACL_DH3_SIZE 183
|
||||
#define HCI_ACL_DM5_SIZE 224
|
||||
#define HCI_ACL_DH5_SIZE 339
|
||||
#define HCI_ACL_2DH3_SIZE 367
|
||||
#define HCI_ACL_3DH3_SIZE 552
|
||||
#define HCI_ACL_2DH5_SIZE 679
|
||||
#define HCI_ACL_3DH5_SIZE 1021
|
||||
#define HCI_SCO_HV1_SIZE 10
|
||||
#define HCI_SCO_HV2_SIZE 20
|
||||
#define HCI_SCO_HV3_SIZE 30
|
||||
#define HCI_SCO_EV3_SIZE 30
|
||||
#define HCI_SCO_EV4_SIZE 120
|
||||
#define HCI_SCO_EV5_SIZE 180
|
||||
#define HCI_SCO_2EV3_SIZE 60
|
||||
#define HCI_SCO_2EV5_SIZE 360
|
||||
#define HCI_SCO_3EV3_SIZE 90
|
||||
#define HCI_SCO_3EV5_SIZE 540
|
||||
|
||||
#define LE_ADVERTISING_DATA_SIZE 31
|
||||
#define LE_EXTENDED_ADVERTISING_DATA_SIZE 229
|
||||
#define LE_EXTENDED_ADVERTISING_MAX_HANDLE 0xEFu
|
||||
#define LE_EXTENDED_ADVERTISING_MAX_CHUNK_LEN 251
|
||||
|
||||
// advertising event properties for extended advertising
|
||||
#define LE_ADVERTISING_PROPERTIES_CONNECTABLE (1u<<0)
|
||||
#define LE_ADVERTISING_PROPERTIES_SCANNABLE (1u<<1)
|
||||
#define LE_ADVERTISING_PROPERTIES_DIRECTED (1u<<2)
|
||||
#define LE_ADVERTISING_PROPERTIES_HIGH_DUTY_CYCLE (1u<<3)
|
||||
#define LE_ADVERTISING_PROPERTIES_LEGACY (1u<<4)
|
||||
#define LE_ADVERTISING_PROPERTIES_ANONYMOUS (1u<<5)
|
||||
#define LE_ADVERTISING_PROPERTIES_INCLUDE_TX_POWER (1u<<6)
|
||||
|
||||
|
||||
// SCO Packet Types
|
||||
#define SCO_PACKET_TYPES_NONE 0x0000
|
||||
#define SCO_PACKET_TYPES_HV1 0x0001
|
||||
#define SCO_PACKET_TYPES_HV2 0x0002
|
||||
#define SCO_PACKET_TYPES_HV3 0x0004
|
||||
#define SCO_PACKET_TYPES_EV3 0x0008
|
||||
#define SCO_PACKET_TYPES_EV4 0x0010
|
||||
#define SCO_PACKET_TYPES_EV5 0x0020
|
||||
#define SCO_PACKET_TYPES_2EV3 0x0040
|
||||
#define SCO_PACKET_TYPES_3EV3 0x0080
|
||||
#define SCO_PACKET_TYPES_2EV5 0x0100
|
||||
#define SCO_PACKET_TYPES_3EV5 0x0200
|
||||
#define SCO_PACKET_TYPES_ALL 0x03FF
|
||||
#define SCO_PACKET_TYPES_SCO 0x0007
|
||||
#define SCO_PACKET_TYPES_ESCO 0x03F8
|
||||
|
||||
// Link Policy Settings
|
||||
#define LM_LINK_POLICY_DISABLE_ALL_LM_MODES 0
|
||||
#define LM_LINK_POLICY_ENABLE_ROLE_SWITCH 1
|
||||
#define LM_LINK_POLICY_ENABLE_HOLD_MODE 2
|
||||
#define LM_LINK_POLICY_ENABLE_SNIFF_MODE 4
|
||||
|
||||
// ACL Connection Modes
|
||||
#define ACL_CONNECTION_MODE_ACTIVE 0
|
||||
#define ACL_CONNECTION_MODE_HOLD 1
|
||||
#define ACL_CONNECTION_MODE_SNIFF 2
|
||||
|
||||
/**
|
||||
Default INQ Mode
|
||||
*/
|
||||
#define GAP_IAC_GENERAL_INQUIRY 0x9E8B33L // General/Unlimited Inquiry Access Code (GIAC)
|
||||
#define GAP_IAC_LIMITED_INQUIRY 0x9E8B00L // Limited Dedicated Inquiry Access Code (LIAC)
|
||||
|
||||
/**
|
||||
SSP IO Capabilities
|
||||
*/
|
||||
#define SSP_IO_CAPABILITY_DISPLAY_ONLY 0
|
||||
#define SSP_IO_CAPABILITY_DISPLAY_YES_NO 1
|
||||
#define SSP_IO_CAPABILITY_KEYBOARD_ONLY 2
|
||||
#define SSP_IO_CAPABILITY_NO_INPUT_NO_OUTPUT 3
|
||||
#define SSP_IO_CAPABILITY_UNKNOWN 0xff
|
||||
|
||||
|
||||
/**
|
||||
SSP Authentication Requirements, see IO Capability Request Reply Command
|
||||
*/
|
||||
|
||||
// Numeric comparison with automatic accept allowed.
|
||||
#define SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_NO_BONDING 0x00
|
||||
|
||||
// Use IO Capabilities to deter- mine authentication procedure
|
||||
#define SSP_IO_AUTHREQ_MITM_PROTECTION_REQUIRED_NO_BONDING 0x01
|
||||
|
||||
// Numeric compar- ison with automatic accept allowed.
|
||||
#define SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_DEDICATED_BONDING 0x02
|
||||
|
||||
// Use IO Capabilities to determine authentication procedure
|
||||
#define SSP_IO_AUTHREQ_MITM_PROTECTION_REQUIRED_DEDICATED_BONDING 0x03
|
||||
|
||||
// Numeric Compari- son with automatic accept allowed.
|
||||
#define SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_GENERAL_BONDING 0x04
|
||||
|
||||
// Use IO capabilities to determine authentication procedure.
|
||||
#define SSP_IO_AUTHREQ_MITM_PROTECTION_REQUIRED_GENERAL_BONDING 0x05
|
||||
|
||||
|
||||
// OGFs
|
||||
#define OGF_LINK_CONTROL 0x01
|
||||
#define OGF_LINK_POLICY 0x02
|
||||
#define OGF_CONTROLLER_BASEBAND 0x03
|
||||
#define OGF_INFORMATIONAL_PARAMETERS 0x04
|
||||
#define OGF_STATUS_PARAMETERS 0x05
|
||||
#define OGF_TESTING 0x06
|
||||
#define OGF_LE_CONTROLLER 0x08
|
||||
#define OGF_VENDOR 0x3f
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
L2CAP Layer
|
||||
*/
|
||||
|
||||
#define L2CAP_HEADER_SIZE 4
|
||||
|
||||
// minimum signaling MTU
|
||||
#define L2CAP_MINIMAL_MTU 48
|
||||
#define L2CAP_DEFAULT_MTU 672
|
||||
|
||||
// Minimum/default MTU
|
||||
#define L2CAP_LE_DEFAULT_MTU 23
|
||||
|
||||
// L2CAP Fixed Channel IDs
|
||||
#define L2CAP_CID_SIGNALING 0x0001
|
||||
#define L2CAP_CID_CONNECTIONLESS_CHANNEL 0x0002
|
||||
#define L2CAP_CID_ATTRIBUTE_PROTOCOL 0x0004
|
||||
#define L2CAP_CID_SIGNALING_LE 0x0005
|
||||
#define L2CAP_CID_SECURITY_MANAGER_PROTOCOL 0x0006
|
||||
#define L2CAP_CID_BR_EDR_SECURITY_MANAGER 0x0007
|
||||
|
||||
// L2CAP Channels in Basic and Enhanced Retransmission Mode
|
||||
|
||||
// connection response result
|
||||
#define L2CAP_CONNECTION_RESULT_SUCCESS 0x0000
|
||||
#define L2CAP_CONNECTION_RESULT_PENDING 0x0001
|
||||
#define L2CAP_CONNECTION_RESULT_PSM_NOT_SUPPORTED 0x0002
|
||||
#define L2CAP_CONNECTION_RESULT_SECURITY_BLOCK 0x0003
|
||||
#define L2CAP_CONNECTION_RESULT_NO_RESOURCES_AVAILABLE 0x0004
|
||||
#define L2CAP_CONNECTION_RESULT_INVALID_SOURCE_CID 0x0006
|
||||
#define L2CAP_CONNECTION_RESULT_SOURCE_CID_ALREADY_ALLOCATED 0x0007
|
||||
|
||||
// L2CAP Channels in LE Credit-Based Flow-Control Mode
|
||||
|
||||
// connection response result
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_SUCCESS 0x0000
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_SPSM_NOT_SUPPORTED 0x0002
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_NO_RESOURCES_AVAILABLE 0x0004
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_INSUFFICIENT_AUTHENTICATION 0x0005
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_INSUFFICIENT_AUTHORIZATION 0x0006
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_ENCYRPTION_KEY_SIZE_TOO_SHORT 0x0007
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_INSUFFICIENT_ENCRYPTION 0x0008
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_INVALID_SOURCE_CID 0x0009
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_SOURCE_CID_ALREADY_ALLOCATED 0x000A
|
||||
#define L2CAP_CBM_CONNECTION_RESULT_UNACCEPTABLE_PARAMETERS 0x000B
|
||||
|
||||
|
||||
// L2CAP Channels in Enhanced Credit-Based Flow-Control Mode
|
||||
|
||||
// number of CIDs in single connection+reconfiguration request/response
|
||||
#define L2CAP_ECBM_MAX_CID_ARRAY_SIZE 5
|
||||
|
||||
// connection response result
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_SUCCESS 0x0000
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_REFUSED_SPSM_NOT_SUPPORTED 0x0002
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_SOME_REFUSED_INSUFFICIENT_RESOURCES_AVAILABLE 0x0004
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_REFUSED_INSUFFICIENT_AUTHENTICATION 0x0005
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_REFUSED_INSUFFICIENT_AUTHORIZATION 0x0006
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_REFUSED_ENCYRPTION_KEY_SIZE_TOO_SHORT 0x0007
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_REFUSED_INSUFFICIENT_ENCRYPTION 0x0008
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_SOME_REFUSED_INVALID_SOURCE_CID 0x0009
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_SOME_REFUSED_SOURCE_CID_ALREADY_ALOCATED 0x000A
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_REFUSED_UNACCEPTABLE_PARAMETERS 0x000B
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_REFUSED_INVALID_PARAMETERS 0x000C
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_PENDING_NO_FURTHER_INFORMATION 0x000D
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_PENDING_AUTHENTICATION 0x000E
|
||||
#define L2CAP_ECBM_CONNECTION_RESULT_ALL_PENDING_AUTHORIZATION 0x000F
|
||||
|
||||
|
||||
// Result for Reconfigure Request
|
||||
#define L2CAP_ECBM_RECONFIGURE_SUCCESS 0
|
||||
#define L2CAP_ECBM_RECONFIGURE_FAILED_MTU_REDUCTION_NOT_ALLOWED 1
|
||||
#define L2CAP_ECBM_RECONFIGURE_FAILED_MPS_REDUCTION_MULTIPLE_CHANNELS 2
|
||||
#define L2CAP_ECBM_RECONFIGURE_FAILED_DESTINATION_CID_INVALID 3
|
||||
#define L2CAP_ECBM_RECONFIGURE_FAILED_UNACCEPTABLE_PARAMETERS 4
|
||||
|
||||
/**
|
||||
SDP Protocol
|
||||
*/
|
||||
|
||||
// Device Vendor ID Sources
|
||||
#define DEVICE_ID_VENDOR_ID_SOURCE_BLUETOOTH 0x0001
|
||||
#define DEVICE_ID_VENDOR_ID_SOURCE_USB 0x0002
|
||||
|
||||
// OBEX
|
||||
#define SDP_vCard_2_1 0x01
|
||||
#define SDP_vCard_3_0 0x02
|
||||
#define SDP_vCal_1_0 0x03
|
||||
#define SDP_iCal_2_0 0x04
|
||||
#define SDP_vNote 0x05
|
||||
#define SDP_vMessage 0x06
|
||||
#define SDP_OBEXFileTypeAny 0xFF
|
||||
|
||||
/**
|
||||
RFCOMM Protocol
|
||||
*/
|
||||
|
||||
// Line Status
|
||||
#define LINE_STATUS_NO_ERROR 0x00
|
||||
#define LINE_STATUS_OVERRUN_ERROR 0x03
|
||||
#define LINE_STATUS_PARITY_ERORR 0x05
|
||||
#define LINE_STATUS_FRAMING_ERROR 0x09
|
||||
|
||||
// Modem Status Flags
|
||||
#define MODEM_STATUS_FC 0x02
|
||||
#define MODEM_STATUS_RTC 0x04
|
||||
#define MODEM_STATUS_RTR 0x08
|
||||
#define MODEM_STATUS_IC 0x40
|
||||
#define MODEM_STATUS_DV 0x80
|
||||
|
||||
typedef enum rpn_baud {
|
||||
RPN_BAUD_2400 = 0,
|
||||
RPN_BAUD_4800,
|
||||
RPN_BAUD_7200,
|
||||
RPN_BAUD_9600,
|
||||
RPN_BAUD_19200,
|
||||
RPN_BAUD_38400,
|
||||
RPN_BAUD_57600,
|
||||
RPN_BAUD_115200,
|
||||
RPN_BAUD_230400
|
||||
} rpn_baud_t;
|
||||
|
||||
typedef enum rpn_data_bits {
|
||||
RPN_DATA_BITS_5 = 0,
|
||||
RPN_DATA_BITS_6 = 0,
|
||||
RPN_DATA_BITS_7 = 0,
|
||||
RPN_DATA_BITS_8 = 0
|
||||
} rpn_data_bits_t;
|
||||
|
||||
typedef enum rpn_stop_bits {
|
||||
RPN_STOP_BITS_1_0 = 0,
|
||||
RPN_STOP_BITS_1_5
|
||||
} rpn_stop_bits_t;
|
||||
|
||||
typedef enum rpn_parity {
|
||||
RPN_PARITY_NONE = 0,
|
||||
RPN_PARITY_ODD = 1,
|
||||
RPN_PARITY_EVEN = 3,
|
||||
RPN_PARITY_MARK = 5,
|
||||
RPN_PARITY_SPACE = 7,
|
||||
} rpn_parity_t;
|
||||
|
||||
#define RPN_FLOW_CONTROL_XONXOFF_ON_INPUT 0x01
|
||||
#define RPN_FLOW_CONTROL_XONXOFF_ON_OUTPUT 0x02
|
||||
#define RPN_FLOW_CONTROL_RTR_ON_INPUT 0x04
|
||||
#define RPN_FLOW_CONTROL_RTR_ON_OUTPUT 0x08
|
||||
#define RPN_FLOW_CONTROL_RTC_ON_INPUT 0x10
|
||||
#define RPN_FLOW_CONTROL_RTC_ON_OUTPUT 0x20
|
||||
|
||||
#define RPN_PARAM_MASK_0_BAUD 0x01
|
||||
#define RPN_PARAM_MASK_0_DATA_BITS 0x02
|
||||
#define RPN_PARAM_MASK_0_STOP_BITS 0x04
|
||||
#define RPN_PARAM_MASK_0_PARITY 0x08
|
||||
#define RPN_PARAM_MASK_0_PARITY_TYPE 0x10
|
||||
#define RPN_PARAM_MASK_0_XON_CHAR 0x20
|
||||
#define RPN_PARAM_MASK_0_XOFF_CHAR 0x40
|
||||
#define RPN_PARAM_MASK_0_RESERVED 0x80
|
||||
|
||||
// @note: values are identical to rpn_flow_control_t
|
||||
#define RPN_PARAM_MASK_1_XONOFF_ON_INPUT 0x01
|
||||
#define RPN_PARAM_MASK_1_XONOFF_ON_OUTPUT 0x02
|
||||
#define RPN_PARAM_MASK_1_RTR_ON_INPUT 0x04
|
||||
#define RPN_PARAM_MASK_1_RTR_ON_OUTPUT 0x08
|
||||
#define RPN_PARAM_MASK_1_RTC_ON_INPUT 0x10
|
||||
#define RPN_PARAM_MASK_1_RTC_ON_OUTPUT 0x20
|
||||
#define RPN_PARAM_MASK_1_RESERVED_0 0x40
|
||||
#define RPN_PARAM_MASK_1_RESERVED_1 0x80
|
||||
|
||||
/**
|
||||
BNEP Protocol
|
||||
*/
|
||||
|
||||
#ifndef ETHER_ADDR_LEN
|
||||
#define ETHER_ADDR_LEN 6
|
||||
#endif
|
||||
|
||||
#ifndef ETHERTYPE_VLAN
|
||||
#define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tag */
|
||||
#endif
|
||||
|
||||
#define BNEP_MTU_MIN 1691
|
||||
|
||||
|
||||
/**
|
||||
PAN Profile
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
BNEP_SECURITY_NONE = 0x0000,
|
||||
BNEP_SECURITY_SERVICE_LEVEL_ENFORCED,
|
||||
BNEP_SECURITY_802_1X
|
||||
} security_description_t;
|
||||
|
||||
typedef enum {
|
||||
PAN_NET_ACCESS_TYPE_PSTN = 0x0000,
|
||||
PAN_NET_ACCESS_TYPE_ISDN,
|
||||
PAN_NET_ACCESS_TYPE_DSL,
|
||||
PAN_NET_ACCESS_TYPE_CABLE_MODEM,
|
||||
PAN_NET_ACCESS_TYPE_10MB_ETHERNET,
|
||||
PAN_NET_ACCESS_TYPE_100MB_ETHERNET,
|
||||
PAN_NET_ACCESS_TYPE_4MB_TOKEN_RING,
|
||||
PAN_NET_ACCESS_TYPE_16MB_TOKEN_RING,
|
||||
PAN_NET_ACCESS_TYPE_100MB_TOKEN_RING,
|
||||
PAN_NET_ACCESS_TYPE_FDDI,
|
||||
PAN_NET_ACCESS_TYPE_GSM,
|
||||
PAN_NET_ACCESS_TYPE_CDMA,
|
||||
PAN_NET_ACCESS_TYPE_GPRS,
|
||||
PAN_NET_ACCESS_TYPE_3G,
|
||||
PAN_NET_ACCESS_TYPE_CELULAR,
|
||||
PAN_NET_ACCESS_TYPE_OTHER = 0xFFFE,
|
||||
PAN_NET_ACCESS_TYPE_NONE
|
||||
} net_access_type_t;
|
||||
|
||||
/**
|
||||
ATT
|
||||
*/
|
||||
|
||||
// Minimum/default MTU
|
||||
#define ATT_DEFAULT_MTU 23
|
||||
|
||||
// MARK: ATT Error Codes
|
||||
#define ATT_ERROR_SUCCESS 0x00
|
||||
#define ATT_ERROR_INVALID_HANDLE 0x01
|
||||
#define ATT_ERROR_READ_NOT_PERMITTED 0x02
|
||||
#define ATT_ERROR_WRITE_NOT_PERMITTED 0x03
|
||||
#define ATT_ERROR_INVALID_PDU 0x04
|
||||
#define ATT_ERROR_INSUFFICIENT_AUTHENTICATION 0x05
|
||||
#define ATT_ERROR_REQUEST_NOT_SUPPORTED 0x06
|
||||
#define ATT_ERROR_INVALID_OFFSET 0x07
|
||||
#define ATT_ERROR_INSUFFICIENT_AUTHORIZATION 0x08
|
||||
#define ATT_ERROR_PREPARE_QUEUE_FULL 0x09
|
||||
#define ATT_ERROR_ATTRIBUTE_NOT_FOUND 0x0a
|
||||
#define ATT_ERROR_ATTRIBUTE_NOT_LONG 0x0b
|
||||
#define ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE 0x0c
|
||||
#define ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH 0x0d
|
||||
#define ATT_ERROR_UNLIKELY_ERROR 0x0e
|
||||
#define ATT_ERROR_INSUFFICIENT_ENCRYPTION 0x0f
|
||||
#define ATT_ERROR_UNSUPPORTED_GROUP_TYPE 0x10
|
||||
#define ATT_ERROR_INSUFFICIENT_RESOURCES 0x11
|
||||
#define ATT_ERROR_VALUE_NOT_ALLOWED 0x13
|
||||
|
||||
// MARK: ATT Error Codes defined by BTstack
|
||||
#define ATT_ERROR_HCI_DISCONNECT_RECEIVED 0x1f
|
||||
#define ATT_ERROR_BONDING_INFORMATION_MISSING 0x70
|
||||
#define ATT_ERROR_DATA_MISMATCH 0x7e
|
||||
#define ATT_ERROR_TIMEOUT 0x7F
|
||||
#define ATT_ERROR_WRITE_RESPONSE_PENDING 0x100
|
||||
|
||||
// MARK: ATT Error Codes from Bluetooth Core Specification Supplement, Version 9 or later
|
||||
#define ATT_ERROR_WRITE_REQUEST_REJECTED 0xFC
|
||||
#define ATT_ERROR_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_IMPROPERLY_CONFIGURED 0xFD
|
||||
#define ATT_ERROR_PROCEDURE_ALREADY_IN_PROGRESS 0xFE
|
||||
#define ATT_ERROR_OUT_OF_RANGE 0xFF
|
||||
|
||||
// MARK: ATT Error Codes from Cycling Power Service spec
|
||||
#define CYCLING_POWER_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS 0x80
|
||||
#define CYCLING_POWER_ERROR_CODE_PROCEDURE_ALREADY_IN_PROGRESS 0xFE
|
||||
#define CYCLING_POWER_ERROR_CODE_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED 0xFD
|
||||
|
||||
// MARK: ATT Error Codes from Cycling Speed and Cadence Service spec
|
||||
#define CYCLING_SPEED_AND_CADENCE_ERROR_CODE_PROCEDURE_ALREADY_IN_PROGRESS 0x80
|
||||
#define CYCLING_SPEED_AND_CADENCE_ERROR_CODE_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED 0x81
|
||||
|
||||
|
||||
// MARK: Attribute Property Flags
|
||||
#define ATT_PROPERTY_BROADCAST 0x01
|
||||
#define ATT_PROPERTY_READ 0x02
|
||||
#define ATT_PROPERTY_WRITE_WITHOUT_RESPONSE 0x04
|
||||
#define ATT_PROPERTY_WRITE 0x08
|
||||
#define ATT_PROPERTY_NOTIFY 0x10
|
||||
#define ATT_PROPERTY_INDICATE 0x20
|
||||
#define ATT_PROPERTY_AUTHENTICATED_SIGNED_WRITE 0x40
|
||||
#define ATT_PROPERTY_EXTENDED_PROPERTIES 0x80
|
||||
|
||||
// MARK: Attribute Property Flag, BTstack extension
|
||||
// value is asked from client
|
||||
#define ATT_PROPERTY_DYNAMIC 0x100
|
||||
|
||||
// Security levels
|
||||
#define ATT_SECURITY_NONE 0
|
||||
#define ATT_SECURITY_ENCRYPTED 1
|
||||
#define ATT_SECURITY_AUTHENTICATED 2
|
||||
#define ATT_SECURITY_AUTHORIZED 3
|
||||
#define ATT_SECURITY_AUTHENTICATED_SC 4
|
||||
|
||||
// ATT Transaction Timeout of 30 seconds for Command/Response or Indication/Confirmation
|
||||
#define ATT_TRANSACTION_TIMEOUT_MS 30000
|
||||
|
||||
#define ATT_TRANSACTION_MODE_NONE 0x0
|
||||
#define ATT_TRANSACTION_MODE_ACTIVE 0x1
|
||||
#define ATT_TRANSACTION_MODE_EXECUTE 0x2
|
||||
#define ATT_TRANSACTION_MODE_CANCEL 0x3
|
||||
#define ATT_TRANSACTION_MODE_VALIDATE 0x4
|
||||
|
||||
// MARK: GATT UUIDs
|
||||
#define GATT_PRIMARY_SERVICE_UUID 0x2800
|
||||
#define GATT_SECONDARY_SERVICE_UUID 0x2801
|
||||
#define GATT_INCLUDE_SERVICE_UUID 0x2802
|
||||
#define GATT_CHARACTERISTICS_UUID 0x2803
|
||||
#define GATT_CHARACTERISTIC_EXTENDED_PROPERTIES 0x2900
|
||||
#define GATT_CHARACTERISTIC_USER_DESCRIPTION 0x2901
|
||||
#define GATT_CLIENT_CHARACTERISTICS_CONFIGURATION 0x2902
|
||||
#define GATT_SERVER_CHARACTERISTICS_CONFIGURATION 0x2903
|
||||
#define GATT_CHARACTERISTIC_PRESENTATION_FORMAT 0x2904
|
||||
#define GATT_CHARACTERISTIC_AGGREGATE_FORMAT 0x2905
|
||||
#define GATT_CLIENT_SUPPORTED_FEATURES 0x2B29
|
||||
#define GATT_SERVER_SUPPORTED_FEATURES 0x2B3A
|
||||
|
||||
#define GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE 0
|
||||
#define GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION 1
|
||||
#define GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION 2
|
||||
|
||||
#define GATT_CLIENT_ANY_CONNECTION 0xffff
|
||||
#define GATT_CLIENT_ANY_VALUE_HANDLE 0x0000
|
||||
|
||||
// GAP Service and Characteristics
|
||||
#define GAP_SERVICE_UUID 0x1800
|
||||
#define GAP_DEVICE_NAME_UUID 0x2a00
|
||||
#define GAP_APPEARANCE_UUID 0x2a01
|
||||
#define GAP_PERIPHERAL_PRIVACY_FLAG 0x2a02
|
||||
#define GAP_RECONNECTION_ADDRESS_UUID 0x2a03
|
||||
#define GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_UUID 0x2a04
|
||||
#define GAP_SERVICE_CHANGED 0x2a05
|
||||
|
||||
// Bluetooth GATT types
|
||||
|
||||
typedef struct {
|
||||
uint16_t year; // 0 - year is not known; or [1582,9999]
|
||||
uint8_t month; // 0 - month is not known; or [1,12]
|
||||
uint8_t day; // 0 - day is not known; or [1,31]
|
||||
uint8_t hours; // [0,23]
|
||||
uint8_t minutes; // [0,59]
|
||||
uint8_t seconds; // [0,59]
|
||||
} gatt_date_time_t;
|
||||
|
||||
typedef enum {
|
||||
GATT_MICROPHONE_CONTROL_MUTE_OFF = 0x00,
|
||||
GATT_MICROPHONE_CONTROL_MUTE_ON,
|
||||
GATT_MICROPHONE_CONTROL_MUTE_DISABLED
|
||||
} gatt_microphone_control_mute_t;
|
||||
|
||||
/**
|
||||
SM - LE Security Manager
|
||||
*/
|
||||
// Bluetooth Spec definitions
|
||||
typedef enum {
|
||||
SM_CODE_PAIRING_REQUEST = 0X01,
|
||||
SM_CODE_PAIRING_RESPONSE,
|
||||
SM_CODE_PAIRING_CONFIRM,
|
||||
SM_CODE_PAIRING_RANDOM,
|
||||
SM_CODE_PAIRING_FAILED,
|
||||
SM_CODE_ENCRYPTION_INFORMATION,
|
||||
SM_CODE_MASTER_IDENTIFICATION,
|
||||
SM_CODE_IDENTITY_INFORMATION,
|
||||
SM_CODE_IDENTITY_ADDRESS_INFORMATION,
|
||||
SM_CODE_SIGNING_INFORMATION,
|
||||
SM_CODE_SECURITY_REQUEST,
|
||||
SM_CODE_PAIRING_PUBLIC_KEY,
|
||||
SM_CODE_PAIRING_DHKEY_CHECK,
|
||||
SM_CODE_KEYPRESS_NOTIFICATION,
|
||||
} SECURITY_MANAGER_COMMANDS;
|
||||
|
||||
// IO Capability Values
|
||||
typedef enum {
|
||||
IO_CAPABILITY_DISPLAY_ONLY = 0,
|
||||
IO_CAPABILITY_DISPLAY_YES_NO,
|
||||
IO_CAPABILITY_KEYBOARD_ONLY,
|
||||
IO_CAPABILITY_NO_INPUT_NO_OUTPUT,
|
||||
IO_CAPABILITY_KEYBOARD_DISPLAY, // not used by secure simple pairing
|
||||
} io_capability_t;
|
||||
|
||||
// Authentication requirement flags
|
||||
#define SM_AUTHREQ_NO_BONDING 0x00
|
||||
#define SM_AUTHREQ_BONDING 0x01
|
||||
#define SM_AUTHREQ_MITM_PROTECTION 0x04
|
||||
#define SM_AUTHREQ_SECURE_CONNECTION 0x08
|
||||
#define SM_AUTHREQ_KEYPRESS 0x10
|
||||
#define SM_AUTHREQ_CT2 0x20
|
||||
|
||||
// Key distribution flags used by spec
|
||||
#define SM_KEYDIST_ENC_KEY 0x01
|
||||
#define SM_KEYDIST_ID_KEY 0x02
|
||||
#define SM_KEYDIST_SIGN 0x04
|
||||
#define SM_KEYDIST_LINK_KEY 0x08
|
||||
|
||||
// Key distribution flags used internally
|
||||
#define SM_KEYDIST_FLAG_ENCRYPTION_INFORMATION 0x01
|
||||
#define SM_KEYDIST_FLAG_MASTER_IDENTIFICATION 0x02
|
||||
#define SM_KEYDIST_FLAG_IDENTITY_INFORMATION 0x04
|
||||
#define SM_KEYDIST_FLAG_IDENTITY_ADDRESS_INFORMATION 0x08
|
||||
#define SM_KEYDIST_FLAG_SIGNING_IDENTIFICATION 0x10
|
||||
|
||||
// STK Generation Methods
|
||||
#define SM_STK_GENERATION_METHOD_JUST_WORKS 0x01
|
||||
#define SM_STK_GENERATION_METHOD_OOB 0x02
|
||||
#define SM_STK_GENERATION_METHOD_PASSKEY 0x04
|
||||
#define SM_STK_GENERATION_METHOD_NUMERIC_COMPARISON 0x08
|
||||
|
||||
// Pairing Failed Reasons
|
||||
#define SM_REASON_RESERVED 0x00
|
||||
#define SM_REASON_PASSKEY_ENTRY_FAILED 0x01
|
||||
#define SM_REASON_OOB_NOT_AVAILABLE 0x02
|
||||
#define SM_REASON_AUTHENTHICATION_REQUIREMENTS 0x03
|
||||
#define SM_REASON_CONFIRM_VALUE_FAILED 0x04
|
||||
#define SM_REASON_PAIRING_NOT_SUPPORTED 0x05
|
||||
#define SM_REASON_ENCRYPTION_KEY_SIZE 0x06
|
||||
#define SM_REASON_COMMAND_NOT_SUPPORTED 0x07
|
||||
#define SM_REASON_UNSPECIFIED_REASON 0x08
|
||||
#define SM_REASON_REPEATED_ATTEMPTS 0x09
|
||||
#define SM_REASON_INVALID_PARAMETERS 0x0a
|
||||
#define SM_REASON_DHKEY_CHECK_FAILED 0x0b
|
||||
#define SM_REASON_NUMERIC_COMPARISON_FAILED 0x0c
|
||||
#define SM_REASON_BR_EDR_PAIRING_IN_PROGRESS 0x0d
|
||||
#define SM_REASON_CROSS_TRANSPORT_KEY_DERIVATION_NOT_ALLOWED 0x0e
|
||||
#define SM_REASON_KEY_REJECTED 0x0f
|
||||
|
||||
// also, invalid parameters
|
||||
// and reserved
|
||||
|
||||
// Keypress Notifications
|
||||
#define SM_KEYPRESS_PASSKEY_ENTRY_STARTED 0x00
|
||||
#define SM_KEYPRESS_PASSKEY_DIGIT_ENTERED 0x01
|
||||
#define SM_KEYPRESS_PASSKEY_DIGIT_ERASED 0x02
|
||||
#define SM_KEYPRESS_PASSKEY_CLEARED 0x03
|
||||
#define SM_KEYPRESS_PASSKEY_ENTRY_COMPLETED 0x04
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
Copyright (C) 2014 BlueKitchen GmbH
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
4. Any redistribution, use, or modification is done solely for
|
||||
personal benefit and not for any commercial purpose or for
|
||||
monetary gain.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
|
||||
GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Please inquire about commercial licensing options at
|
||||
contact@bluekitchen-gmbh.com
|
||||
|
||||
*/
|
||||
|
||||
#if defined ENABLE_CLASSIC
|
||||
|
||||
#define BTSTACK_FILE__ "hids_device.c"
|
||||
|
||||
/**
|
||||
Implementation of the GATT HIDS Device
|
||||
To use with your application, add '#import <hids.gatt>' to your .gatt file
|
||||
*/
|
||||
|
||||
#include "hids_device.h"
|
||||
|
||||
#include "ble/att_db.h"
|
||||
#include "ble/att_server.h"
|
||||
#include "bluetooth_gatt.h"
|
||||
#include "btstack_util.h"
|
||||
#include "btstack_debug.h"
|
||||
|
||||
#define HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS 0x80
|
||||
|
||||
// storage for 'generic' HID Device with single Input, Output, and Feature Report
|
||||
static hids_device_report_t hid_reports_generic_storage[3];
|
||||
|
||||
typedef struct {
|
||||
uint16_t con_handle;
|
||||
|
||||
uint8_t hid_country_code;
|
||||
const uint8_t * hid_descriptor;
|
||||
uint16_t hid_descriptor_size;
|
||||
|
||||
uint16_t hid_report_map_handle;
|
||||
uint8_t hid_protocol_mode;
|
||||
uint16_t hid_protocol_mode_value_handle;
|
||||
|
||||
uint16_t hid_boot_mouse_input_value_handle;
|
||||
uint16_t hid_boot_mouse_input_client_configuration_handle;
|
||||
uint16_t hid_boot_mouse_input_client_configuration_value;
|
||||
|
||||
uint16_t hid_boot_keyboard_input_value_handle;
|
||||
uint16_t hid_boot_keyboard_input_client_configuration_handle;
|
||||
uint16_t hid_boot_keyboard_input_client_configuration_value;
|
||||
|
||||
hids_device_report_t * hid_reports;
|
||||
|
||||
uint8_t hid_input_reports_num;
|
||||
uint8_t hid_output_reports_num;
|
||||
uint8_t hid_feature_reports_num;
|
||||
|
||||
uint16_t hid_control_point_value_handle;
|
||||
uint8_t hid_control_point_suspend;
|
||||
|
||||
btstack_context_callback_registration_t battery_callback;
|
||||
} hids_device_t;
|
||||
|
||||
static hids_device_t hids_device;
|
||||
|
||||
static btstack_packet_handler_t packet_handler;
|
||||
static att_service_handler_t hid_service;
|
||||
|
||||
// TODO: store hids device connection into list
|
||||
static hids_device_t * hids_device_get_instance_for_con_handle(uint16_t con_handle) {
|
||||
UNUSED(con_handle);
|
||||
return &hids_device;
|
||||
}
|
||||
|
||||
static hids_device_t * hids_device_create_instance(void) {
|
||||
memset(&hids_device, 0, sizeof(hids_device_t));
|
||||
return &hids_device;
|
||||
}
|
||||
|
||||
static hids_device_report_t * hids_device_get_report_for_client_configuration_handle(hids_device_t * device, uint16_t client_configuration_handle) {
|
||||
uint8_t pos;
|
||||
uint8_t total_reports = device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
|
||||
for (pos = 0 ; pos < total_reports ; pos++) {
|
||||
if (device->hid_reports[pos].client_configuration_handle == client_configuration_handle) {
|
||||
return &device->hid_reports[pos];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static hids_device_report_t *
|
||||
hids_device_get_report_for_id_and_type(hids_device_t *device, uint16_t report_id, hid_report_type_t type) {
|
||||
uint8_t pos;
|
||||
uint8_t total_reports = device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
|
||||
for (pos = 0 ; pos < total_reports ; pos++) {
|
||||
if ((device->hid_reports[pos].type == type) && (device->hid_reports[pos].id == report_id)) {
|
||||
return &device->hid_reports[pos];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hids_device_emit_event_with_uint8(uint8_t event, hci_con_handle_t con_handle, uint8_t value) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packet_handler) {
|
||||
return;
|
||||
}
|
||||
uint8_t buffer[6];
|
||||
buffer[0] = HCI_EVENT_HIDS_META;
|
||||
buffer[1] = 4;
|
||||
buffer[2] = event;
|
||||
little_endian_store_16(buffer, 3, (uint16_t) con_handle);
|
||||
buffer[5] = value;
|
||||
(*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static void hids_device_emit_event_with_uint8_uint8_t(uint8_t event, hci_con_handle_t con_handle, uint8_t value_1, uint8_t value_2) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packet_handler) {
|
||||
return;
|
||||
}
|
||||
uint8_t buffer[7];
|
||||
buffer[0] = HCI_EVENT_HIDS_META;
|
||||
buffer[1] = 4;
|
||||
buffer[2] = event;
|
||||
little_endian_store_16(buffer, 3, (uint16_t) con_handle);
|
||||
buffer[5] = value_1;
|
||||
buffer[6] = value_2;
|
||||
(*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static void hids_device_emit_event(uint8_t event, hci_con_handle_t con_handle) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packet_handler) {
|
||||
return;
|
||||
}
|
||||
uint8_t buffer[5];
|
||||
buffer[0] = HCI_EVENT_HIDS_META;
|
||||
buffer[1] = 4;
|
||||
buffer[2] = event;
|
||||
little_endian_store_16(buffer, 3, (uint16_t) con_handle);
|
||||
(*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static void hids_device_can_send_now(void * context) {
|
||||
hci_con_handle_t con_handle = (hci_con_handle_t)(uintptr_t) context;
|
||||
// notify client
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packet_handler) {
|
||||
return;
|
||||
}
|
||||
uint8_t buffer[5];
|
||||
buffer[0] = HCI_EVENT_HIDS_META;
|
||||
buffer[1] = 3;
|
||||
buffer[2] = HIDS_SUBEVENT_CAN_SEND_NOW;
|
||||
little_endian_store_16(buffer, 3, (uint16_t) con_handle);
|
||||
(*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
// 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
|
||||
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) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_protocol_mode_value_handle) {
|
||||
log_info("Read protocol mode");
|
||||
return att_read_callback_handle_byte(instance->hid_protocol_mode, offset, buffer, buffer_size);
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_report_map_handle) {
|
||||
log_info("Read report map");
|
||||
return att_read_callback_handle_blob(instance->hid_descriptor, instance->hid_descriptor_size, offset, buffer, buffer_size);
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle) {
|
||||
return att_read_callback_handle_little_endian_16(instance->hid_boot_mouse_input_client_configuration_value, offset, buffer, buffer_size);
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle) {
|
||||
return att_read_callback_handle_little_endian_16(instance->hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size);
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_control_point_value_handle) {
|
||||
if (buffer && (buffer_size >= 1u)) {
|
||||
buffer[0] = instance->hid_control_point_suspend;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
hids_device_report_t * report = hids_device_get_report_for_client_configuration_handle(instance, att_handle);
|
||||
if (report != NULL) {
|
||||
return att_read_callback_handle_little_endian_16(report->client_configuration_value, offset, buffer, buffer_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
UNUSED(transaction_mode);
|
||||
UNUSED(buffer_size);
|
||||
UNUSED(offset);
|
||||
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle) {
|
||||
uint16_t new_value = little_endian_read_16(buffer, 0);
|
||||
instance->hid_boot_mouse_input_client_configuration_value = new_value;
|
||||
hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value);
|
||||
}
|
||||
if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle) {
|
||||
uint16_t new_value = little_endian_read_16(buffer, 0);
|
||||
instance->hid_boot_keyboard_input_client_configuration_value = new_value;
|
||||
hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value);
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_protocol_mode_value_handle) {
|
||||
instance->hid_protocol_mode = buffer[0];
|
||||
log_info("Set protocol mode: %u", instance->hid_protocol_mode);
|
||||
hids_device_emit_event_with_uint8(HIDS_SUBEVENT_PROTOCOL_MODE, con_handle, instance->hid_protocol_mode);
|
||||
}
|
||||
|
||||
if (att_handle == instance->hid_control_point_value_handle) {
|
||||
if (buffer_size < 1u) {
|
||||
return ATT_ERROR_INVALID_OFFSET;
|
||||
}
|
||||
instance->hid_control_point_suspend = buffer[0];
|
||||
instance->con_handle = con_handle;
|
||||
log_info("Set suspend tp: %u", instance->hid_control_point_suspend);
|
||||
if (instance->hid_control_point_suspend == 0u) {
|
||||
hids_device_emit_event(HIDS_SUBEVENT_SUSPEND, con_handle);
|
||||
} else if (instance->hid_control_point_suspend == 1u) {
|
||||
hids_device_emit_event(HIDS_SUBEVENT_EXIT_SUSPEND, con_handle);
|
||||
}
|
||||
}
|
||||
|
||||
hids_device_report_t * report = hids_device_get_report_for_client_configuration_handle(instance, att_handle);
|
||||
if (report != NULL) {
|
||||
uint16_t new_value = little_endian_read_16(buffer, 0);
|
||||
report->client_configuration_value = new_value;
|
||||
log_info("Enable Report (type %u) notifications: %x", (uint8_t) report->type, new_value);
|
||||
|
||||
switch (report->type) {
|
||||
case HID_REPORT_TYPE_INPUT:
|
||||
hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_INPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
|
||||
break;
|
||||
case HID_REPORT_TYPE_OUTPUT:
|
||||
hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_OUTPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
|
||||
break;
|
||||
case HID_REPORT_TYPE_FEATURE:
|
||||
hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_FEATURE_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
|
||||
break;
|
||||
default:
|
||||
btstack_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hids_device_init_with_storage(uint8_t hid_country_code, const uint8_t * hid_descriptor, uint16_t hid_descriptor_size,
|
||||
uint16_t num_reports, hids_device_report_t * report_storage) {
|
||||
|
||||
hids_device_t * instance = hids_device_create_instance();
|
||||
|
||||
btstack_assert(num_reports > 0);
|
||||
btstack_assert(report_storage != NULL);
|
||||
|
||||
instance->hid_country_code = hid_country_code;
|
||||
instance->hid_descriptor = hid_descriptor;
|
||||
instance->hid_descriptor_size = hid_descriptor_size;
|
||||
|
||||
// default
|
||||
instance->hid_protocol_mode = 1;
|
||||
|
||||
// get service handle range
|
||||
uint16_t start_handle = 0;
|
||||
uint16_t end_handle = 0xffff;
|
||||
int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE, &start_handle, &end_handle);
|
||||
btstack_assert(service_found != 0);
|
||||
UNUSED(service_found);
|
||||
|
||||
// get report map handle
|
||||
instance->hid_report_map_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP);
|
||||
|
||||
// get report map handle
|
||||
instance->hid_protocol_mode_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE);
|
||||
|
||||
// get value and client configuration handles for boot mouse input, boot keyboard input and report input
|
||||
instance->hid_boot_mouse_input_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT);
|
||||
instance->hid_boot_mouse_input_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT);
|
||||
|
||||
instance->hid_boot_keyboard_input_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT);
|
||||
instance->hid_boot_keyboard_input_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT);
|
||||
|
||||
instance->hid_control_point_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT);
|
||||
|
||||
log_info("hid_report_map_handle 0x%02x", instance->hid_report_map_handle);
|
||||
log_info("hid_protocol_mode_value_handle 0x%02x", instance->hid_protocol_mode_value_handle);
|
||||
log_info("hid_boot_mouse_input_value_handle 0x%02x", instance->hid_boot_mouse_input_value_handle);
|
||||
log_info("hid_boot_mouse_input_client_configuration_handle 0x%02x", instance->hid_boot_mouse_input_client_configuration_handle);
|
||||
log_info("hid_boot_keyboard_input_value_handle 0x%02x", instance->hid_boot_keyboard_input_value_handle);
|
||||
log_info("hid_boot_keyboard_input_client_configuration_handle 0x%02x", instance->hid_boot_keyboard_input_client_configuration_handle);
|
||||
log_info("hid_control_point_value_handle 0x%02x", instance->hid_control_point_value_handle);
|
||||
|
||||
instance->hid_reports = report_storage;
|
||||
|
||||
uint16_t hid_reports_num = num_reports;
|
||||
uint16_t assigned_reports_num = 0;
|
||||
uint16_t start_chr_handle = start_handle;
|
||||
|
||||
while ((start_chr_handle < end_handle) && (assigned_reports_num < hid_reports_num)) {
|
||||
// mandatory
|
||||
uint16_t chr_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
|
||||
if (chr_value_handle == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// optional
|
||||
uint16_t chr_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
|
||||
|
||||
// mandatory
|
||||
uint16_t report_reference_handle = gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT, ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE);
|
||||
if (report_reference_handle == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// get report id and type from report reference
|
||||
uint16_t report_reference_value_len;
|
||||
const uint8_t * report_reference_value = gatt_server_get_const_value_for_handle(report_reference_handle, &report_reference_value_len);
|
||||
if (report_reference_value == NULL) {
|
||||
break;
|
||||
}
|
||||
if (report_reference_value_len != 2) {
|
||||
break;
|
||||
}
|
||||
uint8_t report_id = report_reference_value[0];
|
||||
hid_report_type_t report_type = (hid_report_type_t) report_reference_value[1];
|
||||
|
||||
// store report info
|
||||
hids_device_report_t * report = &report_storage[assigned_reports_num];
|
||||
report->value_handle = chr_value_handle;
|
||||
report->client_configuration_handle = chr_client_configuration_handle;
|
||||
report->client_configuration_value = 0;
|
||||
report->id = report_id;
|
||||
report->type = report_type;
|
||||
|
||||
switch (report->type) {
|
||||
case HID_REPORT_TYPE_INPUT:
|
||||
instance->hid_input_reports_num++;
|
||||
break;
|
||||
case HID_REPORT_TYPE_OUTPUT:
|
||||
instance->hid_output_reports_num++;
|
||||
break;
|
||||
case HID_REPORT_TYPE_FEATURE:
|
||||
instance->hid_feature_reports_num++;
|
||||
break;
|
||||
default:
|
||||
btstack_unreachable();
|
||||
return;
|
||||
}
|
||||
log_info("hid_report_value_handle 0x%02x, id %u, type %u", report->value_handle, report->id, (uint8_t)report->type);
|
||||
if (report->client_configuration_handle != 0) {
|
||||
log_info("hid_report_client_configuration_handle 0x%02x", report->client_configuration_handle);
|
||||
}
|
||||
|
||||
assigned_reports_num++;
|
||||
start_chr_handle = report_reference_handle + 1;
|
||||
}
|
||||
|
||||
// register service with ATT Server
|
||||
hid_service.start_handle = start_handle;
|
||||
hid_service.end_handle = end_handle;
|
||||
hid_service.read_callback = &att_read_callback;
|
||||
hid_service.write_callback = &att_write_callback;
|
||||
att_server_register_service_handler(&hid_service);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set up HIDS Device
|
||||
*/
|
||||
void hids_device_init(uint8_t country_code, const uint8_t * descriptor, uint16_t descriptor_size) {
|
||||
uint16_t hid_reports_num = sizeof(hid_reports_generic_storage) / sizeof(hids_device_report_t);
|
||||
hids_device_init_with_storage(country_code, descriptor, descriptor_size, hid_reports_num, hid_reports_generic_storage);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Register callback for the HIDS Device client.
|
||||
@param callback
|
||||
*/
|
||||
void hids_device_register_packet_handler(btstack_packet_handler_t callback) {
|
||||
packet_handler = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Request can send now event to send HID Report
|
||||
Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent
|
||||
@param hid_cid
|
||||
*/
|
||||
void hids_device_request_can_send_now_event(hci_con_handle_t con_handle) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
instance->battery_callback.callback = &hids_device_can_send_now;
|
||||
instance->battery_callback.context = (void*)(uintptr_t) con_handle;
|
||||
att_server_register_can_send_now_callback(&instance->battery_callback, con_handle);
|
||||
}
|
||||
|
||||
uint8_t hids_device_send_input_report_for_id(hci_con_handle_t con_handle, uint16_t report_id, const uint8_t * report, uint16_t report_len) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
|
||||
hids_device_report_t * report_storage = hids_device_get_report_for_id_and_type(instance, report_id,
|
||||
HID_REPORT_TYPE_INPUT);
|
||||
if (report_storage == NULL) {
|
||||
return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
|
||||
}
|
||||
|
||||
return att_server_notify(con_handle, report_storage->value_handle, report, report_len);
|
||||
}
|
||||
|
||||
uint8_t hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len) {
|
||||
hids_device_t * device = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!device) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
|
||||
uint8_t pos;
|
||||
uint8_t total_reports = device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
|
||||
for (pos = 0 ; pos < total_reports ; pos++) {
|
||||
hids_device_report_t * report_storage = &device->hid_reports[pos];
|
||||
if (report_storage->type == HID_REPORT_TYPE_INPUT) {
|
||||
return att_server_notify(con_handle, report_storage->value_handle, report, report_len);
|
||||
}
|
||||
}
|
||||
return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Send HID Boot Mouse Input Report
|
||||
*/
|
||||
uint8_t hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
return att_server_notify(con_handle, instance->hid_boot_mouse_input_value_handle, report, report_len);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Send HID Boot Mouse Input Report
|
||||
*/
|
||||
uint8_t hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len) {
|
||||
hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
|
||||
if (!instance) {
|
||||
log_error("no instance for handle 0x%02x", con_handle);
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
return att_server_notify(con_handle, instance->hid_boot_keyboard_input_value_handle, report, report_len);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright (C) 2014 BlueKitchen GmbH
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
4. Any redistribution, use, or modification is done solely for
|
||||
personal benefit and not for any commercial purpose or for
|
||||
monetary gain.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
|
||||
GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Please inquire about commercial licensing options at
|
||||
contact@bluekitchen-gmbh.com
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
@title HID Service Server
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HIDS_DEVICE_H
|
||||
#define HIDS_DEVICE_H
|
||||
|
||||
#if defined ENABLE_CLASSIC
|
||||
|
||||
#include <sdkoverride/att_db.h>
|
||||
#include <stdint.h>
|
||||
#include "btstack_defines.h"
|
||||
#include "btstack_hid.h"
|
||||
#include "bluetooth.h"
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* API_START */
|
||||
|
||||
typedef struct {
|
||||
uint16_t value_handle;
|
||||
uint16_t client_configuration_handle;
|
||||
uint16_t client_configuration_value;
|
||||
|
||||
hid_report_type_t type;
|
||||
uint16_t id;
|
||||
} hids_device_report_t;
|
||||
|
||||
/**
|
||||
@text Implementation of the GATT HIDS Device
|
||||
To use with your application, add '#import <hids.gatt>' to your .gatt file
|
||||
*/
|
||||
|
||||
/**
|
||||
@brief Set up HIDS Device with single INPUT, OUTPUT and FEATURE report
|
||||
*/
|
||||
void hids_device_init(uint8_t hid_country_code, const uint8_t * hid_descriptor, uint16_t hid_descriptor_size);
|
||||
|
||||
/**
|
||||
@brief Set up HIDS Device for multiple instances of INPUT, OUTPUT and FEATURE reports
|
||||
*/
|
||||
void hids_device_init_with_storage(uint8_t hid_country_code, const uint8_t * hid_descriptor, uint16_t hid_descriptor_size,
|
||||
uint16_t num_reports, hids_device_report_t * report_storage);
|
||||
|
||||
/**
|
||||
@brief Register callback for the HIDS Device client.
|
||||
@param callback
|
||||
*/
|
||||
void hids_device_register_packet_handler(btstack_packet_handler_t callback);
|
||||
|
||||
/**
|
||||
@brief Request can send now event to send HID Report
|
||||
Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent
|
||||
@param hid_cid
|
||||
*/
|
||||
void hids_device_request_can_send_now_event(hci_con_handle_t con_handle);
|
||||
|
||||
/**
|
||||
@brief Send HID Input Report for Report ID
|
||||
@param con_handle
|
||||
@param report_id
|
||||
@param report
|
||||
@param report_len
|
||||
@returns status
|
||||
*/
|
||||
uint8_t hids_device_send_input_report_for_id(hci_con_handle_t con_handle, uint16_t report_id, const uint8_t * report, uint16_t report_len);
|
||||
|
||||
/**
|
||||
@brief Send HID Input Report for first Input Report
|
||||
@param con_handle
|
||||
@param report
|
||||
@param report_len
|
||||
@returns status
|
||||
*/
|
||||
uint8_t hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len);
|
||||
|
||||
/**
|
||||
@brief Send HID Boot Mouse Input Report
|
||||
@param con_handle
|
||||
@param report
|
||||
@param report_len
|
||||
@returns status
|
||||
*/
|
||||
uint8_t hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len);
|
||||
|
||||
/**
|
||||
@brief Send HID Boot Mouse Input Report
|
||||
@param con_handle
|
||||
@param report
|
||||
@param report_len
|
||||
@returns status
|
||||
*/
|
||||
uint8_t hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len);
|
||||
|
||||
/* API_END */
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -66,10 +66,14 @@ typedef enum {
|
||||
} wl_status_t;
|
||||
|
||||
/* Encryption modes */
|
||||
enum wl_enc_type { /* Values map to 802.11 encryption suites... */
|
||||
enum wl_enc_type { /* Values map to 802.11 Cipher Algorithm Identifier */
|
||||
ENC_TYPE_WEP = 5,
|
||||
ENC_TYPE_TKIP = 2,
|
||||
ENC_TYPE_WPA = ENC_TYPE_TKIP,
|
||||
ENC_TYPE_CCMP = 4,
|
||||
ENC_TYPE_WPA2 = ENC_TYPE_CCMP,
|
||||
ENC_TYPE_GCMP = 6,
|
||||
ENC_TYPE_WPA3 = ENC_TYPE_GCMP,
|
||||
/* ... except these two, 7 and 8 are reserved in 802.11-2007 */
|
||||
ENC_TYPE_NONE = 7,
|
||||
ENC_TYPE_AUTO = 8,
|
||||
|
||||
+4
-2
@@ -30,7 +30,7 @@ Analog Outputs
|
||||
--------------
|
||||
The RP2040 does not have any onboard DACs, so analog outputs are
|
||||
simulated using the standard method of using pulse width modulation
|
||||
(PWM) using the RP20400's hardware PWM units.
|
||||
(PWM) using the RP2040's hardware PWM units.
|
||||
|
||||
While up to 16 PWM channels can be generated, they are not independent
|
||||
and there are significant restrictions as to allowed pins in parallel.
|
||||
@@ -40,7 +40,9 @@ Analog Output Restrictions
|
||||
--------------------------
|
||||
|
||||
The PWM generator source clock restricts the legal combinations of
|
||||
frequency and ranges. For example, at 1MHz only about 6 bits of range
|
||||
frequency and ranges.
|
||||
At a CPU frequency of 133MHz, the 16 bit maximum range decreases by 1 bit for every doubling of the default PWM frequency of 1 kHz.
|
||||
For example, at 1MHz only about 6 bits of range
|
||||
are possible. When you define an ``analogWriteFreq`` and ``analogWriteRange``
|
||||
that can't be fulfilled by the hardware, the frequency will be preserved
|
||||
but the accuracy (range) will be reduced automatically. Your code will
|
||||
|
||||
@@ -13,7 +13,7 @@ Similar to the `BearSSL::WiFiClientSecure` method, sets the receive and transmit
|
||||
Setting Server Certificates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
TLS servers require a certificate identifying itself and containing its public key, and a private key they will use to encrypt information with. The application author is responsible for generating this certificate and key, either using a self-signed generator or using a commercial certification authority. **Do not re-use the certificates included in the examples provided.**
|
||||
TLS servers require a certificate identifying itself and containing its public key, and a private key they will use to encrypt information with. The application author is responsible for generating this certificate and key, either using a self-signed generator or using a commercial certification authority. **Do not reuse the certificates included in the examples provided.**
|
||||
|
||||
This example command will generate a RSA 2048-bit key and certificate:
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
BOOTSEL Button
|
||||
==============
|
||||
|
||||
The BOOTSEL button on the Pico is not connected to a standard GPIO, so
|
||||
it cannot be read using the usual ``digitalRead`` function. It **can**,
|
||||
however, be read using a special (relatively slow) method.
|
||||
|
||||
The ``BOOTSEL`` object implements a simple way of reading the BOOTSEL
|
||||
button. Simply use the object ``BOOTSEL`` as a boolean (as a conditional
|
||||
in an ``if`` or ``while``, or assigning to a ``bool``):
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
// Print "BEEP" if the BOOTSEL button is pressed
|
||||
if (BOOTSEL) {
|
||||
Serial.println("BEEP!");
|
||||
// Wait until BOOTSEL is released
|
||||
while (BOOTSEL) {
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'3.3.2'
|
||||
version = u'3.6.3'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'3.3.2'
|
||||
release = u'3.6.3'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
EthernetLWIP (Wired Ethernet) Support
|
||||
=====================================
|
||||
|
||||
Wired Ethernet interfaces are supported for all the internal networking
|
||||
libraries (``WiFiClient``, ``WiFiClientSecure``, ``WiFiServer``,
|
||||
``WiFiServerSecure``, ``WiFiUDP``, ``WebServer``, ``Updater``,
|
||||
``HTTPClient``, etc.).
|
||||
|
||||
Using these wired interfaces is very similar to using the Pico-W WiFi
|
||||
so most examples in the core only require minor modifications to use
|
||||
a wired interface.
|
||||
|
||||
Supported Wired Ethernet Modules
|
||||
--------------------------------
|
||||
|
||||
* Wiznet W5100(s)
|
||||
|
||||
* Wiznet W5500
|
||||
|
||||
* ENC28J60
|
||||
|
||||
|
||||
Enabling Wired Ethernet
|
||||
-----------------------
|
||||
|
||||
Simply replace the WiFi include at the top with:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <W5500lwIP.h> // Or W5100lwIP.h or ENC28J60.h
|
||||
|
||||
|
||||
And add a global Ethernet object of the same type:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
Wiznet5500lwIP eth(1); // Parameter is the Chip Select pin
|
||||
|
||||
In your ``setup()`` you may adjust the SPI pins you're using to
|
||||
match your hardware (be sure they are legal for the RP2040!), or
|
||||
skip this if you're using the default ones:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void setup() {
|
||||
SPI.setRX(0);
|
||||
SPI.setCS(1);
|
||||
SPI.setSCK(2);
|
||||
SPI.setTX(3);
|
||||
....
|
||||
}
|
||||
|
||||
And finally replace the ``WiFi.begin()`` and ``WiFi.connected()``
|
||||
calls with ``eth.begin()`` and ``eth.connected()``:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void setup() {
|
||||
....
|
||||
// WiFi.begin(SSID, PASS)
|
||||
eth.begin();
|
||||
|
||||
//while (!WiFi.connected()) {
|
||||
while (!eth.connected()) {
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.print("IP address: ");
|
||||
//Serial.println(WiFi.localIP());
|
||||
Serial.println(eth.localIP());
|
||||
|
||||
....
|
||||
}
|
||||
|
||||
Adjusting LWIP Polling
|
||||
----------------------
|
||||
|
||||
LWIP operates in a polling mode for the wired Ethernet devices. By default it will run
|
||||
every 20ms, meaning that on average it will take half that time (10ms) before a packet
|
||||
received in the Ethernet module is received and operated upon by the Pico. This gives
|
||||
very low CPU utilization but in some cases this latency can affect performance.
|
||||
|
||||
Adding a call to ``lwipPollingPeriod(XXX)`` (where ``XXXX`` is the polling period in
|
||||
milliseconds) can adjust this setting on the fly. Note that if you set it too low, the
|
||||
Pico may not have enough time to service the Ethernet port before the timer fires again,
|
||||
leading to a lock up and hang.
|
||||
|
||||
|
||||
Adjusting SPI Speed
|
||||
-------------------
|
||||
|
||||
By default a 4MHz clock will be used to clock data into and out of the Ethernet module.
|
||||
Depending on the module and your wiring, a higher SPI clock may increase performance (but
|
||||
too high of a clock will cause communications problems or hangs).
|
||||
|
||||
This value may be adjusted using the ``eth.setSPISpeed(hz)`` call **before** starting the
|
||||
device. (You may also use custom ``SPISettings`` instead via ``eth.setSPISettings(spis)```)
|
||||
|
||||
For example, to set the W5500 to use a 30MHZ clock:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <W5500lwIP.h>
|
||||
Wiznet5500lwIP eth(1);
|
||||
|
||||
void setup() {
|
||||
eth.setSPISpeed(30000000);
|
||||
lwipPollingPeriod(3);
|
||||
...
|
||||
eth.begin();
|
||||
...
|
||||
}
|
||||
|
||||
Using the WIZnet W5100S-EVB-Pico
|
||||
--------------------------------
|
||||
|
||||
You can use the onboard Ethernet chip with these drivers by utilizing the following options:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <W5100lwIP.h>
|
||||
Wiznet5100lwIP eth(17); // Note chip select is **17**
|
||||
|
||||
void setup() {
|
||||
// Set SPI to the onboard Wiznet chip
|
||||
SPI.setRX(16);
|
||||
SPI.setCS(17);
|
||||
SPI.setSCK(18);
|
||||
SPI.setTX(19);
|
||||
...
|
||||
eth.begin();
|
||||
...
|
||||
}
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
The following examples allow switching between WiFi and Ethernet:
|
||||
|
||||
* ``WebServer/AdvancedWebServer``
|
||||
|
||||
* ``HTTPClient/BasicHTTPSClient``
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
The same restrictions for ``WiFi`` apply to these Ethernet classes, namely:
|
||||
|
||||
* Only core 0 may run any networking related code.
|
||||
|
||||
* In FreeRTOS, only the ``setup`` and ``loop`` task can call networking libraries, not any tasks.
|
||||
|
||||
Special Thanks
|
||||
--------------
|
||||
|
||||
* LWIPEthernet classes come from the ESP8266 Arduino team
|
||||
|
||||
* Individual Ethernet drivers were written by Nicholas Humfrey
|
||||
|
||||
+28
-25
@@ -18,12 +18,12 @@ The following diagram shows the flash layout used in Arduino-Pico:
|
||||
|
||||
::
|
||||
|
||||
|---------------------|-------------|----|
|
||||
^ ^ ^
|
||||
Sketch File system EEPROM
|
||||
|----|---------------------|-------------|----|
|
||||
^ ^ ^ ^
|
||||
OTA Sketch File system EEPROM
|
||||
|
||||
The file system size is configurable via the IDE menus, rom 64k up to 15MB
|
||||
(assuming you have an RP2040 boad with that much flash)
|
||||
The file system size is configurable via the IDE menus, from 64k up to 15MB
|
||||
(assuming you have an RP2040 board with that much flash)
|
||||
|
||||
**Note:** to use any of file system functions in the sketch, add the
|
||||
following include to the sketch:
|
||||
@@ -38,14 +38,14 @@ following include to the sketch:
|
||||
Compatible Filesystem APIs
|
||||
--------------------------
|
||||
|
||||
LittleFS is an onboard filesystem that sets asidesome program flash for
|
||||
LittleFS is an onboard filesystem that sets aside some program flash for
|
||||
use as a filesystem without requiring any external hardware.
|
||||
|
||||
SDFS is a filesystem for SD cards, based on [SdFat 2.0](https://github.com/earlephilhower/ESP8266SdFat).
|
||||
It supports FAT16 and FAT32 formatted cards, and requires an external
|
||||
SD card reader.
|
||||
|
||||
SD is the Arduino supported, somewhat old and limited SD card filesystem.
|
||||
SD is the Arduino-supported, somewhat old and limited SD card filesystem.
|
||||
It is recommended to use SDFS for new applications instead of SD.
|
||||
|
||||
All three of these filesystems can open and manipulate ``File`` and ``Dir``
|
||||
@@ -75,22 +75,28 @@ Uploading Files to the LittleFS File System
|
||||
menu item to **Tools** menu for uploading the contents of sketch data
|
||||
directory into a new LittleFS flash file system.
|
||||
|
||||
**IDE 1.x**
|
||||
|
||||
- Download the tool: https://github.com/earlephilhower/arduino-pico-littlefs-plugin/releases
|
||||
- In your Arduino sketchbook directory, create ``tools`` directory if
|
||||
it doesn't exist yet.
|
||||
- Unpack the tool into ``tools`` directory (the path will look like
|
||||
``<home_dir>/Arduino/tools/PicoLittleFS/tool/picolittlefs.jar``)
|
||||
- In your Arduino sketchbook directory, create ``tools`` directory if it doesn't exist yet.
|
||||
- Unpack the tool into ``tools`` directory (the path will look like ``<home_dir>/Arduino/tools/PicoLittleFS/tool/picolittlefs.jar``)
|
||||
If upgrading, overwrite the existing JAR file with the newer version.
|
||||
- Restart Arduino IDE.
|
||||
- Open a sketch (or create a new one and save it).
|
||||
- Go to sketch directory (choose Sketch > Show Sketch Folder).
|
||||
- Create a directory named ``data`` and any files you want in the file
|
||||
system there.
|
||||
- Create a directory named ``data`` and any files you want in the file system there.
|
||||
- Make sure you have selected a board, port, and closed Serial Monitor.
|
||||
- Double check theSerial Monitor is closed. Uploads will fail if the Serial
|
||||
Monitor has control of the serial port.
|
||||
- Select ``Tools > Pico LittleFS Data Upload``. This should start
|
||||
uploading the files into the flash file system.
|
||||
- Double check the Serial Monitor is closed. Uploads will fail if the Serial Monitor has control of the serial port.
|
||||
- Select ``Tools > Pico LittleFS Data Upload``. This should start uploading the files into the flash file system.
|
||||
|
||||
**IDE 2.x**
|
||||
|
||||
- Download the new tool: https://github.com/earlephilhower/arduino-littlefs-upload/releases
|
||||
- Exit the IDE, if running
|
||||
- Copy the VSIX file manually to (Linux/Mac) ``~/.arduinoIDE/plugins/`` (you may need to make this directory yourself beforehand) or to (Windows) ``C:\Users\<username>\.arduinoIDE\``
|
||||
- Restart the IDE
|
||||
- Double check the Serial Monitor is closed. Uploads will fail if the Serial Monitor has control of the serial port.
|
||||
- Enter ``[Ctrl]`` + ``[Shift]`` + ``[P]`` to bring up the command palette, then select/type ``Upload LittleFS to Pico/ESP8266``
|
||||
|
||||
SD Library Information
|
||||
----------------------
|
||||
@@ -131,7 +137,7 @@ before mounting. All filesystems have their own ``*Config`` (i.e.
|
||||
All filesystems allow explicitly enabling/disabling formatting when
|
||||
mounts fail. If you do not call this ``setConfig`` method before
|
||||
perforing ``begin()``, you will get the filesystem's default
|
||||
behavior and configuration. By default, SPIFFS will autoformat the
|
||||
behavior and configuration. By default, LittleFS will autoformat the
|
||||
filesystem if it cannot mount it, while SDFS will not.
|
||||
|
||||
begin
|
||||
@@ -144,11 +150,10 @@ begin
|
||||
|
||||
This method mounts file system. It must be called before any
|
||||
other FS APIs are used. Returns *true* if file system was mounted
|
||||
successfully, false otherwise. With no options it will format SPIFFS
|
||||
if it is unable to mount it on the first try.
|
||||
successfully, false otherwise.
|
||||
|
||||
Note that LittleFS will automatically format the filesystem
|
||||
if one is not detected. This is configurable via ``setConfig``
|
||||
if one is not detected. This is configurable via ``setConfig``.
|
||||
|
||||
end
|
||||
~~~
|
||||
@@ -181,8 +186,8 @@ open
|
||||
|
||||
Opens a file. ``path`` should be an absolute path starting with a slash
|
||||
(e.g. ``/dir/filename.txt``). ``mode`` is a string specifying access
|
||||
mode. It can be one of "r", "w", "a", "r+", "w+", "a+". Meaning of these
|
||||
modes is the same as for ``fopen`` C function.
|
||||
mode. It can be one of "r", "w", "a", "r+", "w+", "a+". The meaning of these
|
||||
modes is the same as for the ``fopen`` C function.
|
||||
|
||||
::
|
||||
|
||||
@@ -258,8 +263,6 @@ openDir
|
||||
or LittleFS.openDir(path)
|
||||
|
||||
Opens a directory given its absolute path. Returns a *Dir* object.
|
||||
Please note the previous discussion on the difference in behavior between
|
||||
LittleFS and SPIFFS for this call.
|
||||
|
||||
remove
|
||||
~~~~~~
|
||||
|
||||
@@ -81,6 +81,15 @@ Enables LSB-J format for I2S output. In this mode the MSB comes out at the
|
||||
same time as the LRCLK changes, and not the normal 1-cycle delay. Useful for
|
||||
DAC chips like the PT8211.
|
||||
|
||||
bool setTDMFormat()
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Enabled TDM formatted multi-channel output. Be sure to set the number of channels to
|
||||
the expected value (8 normally) and the bits per sample to 32.
|
||||
|
||||
bool setTDMChannels(int channels)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the number of TDM channels between frame syncs. Generally should be set to 8.
|
||||
|
||||
bool swapClocks()
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Certain boards are hardwired with the WCLK before the BCLK, instead of the normal
|
||||
|
||||
@@ -30,6 +30,7 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
|
||||
Analog I/O <analog>
|
||||
Digital I/O <digital>
|
||||
BOOTSEL Button <bootsel>
|
||||
EEPROM <eeprom>
|
||||
I2S Audio <i2s>
|
||||
PWM Audio <pwm>
|
||||
@@ -50,6 +51,7 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
FreeRTOS SMP (multicore) <freertos>
|
||||
|
||||
WiFi (Pico-W Support) <wifi>
|
||||
Ethernet (Wired) <ethernet>
|
||||
WiFiClient <wificlient>
|
||||
WiFiServer <wifiserver>
|
||||
WiFiUDP <wifiudp>
|
||||
|
||||
+5
-11
@@ -26,9 +26,7 @@ Arduino IDE Installation Warning
|
||||
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.
|
||||
it suggests. Otherwise the Pico board may not be detected.
|
||||
|
||||
**Note for Linux Users**: If you installed the Arduino IDE using Flatpak, which
|
||||
is common in Pop!_OS, Fedora, and Mint, among others, you may need to configure
|
||||
@@ -141,11 +139,13 @@ Uploading Filesystem Images
|
||||
---------------------------
|
||||
The onboard flash filesystem for the Pico, LittleFS, lets you upload a filesystem image from the sketch directory for your sketch to use. Download the needed plugin from
|
||||
|
||||
* https://github.com/earlephilhower/arduino-pico-littlefs-plugin/releases
|
||||
* `IDE 1.x`: https://github.com/earlephilhower/arduino-pico-littlefs-plugin/releases
|
||||
* `IDE 2.x`: https://github.com/earlephilhower/arduino-littlefs-upload/releases
|
||||
|
||||
To install, follow the directions in
|
||||
|
||||
* https://github.com/earlephilhower/arduino-pico-littlefs-plugin/blob/master/README.md
|
||||
* `IDE 1.x`: https://github.com/earlephilhower/arduino-pico-littlefs-plugin/blob/master/README.md
|
||||
* `IDE 2.x`: https://github.com/earlephilhower/arduino-littlefs-upload/blob/main/README.md
|
||||
|
||||
For detailed usage information, please check the repo documentation available at
|
||||
|
||||
@@ -159,12 +159,6 @@ 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.
|
||||
|
||||
Under MacOS, it may be necessary to install the USB support libraries from a command terminal before the ``Picotool`` upload method can be used:
|
||||
|
||||
.. code::
|
||||
|
||||
brew install libusb
|
||||
|
||||
For Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
|
||||
|
||||
.. code::
|
||||
|
||||
+1
-1
@@ -10,5 +10,5 @@ In addition, it contains code from additional open source projects:
|
||||
* The `Pico-SDK <https://github.com/raspberrypi/pico-sdk>`_ and `Pico-Extras <https://github.com/raspberrypi/pico-extras>`_ are by Raspberry Pi (Trading) Ltd. and licensed under the BSD 3-Clause license.
|
||||
* `Arduino-Pico <https://github.com/earlephilhower/arduino-pico>`_ core files are licenses under the LGPL.
|
||||
* `LittleFS <https://github.com/ARMmbed/littlefs>`_ library written by ARM Limited and released under the `BSD 3-clause license <https://github.com/ARMmbed/littlefs/blob/master/LICENSE.md>`_ .
|
||||
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporatio and licensed under the MIT license.
|
||||
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporation and licensed under the MIT license.
|
||||
* Some filesystem code taken from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`_ and licensed under the LGPL.
|
||||
|
||||
@@ -16,6 +16,22 @@ not necessarily simultaneously!).
|
||||
See the ``Multicore.ino`` example in the ``rp2040`` example directory for a
|
||||
quick introduction.
|
||||
|
||||
Stack Sizes
|
||||
-----------
|
||||
|
||||
When the Pico is running in single core mode, core 0 has the full 8KB of stack
|
||||
space available to it. When using multicore ``setup1``/``loop1`` the 8KB is split
|
||||
into two 4K stacks, one per core. It is possible for core 0's stack to overwrite
|
||||
core 1's stack in this case, if you go beyond the 4K limitation.
|
||||
|
||||
To allocate a separate 8K stack for core 1, resulting in 8K stacks being available
|
||||
for both cores, simply define the following variable in your sketch and set it
|
||||
to ``true``:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
bool core1_separate_stack = true;
|
||||
|
||||
Pausing Cores
|
||||
-------------
|
||||
|
||||
|
||||
+3
-4
@@ -191,7 +191,7 @@ Debug Level
|
||||
Done again by directly adding the needed `build
|
||||
flags <https://github.com/earlephilhower/arduino-pico/blob/05356da2c5552413a442f742e209c6fa92823666/boards.txt#L104-L114>`__.
|
||||
When wanting to define multiple build flags, they must be accumulated in
|
||||
either a sing line or a newline-separated expression.
|
||||
either a single line or a newline-separated expression.
|
||||
|
||||
.. code:: ini
|
||||
|
||||
@@ -363,7 +363,7 @@ For further information on customizing debug options, like the initial breakpoin
|
||||
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:
|
||||
For the pico-debug (`download <https://github.com/majbthrd/pico-debug/releases>`__) debugging way, *which needs no additional debug probe*, add this snippet to your ``platformio.ini`` and follow the given procedure:
|
||||
|
||||
.. code:: ini
|
||||
|
||||
@@ -397,5 +397,4 @@ The task "Build Filesystem Image" will take all files in the data directory and
|
||||
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!
|
||||
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!
|
||||
|
||||
+2
-2
@@ -81,8 +81,8 @@ overhead).
|
||||
Hardware Identification
|
||||
-----------------------
|
||||
|
||||
bool isPicoW()
|
||||
~~~~~~~~~~~~~~
|
||||
bool rp2040.isPicoW()
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the core's best guess if this code is running on a Raspberry Pi Pico W.
|
||||
This would let you, possibly, use the same UF2 for Pico and PicoW by simply not
|
||||
doing any WiFi calls.
|
||||
|
||||
@@ -55,3 +55,13 @@ void Serial.ignoreFlowControl(bool ignore)
|
||||
In some cases, the target application will not assert the DTR virtual line, thus preventing writing operations to succeed.
|
||||
|
||||
For this reason, the SerialUSB::ignoreFlowControl() method disables the connection's state verification, enabling the program to write on the port, even though the data might be lost.
|
||||
|
||||
bool Serial.dtr()
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Returns the current state of the DTR virtual line. A USB CDC host (such as the Arduino serial monitor) typically raises the DTR pin when opening the device, and may lower it when closing the device.
|
||||
|
||||
bool Serial.rts()
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Returns the current state of the RTS virtual line.
|
||||
|
||||
+25
-4
@@ -1,5 +1,5 @@
|
||||
SPI (Serial Peripheral Interface)
|
||||
=================================
|
||||
SPI Master (Serial Peripheral Interface)
|
||||
========================================
|
||||
|
||||
The RP2040 has two hardware SPI interfaces, ``spi0 (SPI)`` and ``spi1 (SPI1)``.
|
||||
These interfaces are supported by the ``SPI`` library in master mode.
|
||||
@@ -20,5 +20,26 @@ The Arduino `SPI documentation <https://www.arduino.cc/en/reference/SPI>`_ gives
|
||||
a detailed overview of the library, except for the following RP2040-specific
|
||||
changes:
|
||||
|
||||
* ``SPI.begin(bool hwCS)`` can take an options ``hwCS`` parameter. By passing in ``true`` for ``hwCS`` the sketch does not need to worry about asserting and deasserting the ``CS`` pin between transactions. The default is ``false`` and requires the sketch to handle the CS pin itself, as is the standard way in Arduino.
|
||||
* The interrupt calls (``usingInterrupt``, ``notUsingInterrupt``, ``attachInterrupt``, and ``detachInterrpt``) are not implemented.
|
||||
* ``SPI.begin(bool hwCS)`` can take an options ``hwCS`` parameter.
|
||||
By passing in ``true`` for ``hwCS`` the sketch does not need to worry
|
||||
about asserting and deasserting the ``CS`` pin between transactions.
|
||||
The default is ``false`` and requires the sketch to handle the CS
|
||||
pin itself, as is the standard way in Arduino.
|
||||
|
||||
* The interrupt calls (``attachInterrupt``, and ``detachInterrpt``) are not implemented.
|
||||
|
||||
|
||||
SPI Slave (SPISlave)
|
||||
====================
|
||||
|
||||
Slave mode operation is also supported on either SPI interface. Two callbacks are
|
||||
needed in your app, set through ``SPISlave.onDataRecv`` and ``SPISlave.onDataSent``,
|
||||
in order to consunme the received data and provide data to transmit.
|
||||
|
||||
* The callbacks operate at IRQ time and may be called very frequently at high SPI frequencies. So, make then small, fast, and with no memory allocations or locking.
|
||||
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
|
||||
See the SPItoMyself example for a complete Master and Slave application.
|
||||
|
||||
+16
-1
@@ -14,13 +14,28 @@ from the IDE.
|
||||
The Arduino-Pico core includes ported versions of the basic Arduino
|
||||
``Keyboard``, ``Mouse`` and ``Joystick`` libraries. These libraries
|
||||
allow you to emulate a keyboard, a gamepad or mouse (or all together)
|
||||
with the Pico in your sketches.
|
||||
with the Pico in your sketches. These libraries only are available
|
||||
when using the built-in USB, not the Adafruit library.
|
||||
|
||||
See the examples and Arduino Reference at
|
||||
https://www.arduino.cc/reference/en/language/functions/usb/keyboard/
|
||||
and
|
||||
https://www.arduino.cc/reference/en/language/functions/usb/mouse
|
||||
|
||||
HID Polling Interval
|
||||
--------------------
|
||||
By default, HID devices will request to be polled every 10ms (i.e. 100x
|
||||
per second). If you have a higher performance need, you can override
|
||||
this value by creating a global variable in your main application, set
|
||||
to the polling period:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
int usb_hid_poll_interval = 1; // Set HID poll interval to 1ms (1kHz)
|
||||
void setup() {
|
||||
....
|
||||
}
|
||||
|
||||
Adafruit TinyUSB Arduino Support
|
||||
--------------------------------
|
||||
Examples are provided in the Adafruit_TinyUSB_Arduino for the more
|
||||
|
||||
+4
-2
@@ -40,9 +40,11 @@ Please note that WiFi on the Pico W is a work-in-progress and there are some imp
|
||||
|
||||
* Combined STA/AP mode is not supported
|
||||
|
||||
* Multicore is supported, but only one core may run ``WiFi`` code.
|
||||
* Multicore is supported, but only core 0 may run ``WiFi`` related code.
|
||||
|
||||
* FreeRTOS is not yet supported due to the requirement for a very different LWIP implementation. PRs always appreciated!
|
||||
* FreeRTOS is supported only on core 0 and from within ``setup`` and ``loop``, not tasks, due to the requirement for a very different LWIP implementation. PRs always appreciated!
|
||||
|
||||
* LEAmDNS (``MDNS``) is not supported in FreeRTOS due to internal IRQ-time memory allocations.
|
||||
|
||||
The WiFi library borrows much work from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`__ , especially the ``WiFiClient`` and ``WiFiServer`` classes.
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ NTP allows the Pico to set its internal clock using the internet, and is
|
||||
required for secure connections because the certificates used have valid
|
||||
date stamps.
|
||||
|
||||
After ``WiFi.begin()`` use ``NTP.begin(s1)`` or ``NTP,begin(s1, s2)`` to
|
||||
After ``WiFi.begin()`` use ``NTP.begin(s1)`` or ``NTP.begin(s1, s2)`` to
|
||||
use one or two NTP servers (common ones are ``pool.ntp.org`` and
|
||||
``time.nist.gov``) .
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
// BTstack features that can be enabled
|
||||
#define ENABLE_LOG_INFO
|
||||
#define ENABLE_LOG_DEBUG
|
||||
#define ENABLE_LOG_ERROR
|
||||
#define ENABLE_PRINTF_HEXDUMP
|
||||
#define ENABLE_SCO_OVER_HCI
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ 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()
|
||||
extern unsigned long __lwip_rand(void);
|
||||
#define LWIP_RAND() __lwip_rand()
|
||||
|
||||
// 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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
source [find interface/picoprobe.cfg]
|
||||
source [find target/rp2040.cfg]
|
||||
@@ -152,6 +152,8 @@
|
||||
-Wl,--wrap=realloc
|
||||
-Wl,--wrap=free
|
||||
|
||||
-Wl,--wrap=lwip_init
|
||||
|
||||
-Wl,--wrap=pbuf_header
|
||||
-Wl,--wrap=pbuf_free
|
||||
-Wl,--wrap=pbuf_alloc
|
||||
@@ -207,7 +209,3 @@
|
||||
-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: b17ab37a0e...e291865233
@@ -73,11 +73,9 @@ AudioBufferManager::~AudioBufferManager() {
|
||||
if (_running) {
|
||||
_running = false;
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
dma_channel_set_irq0_enabled(_channelDMA[i], false);
|
||||
dma_channel_cleanup(_channelDMA[i]);
|
||||
__channelMap[_channelDMA[i]] = nullptr;
|
||||
dma_channel_abort(_channelDMA[i]);
|
||||
dma_channel_unclaim(_channelDMA[i]);
|
||||
dma_channel_acknowledge_irq0(_channelDMA[i]);
|
||||
__channelCount--;
|
||||
}
|
||||
if (!__channelCount) {
|
||||
|
||||
@@ -46,7 +46,7 @@ IPAddress netMsk(255, 255, 255, 0);
|
||||
|
||||
|
||||
/** Should I connect to WLAN asap? */
|
||||
boolean connect;
|
||||
bool connect;
|
||||
|
||||
/** Last time I tried to connect to WLAN */
|
||||
unsigned long lastConnectTry = 0;
|
||||
|
||||
@@ -24,7 +24,7 @@ void handleRoot() {
|
||||
}
|
||||
|
||||
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
|
||||
boolean captivePortal() {
|
||||
bool captivePortal() {
|
||||
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
|
||||
Serial.println("Request redirected to captive portal");
|
||||
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/** Is this an IP? */
|
||||
boolean isIp(String str) {
|
||||
bool isIp(String str) {
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
int c = str.charAt(i);
|
||||
if (c != '.' && (c < '0' || c > '9')) {
|
||||
|
||||
@@ -124,6 +124,7 @@ bool EEPROMClass::commit() {
|
||||
flash_range_program((intptr_t)_sector - (intptr_t)XIP_BASE, _data, _size);
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
_dirty = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,127 +1,110 @@
|
||||
/* Cores */
|
||||
#define configNUM_CORES 2
|
||||
#define configUSE_CORE_AFFINITY 1
|
||||
#define configRUN_MULTIPLE_PRIORITIES 1
|
||||
#define configNUM_CORES 2
|
||||
#define configUSE_CORE_AFFINITY 1
|
||||
#define configRUN_MULTIPLE_PRIORITIES 1
|
||||
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configUSE_IDLE_HOOK 1
|
||||
#define configUSE_MINIMAL_IDLE_HOOK 1
|
||||
#define configUSE_TICK_HOOK 1
|
||||
#define configCPU_CLOCK_HZ ( ( unsigned long ) F_CPU )
|
||||
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
|
||||
#define configMAX_PRIORITIES ( 8 )
|
||||
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 )
|
||||
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 164 * 1024 ) )
|
||||
#define configMAX_TASK_NAME_LEN ( 10 )
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configIDLE_SHOULD_YIELD 1
|
||||
#define configUSE_MUTEXES 1
|
||||
#define configQUEUE_REGISTRY_SIZE 8
|
||||
#define configUSE_RECURSIVE_MUTEXES 1
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
#define configUSE_APPLICATION_TASK_TAG 0
|
||||
#define configUSE_COUNTING_SEMAPHORES 1
|
||||
#define configUSE_QUEUE_SETS 1
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
#define configSUPPORT_STATIC_ALLOCATION 1
|
||||
#define configSTACK_DEPTH_TYPE uint32_t
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configUSE_IDLE_HOOK 1
|
||||
#define configUSE_MINIMAL_IDLE_HOOK 1
|
||||
#define configUSE_TICK_HOOK 1
|
||||
#define configCPU_CLOCK_HZ ( ( unsigned long ) F_CPU )
|
||||
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
|
||||
#define configMAX_PRIORITIES ( 8 )
|
||||
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 )
|
||||
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 164 * 1024 ) )
|
||||
#define configMAX_TASK_NAME_LEN ( 10 )
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configIDLE_SHOULD_YIELD 1
|
||||
#define configUSE_MUTEXES 1
|
||||
#define configQUEUE_REGISTRY_SIZE 8
|
||||
#define configUSE_RECURSIVE_MUTEXES 1
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
#define configUSE_APPLICATION_TASK_TAG 0
|
||||
#define configUSE_COUNTING_SEMAPHORES 1
|
||||
#define configUSE_QUEUE_SETS 1
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
#define configSUPPORT_STATIC_ALLOCATION 1
|
||||
#define configSTACK_DEPTH_TYPE uint32_t
|
||||
#define configUSE_TASK_PREEMPTION_DISABLE 1
|
||||
|
||||
#define configUSE_NEWLIB_REENTRANT 1
|
||||
#define configUSE_NEWLIB_REENTRANT 1
|
||||
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1
|
||||
|
||||
/* Run time stats related definitions. */
|
||||
void vMainConfigureTimerForRunTimeStats(void);
|
||||
unsigned long ulMainGetRunTimeCounterValue(void);
|
||||
extern void vMainConfigureTimerForRunTimeStats(void);
|
||||
extern unsigned long ulMainGetRunTimeCounterValue(void);
|
||||
#define configGENERATE_RUN_TIME_STATS 1
|
||||
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() //vMainConfigureTimerForRunTimeStats()
|
||||
#define portGET_RUN_TIME_COUNTER_VALUE() ulMainGetRunTimeCounterValue()
|
||||
|
||||
/* Co-routine definitions. */
|
||||
#define configUSE_CO_ROUTINES 1
|
||||
#define configUSE_CO_ROUTINES 1
|
||||
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
|
||||
|
||||
/* Software timer definitions. */
|
||||
#define configUSE_TIMERS 1
|
||||
#define configTIMER_TASK_PRIORITY ( 2 )
|
||||
#define configTIMER_QUEUE_LENGTH 5
|
||||
#define configTIMER_TASK_STACK_DEPTH ( 1024 )
|
||||
#define configUSE_TIMERS 1
|
||||
#define configTIMER_TASK_PRIORITY ( 2 )
|
||||
#define configTIMER_QUEUE_LENGTH 5
|
||||
#define configTIMER_TASK_STACK_DEPTH ( 1024 )
|
||||
|
||||
/* Set the following definitions to 1 to include the API function, or zero
|
||||
to exclude the API function. */
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
#define INCLUDE_uxTaskPriorityGet 1
|
||||
#define INCLUDE_vTaskDelete 1
|
||||
#define INCLUDE_vTaskCleanUpResources 1
|
||||
#define INCLUDE_vTaskSuspend 1
|
||||
#define INCLUDE_vTaskDelayUntil 1
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_eTaskGetState 1
|
||||
#define INCLUDE_eTaskGetState 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
#define INCLUDE_uxTaskPriorityGet 1
|
||||
#define INCLUDE_vTaskCleanUpResources 1
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_vTaskDelayUntil 1
|
||||
#define INCLUDE_vTaskDelete 1
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
#define INCLUDE_vTaskSuspend 1
|
||||
#define INCLUDE_xQueueGetMutexHolder 1
|
||||
#define INCLUDE_xTaskAbortDelay 1
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_xTaskGetHandle 1
|
||||
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||
#define INCLUDE_xTaskGetSchedulerState 1
|
||||
#define INCLUDE_xTaskResumeFromISR 1
|
||||
#define INCLUDE_xTimerPendFunctionCall 1
|
||||
|
||||
/* This demo makes use of one or more example stats formatting functions. These
|
||||
format the raw data provided by the uxTaskGetSystemState() function in to human
|
||||
readable ASCII form. See the notes in the implementation of vTaskList() within
|
||||
FreeRTOS/Source/tasks.c for limitations. */
|
||||
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
|
||||
|
||||
#define configUSE_MUTEXES 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 2
|
||||
/* Normal assert() semantics without relying on the provision of an assert.h
|
||||
header file. */
|
||||
#define configUSE_MUTEXES 1
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 2
|
||||
|
||||
/* Cortex-M specific definitions. */
|
||||
#undef __NVIC_PRIO_BITS
|
||||
#ifdef __NVIC_PRIO_BITS
|
||||
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
#else
|
||||
#define configPRIO_BITS 3 /* 8 priority levels */
|
||||
#define configPRIO_BITS 3 /* 8 priority levels */
|
||||
#endif
|
||||
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x7
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x7
|
||||
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||
|
||||
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
|
||||
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
|
||||
extern void rtosFatalError(void);
|
||||
#define configASSERT( x ) \
|
||||
if( ( x ) == 0 ) { portDISABLE_INTERRUPTS(); rtosFatalError(); }
|
||||
|
||||
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||
standard names - or at least those used in the unmodified vector table. */
|
||||
//#define xPortPendSVHandler PendSV_Handler
|
||||
//#define xPortSysTickHandler SysTick_Handler
|
||||
//#define vPortSVCHandler SVC_Handler
|
||||
|
||||
/* The size of the global output buffer that is available for use when there
|
||||
are multiple command interpreters running at once (for example, one on a UART
|
||||
and one on TCP/IP). This is done to prevent an output buffer being defined by
|
||||
each implementation - which would waste RAM. In this case, there is only one
|
||||
command interpreter running. */
|
||||
#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2048
|
||||
|
||||
|
||||
#define configUSE_DYNAMIC_EXCEPTION_HANDLERS 0
|
||||
#define configSUPPORT_PICO_SYNC_INTEROP 1
|
||||
#define configSUPPORT_PICO_TIME_INTEROP 1
|
||||
|
||||
#define configSUPPORT_PICO_SYNC_INTEROP 1
|
||||
#define configSUPPORT_PICO_TIME_INTEROP 1
|
||||
|
||||
#include "rp2040_config.h"
|
||||
//#include "task.h"
|
||||
|
||||
@@ -165,14 +165,14 @@ static void __no_inline_not_in_flash_func(IdleThisCore)(void *param) {
|
||||
(void) param;
|
||||
while (true) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
portDISABLE_INTERRUPTS();
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
__otherCoreIdled = true;
|
||||
while (__otherCoreIdled) {
|
||||
/* noop */
|
||||
}
|
||||
portENABLE_INTERRUPTS();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,9 +188,9 @@ extern "C" void __no_inline_not_in_flash_func(__freertos_idle_other_core)() {
|
||||
|
||||
extern "C" void __no_inline_not_in_flash_func(__freertos_resume_other_core)() {
|
||||
__otherCoreIdled = false;
|
||||
portENABLE_INTERRUPTS();
|
||||
xTaskResumeAll();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#include "HID_Bluetooth.h"
|
||||
|
||||
//setup the report map.
|
||||
//more generic function to be used with BLE & BT Classis
|
||||
void __SetupHIDreportmap(void (*WeakMouse)(), void (*WeakKeyboard)(), void (*WeakJoystick)(), bool absMouse, uint16_t *report_size, uint8_t **reportmap) {
|
||||
//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)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(2)) };
|
||||
int size = 0;
|
||||
|
||||
//enable to debug the individual report maps
|
||||
#if 0
|
||||
Serial.printf("Report mouse: %d bytes\n", sizeof(desc_hid_report_mouse));
|
||||
for (uint16_t i = 0; i < sizeof(desc_hid_report_mouse); i++) {
|
||||
Serial.print(desc_hid_report_mouse[i], HEX);
|
||||
Serial.print(" ");
|
||||
if (i % 4 == 3) {
|
||||
Serial.print("\n");
|
||||
}
|
||||
}
|
||||
Serial.printf("Report absmouse: %d bytes\n", sizeof(desc_hid_report_absmouse));
|
||||
for (uint16_t i = 0; i < sizeof(desc_hid_report_absmouse); i++) {
|
||||
Serial.print(desc_hid_report_absmouse[i], HEX);
|
||||
Serial.print(" ");
|
||||
if (i % 4 == 3) {
|
||||
Serial.print("\n");
|
||||
}
|
||||
}
|
||||
Serial.printf("Report kbd: %d bytes\n", sizeof(desc_hid_report_keyboard));
|
||||
for (uint16_t i = 0; i < sizeof(desc_hid_report_keyboard); i++) {
|
||||
Serial.print(desc_hid_report_keyboard[i], HEX);
|
||||
Serial.print(" ");
|
||||
if (i % 4 == 3) {
|
||||
Serial.print("\n");
|
||||
}
|
||||
}
|
||||
Serial.printf("Report joystick: %d bytes\n", sizeof(desc_hid_report_joystick));
|
||||
for (uint16_t i = 0; i < sizeof(desc_hid_report_joystick); i++) {
|
||||
Serial.print(desc_hid_report_joystick[i], HEX);
|
||||
Serial.print(" ");
|
||||
if (i % 4 == 3) {
|
||||
Serial.print("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//accumulate the size of all used HID report descriptors
|
||||
if (WeakKeyboard) {
|
||||
size += sizeof(desc_hid_report_keyboard);
|
||||
}
|
||||
if (WeakMouse && absMouse == false) {
|
||||
size += sizeof(desc_hid_report_mouse);
|
||||
} else if (WeakMouse && absMouse == true) {
|
||||
size += sizeof(desc_hid_report_absmouse);
|
||||
}
|
||||
if (WeakJoystick) {
|
||||
size += sizeof(desc_hid_report_joystick);
|
||||
}
|
||||
|
||||
//no HID used at all
|
||||
if (size == 0) {
|
||||
*report_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//allocate the "real" HID report descriptor
|
||||
*reportmap = (uint8_t *)malloc(size);
|
||||
if (*reportmap) {
|
||||
*report_size = size;
|
||||
|
||||
//now copy the descriptors
|
||||
|
||||
//1.) keyboard descriptor, if requested
|
||||
if (WeakKeyboard) {
|
||||
memcpy(*reportmap, desc_hid_report_keyboard, sizeof(desc_hid_report_keyboard));
|
||||
}
|
||||
|
||||
//2.) mouse descriptor, if necessary. Additional offset & new array is necessary if there is a keyboard.
|
||||
if (WeakMouse && absMouse == false) {
|
||||
//determine if we need an offset (USB keyboard is installed)
|
||||
if (WeakKeyboard) {
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(3)) };
|
||||
memcpy(*reportmap + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
|
||||
} else {
|
||||
memcpy(*reportmap, desc_hid_report_mouse, sizeof(desc_hid_report_mouse));
|
||||
}
|
||||
} else if (WeakMouse && absMouse == true) {
|
||||
//determine if we need an offset (USB keyboard is installed)
|
||||
if (WeakKeyboard) {
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(3)) };
|
||||
memcpy(*reportmap + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
|
||||
} else {
|
||||
memcpy(*reportmap, desc_hid_report_absmouse, sizeof(desc_hid_report_absmouse));
|
||||
}
|
||||
}
|
||||
|
||||
//3.) joystick descriptor. 2 additional checks are necessary for mouse and/or keyboard
|
||||
if (WeakJoystick) {
|
||||
uint8_t reportid = 1;
|
||||
int offset = 0;
|
||||
if (WeakKeyboard) {
|
||||
reportid += 2;
|
||||
offset += sizeof(desc_hid_report_keyboard);
|
||||
}
|
||||
if (WeakMouse && absMouse == false) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_mouse);
|
||||
} else if (WeakMouse && absMouse == true) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_absmouse);
|
||||
}
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(reportid)) };
|
||||
memcpy(*reportmap + offset, desc_local, sizeof(desc_local));
|
||||
}
|
||||
|
||||
//enable for debugging the final report map
|
||||
#if 0
|
||||
Serial.begin(115200);
|
||||
Serial.printf("Final map: %d bytes\n", size);
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
Serial.print(*reportmap[i], HEX);
|
||||
Serial.print(" ");
|
||||
if (i % 4 == 3) {
|
||||
Serial.print("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
Serial.println("No report map pointer provided!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//get Class of Device number for starting HID, type depends on activated libraries
|
||||
uint16_t __BTGetCOD() {
|
||||
//mouse only
|
||||
if (__BTInstallMouse && !__BTInstallKeyboard && !__BTInstallJoystick) {
|
||||
return 0x2580;
|
||||
}
|
||||
//keyboard only
|
||||
if (__BTInstallKeyboard && !__BTInstallMouse && !__BTInstallJoystick) {
|
||||
return 0x2540;
|
||||
}
|
||||
//joystick only
|
||||
if (__BTInstallJoystick && !__BTInstallKeyboard && !__BTInstallMouse) {
|
||||
return 0x2508;
|
||||
}
|
||||
//any other combination will return "combo device"
|
||||
return 0x25C0;
|
||||
}
|
||||
|
||||
//get Class of Device number for starting HID, type depends on activated libraries
|
||||
uint16_t __BLEGetAppearance() {
|
||||
//mouse only
|
||||
if (__BLEInstallMouse && !__BLEInstallKeyboard && !__BLEInstallJoystick) {
|
||||
return 0x03C2;
|
||||
}
|
||||
//keyboard only
|
||||
if (__BLEInstallKeyboard && !__BLEInstallMouse && !__BLEInstallJoystick) {
|
||||
return 0x03C1;
|
||||
}
|
||||
//joystick only
|
||||
if (__BLEInstallJoystick && !__BLEInstallMouse && !__BLEInstallKeyboard) {
|
||||
return 0x03C4;
|
||||
}
|
||||
//any other combination will return "generic HID"
|
||||
return 0x03C0;
|
||||
}
|
||||
|
||||
//keyboard report id is always 1 (compatibility with iOS)
|
||||
int __BTGetKeyboardReportID() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
int __BTGetMouseReportID() {
|
||||
return __BTInstallKeyboard ? 3 : 1;
|
||||
}
|
||||
|
||||
int __BTGetJoystickReportID() {
|
||||
int i = 1;
|
||||
if (__BTInstallKeyboard) {
|
||||
i += 2;
|
||||
}
|
||||
if (__BTInstallMouse) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int __BLEGetKeyboardReportID() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __BLEGetMouseReportID() {
|
||||
return __BLEInstallKeyboard ? 3 : 1;
|
||||
}
|
||||
|
||||
int __BLEGetFeatureReportID() {
|
||||
int feature = 1;
|
||||
if (__BLEInstallKeyboard) {
|
||||
feature += 2;
|
||||
}
|
||||
if (__BLEInstallMouse) {
|
||||
feature ++;
|
||||
}
|
||||
if (__BLEInstallJoystick) {
|
||||
feature ++;
|
||||
}
|
||||
return feature;
|
||||
}
|
||||
|
||||
int __BLEGetJoystickReportID() {
|
||||
int i = 1;
|
||||
if (__BLEInstallKeyboard) {
|
||||
i += 2;
|
||||
}
|
||||
if (__BLEInstallMouse) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
@@ -5,3 +5,39 @@
|
||||
#ifdef ENABLE_BLE
|
||||
#include "PicoBluetoothBLEHID.h"
|
||||
#endif
|
||||
|
||||
#pragma once
|
||||
|
||||
//necessary to implement the absolute mouse descriptor define,
|
||||
//remove if merged into TinyUSB
|
||||
#include <sdkoverride/tusb_absmouse.h>
|
||||
|
||||
|
||||
//override weak declarations to include HID report to report map.
|
||||
//done in each library (KeyboardBT,...)
|
||||
extern void __BTInstallKeyboard() __attribute__((weak));
|
||||
extern void __BTInstallJoystick() __attribute__((weak));
|
||||
extern void __BTInstallMouse() __attribute__((weak));
|
||||
|
||||
//override weak declarations to include HID report to report map.
|
||||
//done in each library (KeyboardBLE,...)
|
||||
extern void __BLEInstallKeyboard() __attribute__((weak));
|
||||
extern void __BLEInstallJoystick() __attribute__((weak));
|
||||
extern void __BLEInstallMouse() __attribute__((weak));
|
||||
|
||||
//setup the report map.
|
||||
//more generic function to be used with BLE & BT Classis
|
||||
void __SetupHIDreportmap(void (*WeakMouse)(), void (*WeakKeyboard)(), void (*WeakJoystick)(), bool absMouse, uint16_t *report_size, uint8_t **reportmap);
|
||||
|
||||
//get Class of Device number for starting HID, type depends on activated libraries
|
||||
uint16_t __BTGetCOD();
|
||||
//get Class of Device number for starting HID, type depends on activated libraries
|
||||
uint16_t __BLEGetAppearance();
|
||||
int __BTGetKeyboardReportID();
|
||||
int __BTGetMouseReportID();
|
||||
int __BTGetJoystickReportID();
|
||||
|
||||
int __BLEGetKeyboardReportID();
|
||||
int __BLEGetMouseReportID();
|
||||
int __BLEGetJoystickReportID();
|
||||
int __BLEGetFeatureReportID();
|
||||
|
||||
@@ -21,17 +21,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sdkoverride/bluetooth.h>
|
||||
#include <_needsbt.h>
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
#include <pico/cyw43_arch.h>
|
||||
#include <class/hid/hid_device.h>
|
||||
|
||||
#include "HID_Bluetooth.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 <sdkoverride/hids_device.h>
|
||||
#include <sdkoverride/att_db.h>
|
||||
#include <btstack.h>
|
||||
#undef hid_report_type_t
|
||||
#undef HID_REPORT_TYPE_FEATURE
|
||||
@@ -43,7 +48,9 @@
|
||||
#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>
|
||||
//#include <ble/gatt-service/hids_device.h>
|
||||
|
||||
|
||||
|
||||
class PicoBluetoothBLEHID_;
|
||||
extern PicoBluetoothBLEHID_ PicoBluetoothBLEHID;
|
||||
@@ -100,8 +107,20 @@ public:
|
||||
// Setup device information service
|
||||
device_information_service_server_init();
|
||||
|
||||
// Setup HID Device service
|
||||
hids_device_init(0, hidDescriptor, hidDescriptorSize);
|
||||
// Setup HID Device service, depending on activated reports
|
||||
uint8_t numreports = 1; //start with 1 (feature report)
|
||||
if (__BLEInstallKeyboard) {
|
||||
numreports += 2; //add keycodes + consumer keys
|
||||
}
|
||||
if (__BLEInstallMouse) {
|
||||
numreports += 1;
|
||||
}
|
||||
if (__BLEInstallJoystick) {
|
||||
numreports += 1;
|
||||
}
|
||||
//allocate memory for hid reports
|
||||
_reportStorage = (hids_device_report_t *) malloc(sizeof(hids_device_report_t) * numreports);
|
||||
hids_device_init_with_storage(0, hidDescriptor, hidDescriptorSize, numreports, _reportStorage);
|
||||
|
||||
// Setup advertisements
|
||||
uint16_t adv_int_min = 0x0030;
|
||||
@@ -134,6 +153,9 @@ public:
|
||||
}
|
||||
|
||||
void packetHandler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
||||
uint8_t result;
|
||||
uint8_t reportID;
|
||||
|
||||
if (type != HCI_EVENT_PACKET) {
|
||||
return;
|
||||
}
|
||||
@@ -174,10 +196,32 @@ public:
|
||||
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);
|
||||
//We cannot distinguish between kbd & mouse in boot mode.
|
||||
//If both are activated, we cannot send
|
||||
if (__BLEInstallKeyboard && !__BLEInstallMouse) {
|
||||
hids_device_send_boot_keyboard_input_report(_con_handle, &(((const uint8_t *)_sendReport)[1]), _sendReportLen);
|
||||
}
|
||||
if (__BLEInstallMouse && !__BLEInstallKeyboard) {
|
||||
hids_device_send_boot_mouse_input_report(_con_handle, &(((const uint8_t *)_sendReport)[1]), _sendReportLen);
|
||||
}
|
||||
if (__BLEInstallMouse && __BLEInstallJoystick) {
|
||||
printf("Error: BLE HID in boot mode, but mouse & keyboard are active\n");
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
hids_device_send_input_report(_con_handle, (const uint8_t *)_sendReport, _sendReportLen);
|
||||
reportID = ((const uint8_t *)_sendReport)[0];
|
||||
result = hids_device_send_input_report_for_id(_con_handle, (uint16_t)reportID, &(((const uint8_t *)_sendReport)[1]), _sendReportLen - 1);
|
||||
if (result) {
|
||||
Serial.printf("Error sending %d - report ID: %d\n", result, reportID);
|
||||
}
|
||||
//else Serial.printf("Sent report for ID: %d\n",reportID);
|
||||
#if 0
|
||||
Serial.printf("Sending report for ID %d, len: %d:\n", reportID, _sendReportLen);
|
||||
for (uint8_t i = 0; i < _sendReportLen; i++) {
|
||||
Serial.printf("0x%02X - ", ((const uint8_t *)_sendReport)[i]);
|
||||
}
|
||||
Serial.println("");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -208,6 +252,10 @@ public:
|
||||
}
|
||||
|
||||
bool send(void *rpt, int len) {
|
||||
// Wait for another report to be sent
|
||||
while (connected() && _needToSend) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
_needToSend = true;
|
||||
_sendReport = rpt;
|
||||
_sendReportLen = len;
|
||||
@@ -236,6 +284,9 @@ public:
|
||||
async_context_release_lock(cyw43_arch_async_context());
|
||||
}
|
||||
|
||||
uint8_t *_attdb = nullptr;
|
||||
int _attdbLen = 0;
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
|
||||
@@ -280,10 +331,17 @@ private:
|
||||
}
|
||||
uint8_t *_advData = nullptr;
|
||||
uint8_t _advDataLen = 0;
|
||||
hids_device_report_t *_reportStorage = nullptr;
|
||||
|
||||
void _buildAttdb(const char *hidName) {
|
||||
free(_attdb);
|
||||
_attdbLen = sizeof(_attdb_head) + 8 + strlen(hidName) + sizeof(_attdb_tail);
|
||||
//add up all different parts of ATT DB
|
||||
_attdbLen = sizeof(_attdb_head) + 8 + strlen(hidName) + sizeof(_attdb_tail) + sizeof(_attdb_batt_hidhead) + sizeof(_attdb_char);
|
||||
//reports
|
||||
_attdbLen += sizeof(_attdb_kbd_report) + sizeof(_attdb_mouse_report) + sizeof(_attdb_joystick_report);
|
||||
//additional boot characteristics
|
||||
_attdbLen += sizeof(_attdb_kbd_boot) + sizeof(_attdb_mouse_boot);
|
||||
|
||||
_attdb = (uint8_t *) malloc(_attdbLen);
|
||||
memcpy(_attdb, _attdb_head, sizeof(_attdb_head));
|
||||
// 0x0003 VALUE CHARACTERISTIC-GAP_DEVICE_NAME - READ -'HID Mouse'
|
||||
@@ -300,10 +358,35 @@ private:
|
||||
_attdb[i++] = 0x2a;
|
||||
memcpy(_attdb + i, hidName, strlen(hidName));
|
||||
i += strlen(hidName);
|
||||
|
||||
memcpy(_attdb + i, _attdb_batt_hidhead, sizeof(_attdb_batt_hidhead));
|
||||
i += sizeof(_attdb_batt_hidhead);
|
||||
|
||||
//1.) KBD report mode
|
||||
memcpy(_attdb + i, _attdb_kbd_report, sizeof(_attdb_kbd_report));
|
||||
i += sizeof(_attdb_kbd_report);
|
||||
|
||||
//2.) mouse report mode
|
||||
memcpy(_attdb + i, _attdb_mouse_report, sizeof(_attdb_mouse_report));
|
||||
i += sizeof(_attdb_mouse_report);
|
||||
|
||||
//3.) joystick report mode
|
||||
memcpy(_attdb + i, _attdb_joystick_report, sizeof(_attdb_joystick_report));
|
||||
i += sizeof(_attdb_joystick_report);
|
||||
//4.) report characteristics
|
||||
memcpy(_attdb + i, _attdb_char, sizeof(_attdb_char));
|
||||
i += sizeof(_attdb_char);
|
||||
|
||||
//5.) KBD boot mode (always included)
|
||||
memcpy(_attdb + i, _attdb_kbd_boot, sizeof(_attdb_kbd_boot));
|
||||
i += sizeof(_attdb_kbd_boot);
|
||||
|
||||
//6.) mouse boot mode (always included)
|
||||
memcpy(_attdb + i, _attdb_mouse_boot, sizeof(_attdb_mouse_boot));
|
||||
i += sizeof(_attdb_mouse_boot);
|
||||
//7.) tail (report)
|
||||
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
|
||||
@@ -315,7 +398,7 @@ private:
|
||||
0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_tail[] = {
|
||||
static constexpr const uint8_t _attdb_batt_hidhead[] = {
|
||||
// #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
|
||||
@@ -333,6 +416,7 @@ private:
|
||||
// #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
|
||||
@@ -386,7 +470,6 @@ private:
|
||||
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
|
||||
@@ -397,6 +480,9 @@ private:
|
||||
// 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,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_kbd_report[] = {
|
||||
// 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
|
||||
@@ -405,9 +491,10 @@ private:
|
||||
// 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)
|
||||
// fixed report id = 1, type = Input (1); keycodes
|
||||
// 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
|
||||
@@ -416,9 +503,12 @@ private:
|
||||
// 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,
|
||||
// fixed report id = 2, type = Input (1) consumer
|
||||
// 0x0025 REPORT_REFERENCE-READ-2-1
|
||||
0x0a, 0x00, 0x02, 0x00, 0x25, 0x00, 0x08, 0x29, 0x2, 0x1,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_mouse_report[] = {
|
||||
// 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
|
||||
@@ -427,59 +517,88 @@ private:
|
||||
// 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
|
||||
// fixed report id = 3, type = Input (1) mouse
|
||||
// 0x0029 REPORT_REFERENCE-READ-3-1
|
||||
0x0a, 0x00, 0x02, 0x00, 0x29, 0x00, 0x08, 0x29, 0x3, 0x1,
|
||||
};
|
||||
static constexpr const uint8_t _attdb_joystick_report[] = {
|
||||
|
||||
// 0x002a CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x03, 0x28, 0x1a, 0x2b, 0x00, 0x4d, 0x2a,
|
||||
// 0x002b 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, 0x2b, 0x00, 0x4d, 0x2a,
|
||||
// 0x002c CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x0a, 0x00, 0x0f, 0xf1, 0x2c, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// fixed report id = 4, type = Input (1) gamepad
|
||||
// 0x002d REPORT_REFERENCE-READ-4-1
|
||||
0x0a, 0x00, 0x02, 0x00, 0x2d, 0x00, 0x08, 0x29, 0x4, 0x1,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_char[] = {
|
||||
// 0x002e CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x03, 0x28, 0x0a, 0x2f, 0x00, 0x4d, 0x2a,
|
||||
// 0x002f VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | ENCRYPTION_KEY_SIZE_16
|
||||
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x08, 0x00, 0x0b, 0xf5, 0x2f, 0x00, 0x4d, 0x2a,
|
||||
// fixed report id = 5, type = Feature (3)
|
||||
// 0x0030 REPORT_REFERENCE-READ-5-3
|
||||
0x0a, 0x00, 0x02, 0x00, 0x30, 0x00, 0x08, 0x29, 0x5, 0x3,
|
||||
// 0x0031 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP - DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x31, 0x00, 0x03, 0x28, 0x02, 0x32, 0x00, 0x4b, 0x2a,
|
||||
// 0x0032 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
|
||||
0x08, 0x00, 0x02, 0x01, 0x32, 0x00, 0x4b, 0x2a,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_kbd_boot[] = {
|
||||
// 0x0033 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x33, 0x00, 0x03, 0x28, 0x1a, 0x34, 0x00, 0x22, 0x2a,
|
||||
// 0x0034 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
|
||||
0x08, 0x00, 0x0a, 0x01, 0x34, 0x00, 0x22, 0x2a,
|
||||
// 0x0035 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
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x35, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
|
||||
|
||||
// 0x0036 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT - DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x36, 0x00, 0x03, 0x28, 0x0e, 0x37, 0x00, 0x32, 0x2a,
|
||||
// 0x0037 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
|
||||
0x08, 0x00, 0x0e, 0x01, 0x37, 0x00, 0x32, 0x2a,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_mouse_boot[] = {
|
||||
// 0x0038 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x38, 0x00, 0x03, 0x28, 0x1a, 0x39, 0x00, 0x33, 0x2a,
|
||||
// 0x0039 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
|
||||
0x08, 0x00, 0x0a, 0x01, 0x39, 0x00, 0x33, 0x2a,
|
||||
// 0x003a CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x33, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x3a, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr const uint8_t _attdb_tail[] = {
|
||||
// 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'
|
||||
// 0x003b CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION - READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x3b, 0x00, 0x03, 0x28, 0x02, 0x3c, 0x00, 0x4a, 0x2a,
|
||||
// 0x003c 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
|
||||
0x0c, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x4a, 0x2a, 0x01, 0x01, 0x00, 0x02,
|
||||
// 0x003d CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT - DYNAMIC | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x3d, 0x00, 0x03, 0x28, 0x04, 0x3e, 0x00, 0x4c, 0x2a,
|
||||
// 0x003e 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,
|
||||
0x08, 0x00, 0x04, 0x01, 0x3e, 0x00, 0x4c, 0x2a,
|
||||
// END
|
||||
0x00, 0x00
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
volatile bool _needToSend = false;
|
||||
void *_sendReport;
|
||||
uint8_t _sendReportID;
|
||||
int _sendReportLen;
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sdkoverride/bluetooth.h>
|
||||
#include <_needsbt.h>
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
@@ -32,6 +33,8 @@
|
||||
#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 <sdkoverride/hids_device.h>
|
||||
#include <sdkoverride/att_db.h>
|
||||
#include <btstack.h>
|
||||
#undef hid_report_type_t
|
||||
#undef HID_REPORT_TYPE_FEATURE
|
||||
|
||||
@@ -6,7 +6,19 @@
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
// Example works with either Wired or WiFi Ethernet, define one of these values to 1, other to 0
|
||||
#define USE_WIFI 1
|
||||
#define USE_WIRED 0
|
||||
|
||||
#if USE_WIFI
|
||||
#include <WiFi.h>
|
||||
#elif USE_WIRED
|
||||
#include <W5500lwIP.h> // Or W5100lwIP.h or ENC28J60lwIP.h
|
||||
Wiznet5500lwIP eth(1 /* chip select */); // or Wiznet5100lwIP or ENC28J60lwIP
|
||||
#endif
|
||||
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#ifndef STASSID
|
||||
@@ -34,8 +46,33 @@ void setup() {
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
#if USE_WIFI
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiMulti.addAP(ssid, pass);
|
||||
#elif USE_WIRED
|
||||
// Set up SPI pinout to match your HW
|
||||
SPI.setRX(0);
|
||||
SPI.setCS(1);
|
||||
SPI.setSCK(2);
|
||||
SPI.setTX(3);
|
||||
|
||||
// Start the Ethernet port
|
||||
if (!eth.begin()) {
|
||||
Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
|
||||
while (1) {
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for connection
|
||||
while (eth.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(eth.localIP());
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
const char *jigsaw_cert = R"EOF(
|
||||
@@ -74,8 +111,12 @@ N6K5xrmaof185pVCxACPLc/BoKyUwMeC8iXCm00=
|
||||
static int cnt = 0;
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
#if USE_WIFI
|
||||
// wait for WiFi connection
|
||||
if ((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
#elif USE_WIRED
|
||||
if (eth.connected()) {
|
||||
#endif
|
||||
HTTPClient https;
|
||||
switch (cnt) {
|
||||
case 0:
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This example just sends out TDM data on 8 channels with their value
|
||||
equal to the channel number.
|
||||
Released to the public domain by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
*/
|
||||
|
||||
#include <I2S.h>
|
||||
|
||||
// Create the I2S port using a PIO state machine
|
||||
I2S i2s(OUTPUT);
|
||||
|
||||
// GPIO pin numbers
|
||||
#define pBCLK 20
|
||||
#define pWS (pBCLK+1)
|
||||
#define pDOUT 22
|
||||
|
||||
const int sampleRate = 1000;
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, 1);
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println("I2S TDM test");
|
||||
|
||||
i2s.setBCLK(pBCLK);
|
||||
i2s.setDATA(pDOUT);
|
||||
i2s.setBitsPerSample(32);
|
||||
i2s.setTDMFormat();
|
||||
i2s.setTDMChannels(8);
|
||||
|
||||
// start I2S at the sample rate with 16-bits per sample
|
||||
if (!i2s.begin(sampleRate)) {
|
||||
Serial.println("Failed to initialize I2S!");
|
||||
while (1); // do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint32_t x = 0;
|
||||
while (1) {
|
||||
i2s.write(x & 0x07);
|
||||
if (!(x % 10000)) {
|
||||
Serial.println(x);
|
||||
}
|
||||
x++;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ setBitsPerSample KEYWORD2
|
||||
setFrequency KEYWORD2
|
||||
setBuffers KEYWORD2
|
||||
setLSBJFormat KEYWORD2
|
||||
setTDMFormat KEYWORD2
|
||||
setTDMChannels KEYWORD2
|
||||
swapClocks KEYWORD2
|
||||
setMCLKmult KEYWORD2
|
||||
setSysClk KEYWORD2
|
||||
|
||||
@@ -55,6 +55,8 @@ I2S::I2S(PinMode direction) {
|
||||
_bufferWords = 0;
|
||||
_silenceSample = 0;
|
||||
_isLSBJ = false;
|
||||
_isTDM = false;
|
||||
_tdmChannels = 8;
|
||||
_swapClocks = false;
|
||||
_multMCLK = 256;
|
||||
}
|
||||
@@ -109,10 +111,10 @@ bool I2S::setFrequency(int newFreq) {
|
||||
_freq = newFreq;
|
||||
if (_running) {
|
||||
if (_MCLKenabled) {
|
||||
int bitClk = _freq * _bps * 2.0 /* channels */ * 2.0 /* edges per clock */;
|
||||
int bitClk = _freq * _bps * (_isTDM ? (double)_tdmChannels : 2.0) /* channels */ * 2.0 /* edges per clock */;
|
||||
pio_sm_set_clkdiv_int_frac(_pio, _sm, clock_get_hz(clk_sys) / bitClk, 0);
|
||||
} else {
|
||||
float bitClk = _freq * _bps * 2.0 /* channels */ * 2.0 /* edges per clock */;
|
||||
float bitClk = _freq * _bps * (_isTDM ? (double)_tdmChannels : 2.0) /* channels */ * 2.0 /* edges per clock */;
|
||||
pio_sm_set_clkdiv(_pio, _sm, (float)clock_get_hz(clk_sys) / bitClk);
|
||||
}
|
||||
}
|
||||
@@ -132,7 +134,7 @@ bool I2S::setSysClk(int samplerate) { // optimise sys_clk for desired samplerate
|
||||
}
|
||||
|
||||
bool I2S::setMCLKmult(int mult) {
|
||||
if (_running || !_isOutput) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
if ((mult % 64) == 0) {
|
||||
@@ -151,6 +153,22 @@ bool I2S::setLSBJFormat() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2S::setTDMFormat() {
|
||||
if (_running || !_isOutput) {
|
||||
return false;
|
||||
}
|
||||
_isTDM = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2S::setTDMChannels(int channels) {
|
||||
if (_running || !_isOutput) {
|
||||
return false;
|
||||
}
|
||||
_tdmChannels = channels;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2S::swapClocks() {
|
||||
if (_running || !_isOutput) {
|
||||
return false;
|
||||
@@ -193,9 +211,9 @@ bool I2S::begin() {
|
||||
_isHolding = 0;
|
||||
int off = 0;
|
||||
if (!_swapClocks) {
|
||||
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program) : &pio_i2s_in_program);
|
||||
_i2s = new PIOProgram(_isOutput ? (_isTDM ? &pio_tdm_out_program : (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program)) : &pio_i2s_in_program);
|
||||
} else {
|
||||
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_swap_program : &pio_i2s_out_swap_program) : &pio_i2s_in_swap_program);
|
||||
_i2s = new PIOProgram(_isOutput ? (_isTDM ? &pio_tdm_out_swap_program : (_isLSBJ ? &pio_lsbj_out_swap_program : &pio_i2s_out_swap_program)) : &pio_i2s_in_swap_program);
|
||||
}
|
||||
if (!_i2s->prepare(&_pio, &_sm, &off)) {
|
||||
_running = false;
|
||||
@@ -204,7 +222,9 @@ bool I2S::begin() {
|
||||
return false;
|
||||
}
|
||||
if (_isOutput) {
|
||||
if (_isLSBJ) {
|
||||
if (_isTDM) {
|
||||
pio_tdm_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks, _tdmChannels);
|
||||
} else if (_isLSBJ) {
|
||||
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
|
||||
} else {
|
||||
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
|
||||
|
||||
@@ -35,6 +35,8 @@ public:
|
||||
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0);
|
||||
bool setFrequency(int newFreq);
|
||||
bool setLSBJFormat();
|
||||
bool setTDMFormat();
|
||||
bool setTDMChannels(int channels);
|
||||
bool swapClocks();
|
||||
bool setMCLKmult(int mult);
|
||||
bool setSysClk(int samplerate);
|
||||
@@ -121,6 +123,8 @@ private:
|
||||
size_t _bufferWords;
|
||||
int32_t _silenceSample;
|
||||
bool _isLSBJ;
|
||||
bool _isTDM;
|
||||
int _tdmChannels;
|
||||
bool _isOutput;
|
||||
bool _swapClocks;
|
||||
bool _MCLKenabled;
|
||||
|
||||
@@ -72,6 +72,38 @@ right:
|
||||
; Loop back to beginning...
|
||||
|
||||
|
||||
.program pio_tdm_out
|
||||
.side_set 2 ; 0 = bclk, 1 = wclk
|
||||
; The C code should place (number of bits - 1) in Y and update SHIFTCTRL
|
||||
; to be 32 (as per the TDM specs)
|
||||
; +----- WCLK
|
||||
; |+---- BCLK
|
||||
mov x, y side 0b11
|
||||
bitloop:
|
||||
out pins, 1 side 0b00
|
||||
jmp x-- bitloop side 0b01
|
||||
|
||||
lastbit:
|
||||
out pins, 1 side 0b10
|
||||
; Loop back to the beginning
|
||||
|
||||
|
||||
.program pio_tdm_out_swap
|
||||
.side_set 2 ; 0 = wclk, 1 = bclk
|
||||
; The C code should place (number of bits - 1) in Y and update SHIFTCTRL
|
||||
; to be 32 (as per the TDM specs)
|
||||
; +----- BCLK
|
||||
; |+---- WCLK
|
||||
mov x, y side 0b11
|
||||
bitloop:
|
||||
out pins, 1 side 0b00
|
||||
jmp x-- bitloop side 0b10
|
||||
|
||||
lastbit:
|
||||
out pins, 1 side 0b01
|
||||
; Loop back to the beginning
|
||||
|
||||
|
||||
|
||||
.program pio_lsbj_out
|
||||
.side_set 2 ; 0 = bclk, 1=wclk
|
||||
@@ -195,6 +227,33 @@ static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
|
||||
}
|
||||
|
||||
static inline void pio_tdm_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap, uint channels) {
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_gpio_init(pio, clock_pin_base);
|
||||
pio_gpio_init(pio, clock_pin_base + 1);
|
||||
|
||||
pio_sm_config sm_config = swap ? pio_tdm_out_swap_program_get_default_config(offset) : pio_tdm_out_program_get_default_config(offset);
|
||||
|
||||
sm_config_set_out_pins(&sm_config, data_pin, 1);
|
||||
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
|
||||
sm_config_set_out_shift(&sm_config, false, true, 32);
|
||||
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &sm_config);
|
||||
|
||||
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
|
||||
pio_sm_set_pins(pio, sm, 0); // clear pins
|
||||
|
||||
// Can't set constant > 31, so push and pop/mov
|
||||
pio_sm_put_blocking(pio, sm, bits * channels - 2);
|
||||
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_y, pio_osr));
|
||||
// Need to make OSR believe there's nothing left to shift out, or the 1st word will be the count we just passed in, not a sample
|
||||
pio_sm_exec(pio, sm, pio_encode_out(pio_osr, 32));
|
||||
}
|
||||
|
||||
|
||||
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_gpio_init(pio, clock_pin_base);
|
||||
|
||||
@@ -106,6 +106,68 @@ static inline pio_sm_config pio_i2s_out_swap_program_get_default_config(uint off
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------- //
|
||||
// pio_tdm_out //
|
||||
// ----------- //
|
||||
|
||||
#define pio_tdm_out_wrap_target 0
|
||||
#define pio_tdm_out_wrap 3
|
||||
|
||||
static const uint16_t pio_tdm_out_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xb822, // 0: mov x, y side 3
|
||||
0x6001, // 1: out pins, 1 side 0
|
||||
0x0841, // 2: jmp x--, 1 side 1
|
||||
0x7001, // 3: out pins, 1 side 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_tdm_out_program = {
|
||||
.instructions = pio_tdm_out_program_instructions,
|
||||
.length = 4,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_tdm_out_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_tdm_out_wrap_target, offset + pio_tdm_out_wrap);
|
||||
sm_config_set_sideset(&c, 2, false, false);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------------- //
|
||||
// pio_tdm_out_swap //
|
||||
// ---------------- //
|
||||
|
||||
#define pio_tdm_out_swap_wrap_target 0
|
||||
#define pio_tdm_out_swap_wrap 3
|
||||
|
||||
static const uint16_t pio_tdm_out_swap_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xb822, // 0: mov x, y side 3
|
||||
0x6001, // 1: out pins, 1 side 0
|
||||
0x1041, // 2: jmp x--, 1 side 2
|
||||
0x6801, // 3: out pins, 1 side 1
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_tdm_out_swap_program = {
|
||||
.instructions = pio_tdm_out_swap_program_instructions,
|
||||
.length = 4,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_tdm_out_swap_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_tdm_out_swap_wrap_target, offset + pio_tdm_out_swap_wrap);
|
||||
sm_config_set_sideset(&c, 2, false, false);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------ //
|
||||
// pio_lsbj_out //
|
||||
// ------------ //
|
||||
@@ -267,6 +329,26 @@ static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint
|
||||
pio_sm_set_pins(pio, sm, 0); // clear pins
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
|
||||
}
|
||||
static inline void pio_tdm_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap, uint channels) {
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_gpio_init(pio, clock_pin_base);
|
||||
pio_gpio_init(pio, clock_pin_base + 1);
|
||||
pio_sm_config sm_config = swap ? pio_tdm_out_swap_program_get_default_config(offset) : pio_tdm_out_program_get_default_config(offset);
|
||||
sm_config_set_out_pins(&sm_config, data_pin, 1);
|
||||
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
|
||||
sm_config_set_out_shift(&sm_config, false, true, 32);
|
||||
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
|
||||
pio_sm_init(pio, sm, offset, &sm_config);
|
||||
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
|
||||
pio_sm_set_pins(pio, sm, 0); // clear pins
|
||||
// Can't set constant > 31, so push and pop/mov
|
||||
pio_sm_put_blocking(pio, sm, bits * channels - 2);
|
||||
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_y, pio_osr));
|
||||
// Need to make OSR believe there's nothing left to shift out, or the 1st word will be the count we just passed in, not a sample
|
||||
pio_sm_exec(pio, sm, pio_encode_out(pio_osr, 32));
|
||||
}
|
||||
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_gpio_init(pio, clock_pin_base);
|
||||
@@ -299,3 +381,4 @@ static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint d
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ void loop() {
|
||||
Joystick.button(i, true);
|
||||
delay(250);
|
||||
Joystick.button(i, false);
|
||||
delay(10); //we need a short delay here, sending packets with less than 1ms leads to packet loss!
|
||||
delay(10); // We need a short delay here, sending packets with less than 1ms leads to packet loss!
|
||||
}
|
||||
//alternativ with manual send:
|
||||
// Alternative with manual send:
|
||||
Joystick.useManualSend(true);
|
||||
Serial.println("Joystick buttons - manual send");
|
||||
for (uint8_t i = 1; i <= 32; i++) {
|
||||
@@ -33,8 +33,8 @@ void loop() {
|
||||
}
|
||||
Joystick.useManualSend(false);
|
||||
|
||||
//iterate all joystick axis
|
||||
//Note: although you can use 0-1023 here (10bit), internally 8bits are used (-127 to 127)
|
||||
// Iterate all joystick axis
|
||||
// Note: although you can use 0-1023 here (10bit), internally 8bits are used (-127 to 127)
|
||||
Serial.println("Joystick X");
|
||||
for (uint16_t i = 0; i < 1023; i++) {
|
||||
Joystick.X(i);
|
||||
@@ -71,8 +71,8 @@ void loop() {
|
||||
delay(20);
|
||||
} Joystick.hat(-1);
|
||||
|
||||
//use int8 mode for the axis.
|
||||
//Note: hat is not used differently.
|
||||
// Use int8 mode for the axis.
|
||||
// Note: hat is not used differently.
|
||||
Serial.println("Now all axis in 8bit mode, -127 to 127");
|
||||
Joystick.use8bit(true);
|
||||
Serial.println("Joystick X");
|
||||
|
||||
@@ -40,7 +40,7 @@ Joystick_::Joystick_(void) {
|
||||
void Joystick_::send_now(void) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
tud_task();
|
||||
if (tud_hid_ready()) {
|
||||
if (__USBHIDReady()) {
|
||||
tud_hid_n_report(0, __USBGetJoystickReportID(), &data, sizeof(data));
|
||||
}
|
||||
tud_task();
|
||||
|
||||
+6
-6
@@ -20,9 +20,9 @@ void loop() {
|
||||
JoystickBLE.button(i, true);
|
||||
delay(250);
|
||||
JoystickBLE.button(i, false);
|
||||
delay(10); //we need a short delay here, sending packets with less than 1ms leads to packet loss!
|
||||
delay(10); // We need a short delay here, sending packets with less than 1ms leads to packet loss!
|
||||
}
|
||||
//alternativ with manual send:
|
||||
// Alternative with manual send:
|
||||
JoystickBLE.useManualSend(true);
|
||||
Serial.println("Joystick buttons - manual send");
|
||||
for (uint8_t i = 1; i <= 32; i++) {
|
||||
@@ -33,8 +33,8 @@ void loop() {
|
||||
}
|
||||
JoystickBLE.useManualSend(false);
|
||||
|
||||
//iterate all joystick axis
|
||||
//Note: although you can use 0-1023 here (10bit), internally 8bits are used (-127 to 127)
|
||||
// Iterate all joystick axis
|
||||
// Note: although you can use 0-1023 here (10bit), internally 8bits are used (-127 to 127)
|
||||
Serial.println("Joystick X");
|
||||
for (uint16_t i = 0; i < 1023; i++) {
|
||||
JoystickBLE.X(i);
|
||||
@@ -71,8 +71,8 @@ void loop() {
|
||||
delay(20);
|
||||
} JoystickBLE.hat(-1);
|
||||
|
||||
//use int8 mode for the axis.
|
||||
//Note: hat is not used differently.
|
||||
// Use int8 mode for the axis.
|
||||
// Note: hat is not used differently.
|
||||
Serial.println("Now all axis in 8bit mode, -127 to 127");
|
||||
JoystickBLE.use8bit(true);
|
||||
Serial.println("Joystick X");
|
||||
|
||||
@@ -25,18 +25,22 @@
|
||||
|
||||
#include "JoystickBLE.h"
|
||||
#include <Arduino.h>
|
||||
#include <HID_Bluetooth.h>
|
||||
#include <PicoBluetoothBLEHID.h>
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Joystick/Gamepad
|
||||
|
||||
// Weak function override to add our descriptor to the list
|
||||
void __BLEInstallJoystick() { /* noop */ }
|
||||
|
||||
JoystickBLE_::JoystickBLE_(void) {
|
||||
// Member vars set in base constructor
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
static const uint8_t desc_joystick[] = {TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(REPORT_ID))};
|
||||
uint8_t *desc_joystickBLE;
|
||||
uint16_t desc_joystickBLE_length;
|
||||
|
||||
void JoystickBLE_::begin(const char *localName, const char *hidName) {
|
||||
if (!localName) {
|
||||
@@ -45,7 +49,10 @@ void JoystickBLE_::begin(const char *localName, const char *hidName) {
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, 0x03c4, desc_joystick, sizeof(desc_joystick));
|
||||
|
||||
__SetupHIDreportmap(__BLEInstallMouse, __BLEInstallKeyboard, __BLEInstallJoystick, false, &desc_joystickBLE_length, &desc_joystickBLE);
|
||||
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, __BLEGetAppearance(), desc_joystickBLE, desc_joystickBLE_length);
|
||||
}
|
||||
|
||||
void JoystickBLE_::end() {
|
||||
@@ -57,7 +64,13 @@ void JoystickBLE_::setBattery(int lvl) {
|
||||
}
|
||||
|
||||
void JoystickBLE_::send_now() {
|
||||
PicoBluetoothBLEHID.send(&data, sizeof(data));
|
||||
//insert report ID; not part of the hid_gamepad_report_t
|
||||
uint8_t *report = (uint8_t *)malloc(sizeof(hid_gamepad_report_t) +1);
|
||||
if (report) {
|
||||
report[0] = __BLEGetJoystickReportID();
|
||||
memcpy(&report[1], (uint8_t*)&data, sizeof(data));
|
||||
PicoBluetoothBLEHID.send(report, sizeof(data) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
JoystickBLE_ JoystickBLE;
|
||||
|
||||
@@ -20,9 +20,9 @@ void loop() {
|
||||
JoystickBT.button(i, true);
|
||||
delay(250);
|
||||
JoystickBT.button(i, false);
|
||||
delay(10); //we need a short delay here, sending packets with less than 1ms leads to packet loss!
|
||||
delay(10); // We need a short delay here, sending packets with less than 1ms leads to packet loss!
|
||||
}
|
||||
//alternativ with manual send:
|
||||
// Alternative with manual send:
|
||||
JoystickBT.useManualSend(true);
|
||||
Serial.println("Joystick buttons - manual send");
|
||||
for (uint8_t i = 1; i <= 32; i++) {
|
||||
@@ -33,8 +33,8 @@ void loop() {
|
||||
}
|
||||
JoystickBT.useManualSend(false);
|
||||
|
||||
//iterate all joystick axis
|
||||
//Note: although you can use 0-1023 here (10bit), internally 8bits are used (-127 to 127)
|
||||
// Iterate all joystick axis
|
||||
// Note: although you can use 0-1023 here (10bit), internally 8bits are used (-127 to 127)
|
||||
Serial.println("Joystick X");
|
||||
for (uint16_t i = 0; i < 1023; i++) {
|
||||
JoystickBT.X(i);
|
||||
@@ -71,8 +71,8 @@ void loop() {
|
||||
delay(20);
|
||||
} JoystickBT.hat(-1);
|
||||
|
||||
//use int8 mode for the axis.
|
||||
//Note: hat is not used differently.
|
||||
// Use int8 mode for the axis.
|
||||
// Note: hat is not used differently.
|
||||
Serial.println("Now all axis in 8bit mode, -127 to 127");
|
||||
JoystickBT.use8bit(true);
|
||||
Serial.println("Joystick X");
|
||||
|
||||
@@ -25,18 +25,22 @@
|
||||
|
||||
#include "JoystickBT.h"
|
||||
#include <Arduino.h>
|
||||
#include <HID_Bluetooth.h>
|
||||
#include <PicoBluetoothHID.h>
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Joystick/Gamepad
|
||||
|
||||
// Weak function override to add our descriptor to the list
|
||||
void __BTInstallJoystick() { /* noop */ }
|
||||
|
||||
JoystickBT_::JoystickBT_() {
|
||||
// HID_Joystick sets up all the member vars
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
static const uint8_t desc_joystick[] = {TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(REPORT_ID))};
|
||||
uint8_t *desc_joystickBT;
|
||||
uint16_t desc_joystickBT_length;
|
||||
|
||||
void JoystickBT_::begin(const char *localName, const char *hidName) {
|
||||
if (!localName) {
|
||||
@@ -45,7 +49,10 @@ void JoystickBT_::begin(const char *localName, const char *hidName) {
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
PicoBluetoothHID.startHID(localName, hidName, 0x2508, 33, desc_joystick, sizeof(desc_joystick));
|
||||
|
||||
__SetupHIDreportmap(__BTInstallMouse, __BTInstallKeyboard, __BTInstallJoystick, false, &desc_joystickBT_length, &desc_joystickBT);
|
||||
|
||||
PicoBluetoothHID.startHID(localName, hidName, __BTGetCOD(), 33, desc_joystickBT, desc_joystickBT_length);
|
||||
}
|
||||
|
||||
void JoystickBT_::end() {
|
||||
@@ -54,7 +61,7 @@ void JoystickBT_::end() {
|
||||
|
||||
//immediately send an HID report
|
||||
void JoystickBT_::send_now() {
|
||||
PicoBluetoothHID.send(REPORT_ID, &data, sizeof(data));
|
||||
PicoBluetoothHID.send(__BTGetJoystickReportID(), &data, sizeof(data));
|
||||
}
|
||||
|
||||
JoystickBT_ JoystickBT;
|
||||
|
||||
@@ -40,7 +40,7 @@ Keyboard_::Keyboard_(void) {
|
||||
void Keyboard_::sendReport(KeyReport* keys) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
tud_task();
|
||||
if (tud_hid_ready()) {
|
||||
if (__USBHIDReady()) {
|
||||
tud_hid_keyboard_report(__USBGetKeyboardReportID(), keys->modifiers, keys->keys);
|
||||
}
|
||||
tud_task();
|
||||
@@ -49,7 +49,7 @@ void Keyboard_::sendReport(KeyReport* keys) {
|
||||
void Keyboard_::sendConsumerReport(uint16_t key) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
tud_task();
|
||||
if (tud_hid_ready()) {
|
||||
if (__USBHIDReady()) {
|
||||
tud_hid_report(__USBGetKeyboardReportID() + 1, &key, sizeof(key));
|
||||
}
|
||||
tud_task();
|
||||
|
||||
@@ -22,19 +22,22 @@
|
||||
|
||||
#include "KeyboardBLE.h"
|
||||
#include "KeyboardLayout.h"
|
||||
#include <HID_Bluetooth.h>
|
||||
#include <PicoBluetoothBLEHID.h>
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
// Weak function override to add our descriptor to the list
|
||||
void __BLEInstallKeyboard() { /* noop */ }
|
||||
|
||||
KeyboardBLE_::KeyboardBLE_(void) {
|
||||
// Base class clears the members we care about
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
|
||||
static const uint8_t desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID))};
|
||||
uint8_t *desc_keyboardBLE;
|
||||
uint16_t desc_keyboardBLE_length;
|
||||
|
||||
void KeyboardBLE_::begin(const char *localName, const char *hidName, const uint8_t *layout) {
|
||||
if (!localName) {
|
||||
@@ -44,7 +47,10 @@ void KeyboardBLE_::begin(const char *localName, const char *hidName, const uint8
|
||||
hidName = localName;
|
||||
}
|
||||
_asciimap = layout;
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, 0x03c1, desc_keyboard, sizeof(desc_keyboard));
|
||||
|
||||
__SetupHIDreportmap(__BLEInstallMouse, __BLEInstallKeyboard, __BLEInstallJoystick, false, &desc_keyboardBLE_length, &desc_keyboardBLE);
|
||||
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, __BLEGetAppearance(), desc_keyboardBLE, desc_keyboardBLE_length);
|
||||
}
|
||||
|
||||
void KeyboardBLE_::end(void) {
|
||||
@@ -60,12 +66,21 @@ void KeyboardBLE_::sendReport(KeyReport* keys) {
|
||||
data.modifier = keys->modifiers;
|
||||
data.reserved = 0;
|
||||
memcpy(data.keycode, keys->keys, sizeof(data.keycode));
|
||||
PicoBluetoothBLEHID.send(&data, sizeof(data));
|
||||
|
||||
//stitch in report id
|
||||
static uint8_t report[sizeof(hid_keyboard_report_t) +1];
|
||||
report[0] = __BLEGetKeyboardReportID();
|
||||
memcpy(&report[1], (uint8_t*)&data, sizeof(hid_keyboard_report_t));
|
||||
PicoBluetoothBLEHID.send(&report, sizeof(hid_keyboard_report_t) +1);
|
||||
}
|
||||
|
||||
void KeyboardBLE_::sendConsumerReport(uint16_t key) {
|
||||
(void) key;
|
||||
// TODO - Need some BLE-specific code to send 2nd report
|
||||
uint8_t report[3];
|
||||
|
||||
report[0] = __BLEGetKeyboardReportID() + 1; //consumer report id
|
||||
report[1] = key & 0xFF;
|
||||
report[2] = (key >> 8) & 0xFF;
|
||||
PicoBluetoothBLEHID.send(&report, 3);
|
||||
}
|
||||
|
||||
KeyboardBLE_ KeyboardBLE;
|
||||
|
||||
@@ -22,19 +22,22 @@
|
||||
|
||||
#include "KeyboardBT.h"
|
||||
#include "KeyboardLayout.h"
|
||||
#include <HID_Bluetooth.h>
|
||||
#include <PicoBluetoothHID.h>
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
// Weak function override to add our descriptor to the list
|
||||
void __BTInstallKeyboard() { /* noop */ }
|
||||
|
||||
KeyboardBT_::KeyboardBT_(void) {
|
||||
// Base class clears the members we care about
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
|
||||
static const uint8_t desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(REPORT_ID + 1))};
|
||||
uint8_t *desc_keyboardBT;
|
||||
uint16_t desc_keyboardBT_length;
|
||||
|
||||
static void _hidReportCB(uint16_t cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t *report) {
|
||||
(void) cid;
|
||||
@@ -56,7 +59,10 @@ void KeyboardBT_::begin(const char *localName, const char *hidName, const uint8_
|
||||
// Required because the hid_report_type_t overlap in BTStack and TUSB
|
||||
auto *fcn = (void (*)(short unsigned int, hid_report_type_t_bt, short unsigned int, int, unsigned char*))_hidReportCB;
|
||||
hid_device_register_report_data_callback(fcn);
|
||||
PicoBluetoothHID.startHID(localName, hidName, 0x2540, 33, desc_keyboard, sizeof(desc_keyboard));
|
||||
|
||||
__SetupHIDreportmap(__BTInstallMouse, __BTInstallKeyboard, __BTInstallJoystick, false, &desc_keyboardBT_length, &desc_keyboardBT);
|
||||
|
||||
PicoBluetoothHID.startHID(localName, hidName, __BTGetCOD(), 33, desc_keyboardBT, desc_keyboardBT_length);
|
||||
}
|
||||
|
||||
void KeyboardBT_::end(void) {
|
||||
@@ -68,11 +74,11 @@ void KeyboardBT_::sendReport(KeyReport* keys) {
|
||||
data.modifier = keys->modifiers;
|
||||
data.reserved = 0;
|
||||
memcpy(data.keycode, keys->keys, sizeof(data.keycode));
|
||||
PicoBluetoothHID.send(REPORT_ID, &data, sizeof(data));
|
||||
PicoBluetoothHID.send(__BLEGetKeyboardReportID(), &data, sizeof(data));
|
||||
}
|
||||
|
||||
void KeyboardBT_::sendConsumerReport(uint16_t key) {
|
||||
PicoBluetoothHID.send(REPORT_ID + 1, &key, sizeof(key));
|
||||
PicoBluetoothHID.send(__BLEGetKeyboardReportID() + 1, &key, sizeof(key));
|
||||
}
|
||||
|
||||
KeyboardBT_ KeyboardBT;
|
||||
|
||||
@@ -268,7 +268,11 @@ bool MDNSResponder::_parseQuery(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHea
|
||||
remote = m_pUDPContext->getRemoteAddress();
|
||||
local = WiFi.localIP();
|
||||
netmask = WiFi.subnetMask();
|
||||
#if LWIP_IPV6
|
||||
if (ip4_addr_netcmp(&remote.u_addr.ip4, &local.u_addr.ip4, &netmask.u_addr.ip4))
|
||||
#else
|
||||
if (ip4_addr_netcmp(&remote, &local, &netmask))
|
||||
#endif
|
||||
// ip_info IPInfo_Local;
|
||||
// ip_info IPInfo_Remote;
|
||||
// if (((IPInfo_Remote.ip.addr = m_pUDPContext->getRemoteAddress()))
|
||||
|
||||
@@ -161,7 +161,7 @@ bool MDNSResponder::_allocUDPContext(void) {
|
||||
|
||||
if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) {
|
||||
m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL);
|
||||
m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this));
|
||||
m_pUDPContext->onRx([this] { this->_callProcess(); });
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1539,15 +1539,6 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address(
|
||||
return bResult;
|
||||
}
|
||||
|
||||
/*
|
||||
MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const)
|
||||
*/
|
||||
const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address*
|
||||
MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(
|
||||
const IPAddress& p_IPAddress) const {
|
||||
return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress));
|
||||
}
|
||||
|
||||
/*
|
||||
MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address
|
||||
*/
|
||||
|
||||
@@ -106,7 +106,6 @@ public:
|
||||
if (!_mounted) {
|
||||
return false;
|
||||
}
|
||||
info.maxOpenFiles = _maxOpenFds;
|
||||
info.blockSize = _blockSize;
|
||||
info.pageSize = _pageSize;
|
||||
info.maxOpenFiles = _maxOpenFds;
|
||||
|
||||
@@ -44,7 +44,7 @@ Mouse_::Mouse_(void) {
|
||||
void Mouse_::move(int x, int y, signed char wheel) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
tud_task();
|
||||
if (tud_hid_ready()) {
|
||||
if (__USBHIDReady()) {
|
||||
tud_hid_mouse_report(__USBGetMouseReportID(), _buttons, limit_xy(x), limit_xy(y), wheel, 0);
|
||||
}
|
||||
tud_task();
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/* Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
Benjamin Aigner <beni@asterics-foundation.org> <aignerb@technikum-wien.at> */
|
||||
/* Released to the public domain */
|
||||
|
||||
#define ENABLE_LOG_INFO
|
||||
#define ENABLE_LOG_DEBUG
|
||||
|
||||
#define USE_MOUSE
|
||||
#define USE_KBD
|
||||
#define USE_JOYSTICK
|
||||
|
||||
#ifdef USE_MOUSE
|
||||
#include <MouseBLE.h>
|
||||
#endif
|
||||
#ifdef USE_KBD
|
||||
#include <KeyboardBLE.h>
|
||||
#endif
|
||||
#ifdef USE_JOYSTICK
|
||||
#include <JoystickBLE.h>
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
//If activated nothing happens until the serial port is opened
|
||||
//while(!Serial);
|
||||
|
||||
|
||||
#if (defined(USE_KBD) || defined(USE_JOYSTICK)) && defined(USE_MOUSE)
|
||||
MouseBLE.begin("BLE Composite");
|
||||
#elif defined(USE_MOUSE)
|
||||
MouseBLE.begin("BLE Mouse");
|
||||
#endif
|
||||
|
||||
#ifdef USE_KBD
|
||||
KeyboardBLE.begin("BLE KBD");
|
||||
#endif
|
||||
|
||||
#ifdef USE_JOYSTICK
|
||||
JoystickBLE.begin("BLE JOY");
|
||||
#endif
|
||||
|
||||
Serial.printf("Press BOOTSEL to start action\n");
|
||||
#ifdef USE_MOUSE
|
||||
Serial.println("First the mouse moves");
|
||||
#endif
|
||||
#ifdef USE_KBD
|
||||
Serial.println("Then \"Hi\" will be printed");
|
||||
#endif
|
||||
#ifdef USE_JOYSTICK
|
||||
Serial.println("Then joystick buttons & axis are changed");
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (BOOTSEL) {
|
||||
#ifdef USE_MOUSE
|
||||
Serial.println("ACTION!!!");
|
||||
float r = 100;
|
||||
float ox = 0.0;
|
||||
float oy = 0.0;
|
||||
for (float a = 0; a < 2.0 * 3.14159; a += 0.1) {
|
||||
float ax = r * cos(a);
|
||||
float ay = r * sin(a);
|
||||
float dx = ax - ox;
|
||||
float dy = ay - oy;
|
||||
MouseBLE.move(dx, dy, 0);
|
||||
ox = ax;
|
||||
oy = ay;
|
||||
delay(10);
|
||||
}
|
||||
MouseBLE.setBattery(random(0, 101)); // Set between 0...100%
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
#ifdef USE_KBD
|
||||
KeyboardBLE.print("Hi");
|
||||
#endif
|
||||
|
||||
#ifdef USE_JOYSTICK
|
||||
JoystickBLE.button(1, true);
|
||||
JoystickBLE.X(0);
|
||||
JoystickBLE.send_now();
|
||||
delay(1000);
|
||||
|
||||
JoystickBLE.button(1, false);
|
||||
JoystickBLE.X(512);
|
||||
JoystickBLE.send_now();
|
||||
#endif
|
||||
|
||||
while (BOOTSEL) {
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,19 @@
|
||||
|
||||
#include "MouseBLE.h"
|
||||
#include <sdkoverride/tusb_absmouse.h>
|
||||
#include <HID_Bluetooth.h>
|
||||
#include <PicoBluetoothBLEHID.h>
|
||||
|
||||
// Weak function override to add our descriptor to the list
|
||||
void __BLEInstallMouse() { /* noop */ }
|
||||
|
||||
MouseBLE_::MouseBLE_(bool absolute) : HID_Mouse(absolute) {
|
||||
_running = false;
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
const uint8_t desc_mouse[] = {TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID))};
|
||||
const uint8_t desc_absmouse[] = {TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(REPORT_ID))};
|
||||
uint8_t *desc_mouseBLE;
|
||||
uint16_t desc_mouseBLE_length;
|
||||
|
||||
void MouseBLE_::begin(const char *localName, const char *hidName) {
|
||||
if (!localName) {
|
||||
localName = "PicoW BLE Mouse";
|
||||
@@ -37,7 +41,10 @@ void MouseBLE_::begin(const char *localName, const char *hidName) {
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, 0x03c2, _absolute ? desc_absmouse : desc_mouse, _absolute ? sizeof(desc_absmouse) : sizeof(desc_mouse));
|
||||
|
||||
__SetupHIDreportmap(__BLEInstallMouse, __BLEInstallKeyboard, __BLEInstallJoystick, _absolute, &desc_mouseBLE_length, &desc_mouseBLE);
|
||||
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, __BLEGetAppearance(), desc_mouseBLE, desc_mouseBLE_length);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
@@ -61,6 +68,8 @@ void MouseBLE_::setAbsolute(bool absolute) {
|
||||
}
|
||||
|
||||
void MouseBLE_::move(int x, int y, signed char wheel) {
|
||||
static uint8_t report[sizeof(hid_abs_mouse_report_t) +1];
|
||||
|
||||
if (!_absolute) {
|
||||
hid_mouse_report_t data;
|
||||
data.buttons = _buttons;
|
||||
@@ -68,7 +77,10 @@ void MouseBLE_::move(int x, int y, signed char wheel) {
|
||||
data.y = limit_xy(y);
|
||||
data.wheel = wheel;
|
||||
data.pan = 0;
|
||||
PicoBluetoothBLEHID.send(&data, sizeof(data));
|
||||
|
||||
report[0] = __BLEGetMouseReportID();
|
||||
memcpy(&report[1], (uint8_t*)&data, sizeof(data));
|
||||
PicoBluetoothBLEHID.send(report, sizeof(data) + 1);
|
||||
} else {
|
||||
hid_abs_mouse_report_t data;
|
||||
data.buttons = _buttons;
|
||||
@@ -76,7 +88,10 @@ void MouseBLE_::move(int x, int y, signed char wheel) {
|
||||
data.y = limit_xy(y);
|
||||
data.wheel = wheel;
|
||||
data.pan = 0;
|
||||
PicoBluetoothBLEHID.send(&data, sizeof(data));
|
||||
|
||||
report[0] = __BLEGetMouseReportID();
|
||||
memcpy(&report[1], (uint8_t*)&data, sizeof(data));
|
||||
PicoBluetoothBLEHID.send(report, sizeof(data) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,15 +21,18 @@
|
||||
|
||||
#include "MouseBT.h"
|
||||
#include <sdkoverride/tusb_absmouse.h>
|
||||
#include <HID_Bluetooth.h>
|
||||
#include <PicoBluetoothHID.h>
|
||||
|
||||
// Weak function override to add our descriptor to the list
|
||||
void __BTInstallMouse() { /* noop */ }
|
||||
|
||||
MouseBT_::MouseBT_(bool absolute) : HID_Mouse(absolute) {
|
||||
_running = false;
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
const uint8_t desc_mouse[] = {TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID))};
|
||||
const uint8_t desc_absmouse[] = {TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(REPORT_ID))};
|
||||
uint8_t *desc_mouseBT;
|
||||
uint16_t desc_mouseBT_length;
|
||||
|
||||
void MouseBT_::begin(const char *localName, const char *hidName) {
|
||||
if (!localName) {
|
||||
@@ -38,7 +41,10 @@ void MouseBT_::begin(const char *localName, const char *hidName) {
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
PicoBluetoothHID.startHID(localName, hidName, 0x2580, 33, _absolute ? desc_absmouse : desc_mouse, _absolute ? sizeof(desc_absmouse) : sizeof(desc_mouse));
|
||||
|
||||
__SetupHIDreportmap(__BTInstallMouse, __BTInstallKeyboard, __BTInstallJoystick, _absolute, &desc_mouseBT_length, &desc_mouseBT);
|
||||
|
||||
PicoBluetoothHID.startHID(localName, hidName, __BTGetCOD(), 33, desc_mouseBT, desc_mouseBT_length);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
@@ -63,7 +69,7 @@ void MouseBT_::move(int x, int y, signed char wheel) {
|
||||
data.y = limit_xy(y);
|
||||
data.wheel = wheel;
|
||||
data.pan = 0;
|
||||
PicoBluetoothHID.send(REPORT_ID, &data, sizeof(data));
|
||||
PicoBluetoothHID.send(__BTGetMouseReportID(), &data, sizeof(data));
|
||||
} else {
|
||||
hid_abs_mouse_report_t data;
|
||||
data.buttons = _buttons;
|
||||
@@ -71,7 +77,7 @@ void MouseBT_::move(int x, int y, signed char wheel) {
|
||||
data.y = limit_xy(y);
|
||||
data.wheel = wheel;
|
||||
data.pan = 0;
|
||||
PicoBluetoothHID.send(REPORT_ID, &data, sizeof(data));
|
||||
PicoBluetoothHID.send(__BTGetMouseReportID(), &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
SD card test
|
||||
|
||||
This example shows how use the utility libraries on which the'
|
||||
SD library is based in order to get info about your SD card.
|
||||
Very useful for testing a card when you're not sure whether its working or not.
|
||||
|
||||
The circuit:
|
||||
SD card attached to SPI bus as follows on RP2040:
|
||||
************ SPI0 ************
|
||||
** MISO (AKA RX) - pin 0, 4, or 16
|
||||
** MOSI (AKA TX) - pin 3, 7, or 19
|
||||
** CS - pin 1, 5, or 17
|
||||
** SCK - pin 2, 6, or 18
|
||||
************ SPI1 ************
|
||||
** MISO (AKA RX) - pin 8 or 12
|
||||
** MOSI (AKA TX) - pin 11 or 15
|
||||
** CS - pin 9 or 13
|
||||
** SCK - pin 10 or 14
|
||||
|
||||
created 28 Mar 2011
|
||||
by Limor Fried
|
||||
modified 9 Apr 2012
|
||||
by Tom Igoe
|
||||
modified 26 Dec 2023
|
||||
by Richard Teel from code provided by Renzo Mischianti
|
||||
SOURCE: https://mischianti.org/raspberry-pi-pico-and-rp2040-boards-how-to-use-sd-card-5/
|
||||
*/
|
||||
|
||||
// This are GP pins for SPI0 on the Raspberry Pi Pico board, and connect
|
||||
// to different *board* level pinouts. Check the PCB while wiring.
|
||||
// Only certain pins can be used by the SPI hardware, so if you change
|
||||
// these be sure they are legal or the program will crash.
|
||||
// See: https://datasheets.raspberrypi.com/picow/PicoW-A4-Pinout.pdf
|
||||
const int _MISO = 4; // AKA SPI RX
|
||||
const int _MOSI = 7; // AKA SPI TX
|
||||
const int _CS = 5;
|
||||
const int _SCK = 6;
|
||||
|
||||
// include the SD library:
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
|
||||
File root;
|
||||
|
||||
void setup() {
|
||||
// Open serial communications and wait for port to open:
|
||||
Serial.begin(115200);
|
||||
|
||||
while (!Serial) {
|
||||
delay(1); // wait for serial port to connect. Needed for native USB port only
|
||||
}
|
||||
|
||||
Serial.println("\nInitializing SD card...");
|
||||
|
||||
bool sdInitialized = false;
|
||||
// Ensure the SPI pinout the SD card is connected to is configured properly
|
||||
// Select the correct SPI based on _MISO pin for the RP2040
|
||||
if (_MISO == 0 || _MISO == 4 || _MISO == 16) {
|
||||
SPI.setRX(_MISO);
|
||||
SPI.setTX(_MOSI);
|
||||
SPI.setSCK(_SCK);
|
||||
sdInitialized = SD.begin(_CS);
|
||||
} else if (_MISO == 8 || _MISO == 12) {
|
||||
SPI1.setRX(_MISO);
|
||||
SPI1.setTX(_MOSI);
|
||||
SPI1.setSCK(_SCK);
|
||||
sdInitialized = SD.begin(_CS, SPI1);
|
||||
} else {
|
||||
Serial.println(F("ERROR: Unknown SPI Configuration"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sdInitialized) {
|
||||
Serial.println("initialization failed. Things to check:");
|
||||
Serial.println("* is a card inserted?");
|
||||
Serial.println("* is your wiring correct?");
|
||||
Serial.println("* did you change the chipSelect pin to match your shield or module?");
|
||||
return;
|
||||
} else {
|
||||
Serial.println("Wiring is correct and a card is present.");
|
||||
}
|
||||
// 0 - SD V1, 1 - SD V2, or 3 - SDHC/SDXC
|
||||
// print the type of card
|
||||
Serial.println();
|
||||
Serial.print("Card type: ");
|
||||
switch (SD.type()) {
|
||||
case 0:
|
||||
Serial.println("SD1");
|
||||
break;
|
||||
case 1:
|
||||
Serial.println("SD2");
|
||||
break;
|
||||
case 3:
|
||||
Serial.println("SDHC/SDXC");
|
||||
break;
|
||||
default:
|
||||
Serial.println("Unknown");
|
||||
}
|
||||
|
||||
Serial.print("Cluster size: ");
|
||||
Serial.println(SD.clusterSize());
|
||||
Serial.print("Blocks x Cluster: ");
|
||||
Serial.println(SD.blocksPerCluster());
|
||||
Serial.print("Blocks size: ");
|
||||
Serial.println(SD.blockSize());
|
||||
|
||||
Serial.print("Total Blocks: ");
|
||||
Serial.println(SD.totalBlocks());
|
||||
Serial.println();
|
||||
|
||||
Serial.print("Total Cluster: ");
|
||||
Serial.println(SD.totalClusters());
|
||||
Serial.println();
|
||||
|
||||
// print the type and size of the first FAT-type volume
|
||||
uint32_t volumesize;
|
||||
Serial.print("Volume type is: FAT");
|
||||
Serial.println(SD.fatType(), DEC);
|
||||
|
||||
volumesize = SD.totalClusters();
|
||||
volumesize *= SD.clusterSize();
|
||||
volumesize /= 1000;
|
||||
Serial.print("Volume size (Kb): ");
|
||||
Serial.println(volumesize);
|
||||
Serial.print("Volume size (Mb): ");
|
||||
volumesize /= 1024;
|
||||
Serial.println(volumesize);
|
||||
Serial.print("Volume size (Gb): ");
|
||||
Serial.println((float)volumesize / 1024.0);
|
||||
|
||||
Serial.print("Card size: ");
|
||||
Serial.println((float)SD.size() / 1000);
|
||||
|
||||
FSInfo fs_info;
|
||||
SDFS.info(fs_info);
|
||||
|
||||
Serial.print("Total bytes: ");
|
||||
Serial.println(fs_info.totalBytes);
|
||||
|
||||
Serial.print("Used bytes: ");
|
||||
Serial.println(fs_info.usedBytes);
|
||||
|
||||
root = SD.open("/");
|
||||
printDirectory(root, 0);
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
// nothing happens after setup finishes.
|
||||
}
|
||||
|
||||
void printDirectory(File dir, int numTabs) {
|
||||
while (true) {
|
||||
|
||||
File entry = dir.openNextFile();
|
||||
if (!entry) {
|
||||
// no more files
|
||||
break;
|
||||
}
|
||||
for (uint8_t i = 0; i < numTabs; i++) {
|
||||
Serial.print('\t');
|
||||
}
|
||||
Serial.print(entry.name());
|
||||
if (entry.isDirectory()) {
|
||||
Serial.println("/");
|
||||
printDirectory(entry, numTabs + 1);
|
||||
} else {
|
||||
// files have sizes, directories do not
|
||||
Serial.print("\t\t");
|
||||
Serial.print(entry.size(), DEC);
|
||||
time_t cr = entry.getCreationTime();
|
||||
time_t lw = entry.getLastWrite();
|
||||
struct tm* tmstruct = localtime(&cr);
|
||||
Serial.printf("\tCREATION: %d-%02d-%02d %02d:%02d:%02d", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
|
||||
tmstruct = localtime(&lw);
|
||||
Serial.printf("\tLAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
|
||||
}
|
||||
entry.close();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,17 @@
|
||||
directory on a SD card
|
||||
|
||||
The circuit:
|
||||
SD card attached to SPI bus as follows:
|
||||
** MISO - pin 4
|
||||
** MOSI - pin 7
|
||||
** CS - pin 5
|
||||
** SCK - pin 6
|
||||
SD card attached to SPI bus as follows on RP2040:
|
||||
************ SPI0 ************
|
||||
** MISO (AKA RX) - pin 0, 4, or 16
|
||||
** MOSI (AKA TX) - pin 3, 7, or 19
|
||||
** CS - pin 1, 5, or 17
|
||||
** SCK - pin 2, 6, or 18
|
||||
************ SPI1 ************
|
||||
** MISO (AKA RX) - pin 8 or 12
|
||||
** MOSI (AKA TX) - pin 11 or 15
|
||||
** CS - pin 9 or 13
|
||||
** SCK - pin 10 or 14
|
||||
|
||||
created Nov 2010
|
||||
by David A. Mellis
|
||||
@@ -17,6 +23,10 @@
|
||||
by Tom Igoe
|
||||
modified 2 Feb 2014
|
||||
by Scott Fitzgerald
|
||||
modified 12 Feb 2023
|
||||
by Earle F. Philhower, III
|
||||
modified 26 Dec 2023
|
||||
by Richard Teel
|
||||
|
||||
This example code is in the public domain.
|
||||
|
||||
@@ -27,8 +37,8 @@
|
||||
// Only certain pins can be used by the SPI hardware, so if you change
|
||||
// these be sure they are legal or the program will crash.
|
||||
// See: https://datasheets.raspberrypi.com/picow/PicoW-A4-Pinout.pdf
|
||||
const int _MISO = 4;
|
||||
const int _MOSI = 7;
|
||||
const int _MISO = 4; // AKA SPI RX
|
||||
const int _MOSI = 7; // AKA SPI TX
|
||||
const int _CS = 5;
|
||||
const int _SCK = 6;
|
||||
|
||||
@@ -41,14 +51,31 @@ void setup() {
|
||||
// Open serial communications and wait for port to open:
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.print("Initializing SD card...");
|
||||
while (!Serial) {
|
||||
delay(1); // wait for serial port to connect. Needed for native USB port only
|
||||
}
|
||||
|
||||
Serial.println("\nInitializing SD card...");
|
||||
|
||||
bool sdInitialized = false;
|
||||
// Ensure the SPI pinout the SD card is connected to is configured properly
|
||||
SPI.setRX(_MISO);
|
||||
SPI.setTX(_MOSI);
|
||||
SPI.setSCK(_SCK);
|
||||
// Select the correct SPI based on _MISO pin for the RP2040
|
||||
if (_MISO == 0 || _MISO == 4 || _MISO == 16) {
|
||||
SPI.setRX(_MISO);
|
||||
SPI.setTX(_MOSI);
|
||||
SPI.setSCK(_SCK);
|
||||
sdInitialized = SD.begin(_CS);
|
||||
} else if (_MISO == 8 || _MISO == 12) {
|
||||
SPI1.setRX(_MISO);
|
||||
SPI1.setTX(_MOSI);
|
||||
SPI1.setSCK(_SCK);
|
||||
sdInitialized = SD.begin(_CS, SPI1);
|
||||
} else {
|
||||
Serial.println(F("ERROR: Unknown SPI Configuration"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SD.begin(_CS)) {
|
||||
if (!sdInitialized) {
|
||||
Serial.println("initialization failed!");
|
||||
return;
|
||||
}
|
||||
@@ -68,8 +95,8 @@ void loop() {
|
||||
void printDirectory(File dir, int numTabs) {
|
||||
while (true) {
|
||||
|
||||
File entry = dir.openNextFile();
|
||||
if (! entry) {
|
||||
File entry = dir.openNextFile();
|
||||
if (!entry) {
|
||||
// no more files
|
||||
break;
|
||||
}
|
||||
@@ -86,7 +113,7 @@ void printDirectory(File dir, int numTabs) {
|
||||
Serial.print(entry.size(), DEC);
|
||||
time_t cr = entry.getCreationTime();
|
||||
time_t lw = entry.getLastWrite();
|
||||
struct tm * tmstruct = localtime(&cr);
|
||||
struct tm* tmstruct = localtime(&cr);
|
||||
Serial.printf("\tCREATION: %d-%02d-%02d %02d:%02d:%02d", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
|
||||
tmstruct = localtime(&lw);
|
||||
Serial.printf("\tLAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
|
||||
|
||||
+23
-23
@@ -31,13 +31,13 @@
|
||||
|
||||
class SDClass {
|
||||
public:
|
||||
boolean begin(uint8_t csPin, HardwareSPI &spi) {
|
||||
bool begin(uint8_t csPin, HardwareSPI &spi) {
|
||||
SDFS.setConfig(SDFSConfig(csPin, SPI_HALF_SPEED, spi));
|
||||
return (boolean)SDFS.begin();
|
||||
return SDFS.begin();
|
||||
}
|
||||
boolean begin(uint8_t csPin, uint32_t cfg = SPI_HALF_SPEED, HardwareSPI &spi = SPI) {
|
||||
bool begin(uint8_t csPin, uint32_t cfg = SPI_HALF_SPEED, HardwareSPI &spi = SPI) {
|
||||
SDFS.setConfig(SDFSConfig(csPin, cfg, spi));
|
||||
return (boolean)SDFS.begin();
|
||||
return SDFS.begin();
|
||||
}
|
||||
|
||||
void end(bool endSPI = true) {
|
||||
@@ -63,43 +63,43 @@ public:
|
||||
return open(filename.c_str(), mode);
|
||||
}
|
||||
|
||||
boolean exists(const char *filepath) {
|
||||
return (boolean)SDFS.exists(filepath);
|
||||
bool exists(const char *filepath) {
|
||||
return SDFS.exists(filepath);
|
||||
}
|
||||
|
||||
boolean exists(const String &filepath) {
|
||||
return (boolean)SDFS.exists(filepath.c_str());
|
||||
bool exists(const String &filepath) {
|
||||
return SDFS.exists(filepath.c_str());
|
||||
}
|
||||
|
||||
boolean rename(const char* filepathfrom, const char* filepathto) {
|
||||
return (boolean)SDFS.rename(filepathfrom, filepathto);
|
||||
bool rename(const char* filepathfrom, const char* filepathto) {
|
||||
return SDFS.rename(filepathfrom, filepathto);
|
||||
}
|
||||
|
||||
boolean rename(const String &filepathfrom, const String &filepathto) {
|
||||
return (boolean)rename(filepathfrom.c_str(), filepathto.c_str());
|
||||
bool rename(const String &filepathfrom, const String &filepathto) {
|
||||
return rename(filepathfrom.c_str(), filepathto.c_str());
|
||||
}
|
||||
|
||||
boolean mkdir(const char *filepath) {
|
||||
return (boolean)SDFS.mkdir(filepath);
|
||||
bool mkdir(const char *filepath) {
|
||||
return SDFS.mkdir(filepath);
|
||||
}
|
||||
|
||||
boolean mkdir(const String &filepath) {
|
||||
return (boolean)SDFS.mkdir(filepath.c_str());
|
||||
bool mkdir(const String &filepath) {
|
||||
return SDFS.mkdir(filepath.c_str());
|
||||
}
|
||||
|
||||
boolean remove(const char *filepath) {
|
||||
return (boolean)SDFS.remove(filepath);
|
||||
bool remove(const char *filepath) {
|
||||
return SDFS.remove(filepath);
|
||||
}
|
||||
|
||||
boolean remove(const String &filepath) {
|
||||
bool remove(const String &filepath) {
|
||||
return remove(filepath.c_str());
|
||||
}
|
||||
|
||||
boolean rmdir(const char *filepath) {
|
||||
return (boolean)SDFS.rmdir(filepath);
|
||||
bool rmdir(const char *filepath) {
|
||||
return SDFS.rmdir(filepath);
|
||||
}
|
||||
|
||||
boolean rmdir(const String &filepath) {
|
||||
bool rmdir(const String &filepath) {
|
||||
return rmdir(filepath.c_str());
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public:
|
||||
}
|
||||
|
||||
size_t totalBlocks() {
|
||||
return (totalClusters() / blocksPerCluster());
|
||||
return (totalClusters() * blocksPerCluster());
|
||||
}
|
||||
|
||||
size_t clusterSize() {
|
||||
|
||||
@@ -183,7 +183,7 @@ public:
|
||||
return _fs.vol()->clusterCount();
|
||||
}
|
||||
size_t totalBlocks() {
|
||||
return (totalClusters() / blocksPerCluster());
|
||||
return (totalClusters() * blocksPerCluster());
|
||||
}
|
||||
size_t clusterSize() {
|
||||
return _fs.vol()->bytesPerCluster();
|
||||
|
||||
@@ -14,7 +14,11 @@ SPI1 KEYWORD1
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
beginTransaction KEYWORD2
|
||||
endTransaction KEYWORD2
|
||||
SPISettings KEYWORD2
|
||||
transfer KEYWORD2
|
||||
transfer16 KEYWORD2
|
||||
setBitOrder KEYWORD2
|
||||
setDataMode KEYWORD2
|
||||
setClockDivider KEYWORD2
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "SPI.h"
|
||||
#include <hardware/spi.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/structs/iobank0.h>
|
||||
#include <hardware/irq.h>
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
// For Serial when selecting TinyUSB. Can't include in the core because Arduino IDE
|
||||
@@ -108,8 +110,8 @@ byte SPIClassRP2040::transfer(uint8_t data) {
|
||||
return 0;
|
||||
}
|
||||
data = (_spis.getBitOrder() == MSBFIRST) ? data : reverseByte(data);
|
||||
spi_set_format(_spi, 8, cpol(), cpha(), SPI_MSB_FIRST);
|
||||
DEBUGSPI("SPI::transfer(%02x), cpol=%d, cpha=%d\n", data, cpol(), cpha());
|
||||
hw_write_masked(&spi_get_hw(_spi)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); // Fast set to 8-bits
|
||||
spi_write_read_blocking(_spi, &data, &ret, 1);
|
||||
ret = (_spis.getBitOrder() == MSBFIRST) ? ret : reverseByte(ret);
|
||||
DEBUGSPI("SPI: read back %02x\n", ret);
|
||||
@@ -122,8 +124,8 @@ uint16_t SPIClassRP2040::transfer16(uint16_t data) {
|
||||
return 0;
|
||||
}
|
||||
data = (_spis.getBitOrder() == MSBFIRST) ? data : reverse16Bit(data);
|
||||
spi_set_format(_spi, 16, cpol(), cpha(), SPI_MSB_FIRST);
|
||||
DEBUGSPI("SPI::transfer16(%04x), cpol=%d, cpha=%d\n", data, cpol(), cpha());
|
||||
hw_write_masked(&spi_get_hw(_spi)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); // Fast set to 16-bits
|
||||
spi_write16_read16_blocking(_spi, &data, &ret, 1);
|
||||
ret = (_spis.getBitOrder() == MSBFIRST) ? ret : reverse16Bit(ret);
|
||||
DEBUGSPI("SPI: read back %02x\n", ret);
|
||||
@@ -145,14 +147,14 @@ void SPIClassRP2040::transfer(const void *txbuf, void *rxbuf, size_t count) {
|
||||
return;
|
||||
}
|
||||
|
||||
hw_write_masked(&spi_get_hw(_spi)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); // Fast set to 8-bits
|
||||
|
||||
DEBUGSPI("SPI::transfer(%p, %p, %d)\n", txbuf, rxbuf, count);
|
||||
const uint8_t *txbuff = reinterpret_cast<const uint8_t *>(txbuf);
|
||||
uint8_t *rxbuff = reinterpret_cast<uint8_t *>(rxbuf);
|
||||
|
||||
// MSB version is easy!
|
||||
if (_spis.getBitOrder() == MSBFIRST) {
|
||||
spi_set_format(_spi, 8, cpol(), cpha(), SPI_MSB_FIRST);
|
||||
|
||||
if (rxbuf == nullptr) { // transmit only!
|
||||
spi_write_blocking(_spi, txbuff, count);
|
||||
return;
|
||||
@@ -177,7 +179,8 @@ void SPIClassRP2040::transfer(const void *txbuf, void *rxbuf, size_t count) {
|
||||
}
|
||||
|
||||
void SPIClassRP2040::beginTransaction(SPISettings settings) {
|
||||
DEBUGSPI("SPI::beginTransaction(clk=%lu, bo=%s\n", _spis.getClockFreq(), (_spis.getBitOrder() == MSBFIRST) ? "MSB" : "LSB");
|
||||
noInterrupts(); // Avoid possible race conditions if IRQ comes in while main app is in middle of this
|
||||
DEBUGSPI("SPI::beginTransaction(clk=%lu, bo=%s)\n", settings.getClockFreq(), (settings.getBitOrder() == MSBFIRST) ? "MSB" : "LSB");
|
||||
if (_initted && settings == _spis) {
|
||||
DEBUGSPI("SPI: Reusing existing initted SPI\n");
|
||||
} else {
|
||||
@@ -188,12 +191,40 @@ void SPIClassRP2040::beginTransaction(SPISettings settings) {
|
||||
}
|
||||
DEBUGSPI("SPI: initting SPI\n");
|
||||
spi_init(_spi, _spis.getClockFreq());
|
||||
spi_set_format(_spi, 8, cpol(), cpha(), SPI_MSB_FIRST);
|
||||
DEBUGSPI("SPI: actual baudrate=%u\n", spi_get_baudrate(_spi));
|
||||
_initted = true;
|
||||
}
|
||||
// Disable any IRQs that are being used for SPI
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
DEBUGSPI("SPI: IRQ masks before = %08x %08x %08x %08x\n", (unsigned)irq_ctrl_base->inte[0], (unsigned)irq_ctrl_base->inte[1], (unsigned)irq_ctrl_base->inte[2], (unsigned)irq_ctrl_base->inte[3]);
|
||||
for (auto entry : _usingIRQs) {
|
||||
int gpio = entry.first;
|
||||
|
||||
// There is no gpio_get_irq, so manually twiddle the register
|
||||
io_rw_32 *en_reg = &irq_ctrl_base->inte[gpio / 8];
|
||||
uint32_t val = ((*en_reg) >> (4 * (gpio % 8))) & 0xf;
|
||||
_usingIRQs.insert_or_assign(gpio, val);
|
||||
DEBUGSPI("SPI: GPIO %d = %lu\n", gpio, val);
|
||||
(*en_reg) ^= val << (4 * (gpio % 8));
|
||||
}
|
||||
DEBUGSPI("SPI: IRQ masks after = %08x %08x %08x %08x\n", (unsigned)irq_ctrl_base->inte[0], (unsigned)irq_ctrl_base->inte[1], (unsigned)irq_ctrl_base->inte[2], (unsigned)irq_ctrl_base->inte[3]);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
void SPIClassRP2040::endTransaction(void) {
|
||||
noInterrupts(); // Avoid race condition so the GPIO IRQs won't come back until all state is restored
|
||||
DEBUGSPI("SPI::endTransaction()\n");
|
||||
// Re-enablke IRQs
|
||||
for (auto entry : _usingIRQs) {
|
||||
int gpio = entry.first;
|
||||
int mode = entry.second;
|
||||
gpio_set_irq_enabled(gpio, mode, true);
|
||||
}
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
(void) irq_ctrl_base;
|
||||
DEBUGSPI("SPI: IRQ masks = %08x %08x %08x %08x\n", (unsigned)irq_ctrl_base->inte[0], (unsigned)irq_ctrl_base->inte[1], (unsigned)irq_ctrl_base->inte[2], (unsigned)irq_ctrl_base->inte[3]);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
bool SPIClassRP2040::setRX(pin_size_t pin) {
|
||||
@@ -205,6 +236,10 @@ bool SPIClassRP2040::setRX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_RX == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.RX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
@@ -222,6 +257,10 @@ bool SPIClassRP2040::setCS(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_CS == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.CS while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
@@ -239,6 +278,10 @@ bool SPIClassRP2040::setSCK(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_SCK == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.SCK while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
@@ -256,6 +299,10 @@ bool SPIClassRP2040::setTX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_TX == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.TX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
@@ -275,6 +322,7 @@ void SPIClassRP2040::begin(bool hwCS) {
|
||||
gpio_set_function(_TX, GPIO_FUNC_SPI);
|
||||
// Give a default config in case user doesn't use beginTransaction
|
||||
beginTransaction(_spis);
|
||||
endTransaction();
|
||||
}
|
||||
|
||||
void SPIClassRP2040::end() {
|
||||
@@ -295,11 +343,13 @@ void SPIClassRP2040::end() {
|
||||
void SPIClassRP2040::setBitOrder(BitOrder order) {
|
||||
_spis = SPISettings(_spis.getClockFreq(), order, _spis.getDataMode());
|
||||
beginTransaction(_spis);
|
||||
endTransaction();
|
||||
}
|
||||
|
||||
void SPIClassRP2040::setDataMode(uint8_t uc_mode) {
|
||||
_spis = SPISettings(_spis.getClockFreq(), _spis.getBitOrder(), uc_mode);
|
||||
beginTransaction(_spis);
|
||||
endTransaction();
|
||||
}
|
||||
|
||||
void SPIClassRP2040::setClockDivider(uint8_t uc_div) {
|
||||
@@ -313,5 +363,10 @@ void SPIClassRP2040::setClockDivider(uint8_t uc_div) {
|
||||
#define __SPI1_DEVICE spi1
|
||||
#endif
|
||||
|
||||
#ifdef PIN_SPI0_MISO
|
||||
SPIClassRP2040 SPI(__SPI0_DEVICE, PIN_SPI0_MISO, PIN_SPI0_SS, PIN_SPI0_SCK, PIN_SPI0_MOSI);
|
||||
#endif
|
||||
|
||||
#ifdef PIN_SPI1_MISO
|
||||
SPIClassRP2040 SPI1(__SPI1_DEVICE, PIN_SPI1_MISO, PIN_SPI1_SS, PIN_SPI1_SCK, PIN_SPI1_MOSI);
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <Arduino.h>
|
||||
#include <api/HardwareSPI.h>
|
||||
#include <hardware/spi.h>
|
||||
#include <map>
|
||||
|
||||
class SPIClassRP2040 : public arduino::HardwareSPI {
|
||||
public:
|
||||
@@ -60,12 +61,12 @@ public:
|
||||
void setDataMode(uint8_t uc_mode) __attribute__((deprecated));
|
||||
void setClockDivider(uint8_t uc_div) __attribute__((deprecated));
|
||||
|
||||
// Unimplemented
|
||||
// List of GPIO IRQs to disable during a transaction
|
||||
virtual void usingInterrupt(int interruptNumber) override {
|
||||
(void) interruptNumber;
|
||||
_usingIRQs.insert({interruptNumber, 0});
|
||||
}
|
||||
virtual void notUsingInterrupt(int interruptNumber) override {
|
||||
(void) interruptNumber;
|
||||
_usingIRQs.erase(interruptNumber);
|
||||
}
|
||||
virtual void attachInterrupt() override { /* noop */ }
|
||||
virtual void detachInterrupt() override { /* noop */ }
|
||||
@@ -83,6 +84,8 @@ private:
|
||||
bool _hwCS;
|
||||
bool _running; // SPI port active
|
||||
bool _initted; // Transaction begun
|
||||
|
||||
std::map<int, int> _usingIRQs;
|
||||
};
|
||||
|
||||
extern SPIClassRP2040 SPI;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Shows how to use SPISlave on a single device.
|
||||
// Core0 runs as an SPI master and initiates a transmission to the slave
|
||||
// Core1 runs the SPI Slave mode and provides a unique reply to messages from the master
|
||||
//
|
||||
// Released to the public domain 2023 by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
#include <SPI.h>
|
||||
#include <SPISlave.h>
|
||||
|
||||
// Wiring:
|
||||
// Master RX GP0 <-> GP11 Slave TX
|
||||
// Master CS GP1 <-> GP9 Slave CS
|
||||
// Master CK GP2 <-> GP10 Slave CK
|
||||
// Master TX GP3 <-> GP8 Slave RX
|
||||
|
||||
SPISettings spisettings(1000000, MSBFIRST, SPI_MODE0);
|
||||
|
||||
// Core 0 will be SPI master
|
||||
void setup() {
|
||||
SPI.setRX(0);
|
||||
SPI.setCS(1);
|
||||
SPI.setSCK(2);
|
||||
SPI.setTX(3);
|
||||
SPI.begin(true);
|
||||
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
int transmits = 0;
|
||||
void loop() {
|
||||
char msg[42];
|
||||
memset(msg, 0, sizeof(msg));
|
||||
sprintf(msg, "What's up? This is transmission %d", transmits);
|
||||
Serial.printf("\n\nM-SEND: '%s'\n", msg);
|
||||
SPI.beginTransaction(spisettings);
|
||||
SPI.transfer(msg, sizeof(msg));
|
||||
SPI.endTransaction();
|
||||
Serial.printf("M-RECV: '%s'\n", msg);
|
||||
transmits++;
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
// Core 1 will be SPI slave
|
||||
|
||||
volatile bool recvBuffReady = false;
|
||||
char recvBuff[42] = "";
|
||||
int recvIdx = 0;
|
||||
void recvCallback(uint8_t *data, size_t len) {
|
||||
memcpy(recvBuff + recvIdx, data, len);
|
||||
recvIdx += len;
|
||||
if (recvIdx == sizeof(recvBuff)) {
|
||||
recvBuffReady = true;
|
||||
recvIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sendcbs = 0;
|
||||
// Note that the buffer needs to be long lived, the SPISlave doesn't copy it. So no local stack variables, only globals or heap(malloc/new) allocations.
|
||||
char sendBuff[42];
|
||||
void sentCallback() {
|
||||
memset(sendBuff, 0, sizeof(sendBuff));
|
||||
sprintf(sendBuff, "Slave to Master Xmission %d", sendcbs++);
|
||||
SPISlave1.setData((uint8_t*)sendBuff, sizeof(sendBuff));
|
||||
}
|
||||
|
||||
// Note that we use SPISlave1 here **not** because we're running on
|
||||
// Core 1, but because SPI0 is being used already. You can use
|
||||
// SPISlave or SPISlave1 on any core.
|
||||
void setup1() {
|
||||
SPISlave1.setRX(8);
|
||||
SPISlave1.setCS(9);
|
||||
SPISlave1.setSCK(10);
|
||||
SPISlave1.setTX(11);
|
||||
// Ensure we start with something to send...
|
||||
sentCallback();
|
||||
// Hook our callbacks into the slave
|
||||
SPISlave1.onDataRecv(recvCallback);
|
||||
SPISlave1.onDataSent(sentCallback);
|
||||
SPISlave1.begin(spisettings);
|
||||
delay(3000);
|
||||
Serial.println("S-INFO: SPISlave started");
|
||||
}
|
||||
|
||||
void loop1() {
|
||||
if (recvBuffReady) {
|
||||
Serial.printf("S-RECV: '%s'\n", recvBuff);
|
||||
recvBuffReady = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map SPI
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
SPISlave KEYWORD1
|
||||
SPISlave1 KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
SPISettings KEYWORD2
|
||||
setRX KEYWORD2
|
||||
setTX KEYWORD2
|
||||
setSCK KEYWORD2
|
||||
setCS KEYWORD2
|
||||
setData KEYWORD2
|
||||
onDataRecv KEYWORD2
|
||||
onDataSent KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
SPI_CLOCK_DIV4 LITERAL1
|
||||
SPI_CLOCK_DIV16 LITERAL1
|
||||
SPI_CLOCK_DIV64 LITERAL1
|
||||
SPI_CLOCK_DIV128 LITERAL1
|
||||
SPI_CLOCK_DIV2 LITERAL1
|
||||
SPI_CLOCK_DIV8 LITERAL1
|
||||
SPI_CLOCK_DIV32 LITERAL1
|
||||
SPI_CLOCK_DIV64 LITERAL1
|
||||
SPI_MODE0 LITERAL1
|
||||
SPI_MODE1 LITERAL1
|
||||
SPI_MODE2 LITERAL1
|
||||
SPI_MODE3 LITERAL1
|
||||
@@ -0,0 +1,10 @@
|
||||
name=SPISlave
|
||||
version=1.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Enables the communication as a slave SPI devices.
|
||||
paragraph=
|
||||
category=Signal Input/Output
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
SPI Slave library for the Raspberry Pi Pico RP2040
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "SPISlave.h"
|
||||
#include <hardware/spi.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/structs/iobank0.h>
|
||||
#include <hardware/irq.h>
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
// For Serial when selecting TinyUSB. Can't include in the core because Arduino IDE
|
||||
// will not link in libraries called from the core. Instead, add the header to all
|
||||
// the standard libraries in the hope it will still catch some user cases where they
|
||||
// use these libraries.
|
||||
// See https://github.com/earlephilhower/arduino-pico/issues/167#issuecomment-848622174
|
||||
#include <Adafruit_TinyUSB.h>
|
||||
#endif
|
||||
|
||||
SPISlaveClass::SPISlaveClass(spi_inst_t *spi, pin_size_t rx, pin_size_t cs, pin_size_t sck, pin_size_t tx) {
|
||||
_spi = spi;
|
||||
_running = false;
|
||||
_initted = false;
|
||||
_spis = SPISettings();
|
||||
_RX = rx;
|
||||
_TX = tx;
|
||||
_SCK = sck;
|
||||
_CS = cs;
|
||||
_recvCB = nullptr;
|
||||
_sentCB = nullptr;
|
||||
_dataOut = nullptr;
|
||||
_dataLeft = 0;
|
||||
}
|
||||
|
||||
inline spi_cpol_t SPISlaveClass::cpol(SPISettings _spis) {
|
||||
switch (_spis.getDataMode()) {
|
||||
case SPI_MODE0:
|
||||
return SPI_CPOL_0;
|
||||
case SPI_MODE1:
|
||||
return SPI_CPOL_0;
|
||||
case SPI_MODE2:
|
||||
return SPI_CPOL_1;
|
||||
case SPI_MODE3:
|
||||
return SPI_CPOL_1;
|
||||
}
|
||||
// Error
|
||||
return SPI_CPOL_0;
|
||||
}
|
||||
|
||||
inline spi_cpha_t SPISlaveClass::cpha(SPISettings _spis) {
|
||||
switch (_spis.getDataMode()) {
|
||||
case SPI_MODE0:
|
||||
return SPI_CPHA_0;
|
||||
case SPI_MODE1:
|
||||
return SPI_CPHA_1;
|
||||
case SPI_MODE2:
|
||||
return SPI_CPHA_0;
|
||||
case SPI_MODE3:
|
||||
return SPI_CPHA_1;
|
||||
}
|
||||
// Error
|
||||
return SPI_CPHA_0;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setRX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({0, 4, 16, 20}) /* SPI0 */,
|
||||
__bitset({8, 12, 24, 28}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_RX = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_RX == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.RX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.RX to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setCS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({1, 5, 17, 21}) /* SPI0 */,
|
||||
__bitset({9, 13, 25, 29}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_CS = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_CS == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.CS while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.CS to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setSCK(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({2, 6, 18, 22}) /* SPI0 */,
|
||||
__bitset({10, 14, 26}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_SCK = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_SCK == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.SCK while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.SCK to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setTX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({3, 7, 19, 23}) /* SPI0 */,
|
||||
__bitset({11, 15, 27}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_TX = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_TX == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.TX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.TX to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SPISlaveClass::_handleIRQ() {
|
||||
// Attempt to read out all RX FIFO datra and return to callback
|
||||
uint8_t buff[8]; // SPI FIFO 8 deep max
|
||||
int cnt;
|
||||
for (cnt = 0; (cnt < 8) && spi_is_readable(_spi); cnt++) {
|
||||
buff[cnt] = spi_get_hw(_spi)->dr;
|
||||
}
|
||||
if (cnt && _recvCB) {
|
||||
_recvCB(buff, cnt);
|
||||
}
|
||||
// Attempt to send as many ytes to the TX FIFO as we have/are free
|
||||
while (spi_is_writable(_spi)) {
|
||||
for (; _dataLeft && spi_is_writable(_spi); _dataLeft--) {
|
||||
spi_get_hw(_spi)->dr = *(_dataOut++);
|
||||
}
|
||||
if (!_dataLeft && _sentCB) {
|
||||
_sentCB();
|
||||
}
|
||||
}
|
||||
// Disable the TX FIFO IRQ if there is still no data to send or we'd always be stuck in an IRQ
|
||||
// Will be re-enabled once user does a setData
|
||||
if (!_dataLeft) {
|
||||
spi_get_hw(_spi)->imsc = 2 | 4; // RTIM + RXIM
|
||||
}
|
||||
}
|
||||
|
||||
void SPISlaveClass::_irq0() {
|
||||
SPISlave._handleIRQ();
|
||||
}
|
||||
|
||||
void SPISlaveClass::_irq1() {
|
||||
SPISlave1._handleIRQ();
|
||||
}
|
||||
|
||||
void SPISlaveClass::setData(const uint8_t *data, size_t len) {
|
||||
_dataOut = data;
|
||||
_dataLeft = len;
|
||||
if (_initted) {
|
||||
spi_get_hw(_spi)->imsc = 2 | 4 | 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SPISlaveClass::begin(SPISettings spis) {
|
||||
DEBUGSPI("SPISlave::begin(%d), rx=%d, cs=%d, sck=%d, tx=%d\n", hwCS, _RX, _CS, _SCK, _TX);
|
||||
gpio_set_function(_RX, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_CS, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_SCK, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_TX, GPIO_FUNC_SPI);
|
||||
if (_initted) {
|
||||
DEBUGSPI("SPISlave: deinitting currently active SPI\n");
|
||||
spi_deinit(_spi);
|
||||
}
|
||||
DEBUGSPI("SPISlave: initting SPI\n");
|
||||
spi_init(_spi, _spis.getClockFreq());
|
||||
DEBUGSPI("SPISlave: actual baudrate=%u\n", spi_get_baudrate(_spi));
|
||||
spi_set_slave(_spi, true);
|
||||
spi_set_format(_spi, 8, cpol(spis), cpha(spis), SPI_MSB_FIRST);
|
||||
|
||||
// Install our IRQ handler
|
||||
if (_spi == spi0) {
|
||||
irq_set_exclusive_handler(SPI0_IRQ, _irq0);
|
||||
} else {
|
||||
irq_set_exclusive_handler(SPI1_IRQ, _irq1);
|
||||
}
|
||||
|
||||
// Set to get IRQs on transmit and receive
|
||||
spi_get_hw(_spi)->imsc = 2 | 4 | 8 ; // RTIM + RXIM + TXIM (not RORIM)
|
||||
_initted = true;
|
||||
irq_set_enabled(_spi == spi0 ? SPI0_IRQ : SPI1_IRQ, true);
|
||||
}
|
||||
|
||||
void SPISlaveClass::end() {
|
||||
DEBUGSPI("SPISlave::end()\n");
|
||||
if (_initted) {
|
||||
DEBUGSPI("SPISlave: deinitting currently active SPI\n");
|
||||
if (_spi == spi0) {
|
||||
irq_remove_handler(SPI0_IRQ, _irq0);
|
||||
} else {
|
||||
irq_remove_handler(SPI1_IRQ, _irq1);
|
||||
}
|
||||
spi_deinit(_spi);
|
||||
_initted = false;
|
||||
}
|
||||
gpio_set_function(_RX, GPIO_FUNC_SIO);
|
||||
gpio_set_function(_CS, GPIO_FUNC_SIO);
|
||||
gpio_set_function(_SCK, GPIO_FUNC_SIO);
|
||||
gpio_set_function(_TX, GPIO_FUNC_SIO);
|
||||
}
|
||||
|
||||
#ifndef __SPI0_DEVICE
|
||||
#define __SPI0_DEVICE spi0
|
||||
#endif
|
||||
#ifndef __SPI1_DEVICE
|
||||
#define __SPI1_DEVICE spi1
|
||||
#endif
|
||||
|
||||
SPISlaveClass SPISlave(__SPI0_DEVICE, PIN_SPI0_MISO, PIN_SPI0_SS, PIN_SPI0_SCK, PIN_SPI0_MOSI);
|
||||
SPISlaveClass SPISlave1(__SPI1_DEVICE, PIN_SPI1_MISO, PIN_SPI1_SS, PIN_SPI1_SCK, PIN_SPI1_MOSI);
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
SPI Slave library for the Raspberry Pi Pico RP2040
|
||||
|
||||
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 <Arduino.h>
|
||||
#include <api/HardwareSPI.h>
|
||||
#include <hardware/spi.h>
|
||||
#include <functional>
|
||||
|
||||
typedef std::function<void(uint8_t *data, size_t len)> SPISlaveRecvHandler;
|
||||
typedef std::function<void(void)> SPISlaveSentHandler;
|
||||
|
||||
class SPISlaveClass {
|
||||
public:
|
||||
SPISlaveClass(spi_inst_t *spi, pin_size_t rx, pin_size_t cs, pin_size_t sck, pin_size_t tx);
|
||||
|
||||
// Assign pins, call before begin()
|
||||
bool setRX(pin_size_t pin);
|
||||
bool setCS(pin_size_t pin);
|
||||
bool setSCK(pin_size_t pin);
|
||||
bool setTX(pin_size_t pin);
|
||||
|
||||
void begin(SPISettings spis);
|
||||
void end();
|
||||
|
||||
// May be set before the initial begin(). If not, then as soon as
|
||||
// begin() is called you will get a callback.
|
||||
void setData(const uint8_t * data, size_t len);
|
||||
inline void setData(const char * data) {
|
||||
setData((const uint8_t *)data, strlen(data));
|
||||
}
|
||||
|
||||
// NOTE: These two callbacks are called from an IRQ context
|
||||
// NOTE: They should also be called before begin()
|
||||
|
||||
// Called when bytes are available to be read from the SPI slave reception buffer
|
||||
void onDataRecv(SPISlaveRecvHandler cb) {
|
||||
_recvCB = cb;
|
||||
}
|
||||
// Called when there is space in the SPI transmission buffer
|
||||
void onDataSent(SPISlaveSentHandler cb) {
|
||||
_sentCB = cb;
|
||||
}
|
||||
|
||||
private:
|
||||
// Naked IRQ callbacks, will thunk to real object ones below
|
||||
static void _irq0();
|
||||
static void _irq1();
|
||||
|
||||
public:
|
||||
void _handleIRQ();
|
||||
|
||||
private:
|
||||
spi_cpol_t cpol(SPISettings _spis);
|
||||
spi_cpha_t cpha(SPISettings _spis);
|
||||
uint8_t reverseByte(uint8_t b);
|
||||
uint16_t reverse16Bit(uint16_t w);
|
||||
void adjustBuffer(const void *s, void *d, size_t cnt, bool by16);
|
||||
|
||||
spi_inst_t *_spi;
|
||||
SPISettings _spis;
|
||||
pin_size_t _RX, _TX, _SCK, _CS;
|
||||
bool _running; // SPI port active
|
||||
bool _initted; // Transaction begun
|
||||
|
||||
SPISlaveRecvHandler _recvCB;
|
||||
SPISlaveSentHandler _sentCB;
|
||||
|
||||
// The current data to be pumped into the transmit FIFO
|
||||
const uint8_t *_dataOut;
|
||||
size_t _dataLeft;
|
||||
|
||||
// Received data will be returned in small chunks directly from a local buffer in _handleIRQ()
|
||||
};
|
||||
|
||||
extern SPISlaveClass SPISlave;
|
||||
extern SPISlaveClass SPISlave1;
|
||||
@@ -28,7 +28,17 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Example works with either Wired or WiFi Ethernet, define one of these values to 1, other to 0
|
||||
#define USE_WIFI 1
|
||||
#define USE_WIRED 0
|
||||
|
||||
#if USE_WIFI
|
||||
#include <WiFi.h>
|
||||
#elif USE_WIRED
|
||||
#include <W5500lwIP.h> // Or W5100lwIP.h or ENC28J60lwIP.h
|
||||
Wiznet5500lwIP eth(1 /* chip select */); // or Wiznet5100lwIP or ENC28J60lwIP
|
||||
#endif
|
||||
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
@@ -58,13 +68,13 @@ void handleRoot() {
|
||||
temp.printf("<html>\
|
||||
<head>\
|
||||
<meta http-equiv='refresh' content='5'/>\
|
||||
<title>Pico-W Demo</title>\
|
||||
<title>" BOARD_NAME " Demo</title>\
|
||||
<style>\
|
||||
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
|
||||
</style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<h1>Hello from the Pico W!</h1>\
|
||||
<h1>Hello from the " BOARD_NAME "!</h1>\
|
||||
<p>Uptime: %02d:%02d:%02d</p>\
|
||||
<p>Free Memory: %d</p>\
|
||||
<p>Page Count: %d</p>\
|
||||
@@ -117,6 +127,8 @@ void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
Serial.begin(115200);
|
||||
|
||||
#if USE_WIFI
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
@@ -130,8 +142,32 @@ void setup(void) {
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
#elif USE_WIRED
|
||||
// Set up SPI pinout to match your HW
|
||||
SPI.setRX(0);
|
||||
SPI.setCS(1);
|
||||
SPI.setSCK(2);
|
||||
SPI.setTX(3);
|
||||
|
||||
// Start the Ethernet port
|
||||
if (!eth.begin()) {
|
||||
Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
|
||||
while (1) {
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for connection
|
||||
while (eth.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(eth.localIP());
|
||||
#endif
|
||||
|
||||
if (MDNS.begin("picow")) {
|
||||
Serial.println("MDNS responder started");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user