Compare commits

...
103 Commits
Author SHA1 Message Date
Earle F. Philhower, III b2b4b2d71d Update version 2023-05-23 17:57:38 -07:00
Earle F. Philhower, III e93bd14ef5 Generate warnings if Pico STDIO init called (#1467)
Warn the user that the Pico SDK STDIO calls (stdio_init_all, stdio_usb_init,
stdio_uart_init) are not supported or needed at compile time.  See multiple
issues #1433 #1347 #1273 #1251 and others.
2023-05-23 17:28:57 -07:00
Earle F. Philhower, III 652f9f9eda Fix FreeRTOS CoreMutex shim to handle ISRs (#1442)
* Fix FreeRTOS CoreMutex shim to handle ISRs

Automatically check, when in FreeRTOS, if we're in an ISR and
if so call the correct mutex grab.

Thanks to @caveman99 for finding and proposing a solution!

Fixes #1441

* Fix the CoreMutex destructor, too
2023-05-23 10:12:52 -07:00
Earle F. Philhower, III cac9eb0cd7 BREAKING: Swap Wire1 and Wire in Adafruit Feather (#1468)
Fixes #1465

The Adafruit Feather came onboard before the Wire swapping was supported
in the core, so it's backwards from the real definition.  Now that swapping is
supported, fix it to match the rest of the boards.
2023-05-23 09:57:28 -07:00
LinusHeu 3b4fb295ea Fix I2S::write(const uint8_t *buffer, size_t size) (#1461)
According to this: https://github.com/earlephilhower/arduino-pico/discussions/1450
2023-05-20 11:33:50 -07:00
Jean-Luc Béchennec a1ae61b5b1 BT Serial connection docs for Mac (#1459) 2023-05-20 10:56:06 -07:00
355abd8c57 Add setInverted() options to SerialPIO (#1451)
Call `SerialPIO::setInverted(txinv, rxinv)` before `SerialPIO::begin()` to enable.

---------

Co-authored-by: Mykle Hansen <mykle@mykle.com>
Co-authored-by: Earle F. Philhower, III <earlephilhower@yahoo.com>
2023-05-19 16:30:26 -07:00
Earle F. Philhower, III b57ac66815 Cleanup legacy includes (#1452) 2023-05-16 23:47:17 -07:00
Earle F. Philhower, III a851a928d2 Merge latest FreeRTOS/SMP upstream branch (#1449)
Update to head of upstream FreeRTOS/SMP branch.  Very minor changes.

Remove the custom getreent implementation, use the one that was defined
in the upstream FreeRTOS/smp branch (callback related vs. static vars).
2023-05-16 12:13:51 -07:00
Earle F. Philhower, III 16c2e8e454 Clean up FreeRTOS, remove core freeze hacks (#1448) 2023-05-15 19:52:38 -07:00
Earle F. Philhower, III ae908c8e9b Use real FreeRTOS tasks to idle core during flash (#1444)
Fixes #1439

Use a real FreeRTOS task, at the highest priority, to idle the other core
while doing flash accesses.

The USB stack seems to have some timing dependent bits which get broken
if FreeRTOS switches out the current task when it's running.  Avoid any
issue by disabling preemption on the core for all tud_task calls.

The PendSV handler disable flag can live completely inside the FreeRTOS
variant port, so remove any reference to it in the main core.

Tested using Multicode-FreeRTOS, #1402 and #1441

The USB FIFO interrupts were still being serviced even when the core was
frozen, leading to crashes.  Explicitly shut off IRQs on both the victim
and the initiator core when freezing.

Removed the need for hack __holdUpPendSV flag
2023-05-15 18:10:30 -07:00
Earle F. Philhower, III 723c81470a Update contrib.rst 2023-05-15 10:09:30 -07:00
Alessandro Ranellucci a7905fba3e Add Arduino CLI installation instructions (#1447) 2023-05-15 09:29:12 -07:00
Earle F. Philhower, III 6e52b72523 Update macro for detecting this core in contributing docs (#1436) 2023-05-11 09:45:34 -07:00
git2212 ef4ec4185a Add dummy Serial implementation when NO_USB defined (#1438)
Fixes #1434
2023-05-11 09:45:01 -07:00
Earle F. Philhower, III 3dee0a276b Update version 2023-05-05 09:00:07 -07:00
Maximilian Gerhardt e95248a787 Support and Document pico-debug in PlatformIO (#1427) 2023-05-05 07:32:41 -07:00
Earle F. Philhower, III 414ff23141 Remove circular dependency on WiFi/LWIP_Ethernet (#1415)
Fixes #1408

The wl_* types are required in multiple libraries which in turn seem
to have a circular dependency that Platform.IO has issues with in certain
modes.

Avoid the issue by moving the common headers into the core directories
so they will be accessible by all libs.

Move StackThunk to core directory, too
2023-05-04 19:54:24 -07:00
Paint Your Dragon 2c0ce6f416 Add Feathers: CAN and Prop-Maker (#1421) 2023-05-02 15:41:05 -07:00
Earle F. Philhower, III 89fe754a9f Fix USB Mouse/Joystick HID report ID (#1418)
Fixes #1410

The USB Keyboard now has 2 reports (keyboard + consumer control), so when both
Keyboard and Mouse libraries are included, the Mouse must be report 3, not 2.

Update the Mouse and Joystick report IDs appropriately.
2023-05-01 21:14:11 -07:00
Earle F. Philhower, III 01ee673dc2 Protect the HW random generation from FreeRTOS (#1395)
Fixes #1394

The Pico_Rand SDK calls gather bits from the HW ROSC at precise intervals.
If there is jitter in the sleep_until() call then the ROSC bit collection
will always think it's failed to acquire the right bit and retry infintitely.

Avoid by wrapping the HW random number calls and the sleep_until() routine.
Only when in FreeRTOS set a flag to silently make sleep_until() into a
busy wait loop while in a random number generation step.  When not in the
random code, do the normal sleep_until call.
2023-05-01 19:42:21 -07:00
Earle F. Philhower, III 5a949443a5 Fix USB crashes in FreeRTOS (#1419)
Fixes #1402

The global USB mutex is auto-shadowed with a FreeRTOS semaphore while in
FreeRTOS mode.  Unfortunately, while the core was using the proper
FreeRTOS semaphore to lock access to the USB port, the actual FreeRTOS
USB task was using the naked Pico SDK mutex, leading to cases where it
could acquire the mutex even though some other FreeRTOS task actually
owned the shadowed mutex.

Properly lock the shadowed Semaphore, not mutex_t, in the FreeRTOS
USB periodic task.
2023-05-01 19:34:46 -07:00
Maximilian Gerhardt a916695155 Add PICO_FLASH_SIZE_BYTES to PIO (#1416) 2023-05-01 11:48:28 -07:00
Earle F. Philhower, III 611547ee97 Add PICO_FLASH_SIZE_BYTES define (#1414)
Matches the flash size defined in the menus.

Fixes #1399
2023-05-01 11:02:01 -07:00
Earle F. Philhower, III ff9f228e1b FreeRTOS compilation fix when no LED present (#1413)
Fixes #1412
2023-05-01 10:46:23 -07:00
Earle F. Philhower, III f33dfd2313 Don't delete first cert, taken from ESP8266 repo (#1392)
See https://github.com/esp8266/Arduino/pull/8907
2023-04-22 15:34:59 -07:00
Earle F. Philhower, III 7ef00c8761 Update to Adafruit_TinyUSB 2.1.0 (#1387)
Fixes #1382
2023-04-21 10:20:14 -07:00
Maximilian Gerhardt 437d03583a Sync macros used in PlatformIO and platform.txt (#1386) 2023-04-21 09:57:50 -07:00
Wvirgil123 0322a6871c Add board Seeed Indicator RP2040 (#1375) 2023-04-21 09:48:38 -07:00
Maximilian Gerhardt 4f643d27d9 Enable BlackMagicProbe for PlatformIO (#1378) 2023-04-15 12:35:45 -07:00
Earle F. Philhower, III 3dcb4bbf8b Clear compile errors if BT needed but not on (#1371)
Fixes #1370

Adds a simple helper assertion to tell the user how to enable BT if it's
not enabled, instead of some odd compilation warnings about undefined
functions.
2023-04-12 08:21:24 -07:00
Paint Your Dragon 59981aed7f Rename USB HOST pins (#1367)
Change “N” to “M” and add “HOST” in the USB pins. Only two pins are actually affected; diff appears large to maintain the pleasant aligned-columns format.
2023-04-10 10:53:28 -07:00
Earle F. Philhower, III 07781e9cf5 Rename BTstack lib to BTstackLib (#1360)
Fixes #1356

Mac and Windows have case-insensitive filesystems, so the will find
the internal (all-lowercase) "btstack.h" and not the library's "BTstack.h",
causing compilation errors.

Rename the library and header to avoid the issue.
2023-04-06 17:09:07 -07:00
Earle F. Philhower, III c276a36c9b Update version 2023-04-05 13:38:25 -07:00
Earle F. Philhower, III ff0d794c50 Add MacOS Picotool upload help (#1355)
Fixes #1344
2023-04-04 14:24:15 -07:00
Maximilian Gerhardt 369c878711 Add BTC and BLE docs to PlatformIO (#1354) 2023-04-04 14:20:39 -07:00
Pontus Oldberg 5bf570c27d Add support for Challenger UWB board, fix some defines (#1351) 2023-04-03 08:58:26 -07:00
Earle F. Philhower, III 0963611fc7 Add absolute mouse support (#1342)
Fixes #1338

To be revisited when TinyUSB native support is added and picked up in the SDK
2023-03-30 16:09:04 -07:00
Earle F. Philhower, III 00644db9b1 Update to fixed HID_Mouse submodule (#1340) 2023-03-30 12:24:13 -07:00
Paint Your Dragon f8ba72aa15 Add Feather ThinkINK and USB Host (#1334) 2023-03-29 11:18:45 -07:00
Paint Your Dragon 371b2c87b1 Add Feather RP2040 RFM, fix pins in SCORPIO, DVI board defs (#1333) 2023-03-28 12:58:49 -07:00
Earle F. Philhower, III 387655606e Update README.md 2023-03-21 07:17:40 -07:00
Earle F. Philhower, III e1b881a688 Add Win32 Long Path info to P.IO docs (#1321) 2023-03-20 15:31:39 -07:00
Mark Hildreth 869ea4c16b Add flatpak-specific IDE installation instructions (#1317) 2023-03-19 11:43:47 -07:00
Earle F. Philhower, III 71238ccd74 Clean up CoreMutex and __isFreeRTOS definition (#1312)
See #1311 for more info.  __isFreeRTOS is C++ linkage and only used
in the core proper (all C++).
2023-03-16 14:42:28 -07:00
Earle F. Philhower, III 1dfd9ebac7 Add HID consumer (aka media) keys for USB, BT (#1309)
Allow sending thigns like KEY_MUTE or KEY_SCAN_NEXT from the USB and
Bluetooth Classic keyboard libraries.

BLE requires some add'l magic, not yet discovered.

Pull in latest upstream Keyboard library changes
2023-03-15 19:10:31 -07:00
Earle F. Philhower, III 3dbe5cf930 Add I2S::swapClocks() for boards w/reversed pins (#1298)
Allow users of boards like the Pico-Audiom where the LRCLK comes
before the BCLK pin, to swap the BCLK/LRCLK of the I2S interface.

Fixes #1287
2023-03-15 15:23:56 -07:00
Earle F. Philhower, III 8e8fea4b7d Add (unsupported) BTstack Arduino library (#1305)
The BTstack driver includes a basic Arduino library as part of the ports
directory.
2023-03-15 13:02:24 -07:00
Earle F. Philhower, III ebe5bfbc07 Increase UDP PCBs to avoid DNS OOM error (#1304)
Fixes #1285 (or at least works well enough for now)
2023-03-14 07:28:24 -07:00
Limor "Ladyada" Fried d67930eeef Feather DVI peripheral fix (#1301)
* make the main SPI SPI (even tho its on SPI1)

* fix Wire to default pins
2023-03-13 17:07:55 -07:00
Earle F. Philhower, III 084d5b0b87 Add 4 and 8 MB VCC_GND boards via Flash Size menu (#1297)
Thanks to @e-tinkers for the initial PR!
2023-03-11 09:52:33 -08:00
Earle F. Philhower, III 8dc44b5e0d Astyle format variants format (#1295) 2023-03-10 13:00:04 -08:00
Earle F. Philhower, III 7851dc8cb7 Update version 2023-03-10 09:08:28 -08:00
Philipp Molitor 75c553c391 Fix pin assigment for Waveshare RP2040 1.28 LCD PIN_BAT_ADC (#1292) 2023-03-10 04:31:33 -08:00
Earle F. Philhower, III 6bff270402 Manually add MDNS multicast Ethernet MACs to CYW43 (#1290)
SDK 1.5 changed the behavior of the underlying CYW43 blob, and it seems
to block MDNS multicast by default.  Manually add back the Ethernet MACs
used for MDNS multicast in IPV4 and IPV6.

Fixes #1267
2023-03-09 14:15:14 -08:00
nanoparticle 72f1e53106 Add board definition for Neko Systems BL2040 Mini (#1258) 2023-03-09 10:30:54 -08:00
Earle F. Philhower, III e6c7b97b5e Adjust LWIP intf to avoid hangs/crashes under load (#1286)
It seems possible now for TCP connection _pcbs to disappear while being
processed, due to the new async context configuration.  This would cause
LWIP to panic when a NULL pcb was passed in.

Check for and avoid passing in null PCBs in the ClientContext.

Undo special-casing of sys_check_timeouts wrapper

AdvancedWebServer with heavy F5-refresh and #1274 test both pass.

Fixes #1274
2023-03-08 11:48:23 -08:00
whimsee 54f9d3c414 Reassign I2C pins to correct buses (#1255) 2023-03-05 14:56:53 -08:00
Tim Boldtandtimboldt 9e272733cf Add instructions for making I2S input work with Adafruit microphone (#1272)
Co-authored-by: timboldt <tim.boldt@gmail.com>
2023-03-05 14:24:31 -08:00
Earle F. Philhower, III 51afd3440f Add Bluetooth to the PIO CI (#1269) 2023-03-05 08:48:24 -08:00
Sanjay Govind f67bc43584 Add Bluetooth support to Platform.io (#1259) 2023-03-05 08:23:22 -08:00
Earle F. Philhower, III 79e1af7bde Add Keyboard LED callback for USB and BT (not BLE) (#1265)
BLE seems to require some kind of characteristic callback that is not yeet
implemented here.  USB and BT tested and examples updated.
2023-03-04 14:18:40 -08:00
Earle F. Philhower, III 173c44417c Allow setting names for BT/BLE HID devices (#1260) 2023-03-04 12:38:34 -08:00
Earle F. Philhower, III 7aa1c08d17 Use generic HID classes to minimize code duplic'n (#1254)
Move the Joystick, Keyboard, and Mouse into a base class which handles
the operation/input, and a subclass which will implement the reporting
as a HID device via USB, Bluetooth Classic, or Bluetooth Low Energy (BLE).

Reduce copies of library code and makes maintainability much better.
2023-03-03 11:12:09 -08:00
Rei Vilo 0be1d9c3ea Fix picow default libname in platform.txt (#1250) 2023-03-03 07:40:02 -08:00
Earle F. Philhower, III 305e194993 Add setBattery to BLE HID devices (#1246) 2023-03-02 15:55:18 -08:00
Paint Your Dragon 719ab019a4 Add Adafruit Feather RP2040 DVI (#1245)
Includes option for QSPI/4 flash access on specific Adafruit boards for use when overclocking.
2023-03-02 15:54:19 -08:00
Earle F. Philhower, III e9df18f910 Update PicoBluetoothBLEHID.cpp 2023-03-02 08:18:02 -08:00
Earle F. Philhower, III 06a1fdac50 Update version 2023-03-01 16:59:38 -08:00
Earle F. Philhower, III 7308ef4117 Update bluetooth.rst 2023-03-01 16:44:35 -08:00
Earle F. Philhower, III 67c1db9957 JoystickBLE actually implements a gamepad, update appearance 2023-03-01 16:30:51 -08:00
Earle F. Philhower, III f5a621935d Add BLE HID libraries and examples (#1240) 2023-03-01 16:29:25 -08:00
Earle F. Philhower, III 354edb30d3 Move pico-sdk to RPI's original version (#1239)
No need to use my own fork, we're using the default upstream code.
2023-03-01 10:24:45 -08:00
Earle F. Philhower, III 96113fe848 Fix BLE enable definition (#1238)
BLE tested working with BTStack BLE hog-keyboard-demo.
2023-03-01 10:08:03 -08:00
Earle F. Philhower, III fd1596b6c9 Update keywords.txt 2023-02-28 12:41:22 -08:00
Earle F. Philhower, III 9760efbca3 Clean up the BT HID libraries a bit (#1236) 2023-02-28 10:10:39 -08:00
Earle F. Philhower, III d92c1025ba Update to SDK 1.5, add alpha-level BT support, use Pico-SDK CYW43 infrastructure (#1167)
* Update to Pico-SDK v1.5
* Hook in pico_rand, use ioctl to set ipv6 allmulti
* Move into PicoSDK LWIP mutex, hack timer sizes
* Utilize much of the PicoSDK infrastructure for WiFi
* Add WiFi::begin(ssid, pass, bssid)
* WiFiMulti to use BSSID, make more robust

WiFiMulti will now be more aggressive and try all matching SSIDs, in order
of RSSI, using the BSSID to identify individual APs in a mesh.

Before, if the highest RSSI AP didn't connect, it would fail immediately.
Now, it will go down the list, ordered by RSSI, to attempt to get a link.

* Add Bluetooth support from Pico-SDK
Able to build and run the HID Keyboard Demo from the Arduino IDE, almost
as-is.

Will probably need to make BT configurable.  Enabling BT on a plain WiFi
sketch uses 50KB of flash and 16KB of RAM even if no BT is used.

* Separate picow libs, BT through menus, example

Build normal Pico.a and 4 different options for PicoW IP/BT configuration.
Use IP=>IP/Bluetooth menu to select between options.

* CMakefile rationalization

* Move BT TLV(pairing) out of last 2 flash sectors

The pairing keys for BT are stored at the end of flash by default, but
we use the last sector of flash for EPROM and the penultimate one for
the filesystem.  Overwriting those in BT could cause some real exciting
crashes down the line.

Move the store to an app-build specific address using a dummy const
array to allocate space in the application image itself.

* PicoBluetoothHID with BT Mouse, Joystick, Keyboard

Add simple Bluetooth Classic HID helper function and port the existing
USB HID devices to it.  Port their examples.

* Protect BT key storage from multicore

* Add short-n-sweet Bluetooth documents

* Add Bluetooth Serial port library

* Turn off BT when the BT libraries exit
2023-02-27 20:09:02 -08:00
Earle F. Philhower, III de55db12f1 Update rp2040.rst 2023-02-26 18:54:32 -08:00
Earle F. Philhower, III 264b9efb36 Return custom USB product and manufacturer (#1223)
Return the pre-existing USB_PRODUCT/MANUFACTURER to the USB host in
the ID stage, allowing for reports like:

[1412958.589070] usb 1-6.3.4.1: New USB device found, idVendor=2e8a, idProduct=f00a, bcdDevice= 1.00
[1412958.589076] usb 1-6.3.4.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[1412958.589079] usb 1-6.3.4.1: Product: Pico W
[1412958.589080] usb 1-6.3.4.1: Manufacturer: Raspberry Pi
[1412958.589081] usb 1-6.3.4.1: SerialNumber: E6614C775B6C7F31

or

[1413190.272233] usb 1-6.3.4.1: New USB device found, idVendor=2e8a, idProduct=1037, bcdDevice= 1.00
[1413190.272239] usb 1-6.3.4.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[1413190.272242] usb 1-6.3.4.1: Product: HunterCat NFC RP2040
[1413190.272243] usb 1-6.3.4.1: Manufacturer: ElectronicCats
[1413190.272244] usb 1-6.3.4.1: SerialNumber: E6614C775B6C7F31
2023-02-23 17:13:12 -08:00
Earle F. Philhower, III 9fd5cdf1ba Move PicoW auto-reassignment of LED pin to code (#1208)
Many examples require that LED_BUILTIN be defined as a constant, so we
can't use a ternary operator to swap between Pico and PicoW LED pins.

Instead, do the check in the digitalWrite/digitalRead/pinMode calls
to cover most of the uses.
2023-02-22 16:13:24 -08:00
Earle F. Philhower, III 1b33cbe591 Always apply 5M speed to CMSIS_DAP TCL script (#1218)
Still need it on the command line in platform.txt for upload/etc., but make the
debug script in lib/picoprobe_cmsis_dap.tcl include the adapter speed setting.
2023-02-21 12:30:05 -08:00
MisterSilvereagleandEarle F. Philhower, III a97d5eab22 Fix spelling mistake (#1211)
Co-authored-by: Earle F. Philhower, III <earlephilhower@yahoo.com>
2023-02-20 09:58:50 -08:00
crp500 83e790851f Update PlayStereo.ino (#1210)
The PWMAudio lib is expecting an int16_t value but the example passes a sample cast to uint16_t 
So any values from the lookup table that are negative are recast to 0 i think.
So half the sine wave in the case of the example is lost.
2023-02-20 09:58:14 -08:00
Earle F. Philhower, III 11ac5ed33e Update version 2023-02-18 13:04:53 -08:00
Earle F. Philhower, III b7912f182b Add isPicoW call to RP2040 object (#1204)
Use the RPi code to get a best-guess as to whether the board is a Pico or
PicoW.

Fixes #849
2023-02-18 13:00:02 -08:00
Earle F. Philhower, III 6db0ac8471 Add release package fixups for picoprobe-cmsis-dap (#1205) 2023-02-18 12:14:23 -08:00
Ha Thach 060aa52c89 Add picoprobe CMSIS-DAP support (#1198) 2023-02-18 12:09:02 -08:00
Carter Nelson ba413bb7ad Remove extra SPI byte flip (#1202)
Fixes #1201
2023-02-17 12:42:31 -08:00
Earle F. Philhower, III 97e787e48a Reduce unintialized_ram overhead to 0 in most cases (#1200)
Use GNU LD MAX() to ensure the uninitialized RAM portions are after the
OTA region.  For most apps this already happens, so there will be no
overhead added.
2023-02-16 18:23:15 -08:00
Earle F. Philhower, III b473139758 Enable use of uninitialized_ram() macro (#1199)
The uninitted RAM macro would fail because the OTA code would clear out the
first 50KB or so of RAM to copy its code and global values.

Move all global values to the end of RAM in OTA, and then align any uninitted
vars to a 16KB unit to ensure it won't appear in the 1st part of RAM that is
still needed to copy the OTA executable.

In the normal case, without any uninit_ram vars, no additional space is used.
When uninit_ram is used, up to 16KB of space may be lost to get that alignment,
but it will work properly.

Fixes #1188
2023-02-16 17:59:15 -08:00
Earle F. Philhower, III 7afcec8edc Update picotool refs in JSON (#1197) 2023-02-15 18:49:40 -08:00
Earle F. Philhower, III d1a3009447 Upgrade to toolchain 1.5.0-b (#1196)
Includes fixes for ::printf/etc. using stdout/stderr
Fixes #1181
2023-02-15 18:20:18 -08:00
Earle F. Philhower, III 651d596246 Allow FreeRTOS to ::printf (#1195)
The stdin/stdout FILEs have an internal mutex which needs to be initted
to a FreeRTOS one, or any sketch with ::printf will hang.  Automatically
create and acquire/release the shadowed mutex.

Requires new build of pico-quick-toolchain to function properly.  Tested
on preliminary local build.
2023-02-15 15:58:45 -08:00
Earle F. Philhower, III 75bab41e39 Make uf2conf.py flush STDOUT for real-time updates (#1194)
Without flushing STDOUT, the upload script's output aften are buffered
and not displayed until it completes.  Add in flush commands to allow
the IDE to display status as it changes.
2023-02-15 12:13:55 -08:00
Earle F. Philhower, III 36e0b4a908 Unbreak FreeRTOS (#1193)
USB changes caused FreeRTOS to not be able to swap tasks when the Serial port
was connected.  Clear the "stop PendSV" flag after checking for reset signal.
2023-02-15 12:10:52 -08:00
Earle F. Philhower, III 974301eaaf Add rp2040.cpuid() call to get running core (#1190)
Per question received via email.
2023-02-14 16:22:04 -08:00
Earle F. Philhower, III 2acc161ad2 Update contrib.rst 2023-02-14 13:24:02 -08:00
Earle F. Philhower, III 7322bad830 Add docs on adding a new board to the core, too 2023-02-14 08:46:24 -08:00
Earle F. Philhower, III 6afd3260ef Update README.md 2023-02-13 14:56:46 -08:00
Earle F. Philhower, III 76e7cca821 Add contributing docs (#1183) 2023-02-13 14:54:52 -08:00
Earle F. Philhower, III 84206eb237 Fix SD.H FILE_WRITE mapping (#1178)
O_RDWR != O_READ|O_WRITE.  Posix is weird.  Thanks @mcspr
2023-02-12 17:46:50 -08:00
Earle F. Philhower, III 5e576c1a08 Update SD examples with working SPI configs (#1175)
Fixes #1172
2023-02-12 11:01:01 -08:00
Earle F. Philhower, III fc180041aa Implement WiFi::softAPgetStationNum (#1174) 2023-02-12 10:38:37 -08:00
323 changed files with 17223 additions and 2204 deletions
+2
View File
@@ -217,3 +217,5 @@ jobs:
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/WiFi/examples/ScanNetworks/ScanNetworks.ino
- name: Build Signed OTA Example
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino
- name: Build Bluetooth Example
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH" libraries/MouseBLE/examples/BLECircle/BLECircle.ino
+7 -7
View File
@@ -3,7 +3,7 @@
url = https://github.com/earlephilhower/ArduinoCore-API.git
[submodule "pico-sdk"]
path = pico-sdk
url = https://github.com/earlephilhower/pico-sdk.git
url = https://github.com/raspberrypi/pico-sdk.git
[submodule "system/pyserial"]
path = tools/pyserial
url = https://github.com/pyserial/pyserial.git
@@ -14,14 +14,14 @@
path = libraries/ESP8266SdFat
url = https://github.com/earlephilhower/ESP8266SdFat.git
[submodule "libraries/Keyboard"]
path = libraries/Keyboard
url = https://github.com/earlephilhower/Keyboard
path = libraries/HID_Keyboard
url = https://github.com/earlephilhower/Keyboard.git
[submodule "libraries/Mouse"]
path = libraries/Mouse
url = https://github.com/earlephilhower/Mouse
path = libraries/HID_Mouse
url = https://github.com/earlephilhower/Mouse.git
[submodule "libraries/Joystick"]
path = libraries/Joystick
url = https://github.com/benjaminaigner/Joystick
path = libraries/HID_Joystick
url = https://github.com/earlephilhower/Joystick.git
[submodule "libraries/Adafruit_TinyUSB_Arduino"]
path = libraries/Adafruit_TinyUSB_Arduino
url = https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git
+35 -2
View File
@@ -9,6 +9,9 @@ This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosys
# Documentation
See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for more detailed usage information.
# Contributing
Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blob/master/docs/contrib.rst) for more information on submitting pull requests and porting libraries or sketches to this core.
# Supported Boards
* Raspberry Pi Pico
* Raspberry Pi Pico W
@@ -37,11 +40,14 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
* Invector Labs Challenger RP2040 LoRa
* Invector Labs Challenger RP2040 SubGHz
* Invector Labs Challenger RP2040 SD/RTC
* Invector Labs Challenger RP2040 UWB
* Invector Labs RPICO32
* Melopero Cookie RP2040
* Melopero Shake RP2040
* Neko Systems BL2040 Mini
* nullbits Bit-C PRO
* Pimoroni PGA2040
* Seeed Indicator RP2040
* Seeed XIAO RP2040
* Solder Party RP2040 Stamp
* SparkFun ProMicro RP2040
@@ -60,13 +66,33 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
* Generic (configurable flash, I/O pins)
# Installing via Arduino Boards Manager
**Windows Users**: Please do not use the Windows Store version of the actual Arduino application
## Windows-specific Notes
Please do not use the Windows Store version of the actual Arduino application
because it has issues detecting attached Pico boards. Use the "Windows ZIP" or plain "Windows"
executable (EXE) download direct from https://arduino.cc. and allow it to install any device
drivers it suggests. Otherwise the Pico board may not be detected. Also, if trying out the
2.0 beta Arduino please install the release 1.8 version beforehand to ensure needed device drivers
are present. (See #20 for more details.)
## Linux-specific Notes
Installing Arduino using flatpak (often used by "App Stores" in various Linux
distributions) will mean it has restricted access to the host. This might cause uploads to fail
with error messages such as the following:
```
Scanning for RP2040 devices
...
No drive to deploy.
```
If you encounter this, you will need to either install Arduino in a different manner, or override
the flatpak sandboxing feature using the following command, then restarting Arduino.
```
flatpak override --user --filesystem=host:ro cc.arduino.IDE2
```
## Installation
Open up the Arduino IDE and go to File->Preferences.
In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field:
@@ -84,6 +110,12 @@ Type "pico" in the search box and select "Add":
![image](https://user-images.githubusercontent.com/11875/111917223-12063680-8a3c-11eb-8884-4f32b8f0feb1.png)
# Installing via GIT
**Windows Users:** Before installing via `git` on Windows, please read and follow the directions in
[this link](https://arduino-pico.readthedocs.io/en/latest/platformio.html#important-steps-for-windows-users-before-installing).
If Win32 long paths are not enabled, and `git` not configured to use them then there
may be errors when attempting to clone the submodules.
To install via GIT (for latest and greatest versions):
````
mkdir -p ~/Arduino/hardware/pico
@@ -160,7 +192,8 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
# Features
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
* WiFi (Pico W)
* HTTP client and server (WebServer)
* SSL/TLS/HTTPS
+3775 -411
View File
File diff suppressed because it is too large Load Diff
+15 -4
View File
@@ -28,7 +28,7 @@
#include "api/ArduinoAPI.h"
#include "api/itoa.h" // ARM toolchain doesn't provide itoa etc, provide them
#include <pins_arduino.h>
#include "hardware/gpio.h" // Required for the port*Register macros
#include <hardware/gpio.h> // Required for the port*Register macros
#include "debug_internal.h"
#include <RP2040.h> // CMSIS
@@ -84,13 +84,13 @@ void analogWriteFreq(uint32_t freq);
void analogWriteRange(uint32_t range);
void analogWriteResolution(int res);
// FreeRTOS potential calls
extern bool __isFreeRTOS;
#ifdef __cplusplus
} // extern "C"
#endif
// FreeRTOS potential calls
extern bool __isFreeRTOS;
// Ancient AVR defines
#define HAVE_HWSERIAL0
#define HAVE_HWSERIAL1
@@ -129,3 +129,14 @@ constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
return i < N ? (1L << a[i]) | __bitset(a, i + 1) : 0;
}
#endif
// Warn users trying to use Pico SDK's STDIO implementation
#include <pico/stdio.h> // Ensure it won't be re-included elsewhere
#undef stdio_uart_init
#define stdio_uart_init(...) static_assert(0, "stdio_uart_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
#undef stdio_init_all
#define stdio_init_all(...) static_assert(0, "stdio_init_all is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
#undef stdio_usb_init
#define stdio_usb_init(...) static_assert(0, "stdio_usb_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
+5 -5
View File
@@ -4,11 +4,11 @@
SPDX-License-Identifier: BSD-3-Clause
*/
#include <Arduino.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/sync.h"
#include "hardware/structs/ioqspi.h"
#include "hardware/structs/sio.h"
#include <pico/stdlib.h>
#include <hardware/gpio.h>
#include <hardware/sync.h>
#include <hardware/structs/ioqspi.h>
#include <hardware/structs/sio.h>
// This example blinks the Pico LED when the BOOTSEL button is pressed.
//
+5 -3
View File
@@ -30,10 +30,12 @@ CoreMutex::CoreMutex(mutex_t *mutex, uint8_t option) {
_option = option;
if (__isFreeRTOS) {
auto m = __get_freertos_mutex_for_ptr(mutex);
if (_option & FromISR) {
if (__freertos_check_if_in_isr()) {
__freertos_mutex_take_from_isr(m);
} else {
__freertos_mutex_take(m);
if (!__freertos_mutex_try_take(m)) {
return;
}
}
} else {
uint32_t owner;
@@ -54,7 +56,7 @@ CoreMutex::~CoreMutex() {
if (_acquired) {
if (__isFreeRTOS) {
auto m = __get_freertos_mutex_for_ptr(_mutex);
if (_option & FromISR) {
if (__freertos_check_if_in_isr()) {
__freertos_mutex_give_from_isr(m);
} else {
__freertos_mutex_give(m);
+3 -4
View File
@@ -23,12 +23,11 @@
#pragma once
#include "pico/mutex.h"
#include <pico/mutex.h>
#include "_freertos.h"
enum {
DebugEnable = 1,
FromISR = 1 << 1,
DebugEnable = 1
};
class CoreMutex {
@@ -43,5 +42,5 @@ public:
private:
mutex_t *_mutex;
bool _acquired;
u_int8_t _option;
uint8_t _option;
};
+24 -29
View File
@@ -27,6 +27,7 @@
#include <hardware/structs/rosc.h>
#include <hardware/structs/systick.h>
#include <pico/multicore.h>
#include <pico/rand.h>
#include <pico/util/queue.h>
#include "CoreMutex.h"
#include "ccount.pio.h"
@@ -90,15 +91,14 @@ public:
if (!_multicore) {
return;
}
__holdUpPendSV = 1;
if (__isFreeRTOS) {
vTaskPreemptionDisable(nullptr);
vTaskSuspendAll();
__freertos_idle_other_core();
} else {
mutex_enter_blocking(&_idleMutex);
__otherCoreIdled = false;
multicore_fifo_push_blocking(_GOTOSLEEP);
while (!__otherCoreIdled) { /* noop */ }
}
mutex_enter_blocking(&_idleMutex);
__otherCoreIdled = false;
multicore_fifo_push_blocking(_GOTOSLEEP);
while (!__otherCoreIdled) { /* noop */ }
}
void resumeOtherCore() {
@@ -108,10 +108,8 @@ public:
mutex_exit(&_idleMutex);
__otherCoreIdled = false;
if (__isFreeRTOS) {
xTaskResumeAll();
vTaskPreemptionEnable(nullptr);
__freertos_resume_other_core();
}
__holdUpPendSV = 0;
// Other core will exit busy-loop and return to operation
// once __otherCoreIdled == false.
@@ -246,6 +244,11 @@ public:
return clock_get_hz(clk_sys);
}
// Get current CPU core number
static int cpuid() {
return sio_hw->cpuid;
}
// Get CPU cycle count. Needs to do magic to extens 24b HW to something longer
volatile uint64_t _epoch = 0;
inline uint32_t getCycleCount() {
@@ -336,28 +339,20 @@ public:
_MFIFO fifo;
// TODO - Not so great HW random generator. 32-bits wide. Cryptographers somewhere are crying
uint32_t hwrand32() {
// Try and whiten the HW ROSC bit
uint32_t r = 0;
for (int k = 0; k < 32; k++) {
unsigned long int b;
do {
b = rosc_hw->randombit & 1;
if (b != (rosc_hw->randombit & 1)) {
break;
}
} while (true);
r <<= 1;
r |= b;
}
// Stir using the cycle count LSBs. In any WiFi use case this will be a random # since the connection time is not cycle-accurate
uint64_t rr = (((uint64_t)~r) << 32LL) | r;
rr >>= rp2040.getCycleCount() & 32LL;
return (uint32_t)rr;
return get_rand_32();
}
bool isPicoW() {
#if !defined(ARDUINO_RASPBERRY_PI_PICO_W)
return false;
#else
extern bool __isPicoW;
return __isPicoW;
#endif
}
private:
static void _SystickHandler() {
rp2040._epoch += 1LL << 24;
+92 -23
View File
@@ -25,17 +25,17 @@
#include "CoreMutex.h"
#include "RP2040USB.h"
#include "tusb.h"
#include "class/hid/hid_device.h"
#include "class/audio/audio.h"
#include "class/midi/midi.h"
#include "pico/time.h"
#include "hardware/irq.h"
#include "pico/mutex.h"
#include "pico/unique_id.h"
#include "pico/usb_reset_interface.h"
#include "hardware/watchdog.h"
#include "pico/bootrom.h"
#include <tusb.h>
#include <class/hid/hid_device.h>
#include <class/audio/audio.h>
#include <pico/time.h>
#include <hardware/irq.h>
#include <pico/mutex.h>
#include <pico/unique_id.h>
#include <pico/usb_reset_interface.h>
#include <hardware/watchdog.h>
#include <pico/bootrom.h>
#include "sdkoverride/tusb_absmouse.h"
#include <device/usbd_pvt.h>
// Big, global USB mutex, shared with all USB devices to make sure we don't
@@ -100,7 +100,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1
};
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallAbsoluteMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
// Can use as-is, this is the default USB case
return (const uint8_t *)&usbd_desc_device;
}
@@ -108,7 +108,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
if (__USBInstallKeyboard) {
usbd_desc_device.idProduct |= 0x8000;
}
if (__USBInstallMouse) {
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
usbd_desc_device.idProduct |= 0x4000;
}
if (__USBInstallJoystick) {
@@ -129,15 +129,15 @@ int __USBGetKeyboardReportID() {
}
int __USBGetMouseReportID() {
return __USBInstallKeyboard ? 2 : 1;
return __USBInstallKeyboard ? 3 : 1;
}
int __USBGetJoystickReportID() {
int i = 1;
if (__USBInstallKeyboard) {
i++;
i += 2;
}
if (__USBInstallMouse) {
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
i++;
}
return i;
@@ -156,8 +156,9 @@ static uint8_t *GetDescHIDReport(int *len) {
void __SetupDescHIDReport() {
//allocate memory for the HID report descriptors. We don't use them, but need the size here.
uint8_t desc_hid_report_mouse[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_absmouse[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_joystick[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(2)) };
int size = 0;
//accumulate the size of all used HID report descriptors
@@ -166,6 +167,8 @@ void __SetupDescHIDReport() {
}
if (__USBInstallMouse) {
size += sizeof(desc_hid_report_mouse);
} else if (__USBInstallAbsoluteMouse) {
size += sizeof(desc_hid_report_absmouse);
}
if (__USBInstallJoystick) {
size += sizeof(desc_hid_report_joystick);
@@ -194,11 +197,19 @@ void __SetupDescHIDReport() {
if (__USBInstallMouse) {
//determine if we need an offset (USB keyboard is installed)
if (__USBInstallKeyboard) {
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2)) };
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(3)) };
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
} else {
memcpy(__hid_report, desc_hid_report_mouse, sizeof(desc_hid_report_mouse));
}
} else if (__USBInstallAbsoluteMouse) {
//determine if we need an offset (USB keyboard is installed)
if (__USBInstallKeyboard) {
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(3)) };
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
} else {
memcpy(__hid_report, desc_hid_report_absmouse, sizeof(desc_hid_report_absmouse));
}
}
//3.) joystick descriptor. 2 additional checks are necessary for mouse and/or keyboard
@@ -206,12 +217,15 @@ void __SetupDescHIDReport() {
uint8_t reportid = 1;
int offset = 0;
if (__USBInstallKeyboard) {
reportid++;
reportid += 2;
offset += sizeof(desc_hid_report_keyboard);
}
if (__USBInstallMouse) {
reportid++;
offset += sizeof(desc_hid_report_mouse);
} else if (__USBInstallAbsoluteMouse) {
reportid++;
offset += sizeof(desc_hid_report_absmouse);
}
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(reportid)) };
memcpy(__hid_report + offset, desc_local, sizeof(desc_local));
@@ -235,7 +249,7 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
void __SetupUSBDescriptor() {
if (!usbd_desc_cfg) {
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallAbsoluteMouse || __USBInstallJoystick;
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMassStorage ? 1 : 0);
@@ -301,15 +315,15 @@ void __SetupUSBDescriptor() {
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
#define DESC_STR_MAX (20)
#define DESC_STR_MAX (32)
static uint16_t desc_str[DESC_STR_MAX];
static char idString[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1];
static const char *const usbd_desc_str[] = {
[USBD_STR_0] = "",
[USBD_STR_MANUF] = "Raspberry Pi",
[USBD_STR_PRODUCT] = "PicoArduino",
[USBD_STR_MANUF] = USB_MANUFACTURER,
[USBD_STR_PRODUCT] = USB_PRODUCT,
[USBD_STR_SERIAL] = idString,
[USBD_STR_CDC] = "Board CDC",
#ifdef ENABLE_PICOTOOL_USB
@@ -383,6 +397,7 @@ void __USBStart() {
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) __attribute__((weak));
extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
// TODO not implemented
(void) instance;
@@ -396,6 +411,7 @@ extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, h
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) __attribute__((weak));
extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
// TODO set LED based on CAPLOCK, NUMLOCK etc...
(void) instance;
@@ -533,6 +549,59 @@ usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
*driver_count = 1;
return &_resetd_driver;
}
#elif defined NO_USB
// will ensure backward compatibility with existing code when using pico-debug
#warning "NO_USB selected. No output to Serial will occur!"
#include <Arduino.h>
void SerialUSB::begin(unsigned long baud) {
}
void SerialUSB::end() {
}
int SerialUSB::peek() {
return 0;
}
int SerialUSB::read() {
return -1;
}
int SerialUSB::available() {
return 0;
}
int SerialUSB::availableForWrite() {
return 0;
}
void SerialUSB::flush() {
}
size_t SerialUSB::write(uint8_t c) {
(void) c;
return 0;
}
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
(void) buf;
(void) length;
return 0;
}
SerialUSB::operator bool() {
return false;
}
SerialUSB Serial;
#endif
+7 -1
View File
@@ -19,13 +19,19 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pico/mutex.h"
#include <pico/mutex.h>
// Weak function definitions for each type of endpoint
extern void __USBInstallSerial() __attribute__((weak));
extern void __USBInstallKeyboard() __attribute__((weak));
extern void __USBInstallJoystick() __attribute__((weak));
// One or the other allowed, not both
extern void __USBInstallMouse() __attribute__((weak));
extern void __USBInstallAbsoluteMouse() __attribute__((weak));
extern void __USBInstallMassStorage() __attribute__((weak));
// Big, global USB mutex, shared with all USB devices to make sure we don't
+4 -4
View File
@@ -1,5 +1,5 @@
#pragma once
#define ARDUINO_PICO_MAJOR 2
#define ARDUINO_PICO_MINOR 7
#define ARDUINO_PICO_REVISION 2
#define ARDUINO_PICO_VERSION_STR "2.7.2"
#define ARDUINO_PICO_MAJOR 3
#define ARDUINO_PICO_MINOR 2
#define ARDUINO_PICO_REVISION 1
#define ARDUINO_PICO_VERSION_STR "3.2.1"
+21 -14
View File
@@ -46,22 +46,24 @@ static pio_program_t *pio_make_uart_prog(int repl, const pio_program_t *pg) {
return p;
}
static PIOProgram *_getTxProgram(int bits) {
auto f = _txMap.find(bits);
static PIOProgram *_getTxProgram(int bits, bool inverted) {
int key = inverted ? -bits : bits;
auto f = _txMap.find(key);
if (f == _txMap.end()) {
pio_program_t * p = pio_make_uart_prog(bits, &pio_tx_program);
_txMap.insert({bits, new PIOProgram(p)});
f = _txMap.find(bits);
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_tx_inv_program : &pio_tx_program);
_txMap.insert({key, new PIOProgram(p)});
f = _txMap.find(key);
}
return f->second;
}
static PIOProgram *_getRxProgram(int bits) {
auto f = _rxMap.find(bits);
static PIOProgram *_getRxProgram(int bits, bool inverted) {
int key = inverted ? -bits : bits;
auto f = _rxMap.find(key);
if (f == _rxMap.end()) {
pio_program_t * p = pio_make_uart_prog(bits, &pio_rx_program);
_rxMap.insert({bits, new PIOProgram(p)});
f = _rxMap.find(bits);
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_rx_inv_program : &pio_rx_program);
_rxMap.insert({key, new PIOProgram(p)});
f = _rxMap.find(key);
}
return f->second;
}
@@ -96,7 +98,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
return;
}
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
uint32_t decode = _rxPIO->rxf[_rxSM];
uint32_t decode = _rxPIO->rxf[_rxSM] ^ (_rxInverted ? 0xffffffff : 0);
decode >>= 33 - _rxBits;
uint32_t val = 0;
for (int b = 0; b < _bits + 1; b++) {
@@ -189,7 +191,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
if (_tx != NOPIN) {
_txBits = _bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1/*start bit*/;
_txPgm = _getTxProgram(_txBits);
_txPgm = _getTxProgram(_txBits, _txInverted);
int off;
if (!_txPgm->prepare(&_txPIO, &_txSM, &off)) {
DEBUGCORE("ERROR: Unable to allocate PIO TX UART, out of PIO resources\n");
@@ -216,7 +218,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
_reader = 0;
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
_rxPgm = _getRxProgram(_rxBits);
_rxPgm = _getRxProgram(_rxBits, _rxInverted);
int off;
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off)) {
DEBUGCORE("ERROR: Unable to allocate PIO RX UART, out of PIO resources\n");
@@ -346,6 +348,11 @@ void SerialPIO::flush() {
delay((1000 * (_txBits + 1)) / _baud);
}
void SerialPIO::setInverted(bool invTx, bool invRx) {
_txInverted = invTx;
_rxInverted = invRx;
}
size_t SerialPIO::write(uint8_t c) {
CoreMutex m(&_mutex);
if (!_running || !m || (_tx == NOPIN)) {
@@ -364,7 +371,7 @@ size_t SerialPIO::write(uint8_t c) {
}
val <<= 1; // Start bit = low
pio_sm_put_blocking(_txPIO, _txSM, val);
pio_sm_put_blocking(_txPIO, _txSM, _txInverted ? ~val : val);
return 1;
}
+4
View File
@@ -41,6 +41,8 @@ public:
void begin(unsigned long baud, uint16_t config) override;
void end() override;
void setInverted(bool invTx = true, bool invRx = true);
virtual int peek() override;
virtual int read() override;
virtual int available() override;
@@ -63,6 +65,8 @@ protected:
int _stop;
bool _overflow;
mutex_t _mutex;
bool _txInverted = false;
bool _rxInverted = false;
PIOProgram *_txPgm;
PIO _txPIO;
+12 -10
View File
@@ -25,15 +25,15 @@
#include <Arduino.h>
#include "CoreMutex.h"
#include "tusb.h"
#include "pico/time.h"
#include "pico/binary_info.h"
#include "pico/bootrom.h"
#include "hardware/irq.h"
#include "pico/mutex.h"
#include "hardware/watchdog.h"
#include "pico/unique_id.h"
#include "hardware/resets.h"
#include <tusb.h>
#include <pico/time.h>
#include <pico/binary_info.h>
#include <pico/bootrom.h>
#include <hardware/irq.h>
#include <pico/mutex.h>
#include <hardware/watchdog.h>
#include <pico/unique_id.h>
#include <hardware/resets.h>
#ifndef DISABLE_USB_SERIAL
// Ensure we are installed in the USB chain
@@ -177,8 +177,10 @@ static bool _rts = false;
static int _bps = 115200;
static bool _rebooting = false;
static void CheckSerialReset() {
__holdUpPendSV = 1; // Ensure we don't get swapped out by FreeRTOS
if (!_rebooting && (_bps == 1200) && (!_dtr)) {
if (__isFreeRTOS) {
__freertos_idle_other_core();
}
_rebooting = true;
// Disable NVIC IRQ, so that we don't get bothered anymore
irq_set_enabled(USBCTRL_IRQ, false);
+2 -2
View File
@@ -16,8 +16,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stdlib.h"
#include "stdint.h"
#include <stdlib.h>
#include <stdint.h>
void randomSeed(uint32_t dwSeed) {
if (dwSeed != 0) {
+51 -3
View File
@@ -19,8 +19,9 @@
*/
#include "_freertos.h"
#include "pico/mutex.h"
#include <pico/mutex.h>
#include <stdlib.h>
#include "Arduino.h"
typedef struct {
mutex_t *src;
@@ -28,7 +29,7 @@ typedef struct {
} FMMap;
static FMMap *_map = nullptr;
extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive) {
if (!_map) {
_map = (FMMap *)calloc(sizeof(FMMap), 16);
}
@@ -42,7 +43,12 @@ extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
for (int i = 0; i < 16; i++) {
if (_map[i].src == nullptr) {
// Make a new mutex
SemaphoreHandle_t fm = __freertos_mutex_create();
SemaphoreHandle_t fm;
if (recursive) {
fm = _freertos_recursive_mutex_create();
} else {
fm = __freertos_mutex_create();
}
if (fm == nullptr) {
return nullptr;
}
@@ -54,3 +60,45 @@ extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
}
return nullptr; // Need to make space for more mutex maps!
}
// The HW Random code needs a precise sleep_until() in order to assure it
// grabs the ROSC bit when it wants. Unfortunately, under FreeRTOS the
// sleep_until() becomes imprecise and the "did I get the bit when I wanted"
// check in the pico_rand code always fails and you get an infinite loop.
// This block wraps the 2 get_rand calls to set a flag to convert
// sleep_until() (which can do a task swap and cause bad timing) into a
// busy wait.
extern "C" {
static bool __inRand = false;
extern uint64_t __real_get_rand_64();
uint64_t __wrap_get_rand_64() {
if (__isFreeRTOS) {
rp2040.idleOtherCore();
__inRand = true;
auto r = __real_get_rand_64();
__inRand = false;
rp2040.resumeOtherCore();
return r;
} else {
return __real_get_rand_64();
}
}
uint32_t __wrap_get_rand_32() {
return (uint32_t) __wrap_get_rand_64();
}
extern void __real_sleep_until(absolute_time_t t);
void __wrap_sleep_until(absolute_time_t t) {
if (__inRand) {
busy_wait_until(t);
} else {
__real_sleep_until(t);
}
}
}
+6 -14
View File
@@ -19,7 +19,7 @@
*/
#pragma once
#include "pico/mutex.h"
#include <pico/mutex.h>
// Cannot include refs to FreeRTOS's actual semaphore calls because they
// are implemented as macros, so we have a wrapper in our variant hook
@@ -37,6 +37,8 @@ extern "C" {
typedef QueueHandle_t SemaphoreHandle_t;
#endif
extern bool __freertos_check_if_in_isr() __attribute__((weak));
extern SemaphoreHandle_t __freertos_mutex_create() __attribute__((weak));
extern SemaphoreHandle_t _freertos_recursive_mutex_create() __attribute__((weak));
@@ -50,17 +52,7 @@ extern "C" {
extern int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
#ifndef INC_FREERTOS_H
extern void vTaskSuspendAll() __attribute__((weak));
extern int32_t xTaskResumeAll() __attribute__((weak));
typedef struct tskTaskControlBlock * TaskHandle_t;
extern void vTaskPreemptionDisable(TaskHandle_t p) __attribute__((weak));
extern void vTaskPreemptionEnable(TaskHandle_t p) __attribute__((weak));
#endif
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m);
extern void __freertos_idle_other_core() __attribute__((weak));
extern void __freertos_resume_other_core() __attribute__((weak));
}
// Halt the FreeRTOS PendSV task switching magic
extern "C" int __holdUpPendSV;
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive = false);
+7
View File
@@ -0,0 +1,7 @@
// Simple helper header to ensure pico libs support ?BT
#ifndef ENABLE_CLASSIC
#define ENABLE_CLASSIC 0
#endif
static_assert(ENABLE_CLASSIC, "This library needs Bluetooth enabled. Use the 'Tools->IP/Bluetooth Stack' menu in the IDE to enable it.");
+104
View File
@@ -0,0 +1,104 @@
/*
CYW43 TCP/Ethernet wrappers
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
#include <lwip/netif.h>
extern "C" {
#include <cyw43.h>
#include <cyw43_stats.h>
}
#include <pico/cyw43_arch.h>
#include <Arduino.h>
// From cyw43_ctrl.c
#define WIFI_JOIN_STATE_KIND_MASK (0x000f)
#define WIFI_JOIN_STATE_ACTIVE (0x0001)
#define WIFI_JOIN_STATE_FAIL (0x0002)
#define WIFI_JOIN_STATE_NONET (0x0003)
#define WIFI_JOIN_STATE_BADAUTH (0x0004)
#define WIFI_JOIN_STATE_AUTH (0x0200)
#define WIFI_JOIN_STATE_LINK (0x0400)
#define WIFI_JOIN_STATE_KEYED (0x0800)
#define WIFI_JOIN_STATE_ALL (0x0e01)
// The core can't directly call a library, so put in a dummy weak one to be overridden by one in lwip_cyw43 library
extern struct netif *__getCYW43Netif() __attribute__((weak));
struct netif *__getCYW43Netif() {
return nullptr;
}
// CB from the cyw43 driver
extern "C" void __wrap_cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
(void) cb_data;
(void) itf;
struct netif *netif = __getCYW43Netif();
if (netif && (netif->flags & NETIF_FLAG_LINK_UP)) {
struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != nullptr) {
pbuf_take(p, buf, len);
if ((netif->input(p, netif) != ERR_OK)) {
pbuf_free(p);
}
CYW43_STAT_INC(PACKET_IN_COUNT);
}
}
}
extern "C" void __wrap_cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
(void) self;
(void) itf;
struct netif *netif = __getCYW43Netif();
if (netif) {
netif_set_link_up(netif);
}
}
extern "C" void __wrap_cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
(void) self;
(void) itf;
struct netif *netif = __getCYW43Netif();
if (netif) {
netif_set_link_down(netif);
}
self->wifi_join_state &= ~WIFI_JOIN_STATE_ACTIVE;
}
extern "C" int __wrap_cyw43_tcpip_link_status(cyw43_t *self, int itf) {
struct netif *netif = __getCYW43Netif();
//if ((CYW43::_netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) == (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP))
// Fake this since it's only used in the SDK
if (netif && ((netif->flags & (NETIF_FLAG_LINK_UP)) == (NETIF_FLAG_LINK_UP))) {
return CYW43_LINK_UP;
} else {
return cyw43_wifi_link_status(self, itf);
}
}
// CBs from the SDK, not needed here as we do TCP later in the game
extern "C" void __wrap_cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
(void) self;
(void) itf;
}
extern "C" void __wrap_cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
(void) self;
(void) itf;
}
#endif
+16 -4
View File
@@ -80,7 +80,7 @@ static SemaphoreHandle_t __getFreeRTOSMutex(_LOCK_T lock) {
} else if (l == &__lock___arc4random_mutex) {
return __lock___arc4random_mutex_freertos;
}
return nullptr;
return __get_freertos_mutex_for_ptr(l, false);
}
static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
@@ -96,12 +96,18 @@ static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
} else if (l == &__lock___env_recursive_mutex) {
return __lock___env_recursive_mutex_freertos;
}
return nullptr;
return __get_freertos_mutex_for_ptr((mutex_t *)l, true);
}
void __retarget_lock_init(_LOCK_T *lock) {
if (__freeRTOSinitted) {
/* Already done in initFreeRTOSMutexes() */
mutex_t *l = (mutex_t *)lock;
if ((l == &__lock___at_quick_exit_mutex) || (l == &__lock___tz_mutex) || (l == &__lock___dd_hash_mutex) || (l == &__lock___arc4random_mutex)) {
/* Already done in initFreeRTOSMutexes() */
} else {
// Will init the mutex as well
__get_freertos_mutex_for_ptr(l, false);
}
} else {
mutex_init((mutex_t*) lock);
}
@@ -109,7 +115,13 @@ void __retarget_lock_init(_LOCK_T *lock) {
void __retarget_lock_init_recursive(_LOCK_T *lock) {
if (__freeRTOSinitted) {
/* Already done in initFreeRTOSMutexes() */
recursive_mutex_t *l = (recursive_mutex_t *)lock;
if ((l == &__lock___sinit_recursive_mutex) || (l == &__lock___sfp_recursive_mutex) || (l == &__lock___atexit_recursive_mutex) || (l == &__lock___malloc_recursive_mutex) || (l == &__lock___env_recursive_mutex)) {
/* Already done in initFreeRTOSMutexes() */
} else {
// Will init the mutex as well
__get_freertos_mutex_for_ptr((mutex_t *)l, true);
}
} else {
recursive_mutex_init((recursive_mutex_t*) lock);
}
+13 -32
View File
@@ -1,7 +1,7 @@
/*
LWIP wrappers to protect against timer-based re-entrancy
Copyright (c) 202s Earle F. Philhower, III <earlephilhower@yahoo.com>
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -18,45 +18,25 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pico/mutex.h"
#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/dns.h"
#include "lwip/raw.h"
#include "lwip/timeouts.h"
// Global indicator that we're inside an LWIP block
extern "C" {
volatile bool __inLWIP = false;
}
auto_init_recursive_mutex(__mtxLWIP);
#include <pico/mutex.h>
#include <lwip/pbuf.h>
#include <lwip/udp.h>
#include <lwip/tcp.h>
#include <lwip/dns.h>
#include <lwip/raw.h>
#include <lwip/timeouts.h>
#include <pico/cyw43_arch.h>
class LWIPMutex {
public:
LWIPMutex() {
noInterrupts();
recursive_mutex_enter_blocking(&__mtxLWIP);
__inLWIP = true;
_ref++;
interrupts();
cyw43_arch_lwip_begin();
}
~LWIPMutex() {
noInterrupts();
if (0 == --_ref) {
__inLWIP = false;
}
recursive_mutex_exit(&__mtxLWIP);
interrupts();
cyw43_arch_lwip_end();
}
private:
static int _ref;
};
int LWIPMutex::_ref = 0;
extern "C" {
@@ -275,7 +255,8 @@ extern "C" {
return __real_udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
}
extern void __real_sys_check_timeouts(void);
// sys_check_timeouts is special case because the async process will call it. If we're already in a timeout check, just do a noop
extern void __real_sys_check_timeouts();
void __wrap_sys_check_timeouts(void) {
LWIPMutex m;
__real_sys_check_timeouts();
+1 -1
View File
@@ -27,7 +27,6 @@
RP2040 rp2040;
extern "C" {
volatile bool __otherCoreIdled = false;
int __holdUpPendSV = 0;
};
mutex_t _pioMutex;
@@ -163,6 +162,7 @@ extern "C" void __register_impure_ptr(struct _reent *p) {
}
}
extern "C" struct _reent *__wrap___getreent() __attribute__((weak));
extern "C" struct _reent *__wrap___getreent() {
if (get_core_num() == 0) {
return _impure_ptr;
+43 -2
View File
@@ -29,7 +29,7 @@
; We shift out the start and stop bit as part of the FIFO
set x, 9
pull side 1 ; Force stop bit
pull side 1 ; Force stop bit high
; Send the bits
bitloop:
@@ -41,6 +41,26 @@ wait_bit:
; inverted-logic version (inverts the stop bit)
.program pio_tx_inv
.side_set 1 opt
; We shift out the start and stop bit as part of the FIFO
set x, 9
pull side 0 ; Force stop bit low
; Send the bits
bitloop:
out pins, 1
mov y, isr ; ISR is loaded by the setup routine with the period-1 count
wait_bit:
jmp y-- wait_bit
jmp x-- bitloop
% c-sdk {
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
@@ -68,7 +88,7 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
}
%}
.program pio_rx
@@ -91,6 +111,27 @@ wait_half:
push ; Stuff it and wait for next start
.program pio_rx_inv
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
start:
set x, 18 ; Preload bit counter...we'll shift in the start bit and stop bit, and each bit will be double-recorded (to be fixed by RP2040 code)
wait 1 pin 0 ; Stall until start bit is asserted
bitloop:
; Delay until 1/2 way into the bit time
mov y, osr
wait_half:
jmp y-- wait_half
; Read in the bit
in pins, 1 ; Shift data bit into ISR
jmp x-- bitloop ; Loop all bits
push ; Stuff it and wait for next start
% c-sdk {
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
+68
View File
@@ -2,6 +2,8 @@
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
@@ -37,6 +39,39 @@ static inline pio_sm_config pio_tx_program_get_default_config(uint offset) {
sm_config_set_sideset(&c, 2, true, false);
return c;
}
#endif
// ---------- //
// pio_tx_inv //
// ---------- //
#define pio_tx_inv_wrap_target 0
#define pio_tx_inv_wrap 5
static const uint16_t pio_tx_inv_program_instructions[] = {
// .wrap_target
0xe029, // 0: set x, 9
0x90a0, // 1: pull block side 0
0x6001, // 2: out pins, 1
0xa046, // 3: mov y, isr
0x0084, // 4: jmp y--, 4
0x0042, // 5: jmp x--, 2
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_tx_inv_program = {
.instructions = pio_tx_inv_program_instructions,
.length = 6,
.origin = -1,
};
static inline pio_sm_config pio_tx_inv_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_tx_inv_wrap_target, offset + pio_tx_inv_wrap);
sm_config_set_sideset(&c, 2, true, false);
return c;
}
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
// Tell PIO to initially drive output-high on the selected pin, then map PIO
@@ -90,6 +125,39 @@ static inline pio_sm_config pio_rx_program_get_default_config(uint offset) {
sm_config_set_wrap(&c, offset + pio_rx_wrap_target, offset + pio_rx_wrap);
return c;
}
#endif
// ---------- //
// pio_rx_inv //
// ---------- //
#define pio_rx_inv_wrap_target 0
#define pio_rx_inv_wrap 6
static const uint16_t pio_rx_inv_program_instructions[] = {
// .wrap_target
0xe032, // 0: set x, 18
0x20a0, // 1: wait 1 pin, 0
0xa047, // 2: mov y, osr
0x0083, // 3: jmp y--, 3
0x4001, // 4: in pins, 1
0x0042, // 5: jmp x--, 2
0x8020, // 6: push block
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_rx_inv_program = {
.instructions = pio_rx_inv_program_instructions,
.length = 7,
.origin = -1,
};
static inline pio_sm_config pio_rx_inv_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_rx_inv_wrap_target, offset + pio_rx_inv_wrap);
return c;
}
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
@@ -0,0 +1,178 @@
/*
Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
SPDX-License-Identifier: BSD-3-Clause
*/
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
#include <btstack.h>
#include <pico/btstack_flash_bank.h>
#include <hardware/flash.h>
#include <hardware/sync.h>
#include <string.h>
#include <Arduino.h>
const uint8_t __bluetooth_tlv[8192] __attribute__((aligned(4096))) = { 0 };
extern const uint8_t __flash_binary_start;
#undef PICO_FLASH_BANK_TOTAL_SIZE
#undef PICO_FLASH_BANK_STORAGE_OFFSET
#define PICO_FLASH_BANK_TOTAL_SIZE sizeof(__bluetooth_tlv)
#define PICO_FLASH_BANK_STORAGE_OFFSET ((unsigned int)(__bluetooth_tlv - &__flash_binary_start))
#if 0
// Check sizes
static_assert(PICO_FLASH_BANK_TOTAL_SIZE % (FLASH_SECTOR_SIZE * 2) == 0, "PICO_FLASH_BANK_TOTAL_SIZE invalid");
static_assert(PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
static_assert(PICO_FLASH_BANK_STORAGE_OFFSET + PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
#endif
// Size of one bank
#define PICO_FLASH_BANK_SIZE (PICO_FLASH_BANK_TOTAL_SIZE / 2)
#if 0
#define DEBUG_PRINT(format,args...) printf(format, ## args)
#else
#define DEBUG_PRINT(...)
#endif
static uint32_t pico_flash_bank_get_size(void * context) {
(void)(context);
return PICO_FLASH_BANK_SIZE;
}
static uint32_t pico_flash_bank_get_alignment(void * context) {
(void)(context);
return 1;
}
static void pico_flash_bank_erase(void * context, int bank) {
(void)(context);
DEBUG_PRINT("erase: bank %d\n", bank);
noInterrupts();
rp2040.idleOtherCore();
flash_range_erase(PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank), PICO_FLASH_BANK_SIZE);
rp2040.resumeOtherCore();
interrupts();
}
static void pico_flash_bank_read(void *context, int bank, uint32_t offset, uint8_t *buffer, uint32_t size) {
(void)(context);
DEBUG_PRINT("read: bank %d offset %u size %u\n", bank, offset, size);
assert(bank <= 1);
if (bank > 1) {
return;
}
assert(offset < PICO_FLASH_BANK_SIZE);
if (offset >= PICO_FLASH_BANK_SIZE) {
return;
}
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
if ((offset + size) > PICO_FLASH_BANK_SIZE) {
return;
}
// Flash is xip
memcpy(buffer, (void *)(XIP_BASE + PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank) + offset), size);
}
static void pico_flash_bank_write(void * context, int bank, uint32_t offset, const uint8_t *data, uint32_t size) {
(void)(context);
DEBUG_PRINT("write: bank %d offset %u size %u\n", bank, offset, size);
assert(bank <= 1);
if (bank > 1) {
return;
}
assert(offset < PICO_FLASH_BANK_SIZE);
if (offset >= PICO_FLASH_BANK_SIZE) {
return;
}
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
if ((offset + size) > PICO_FLASH_BANK_SIZE) {
return;
}
if (size == 0) {
return;
}
// calc bank start position
const uint32_t bank_start_pos = PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank);
// Calculate first and last page in the bank
const uint32_t first_page = offset / FLASH_PAGE_SIZE;
const uint32_t last_page = (offset + size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
// Now we only care about the offset in the first page
offset %= FLASH_PAGE_SIZE;
// Amount of data we've copied
uint32_t data_pos = 0;
uint32_t size_left = size;
// Write all the pages required
for (uint32_t page = first_page; page < last_page; page++) {
uint8_t page_data[FLASH_PAGE_SIZE];
assert(data_pos < size && size_left <= size);
// Copy data we're not going to overwrite in the first page
if (page == first_page && offset > 0) {
memcpy(page_data,
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE)),
offset);
}
// Copy the data we're not going to overwrite in the last page
if (page == last_page - 1 && (offset + size_left) < FLASH_PAGE_SIZE) {
memcpy(page_data + offset + size_left,
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE) + offset + size_left),
FLASH_PAGE_SIZE - offset - size_left);
}
// Now copy the new data into the page
const uint32_t size_to_copy = MIN(size_left, FLASH_PAGE_SIZE - offset);
memcpy(page_data + offset, data + data_pos, size_to_copy);
data_pos += size_to_copy;
size_left -= size_to_copy;
// zero offset for the following pages
offset = 0;
// Now program the entire page
noInterrupts();
rp2040.idleOtherCore();
flash_range_program(bank_start_pos + (page * FLASH_PAGE_SIZE), page_data, FLASH_PAGE_SIZE);
rp2040.resumeOtherCore();
interrupts();
}
}
static const hal_flash_bank_t pico_flash_bank_instance_obj = {
/* uint32_t (*get_size)(..) */ &pico_flash_bank_get_size,
/* uint32_t (*get_alignment)(..); */ &pico_flash_bank_get_alignment,
/* void (*erase)(..); */ &pico_flash_bank_erase,
/* void (*read)(..); */ &pico_flash_bank_read,
/* void (*write)(..); */ &pico_flash_bank_write,
};
extern "C" {
const hal_flash_bank_t *pico_flash_bank_instance(void) {
#ifndef NDEBUG
// Check we're not overlapping the binary in flash
//extern char __flash_binary_end;
// assert((uintptr_t)&__flash_binary_end - XIP_BASE <= PICO_FLASH_BANK_STORAGE_OFFSET);
#endif
return &pico_flash_bank_instance_obj;
}
}
#endif
@@ -1,348 +0,0 @@
/*
Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
SPDX-License-Identifier: BSD-3-Clause
*/
// Taken from the Pico-SDK v1.4.0 and hacked by EFP3 to allow overriding the LWIP setup
//#include <stdio.h>
#include "pico/cyw43_arch.h"
#include "pico/mutex.h"
#include "pico/sem.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "cyw43_stats.h"
#include <lwip/init.h>
#include "lwip/timeouts.h"
#ifndef CYW43_PIN_WL_HOST_WAKE
#define CYW43_PIN_WL_HOST_WAKE 24
#endif
#ifndef CYW43_PIN_WL_REG_ON
#define CYW43_PIN_WL_REG_ON 23
#endif
#ifndef CYW43_WL_GPIO_COUNT
#define CYW43_WL_GPIO_COUNT 3
#endif
#ifndef CYW43_WL_GPIO_LED_PIN
#define CYW43_WL_GPIO_LED_PIN 0
#endif
extern volatile bool __inLWIP;
// note same code
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && !NO_SYS
#error PICO_CYW43_ARCH_THREADSAFE_BACKGROUND requires lwIP NO_SYS=1
#endif
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && MEM_LIBC_MALLOC
#error MEM_LIBC_MALLOC is incompatible with PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
#endif
// todo right now we are now always doing a cyw43_dispatch along with a lwip one when hopping cores in low_prio_irq_schedule_dispatch
#ifndef CYW43_SLEEP_CHECK_MS
#define CYW43_SLEEP_CHECK_MS 50 // How often to run lwip callback
#endif
static alarm_id_t periodic_alarm = -1;
static inline uint recursive_mutex_enter_count(recursive_mutex_t *mutex) {
return mutex->enter_count;
}
static inline lock_owner_id_t recursive_mutex_owner(recursive_mutex_t *mutex) {
return mutex->owner;
}
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
enum {
CYW43_DISPATCH_SLOT_CYW43 = 0,
CYW43_DISPATCH_SLOT_ADAPTER,
CYW43_DISPATCH_SLOT_ENUM_COUNT
};
#ifndef CYW43_DISPATCH_SLOT_COUNT
#define CYW43_DISPATCH_SLOT_COUNT CYW43_DISPATCH_SLOT_ENUM_COUNT
#endif
typedef void (*low_prio_irq_dispatch_t)(void);
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f);
static uint8_t cyw43_core_num;
#ifndef NDEBUG
static bool in_low_priority_irq;
#endif
static uint8_t low_priority_irq_num;
static bool low_priority_irq_missed;
static low_prio_irq_dispatch_t low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_COUNT];
static recursive_mutex_t cyw43_mutex;
semaphore_t cyw43_irq_sem;
// Called in low priority pendsv interrupt only to do lwip processing and check cyw43 sleep
static void periodic_worker(void) {
#if CYW43_USE_STATS && LWIP_SYS_CHECK_MS
static uint32_t counter;
if (counter++ % (30000 / LWIP_SYS_CHECK_MS) == 0) {
cyw43_dump_stats();
}
#endif
CYW43_STAT_INC(LWIP_RUN_COUNT);
//#if CYW43_LWIP
if (!__inLWIP) {
sys_check_timeouts();
}
//#endif
if (cyw43_poll) {
if (cyw43_sleep > 0) {
if (--cyw43_sleep == 0) {
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
}
}
}
}
// Regular callback to get lwip to check for timeouts
static int64_t periodic_alarm_handler(__unused alarm_id_t id, __unused void *user_data) {
// Do lwip processing in low priority pendsv interrupt
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_ADAPTER, periodic_worker);
return CYW43_SLEEP_CHECK_MS * 1000;
}
void cyw43_await_background_or_timeout_us(uint32_t timeout_us) {
// if we are called from within an IRQ, then don't wait (we are only ever called in a polling loop)
if (!__get_current_exception()) {
sem_acquire_timeout_us(&cyw43_irq_sem, timeout_us);
}
}
// GPIO interrupt handler to tell us there's cyw43 has work to do
static void gpio_irq_handler(void) {
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
if (events & GPIO_IRQ_LEVEL_HIGH) {
// As we use a high level interrupt, it will go off forever until it's serviced
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
// which is called at the end of cyw43_poll_func
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
// also clear the force bit which we use to progratically cause this handler to fire (on the right core)
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
hw_clear_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
CYW43_STAT_INC(IRQ_COUNT);
}
}
// Low priority interrupt handler to perform background processing
static void low_priority_irq_handler(void) {
assert(cyw43_core_num == get_core_num());
if (recursive_mutex_try_enter(&cyw43_mutex, NULL)) {
if (__inLWIP || (recursive_mutex_enter_count(&cyw43_mutex) != 1)) {
low_priority_irq_missed = true;
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
} else {
CYW43_STAT_INC(PENDSV_RUN_COUNT);
#ifndef NDEBUG
in_low_priority_irq = true;
#endif
for (size_t i = 0; i < count_of(low_priority_irq_dispatch_slots); i++) {
if (low_priority_irq_dispatch_slots[i] != NULL) {
low_prio_irq_dispatch_t f = low_priority_irq_dispatch_slots[i];
low_priority_irq_dispatch_slots[i] = NULL;
f();
}
}
#ifndef NDEBUG
in_low_priority_irq = false;
#endif
}
recursive_mutex_exit(&cyw43_mutex);
} else {
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
low_priority_irq_missed = true;
}
sem_release(&cyw43_irq_sem);
}
static bool low_prio_irq_init(uint8_t priority) {
assert(get_core_num() == cyw43_core_num);
int irq = user_irq_claim_unused(false);
if (irq < 0) {
return false;
}
low_priority_irq_num = (uint8_t) irq;
irq_set_exclusive_handler(low_priority_irq_num, low_priority_irq_handler);
irq_set_enabled(low_priority_irq_num, true);
irq_set_priority(low_priority_irq_num, priority);
return true;
}
static void low_prio_irq_deinit(void) {
if (low_priority_irq_num > 0) {
irq_set_enabled(low_priority_irq_num, false);
irq_remove_handler(low_priority_irq_num, low_priority_irq_handler);
user_irq_unclaim(low_priority_irq_num);
low_priority_irq_num = 0;
}
}
int cyw43_arch_init(void) {
cyw43_core_num = get_core_num();
recursive_mutex_init(&cyw43_mutex);
cyw43_init(&cyw43_state);
sem_init(&cyw43_irq_sem, 0, 1);
// Start regular lwip callback to handle timeouts
periodic_alarm = add_alarm_in_us(CYW43_SLEEP_CHECK_MS * 1000, periodic_alarm_handler, NULL, true);
if (periodic_alarm < 0) {
return PICO_ERROR_GENERIC;
}
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
irq_set_enabled(IO_IRQ_BANK0, true);
lwip_init();
// start low priority handler (no background work is done before this)
bool ok = low_prio_irq_init(PICO_LOWEST_IRQ_PRIORITY);
if (!ok) {
cyw43_arch_deinit();
return PICO_ERROR_GENERIC;
}
return PICO_OK;
}
void cyw43_arch_deinit(void) {
if (periodic_alarm >= 0) {
cancel_alarm(periodic_alarm);
periodic_alarm = -1;
}
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
gpio_remove_raw_irq_handler(IO_IRQ_BANK0, gpio_irq_handler);
low_prio_irq_deinit();
}
void cyw43_post_poll_hook(void) {
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
}
// This is called in the gpio and low_prio_irq interrupts and on either core
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f) {
assert(slot < count_of(low_priority_irq_dispatch_slots));
low_priority_irq_dispatch_slots[slot] = f;
if (cyw43_core_num == get_core_num()) {
//on same core, can dispatch directly
irq_set_pending(low_priority_irq_num);
} else {
// on wrong core, so force via GPIO IRQ which itself calls this method for the CYW43 slot.
// since the CYW43 slot always uses the same function, this is fine with the addition of an
// extra (but harmless) CYW43 slot call when another SLOT is invoked.
// We could do better, but would have to track why the IRQ was called.
io_irq_ctrl_hw_t *irq_ctrl_base = cyw43_core_num ?
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
hw_set_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
}
}
void cyw43_schedule_internal_poll_dispatch(void (*func)(void)) {
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, func);
}
// Prevent background processing in pensv and access by the other core
// These methods are called in pensv context and on either core
// They can be called recursively
void cyw43_thread_enter(void) {
// Lock the other core and stop low_prio_irq running
recursive_mutex_enter_blocking(&cyw43_mutex);
}
#ifndef NDEBUG
void cyw43_thread_lock_check(void) {
// Lock the other core and stop low_prio_irq running
if (recursive_mutex_enter_count(&cyw43_mutex) < 1 || recursive_mutex_owner(&cyw43_mutex) != lock_get_caller_owner_id()) {
panic("cyw43_thread_lock_check failed");
}
}
#endif
// Re-enable background processing
void cyw43_thread_exit(void) {
// Run low_prio_irq if needed
if (1 == recursive_mutex_enter_count(&cyw43_mutex)) {
// note the outer release of the mutex is not via cyw43_exit in the low_priority_irq case (it is a direct mutex exit)
assert(!in_low_priority_irq);
// if (low_priority_irq_missed) {
// low_priority_irq_missed = false;
if (low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_CYW43]) {
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
}
// }
}
recursive_mutex_exit(&cyw43_mutex);
}
static void cyw43_delay_until(absolute_time_t until) {
// sleep can be called in IRQs, so there's not much we can do there
if (__get_current_exception()) {
busy_wait_until(until);
} else {
sleep_until(until);
}
}
void cyw43_delay_ms(uint32_t ms) {
cyw43_delay_until(make_timeout_time_ms(ms));
}
void cyw43_delay_us(uint32_t us) {
cyw43_delay_until(make_timeout_time_us(us));
}
void cyw43_arch_poll() {
// should not be necessary
// if (cyw43_poll) {
// low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
// }
}
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
void __attribute__((weak)) cyw43_cb_tcpip_init(cyw43_t *self, int itf);
void cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
(void) self;
(void) itf;
}
void __attribute__((weak)) cyw43_cb_tcpip_deinit(cyw43_t *self, int itf);
void cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
(void) self;
(void) itf;
}
void __attribute__((weak)) cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf);
void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
(void) self;
(void) itf;
}
void __attribute__((weak)) cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf);
void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
(void) self;
(void) itf;
}
void __attribute__((weak)) cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf);
void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
(void) cb_data;
(void) itf;
(void) len;
(void) buf;
}
#endif
#endif
+101
View File
@@ -0,0 +1,101 @@
/*
Expost absolute mouse HID descriptor and report
Taken from @tobozo PR https://github.com/hathach/tinyusb/pull/1363
TODO - remove once that PR merged with TinyUSB
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "tusb.h"
#include "class/hid/hid_device.h"
// Absolute Mouse: same as the Standard (relative) Mouse Report but
// with int16_t instead of int8_t for X and Y coordinates.
typedef struct TU_ATTR_PACKED {
uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
int16_t x; /**< Current x position of the mouse. */
int16_t y; /**< Current y position of the mouse. */
int8_t wheel; /**< Current delta wheel movement on the mouse. */
int8_t pan; // using AC Pan
} hid_abs_mouse_report_t;
// Absolute Mouse Report Descriptor Template
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
__VA_ARGS__ \
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
HID_USAGE_MIN ( 1 ) ,\
HID_USAGE_MAX ( 5 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX ( 1 ) ,\
/* Left, Right, Middle, Backward, Forward buttons */ \
HID_REPORT_COUNT( 5 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
/* 3 bit padding */ \
HID_REPORT_COUNT( 1 ) ,\
HID_REPORT_SIZE ( 3 ) ,\
HID_INPUT ( HID_CONSTANT ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
/* X, Y absolute position [0, 32767] */ \
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
HID_LOGICAL_MIN ( 0x00 ) ,\
HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
HID_REPORT_SIZE ( 16 ) ,\
HID_REPORT_COUNT ( 2 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
/* Vertical wheel scroll [-127, 127] */ \
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
HID_LOGICAL_MIN ( 0x81 ) ,\
HID_LOGICAL_MAX ( 0x7f ) ,\
HID_REPORT_COUNT( 1 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
/* Horizontal wheel scroll [-127, 127] */ \
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
HID_LOGICAL_MIN ( 0x81 ), \
HID_LOGICAL_MAX ( 0x7f ), \
HID_REPORT_COUNT( 1 ), \
HID_REPORT_SIZE ( 8 ), \
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
HID_COLLECTION_END , \
HID_COLLECTION_END \
static inline bool tud_hid_abs_mouse_report(uint8_t report_id,
uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
hid_abs_mouse_report_t report = {
.buttons = buttons,
.x = x,
.y = y,
.wheel = vertical,
.pan = horizontal
};
return tud_hid_n_report(0, report_id, &report, sizeof(report));
}
+1 -1
View File
@@ -77,7 +77,7 @@ static std::map<pin_size_t, CBInfo> _map;
void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
(void) events;
// Only need to lock around the std::map check, not the whole IRQ callback
CoreMutex m(&_irqMutex, (FromISR | DebugEnable));
CoreMutex m(&_irqMutex);
if (m) {
auto irq = _map.find(gpio);
if (irq != _map.end()) {
+1 -1
View File
@@ -47,7 +47,7 @@ Adjusts the pin to record. Only legal before ``ADCInput::begin()``.
bool setFrequency(long sampleRate)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the ADC sampling frequency, but does not start recording (however if the
device was already running, it will wontinue to run at the new frequency). Note
device was already running, it will continue to run at the new frequency). Note
that every pin requested will be sampled at this frequency, one after the other.
That is, if you have code like this:
+2 -2
View File
@@ -14,12 +14,12 @@ Returns a value from 0...4095 correspionding to the ADC reading
of the specific pin.
void analogReadResolution(int bits)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Determines the resolution (in bits) of the value returned by the analogRead() function.
Default resolution is 10bit.
float analogReadTemp(float vref = 3.3f)
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Returns the temperature, in Celsius, of the onboard thermal sensor.
If you have a custom Vref for the ADC on your RP2040 board, you can pass it in as a parameter. Calling with no parameters assumes the normal, 3.3V Vref.
This reading is not exceedingly accurate and of relatively low
+49
View File
@@ -0,0 +1,49 @@
Bluetooth on PicoW Support
==========================
As of the Pico-SDK version 1.5.0, the PicoW has **BETA** Bluetooth support.
So, since this core builds off the SDK the best that can be suggested it
that we have **ALPHA** Bluetooth support. As such, bug reports are welcome,
but Pull Requests fixing problems you find are seriously appreciated.
Enabling Bluetooth
------------------
To enable Bluetooth (BT), use the ``Tools->IP/Bluetooth Stack`` menu. It
requires around 80KB of flash and 20KB of RAM when enabled.
Both Bluetooth Classic and BluetoothBLE are enabled in ``btstack_config.h``.
Included Bluetooth Libraries
----------------------------
You may use the ``KeyboardBT``, ``MouseBT``, or ``JoystickBT`` to emulate a
Bluetooth Classic HID device using the same API as their USB versions.
You may use the ``KeyboardBLE``, ``MouseBLE``, or ``JoystickBLE`` to emulate a
Bluetooth Low Energy (BLE) HID device using the same API as their USB versions.
The ``SerialBT`` library implements a very simple SPP (Serial Port Profile)
Serial-compatible port.
Writing Custom Bluetooth Applications
-------------------------------------
You may also write full applications using the ``BTStack`` standard callback
method, but please be aware that the Raspberry Pi team has built an
interrupt-driven version of the BT execute loop, so there is no need
to actually call ``btstack_run_loop_execute`` because the ``async_context``
handler will do it for you.
There is no need to call ``cyw43_arch_init`` in your code, either, as that
is part of the PicoW variant booting process.
For many BTStack examples, you simply need call the included
``btstack_main()`` and make sure that ``hci_power_control(HCI_POWER_ON);`` is
called afterwards to start processing (in the background).
You will also need to acquire the BT ``async_context`` system lock before
calling any BTStack APIs. See the ``libraries/PicoBluetoothHID`` helper
class for an example of how to do this.
Note that if you need to modify the system ``btstack_config.h`` file, do so
in the ``tools/libpico`` directory and rebuild the Pico SDK static library.
Otherwise the change will not take effect in the precompiled code, leading
to really bad behavior.
+2 -2
View File
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
# built documents.
#
# The short X.Y version.
version = u'2.7.2'
version = u'3.2.1'
# The full version, including alpha/beta/rc tags.
release = u'2.7.2'
release = u'3.2.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+100
View File
@@ -0,0 +1,100 @@
Contributing and Porting to the Core
====================================
First of all, thank you for contributing to the project. It's a lot of work
keeping up with all the different uses of the RP2040, so the more people
working on the code, the better. Your assistance can help the project
succeed.
Contributing to the Core (Pull Requests)
----------------------------------------
We use the standard GitHub Pull Request model. If you're unfamiliar with it,
this `guide <https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/>`__
gives a simple overview of the process.
All pull requests have to pass a set of Continuous Integration (CI) checks
which help make sure the code compiles under different configurations and has
no spelling or style errors.
Tips for a Good Pull Request (PR)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All code in the core and libraries, except for INO sketches, uses a 4-space
indent with cuddled brackets. When in doubt, copy your formatting from the
surrounding code. You should install ``astyle`` and run ``tests/restyle.sh``
on your machine before committing and pushing any pull requests to ensure
the formatting is correct.
Describe the change you're proposing and why it's important in your
``git commit`` message. If it fixes an open issue, place ``Fixes #xxxx``
(where xxxx is the issue number) in the message to link the two.
Try and only change one thing per pull request. That makes it easier to
review and prioritize. Opening up a separate PR per change also helps keep
track of them when release messages are generated.
Adding a New Board
------------------
Adding a new board requires:
* Updated ``tools/makeboards.py`` script
* Updated ``boards.txt`` file, generated by ``makeboard.py``
* Updated ``package_pico_index.template.json`` file, generated by ``makeboard.py``
* New ``tools/json/BOARD_NAME.json`` board file for Platform.IO
* New ``variants/BOARD_NAME/pins_arduino.h`` header defining the I/O pins
To add a new RP2040 board you will need to update the ``tools/makeboards.py``
script. Do *NOT* manually edit ``boards.txt``, that file is machine generated.
You will need to add a ``MakeBoard`` call at the end of the file. Please be sure
to add your board so that it sorts alphabetically, starting with the company name
and then the board name. Otherwise it is hard to find a specific board in the menu.
Run ``python3 tools/makeboards.py`` to update the ``boards.txt`` file and generate
a Platform.IO JSON file in the ``tools/json`` directory.
Create a folder called ``variants/BOARD_NAME`` and place in a ``pins_arduino.h``
file in it that contains your default pin name mapping (i.e. SPI0/1 pins, UART
pins, LED_DEFAULT, etc.). Copying one of the existing ones as a template can
make this task much simpler.
In your ``git commit`` be sure to add the newly generated ``tools/json/XXX.json``
file as well as the modified ``makeboards`` script and ``boards.txt``, the new
``pins_arduino.h`` header you generated, and the Arduino packaging JSON
``package/package_pico_index.template.json``. You should also add a note in
the ``README.md`` file listing your new board.
Submit the updated commit as a PR and, if all goes well, your board will be in
on the next core release.
Porting Libraries and Applications to the Core
----------------------------------------------
We try and follow Arduino standards so, with luck, porting to this core should
be relatively straightforward. The ``WiFi`` library and associates support
libraries like ``WebServer`` are modeled after the ESP32 and ESP8266 versions
of those libraries, combined with the "standard" Arduino ``WiFi`` one.
Compiler Defines for Porting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are adding RP2040 support to an existing library and need to isolate
code that only runs on this core, use the following define.
.. code:: cpp
#if defined(ARDUINO_ARCH_RP2040) && !defined(__MBED__)
~~~ your changes ~~~
#endif
Library Architectures
~~~~~~~~~~~~~~~~~~~~~
After adding support in the code, libraries need their ``library.properties``
and ``library.json`` files updated to indicate support, or the IDE will
not know your new code is compatible here.
Add ``rp2040`` to ``architectures`` (in ``library.properties``) and
``"rp2040"`` to ``platforms[]`` (in ``library.json``) to let the tools know.
+2 -2
View File
@@ -1,5 +1,5 @@
Getting Help and Contributing
=============================
Getting Help
============
This is a community supported project and has multiple ways to get assistance.
Posting complete details, in a polite and organized way will get the best
+7
View File
@@ -64,6 +64,13 @@ Enables LSB-J format for I2S output. In this mode the MSB comes out at the
same time as the LRCLK changes, and not the normal 1-cycle delay. Useful for
DAC chips like the PT8211.
bool swapClocks()
~~~~~~~~~~~~~~~~~
Certain boards are hardwired with the WCLK before the BCLK, instead of the normal
way around. This call swaps the WCLK and BCLK pins. Note that you still call
``setBCLK(x)`` with ``x`` being the lowest pin ID (i.e. in swapClocks mode the
``setBCLK`` call actually sets LRCLK).
bool begin()/begin(long sampleRate)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Start the I2S device up with the given sample rate, or with the value set
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

+5 -1
View File
@@ -16,7 +16,9 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
:maxdepth: 2
:caption: Contents:
Getting Help and Contributing <help>
Getting Help <help>
Contributing <contrib>
Installation <install>
IDE Menus <ide>
@@ -41,6 +43,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
USB (Arduino and Adafruit_TinyUSB) <usb>
Multicore Processing <multicore>
Bluetooth (Alpha/Beta) <bluetooth>
Single File USB Drive <singlefile>
FreeRTOS SMP (multicore) <freertos>
+23 -1
View File
@@ -38,6 +38,22 @@ prevent the Arduino IDE from accessing. For Arduino IDE V2, override the filesys
restriction using ``flatpak override --user --filesystem=host cc.arduino.IDE2`` . For
For Arduino IDE < V2, use ``flatpak override --user --filesystem=host cc.arduino.arduinoide``.
Installing via Arduino CLI
--------------------------
To install using the Arduino command line tool (arduino-cli):
.. code:: bash
arduino-cli config add board_manager.additional_urls https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
arduino-cli core update-index
arduino-cli core install rp2040:rp2040
To list the supported boards:
.. code:: bash
arduino-cli board listall | grep rp2040
Installing via GIT
------------------
To install via GIT (for latest and greatest versions):
@@ -143,7 +159,13 @@ So for the first sketch you will need to rebuild (with the ``Upload Method->Pico
After the initial upload, as long as the running binary was built using the ``Picotool`` upload method, then the normal upload process should work.
Note that for Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
Under MacOS, it may be necessary to install the USB support libraries from a command terminal before the ``Picotool`` upload method can be used:
.. code::
brew install libusb
For Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
.. code::
+85 -1
View File
@@ -23,6 +23,46 @@ Especially useful is the `Getting started with VSCode + PlatformIO <https://docs
Hereafter it is assumed that you have a basic understanding of PlatformIO in regards to project creation, project file structure and building and uploading PlatformIO projects, through reading the above pages.
Important steps for Windows users, before installing
----------------------------------------------------
By default, Windows has a limited path length that is not long enough to fully clone the ``Pico-SDK``'s ``tinyusb`` repository, resulting in error messages like the one below while attempting to fetch the repository.
.. code::
error: unable to create file '.....' : Filename too long
To work around this requires performing two steps and rebooting Windows once. These steps will enable longer file paths at the Windows OS and the ``git`` level.
Step 1: Enabling long paths in git
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Open up a Windows ``cmd`` or ``terminal`` window and execute the following command
.. code::
git config --system core.longpaths true
Step 2: Enabling long paths in the Windows OS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(taken from https://www.microfocus.com/documentation/filr/filr-4/filr-desktop/t47bx2ogpfz7.html)
1. Click Window key and type gpedit.msc, then press the Enter key. This launches the Local Group Policy Editor.
2. Navigate to Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem.
3. Double click Enable NTFS/Win32 long paths and close the dialog.
.. image:: images/longpath.png
Step 3: Reboot the computer
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once the two prior stages are complete, please do a full reboot or power cycle so that the new settings will take effect.
Current state of development
----------------------------
@@ -234,6 +274,17 @@ The lwIP stack can be configured to support only IPv4 (default) or additionally
to the ``platformio.ini``.
Bluetooth Stack
---------------
The Bluetooth Classic (BTC) and Bluetooth Low Energy (BLE) stack can be activated by adding
.. code:: ini
; BTC and BLE
build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH
to the ``platformio.ini``.
Selecting a different core version
----------------------------------
@@ -290,10 +341,15 @@ To specify the debugging adapter, use ``debug_tool`` (`documentation <https://do
* ``cmsis-dap``
* ``jlink``
* ``raspberrypi-swd``
* ``blackmagic``
* ``pico-debug``
These values can also be used in ``upload_protocol`` if you want PlatformIO to upload the regular firmware through this method, which you likely want.
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`_) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`_ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`__) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`__ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
.. note::
Newer PicoProbe firmware versions have dropped the proprietary "PicoProbe" USB communication protocol and emulate a **CMSIS-DAP** instead. Meaning, you have to use ``debug_tool = cmsis-dap`` for these newer firmwares, such as those obtained from `raspberrypi/picoprobe <https://github.com/raspberrypi/picoprobe/releases>`__
With that set up, debugging can be started via the left debugging sidebar and works nicely: Setup breakpoints, inspect the value of variables in the code, step through the code line by line. When a breakpoint is hit or execution is halted, you can even see the execution state both Cortex-M0+ cores of the RP2040.
@@ -301,6 +357,30 @@ With that set up, debugging can be started via the left debugging sidebar and wo
For further information on customizing debug options, like the initial breakpoint or debugging / SWD speed, consult `the documentation <https://docs.platformio.org/en/latest/projectconf/section_env_debug.html>`_.
.. note::
For the BlackMagicProbe debugging probe (as can be e.g., created by simply flashing a STM32F103C8 "Bluepill" board), you currently have to use the branch ``fix/rp2040-flash-reliability`` (or at least commit ``1d001bc``) **and** use the `official ARM provided toolchain <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503393266>`_.
You can obtain precompiled binaries from `here <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503372723>`__. A flashing guide is available `here <https://primalcortex.wordpress.com/2017/06/13/building-a-black-magic-debug-probe/>`__. You then have to configure the target serial port ("GDB port") in your project per `documentation <https://docs.platformio.org/en/latest/plus/debug-tools/blackmagic.html#debugging-tool-blackmagic>`__.
.. note::
For the pico-debug (`download <https://github.com/majbthrd/pico-debug/releases>`__) debugging way, *which needs to no additional debug probe*, add this snippet to your ``platformio.ini`` and follow the given procedure:
.. code:: ini
upload_protocol = pico-debug
debug_tool = pico-debug
build_flags = -DPIO_FRAMEWORK_ARDUINO_NO_USB
1. Build your firmware normally
2. Plug in the Pico in BOOTSEL mode
3. Drag and drop your ``.pio/build/<env>/firmware.uf2`` onto the boot drive
4. Unplug and replug your Pico back into BOOTSEL mode for the second time
5. Drag and drop the downloaded ``pico-debug-gimmecache.uf2`` file onto the boot drive
6. A CMSIS-DAP device should now appear on your computer
7. Start debugging via the debug sidebar as normal
Note the restrictions: The second core cannot be used, the USB port cannot be used (no USB serial, only UART serial), 16KB less RAM is available.
Filesystem Uploading
--------------------
@@ -315,3 +395,7 @@ The files you want to upload should be placed in a folder called ``data`` inside
The task "Build Filesystem Image" will take all files in the data directory and create a ``littlefs.bin`` file from it using the ``mklittlefs`` tool.
The task "Upload Filesystem Image" will upload the filesystem image to the Pico via the specified ``upload_protocol``.
.. note::
Set the space available for the filesystem in the ``platformio.ini`` using e.g., ``board_build.filesystem_size = 0.5m``, or filesystem
creation will fail!
+18 -2
View File
@@ -13,6 +13,10 @@ Returns the current frequency of the core clock. This is read at runtime,
versus the constant ``F_CPU`` macro that is also available. This is useful
in cases where your code changes the core clock (i.e. low power modes, etc.)
int rp2040.cpuid()
~~~~~~~~~~~~~~~~~~
Returns the current core ID (0 or 1) of the executing task.
uint32_t rp2040.getCycleCount()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Returns a 32-bit cycle count from then the core started running. Because it
@@ -41,13 +45,16 @@ Hardware Watchdog
-----------------
void rp2040.wdt_begin(uint32_t delay_ms)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Enables the hardware watchdog timer with a delay value of delay_ms
milliseconds. Note that on the RP2040, once this function has called, the
hardware watchdog can _not_ be disabled.
The maximum ``delay_ms`` allowed in this call is ``8300``, corresponding
to 8.3 seconds. Any higher values will be truncated by the hardware.
void rp2040.wdt_reset()
~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~
Reloads the watchdog's counter with the amount of time set by wdt_begin.
@@ -71,6 +78,15 @@ Returns the total heap that was available to this program at compile time (i.e.
the Pico RAM size minus things like the ``.data`` and ``.bss`` sections and other
overhead).
Hardware Identification
-----------------------
bool isPicoW()
~~~~~~~~~~~~~~
Returns the core's best guess if this code is running on a Raspberry Pi Pico W.
This would let you, possibly, use the same UF2 for Pico and PicoW by simply not
doing any WiFi calls.
Bootloader
----------
+74
View File
@@ -0,0 +1,74 @@
#pragma once
// BTstack features that can be enabled
#define ENABLE_LOG_INFO
#define ENABLE_LOG_ERROR
#define ENABLE_PRINTF_HEXDUMP
#define ENABLE_SCO_OVER_HCI
#ifdef ENABLE_CLASSIC
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
#endif
#ifdef ENABLE_BLE
#define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
#define ENABLE_LE_PERIPHERAL
#define ENABLE_LE_CENTRAL
#endif
// BTstack configuration. buffers, sizes, ...
#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
#define MAX_NR_AVDTP_CONNECTIONS 1
#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
#define MAX_NR_AVRCP_CONNECTIONS 2
#define MAX_NR_BNEP_CHANNELS 1
#define MAX_NR_BNEP_SERVICES 1
#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
#define MAX_NR_GATT_CLIENTS 1
#define MAX_NR_HCI_CONNECTIONS 2
#define MAX_NR_HID_HOST_CONNECTIONS 1
#define MAX_NR_HIDS_CLIENTS 1
#define MAX_NR_HFP_CONNECTIONS 1
#define MAX_NR_L2CAP_CHANNELS 4
#define MAX_NR_L2CAP_SERVICES 3
#define MAX_NR_RFCOMM_CHANNELS 1
#define MAX_NR_RFCOMM_MULTIPLEXERS 1
#define MAX_NR_RFCOMM_SERVICES 1
#define MAX_NR_SERVICE_RECORD_ITEMS 4
#define MAX_NR_SM_LOOKUP_ENTRIES 3
#define MAX_NR_WHITELIST_ENTRIES 16
#define MAX_NR_LE_DEVICE_DB_ENTRIES 16
// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
#define MAX_NR_CONTROLLER_ACL_BUFFERS 3
#define MAX_NR_CONTROLLER_SCO_PACKETS 3
// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
#define HCI_HOST_ACL_PACKET_LEN 1024
#define HCI_HOST_ACL_PACKET_NUM 3
#define HCI_HOST_SCO_PACKET_LEN 120
#define HCI_HOST_SCO_PACKET_NUM 3
// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
#define NVM_NUM_DEVICE_DB_ENTRIES 16
#define NVM_NUM_LINK_KEYS 16
// We don't give btstack a malloc, so use a fixed-size ATT DB.
#define MAX_ATT_DB_SIZE 512
// BTstack HAL configuration
#define HAVE_EMBEDDED_TIME_MS
// map btstack_assert onto Pico SDK assert()
#define HAVE_ASSERT
// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
#define HCI_RESET_RESEND_TIMEOUT_MS 1000
#define ENABLE_SOFTWARE_AES128
#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
#undef HAVE_BTSTACK_STDIN
+7 -1
View File
@@ -13,6 +13,9 @@ extern void interrupts();
#define SYS_ARCH_PROTECT(lev) noInterrupts
#define SYS_ARCH_UNPROTECT(lev) interrupts
extern unsigned long get_rand_32(void);
#define LWIP_RAND() get_rand_32()
// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
@@ -25,7 +28,7 @@ extern void interrupts();
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ARP 2
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
@@ -53,6 +56,9 @@ extern void interrupts();
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
// See #1285
#define MEMP_NUM_UDP_PCB 6
#if LWIP_IPV6
#define LWIP_IPV6_DHCP6 1
#endif
+2 -2
View File
@@ -12,8 +12,8 @@
#define _PICO_VERSION_H
#define PICO_SDK_VERSION_MAJOR 1
#define PICO_SDK_VERSION_MINOR 4
#define PICO_SDK_VERSION_MINOR 5
#define PICO_SDK_VERSION_REVISION 0
#define PICO_SDK_VERSION_STRING "1.4.0"
#define PICO_SDK_VERSION_STRING "1.5.0"
#endif
+2
View File
@@ -44,6 +44,8 @@ restartCore1 KEYWORD2
reboot KEYWORD2
restart KEYWORD2
isPicoW KEYWORD2
getChipID KEYWORD2
hwrand32 KEYWORD2
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10 -6
View File
@@ -191,11 +191,6 @@ SECTIONS
__data_end__ = .;
} > RAM AT> FLASH
.uninitialized_data (COPY): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
@@ -213,7 +208,7 @@ SECTIONS
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
.bss : {
.bss : {
. = ALIGN(4);
__bss_start__ = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
@@ -222,6 +217,15 @@ SECTIONS
__bss_end__ = .;
} > RAM
/* At most one of the following two will be engaged, depending on the SDK version */
.uninitialized_ram MAX(0x20003000, .) (NOLOAD) : {
*(.uninitialized_ram*)
} > RAM
.uninitialized_data MAX(0x20003000, .) (NOLOAD) : {
*(.uninitialized_data*)
} > RAM
.heap (COPY):
{
__end__ = .;
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
source [find interface/cmsis-dap.cfg]
adapter speed 5000
source [find target/rp2040.cfg]
+6
View File
@@ -40,8 +40,11 @@
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_vreg/include
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_watchdog/include
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_xosc/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_async_context/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_bootrom/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_btstack/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_arch/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_driver/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_double/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_float/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_int64_ops/include
@@ -50,8 +53,11 @@
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_platform/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_printf/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_runtime/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_rand/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio_uart/include
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_unique_id/include
-iwithprefixbefore/pico-sdk/lib/cyw43-driver/src
-iwithprefixbefore/pico-sdk/lib/lwip/src/include
-iwithprefixbefore/pico-sdk/lib/btstack/src
-iwithprefixbefore/pico-sdk/lib/btstack/platform/embedded
+11
View File
@@ -200,3 +200,14 @@
-Wl,--wrap=raw_bind
-Wl,--wrap=raw_sendto
-Wl,--wrap=raw_remove
-Wl,--wrap=cyw43_cb_process_ethernet
-Wl,--wrap=cyw43_cb_tcpip_set_link_up
-Wl,--wrap=cyw43_cb_tcpip_set_link_down
-Wl,--wrap=cyw43_tcpip_link_status
-Wl,--wrap=cyw43_cb_tcpip_init
-Wl,--wrap=cyw43_cb_tcpip_deinit
-Wl,--wrap=get_rand_64
-Wl,--wrap=get_rand_32
-Wl,--wrap=sleep_until
+1
View File
@@ -0,0 +1 @@
This is the BTstack ports/arduino library, with compile errors fixed only.
+105
View File
@@ -0,0 +1,105 @@
#include <BTstackLib.h>
#include <stdio.h>
#include "ble/att_server.h"
#include "ble/gatt_client.h"
#include "ble/gatt-service/ancs_client.h"
#include "ble/sm.h"
#include "btstack_event.h"
#include <SPI.h>
/*
EXAMPLE_START(ANCS): ANCS Client
*/
/*
@section Advertisement
@text An ANCS Client needs to include the ANCS UUID in its advertisement to
get recognized by iOS
*/
/* LISTING_START(ANCSAdvertisement): ANCS Advertisement */
const uint8_t adv_data[] = {
// Flags general discoverable
0x02, 0x01, 0x02,
// Name
0x05, 0x09, 'A', 'N', 'C', 'S',
// Service Solicitation, 128-bit UUIDs - ANCS (little endian)
0x11, 0x15, 0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79
};
/* LISTING_END(ANCSAdvertisement): ANCS Advertisement */
/*
@section Setup
@text In the setup, the LE Security Manager is configured to accept pairing requests.
Then, the ANCS Client library is initialized and and ancs_callback registered.
Finally, the Advertisement data is set and Advertisements are started.
*/
/* LISTING_START(ANCSSetup): ANCS Setup */
void setup(void) {
Serial.begin(9600);
Serial.println("BTstack ANCS Client starting up...");
// startup BTstack and configure log_info/log_error
BTstack.setup();
sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
sm_set_authentication_requirements(SM_AUTHREQ_BONDING);
// setup ANCS Client
ancs_client_init();
ancs_client_register_callback(&ancs_callback);
// enable advertisements
BTstack.setAdvData(sizeof(adv_data), adv_data);
BTstack.startAdvertising();
}
/* LISTING_END(ANCSSetup): ANCS Setup */
void loop(void) {
BTstack.loop();
}
/*
@section ANCS Callback
@text In the ANCS Callback, connect and disconnect events are received.
For actual notifications, ancs_client_attribute_name_for_id allows to
look up the name. To get the notification body, e.g., the actual message,
the GATT Client needs to be used directly.
*/
/* LISTING_START(ANCSCallback): ANCS Callback */
void ancs_callback(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
(void) packet_type;
(void) channel;
(void) size;
const char * attribute_name;
if (hci_event_packet_get_type(packet) != HCI_EVENT_ANCS_META) {
return;
}
switch (hci_event_ancs_meta_get_subevent_code(packet)) {
case ANCS_SUBEVENT_CLIENT_CONNECTED:
Serial.println("ANCS Client: Connected");
break;
case ANCS_SUBEVENT_CLIENT_DISCONNECTED:
Serial.println("ANCS Client: Disconnected");
break;
case ANCS_SUBEVENT_CLIENT_NOTIFICATION:
attribute_name = ancs_client_attribute_name_for_id(ancs_subevent_client_notification_get_attribute_id(packet));
if (!attribute_name) {
break;
}
Serial.print("Notification: ");
Serial.print(attribute_name);
Serial.print(" - ");
Serial.println(ancs_subevent_client_notification_get_text(packet));
break;
default:
break;
}
}
/* LISTING_END(ANCSCallback): ANCS Callback */
@@ -0,0 +1,328 @@
#include <BTstackLib.h>
#include <SPI.h>
/*
EXAMPLE_START(LECentral): LE Central
@text Compared with the other examples, the LE Central is
a bit more complex. This is because it performs multiple
steps in sequence as it is common with GATT Client APIs.
It shows how to first scan for other
devices and then connect to one. When connected, a series of
GATT Client operations are performed: first the list of
GATT Services is queried. If a particular service is found,
the list of its GATT Characteristics is retrieved and a set
of known Characteristics are cached for later access.
*/
/*
@section Characteristic Summary
@text As multiple Characteristics need to be found, a custom
struct is used to collect all information about it. This allows
to define the list of necessary characteristics in the
characteristics[] array
*/
/* LISTING_START(LECentralSummary): Characteristic Summary */
// BLE Shield Service V2 incl. used Characteristics
UUID bleShieldServiceV2UUID("B8E06067-62AD-41BA-9231-206AE80AB550");
typedef struct characteristic_summary {
UUID uuid;
const char * name;
bool found;
BLECharacteristic characteristic;
} characteristic_summary_t;
typedef enum characteristicIDs {
charRX = 0,
charTX,
charBaud,
charBdAddr,
numCharacteristics /* last one */
} characteristicIDs_t;
characteristic_summary characteristics[] = {
{ UUID("f897177b-aee8-4767-8ecc-cc694fd5fcee"), "RX", false, BLECharacteristic() },
{ UUID("bf45e40a-de2a-4bc8-bba0-e5d6065f1b4b"), "TX", false, BLECharacteristic() },
{ UUID("2fbc0f31-726a-4014-b9fe-c8be0652e982"), "Baudrate", false, BLECharacteristic() },
{ UUID("65c228da-bad1-4f41-b55f-3d177f4e2196"), "BD ADDR", false, BLECharacteristic() }
};
/* LISTING_END(LECentralSummary): Characteristic Summary */
// Application state
BLEDevice myBLEDevice;
BLEService myBLEService;
bool serviceFound;
bool sendCounter = false;
int counter = 0;
char counterString[20];
// static btstack_timer_source_t heartbeat;
/*
@section Setup
@text In the setup, various callbacks are registered. After that
we start scanning for other devices
*/
/* LISTING_START(LECentralSetup): LE Central Setup */
void setup(void) {
Serial.begin(9600);
BTstack.setBLEAdvertisementCallback(advertisementCallback);
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
BTstack.setGATTServiceDiscoveredCallback(gattServiceDiscovered);
BTstack.setGATTCharacteristicDiscoveredCallback(gattCharacteristicDiscovered);
BTstack.setGATTCharacteristicNotificationCallback(gattCharacteristicNotification);
BTstack.setGATTCharacteristicReadCallback(gattReadCallback);
BTstack.setGATTCharacteristicWrittenCallback(gattWrittenCallback);
BTstack.setGATTCharacteristicSubscribedCallback(gattSubscribedCallback);
BTstack.setup();
BTstack.bleStartScanning();
}
/* LISTING_END(LECentralSetup): LE Central Setup */
/*
@section Loop
@text In the standard Arduino loop() function, BTstack's loop() is called first
If we're connected, we send the string "BTstack" plus a counter as fast as possible.
As the Bluetooth module might be busy, it's important to check the result of the
writeCharacteristicWithoutResponse() call. If it's not ok, we just try again in the
next loop iteration.
*/
/* LISTING_START(LECentralLoop): Loop */
void loop(void) {
BTstack.loop();
// send counter as fast as possible
if (sendCounter) {
sprintf(counterString, "BTstack %u\n", counter);
int result = myBLEDevice.writeCharacteristicWithoutResponse(&characteristics[charTX].characteristic, (uint8_t*) counterString, strlen(counterString));
if (result == 0) {
Serial.print("Wrote without response: ");
Serial.println(counterString);
counter++;
}
}
}
/* LISTING_END(LECentralLoop): Loop */
/*
@section Advertisement Callback
@text When an Advertisement is received, we check if it contains
the UUID of the service we're interested in. Only a single service
with a 128-bit UUID can be contained in and Advertisement and not
all BLE devices provides this. Other options are to match on the
reported device name or the BD ADDR prefix.
If we found an interesting device, we try to connect to it.
*/
/* LISTING_START(LECentralAdvertisementCallback): Advertisement Callback */
void advertisementCallback(BLEAdvertisement *bleAdvertisement) {
Serial.print("Device discovered: ");
Serial.print(bleAdvertisement->getBdAddr()->getAddressString());
Serial.print(", RSSI: ");
Serial.println(bleAdvertisement->getRssi());
if (bleAdvertisement->containsService(&bleShieldServiceV2UUID)) {
Serial.println("\nBLE ShieldService V2 found!\n");
BTstack.bleStopScanning();
BTstack.bleConnect(bleAdvertisement, 10000); // 10 s
}
}
/* LISTING_END(LECentralAdvertisementCallback): Advertisement Callback */
/*
@section Device Connected Callback
@text At the end of bleConnect(), the device connected callback is callec.
The status argument tells if the connection timed out, or if the connection
was established successfully.
On a successful connection, a GATT Service Discovery is started.
*/
/* LISTING_START(LECentralDeviceConnectedCallback): Device Connected Callback */
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
switch (status) {
case BLE_STATUS_OK:
Serial.println("Device connected!");
myBLEDevice = *device;
counter = 0;
myBLEDevice.discoverGATTServices();
break;
case BLE_STATUS_CONNECTION_TIMEOUT:
Serial.println("Error while Connecting the Peripheral");
BTstack.bleStartScanning();
break;
default:
break;
}
}
/* LISTING_END(LECentralDeviceConnectedCallback): Device Connected Callback */
/*
@section Device Disconnected Callback
@text If the connection to a device breaks, the device disconnected callback
is called. Here, we start scanning for new devices again.
*/
/* LISTING_START(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
void deviceDisconnectedCallback(BLEDevice * device) {
(void) device;
Serial.println("Disconnected, starting over..");
sendCounter = false;
BTstack.bleStartScanning();
}
/* LISTING_END(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
/*
@section Service Discovered Callback
@text The service discovered callback is called for each service and after the
service discovery is complete. The status argument is provided for this.
The main information about a discovered Service is its UUID.
If we find our service, we store the reference to this service.
This allows to discover the Characteristics for our service after
the service discovery is complete.
*/
/* LISTING_START(LECentralServiceDiscoveredCallback): Service Discovered Callback */
void gattServiceDiscovered(BLEStatus status, BLEDevice *device, BLEService *bleService) {
switch (status) {
case BLE_STATUS_OK:
Serial.print("Service Discovered: :");
Serial.println(bleService->getUUID()->getUuidString());
if (bleService->matches(&bleShieldServiceV2UUID)) {
serviceFound = true;
Serial.println("Our service located!");
myBLEService = *bleService;
}
break;
case BLE_STATUS_DONE:
Serial.println("Service discovery finished");
if (serviceFound) {
device->discoverCharacteristicsForService(&myBLEService);
}
break;
default:
Serial.println("Service discovery error");
break;
}
}
/* LISTING_END(LECentralServiceDiscoveredCallback): Service Discovered Callback */
/*
@section Characteristic Discovered Callback
@text Similar to the Service Discovered callback, the Characteristic Discovered
callback is called for each Characteristic found and after the discovery is complete.
The main information is again its UUID. If we find a Characteristic that we're
interested in, it's name is printed and a reference stored for later.
On discovery complete, we subscribe to a particular Characteristic to receive
Characteristic Value updates in the Notificaation Callback.
*/
/* LISTING_START(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
void gattCharacteristicDiscovered(BLEStatus status, BLEDevice *device, BLECharacteristic *characteristic) {
switch (status) {
case BLE_STATUS_OK:
Serial.print("Characteristic Discovered: ");
Serial.print(characteristic->getUUID()->getUuidString());
Serial.print(", handle 0x");
Serial.println(characteristic->getCharacteristic()->value_handle, HEX);
int i;
for (i = 0; i < numCharacteristics; i++) {
if (characteristic->matches(&characteristics[i].uuid)) {
Serial.print("Characteristic found: ");
Serial.println(characteristics[i].name);
characteristics[i].found = 1;
characteristics[i].characteristic = *characteristic;
break;
}
}
break;
case BLE_STATUS_DONE:
Serial.print("Characteristic discovery finished, status ");
Serial.println(status, HEX);
if (characteristics[charRX].found) {
device->subscribeForNotifications(&characteristics[charRX].characteristic);
}
break;
default:
Serial.println("Characteristics discovery error");
break;
}
}
/* LISTING_END(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
/*
@section Subscribed Callback
@text After the subscribe operation is complete, we get notified if it was
successful. In this example, we read the Characteristic that contains the
BD ADDR of the other device. This isn't strictly necessary as we already
know the device address from the Advertisement, but it's a common pattern
with iOS as the device address is hidden from applications.
*/
/* LISTING_START(LECentralSubscribedCallback): Subscribed Callback */
void gattSubscribedCallback(BLEStatus status, BLEDevice * device) {
(void) status;
device->readCharacteristic(&characteristics[charBdAddr].characteristic);
}
/* LISTING_END(LECentralSubscribedCallback): Subscribed Callback */
/*
@section Read Callback
@text The Read callback is called with the result from a read operation.
Here, we write to the TX Characteristic next.
*/
/* LISTING_START(LECentralReadCallback): Read Callback */
void gattReadCallback(BLEStatus status, BLEDevice *device, uint8_t *value, uint16_t length) {
(void) status;
(void) length;
Serial.print("Read callback: ");
Serial.println((const char *)value);
device->writeCharacteristic(&characteristics[charTX].characteristic, (uint8_t*) "Hello!", 6);
}
/* LISTING_END(LECentralReadCallback): Read Callback */
/*
@section Written Callback
@text After the write operation is complete, the Written Callback is callbed with
the result in the status argument. As we're done with the initial setup of the remote
device, we set the flag to write the test string as fast as possible.
*/
/* LISTING_START(LECentralWrittenCallback): Written Callback */
void gattWrittenCallback(BLEStatus status, BLEDevice *device) {
(void) status;
(void) device;
sendCounter = true;
}
/* LISTING_END(LECentralWrittenCallback): Written Callback */
/*
@section Notification Callback
@text Notifications for Characteristic Value Updates are delivered via the
Notification Callback. When more than one Characteristic is subscribed,
the value handle can be used to distinguish between them. The
BLECharacteristic.isValueHandle(int handle) allows to test if a value handle
belongs to a particular Characteristic.
*/
/* LISTING_START(LECentralNotificationCallback): Notification Callback */
void gattCharacteristicNotification(BLEDevice *device, uint16_t value_handle, uint8_t *value, uint16_t length) {
(void) device;
(void) value_handle;
(void) length;
Serial.print("Notification: ");
Serial.println((const char *)value);
}
/* LISTING_END(LECentralNotificationCallback): Notification Callback */
@@ -0,0 +1,120 @@
// LE Peripheral Example - not working yet
#include <BTstackLib.h>
#include <SPI.h>
/*
EXAMPLE_START(LEPeripheral): LE Peripheral
@text BTstack allows to setup a GATT Services and Characteristics directly
from the setup function without using other tools outside of the Arduino IDE.
@section Setup
@text First, a number of callbacks are set. Then, a Service with a Read-only
Characteristic and a dynamic Characteristic is added to the GATT database.
In BTstack, a dynamic Characteristic is a Characteristic where reads and writes
are forwarded to the Sketch. In this example, the dynamic Characteristic is
provided by the single byte variable characteristic_data.
*/
/* LISTING_START(LEPeripheralSetup): Setup */
static char characteristic_data = 'H';
void setup(void) {
Serial.begin(9600);
// set callbacks
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
BTstack.setGATTCharacteristicRead(gattReadCallback);
BTstack.setGATTCharacteristicWrite(gattWriteCallback);
// setup GATT Database
BTstack.addGATTService(new UUID("B8E06067-62AD-41BA-9231-206AE80AB551"));
BTstack.addGATTCharacteristic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fcef"), ATT_PROPERTY_READ, "This is a String!");
BTstack.addGATTCharacteristicDynamic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fce0"), ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_NOTIFY, 0);
// startup Bluetooth and activate advertisements
BTstack.setup();
BTstack.startAdvertising();
}
/* LISTING_END(LEPeripheralSetup): Setup */
void loop(void) {
BTstack.loop();
}
/*
@section Device Connected Callback
@text When a remove device connects, device connected callback is callec.
*/
/* LISTING_START(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
(void) device;
switch (status) {
case BLE_STATUS_OK:
Serial.println("Device connected!");
break;
default:
break;
}
}
/* LISTING_END(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
/*
@section Device Disconnected Callback
@text If the connection to a device breaks, the device disconnected callback
is called.
*/
/* LISTING_START(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
void deviceDisconnectedCallback(BLEDevice * device) {
(void) device;
Serial.println("Disconnected.");
}
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
/*
@section Read Callback
@text In BTstack, the Read Callback is first called to query the size of the
Characteristic Value, before it is called to provide the data.
Both times, the size has to be returned. The data is only stored in the provided
buffer, if the buffer argeument is not NULL.
If more than one dynamic Characteristics is used, the value handle is used
to distinguish them.
*/
/* LISTING_START(LEPeripheralReadCallback): Read Callback */
uint16_t gattReadCallback(uint16_t value_handle, uint8_t * buffer, uint16_t buffer_size) {
(void) value_handle;
(void) buffer_size;
if (buffer) {
Serial.print("gattReadCallback, value: ");
Serial.println(characteristic_data, HEX);
buffer[0] = characteristic_data;
}
return 1;
}
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Read Callback */
/*
@section Write Callback
@text When the remove device writes a Characteristic Value, the Write callback
is called. The buffer arguments points to the data of size size/
If more than one dynamic Characteristics is used, the value handle is used
to distinguish them.
*/
/* LISTING_START(LEPeripheralWriteCallback): Write Callback */
int gattWriteCallback(uint16_t value_handle, uint8_t *buffer, uint16_t size) {
(void) value_handle;
(void) size;
characteristic_data = buffer[0];
Serial.print("gattWriteCallback , value ");
Serial.println(characteristic_data, HEX);
return 0;
}
/* LISTING_END(LEPeripheralWriteCallback): Write Callback */
@@ -0,0 +1,25 @@
#include <BTstackLib.h>
#include <stdio.h>
#include <SPI.h>
/* EXAMPLE_START(iBeacon): iBeacon Simulator
@section Setup
@text After BTstack.setup(), iBeaconConfigure() configures BTstack
to send out iBeacons Advertisements with the provided Major ID,
Minor ID and UUID.
*/
/* LISTING_START(iBeaconSetup): iBeacon Setup */
UUID uuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0");
void setup(void) {
Serial.begin(9600);
BTstack.setup();
BTstack.iBeaconConfigure(&uuid, 4711, 2);
BTstack.startAdvertising();
}
/* LISTING_END(iBeaconSetup) */
void loop(void) {
BTstack.loop();
}
@@ -0,0 +1,58 @@
#include <BTstackLib.h>
#include <SPI.h>
/* EXAMPLE_START(iBeaconScanner): iBeacon Scanner
@section Setup
@text After BTstack.setup(), BTstack is configured to call
advertisementCallback whenever an Advertisement was received.
Then, a device discovery is started
*/
/* LISTING_START(iBeaconSetup): iBeacon Scanner Setup */
void setup(void) {
Serial.begin(9600);
BTstack.setup();
BTstack.setBLEAdvertisementCallback(advertisementCallback);
BTstack.bleStartScanning();
}
/* LISTING_END(iBeaconSetup): iBeacon Scanner Setup */
void loop(void) {
BTstack.loop();
}
/*
@section Advertisement Callback
@text Whenever an Advertisement is received, isIBeacon() checks if
it contains an iBeacon. If yes, the Major ID, Minor ID, and UUID
is printed.
If it's not an iBeacon, only the BD_ADDR and the received signal strength
(RSSI) is shown.
*/
/* LISTING_START(iBeaconCallback): iBeacon Scanner Callback */
void advertisementCallback(BLEAdvertisement *adv) {
if (adv->isIBeacon()) {
Serial.print("iBeacon found ");
Serial.print(adv->getBdAddr()->getAddressString());
Serial.print(", RSSI ");
Serial.print(adv->getRssi());
Serial.print(", UUID ");
Serial.print(adv->getIBeaconUUID()->getUuidString());
Serial.print(", MajorID ");
Serial.print(adv->getIBeaconMajorID());
Serial.print(", MinorID ");
Serial.print(adv->getIBecaonMinorID());
Serial.print(", Measured Power ");
Serial.println(adv->getiBeaconMeasuredPower());
} else {
Serial.print("Device discovered: ");
Serial.print(adv->getBdAddr()->getAddressString());
Serial.print(", RSSI ");
Serial.println(adv->getRssi());
}
}
/* LISTING_END(iBeaconCallback): iBeacon Scanner Callback */
+100
View File
@@ -0,0 +1,100 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Library (KEYWORD3)
#######################################
BTstack KEYWORD3 RESERVED_WORD
#######################################
# Datatypes (KEYWORD1)
#######################################
BLEStatus KEYWORD2
UUID KEYWORD2
BD_ADDR_TYPE KEYWORD2
BD_ADDR KEYWORD2
BLEAdvertisement KEYWORD2
BLECharacteristic KEYWORD2
BLEService KEYWORD2
BLEDevice KEYWORD2
BTstackManager KEYWORD2
#######################################
# Methods and Functions (KEYWORD2)
#######################################
getUuidString KEYWORD2
getUuid128String KEYWORD2
getUuid KEYWORD2
matches KEYWORD2
getAddress KEYWORD2
getAddressString KEYWORD2
getAddressType KEYWORD2
getBdAddr KEYWORD2
getBdAddrType KEYWORD2
getRssi KEYWORD2
containsService KEYWORD2
nameHasPrefix KEYWORD2
getAdvData KEYWORD2
isIBeacon KEYWORD2
getIBeaconUUID KEYWORD2
getIBeaconMajorID KEYWORD2
getIBecaonMinorID KEYWORD2
getiBeaconMeasuredPower KEYWORD2
isValueHandle KEYWORD2
getCharacteristic KEYWORD2
getUUID KEYWORD2
getService KEYWORD2
getHandle KEYWORD2
discoverGATTServices KEYWORD2
discoverCharacteristicsForService KEYWORD2
readCharacteristic KEYWORD2
writeCharacteristic KEYWORD2
writeCharacteristicWithoutResponse KEYWORD2
subscribeForNotifications KEYWORD2
unsubscribeFromNotifications KEYWORD2
subscribeForIndications KEYWORD2
unsubscribeFromIndications KEYWORD2
setPublicBdAddr KEYWORD2
enablePacketLogger KEYWORD2
enableDebugLogger KEYWORD2
setAdvData KEYWORD2
iBeaconConfigure KEYWORD2
startAdvertising KEYWORD2
stopAdvertising KEYWORD2
bleStartScanning KEYWORD2
bleStopScanning KEYWORD2
bleConnect KEYWORD2
bleDisconnect KEYWORD2
setBLEAdvertisementCallback KEYWORD2
setBLEDeviceConnectedCallback KEYWORD2
setBLEDeviceDisconnectedCallback KEYWORD2
setGATTServiceDiscoveredCallback KEYWORD2
setGATTCharacteristicDiscoveredCallback KEYWORD2
setGATTCharacteristicReadCallback KEYWORD2
setGATTCharacteristicNotificationCallback KEYWORD2
setGATTCharacteristicIndicationCallback KEYWORD2
setGATTDoneCallback KEYWORD2
setGATTCharacteristicWrittenCallback KEYWORD2
setGATTCharacteristicSubscribedCallback KEYWORD2
setGATTCharacteristicUnsubscribedCallback KEYWORD2
setGATTCharacteristicRead KEYWORD2
setGATTCharacteristicWrite KEYWORD2
addGATTService KEYWORD2
addGATTCharacteristic KEYWORD2
addGATTCharacteristicDynamic KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
BLE_STATUS_OK LITERAL1
BLE_STATUS_DONE LITERAL1
BLE_STATUS_CONNECTION_TIMEOUT LITERAL1
BLE_STATUS_CONNECTION_ERROR LITERAL1
BLE_STATUS_OTHER_ERROR LITERAL1
PUBLIC_ADDRESS LITERAL1
PRIVAT_ADDRESS LITERAL1
+10
View File
@@ -0,0 +1,10 @@
name=BTstack
version=1.0
author=bluekitchen-gmbh
maintainer=Earle Philhower
sentence=BTstack Arduino library distributed with BTstack
paragraph=
category=Data Processing
url=
architectures=rp2040
dot_a_linkage=true
+907
View File
@@ -0,0 +1,907 @@
/**
Arduino Wrapper for BTstack
*/
#include <Arduino.h>
#include <SPI.h>
#ifdef __AVR__
#include <avr/wdt.h>
#endif
#if __arm__
//#include <Reset.h> // also provides NVIC_SystemReset
#endif
#include "BTstackLib.h"
#include "btstack_memory.h"
#include "hal_tick.h"
#include "hal_cpu.h"
#include "hci_cmd.h"
#include "btstack_util.h"
#include "btstack_run_loop.h"
#include "btstack_event.h"
#include "btstack_run_loop_embedded.h"
#include "hci_transport.h"
#include "hci_transport_h4.h"
#include "ad_parser.h"
//#include "btstack_chipset_em9301.h"
#include "btstack_debug.h"
#include "gap.h"
#include "hci.h"
#include "hci_dump.h"
#include "hci_dump_embedded_stdout.h"
#include "l2cap.h"
#include "ble/att_db.h"
#include "ble/att_server.h"
#include "ble/att_db_util.h"
#include "ble/le_device_db.h"
#include "ble/sm.h"
// Pin 13 has an LED connected on most Arduino boards.
//#define PIN_LED 13
// prototypes
extern "C" void hal_uart_dma_process(void);
enum {
SET_ADVERTISEMENT_PARAMS = 1 << 0,
SET_ADVERTISEMENT_DATA = 1 << 1,
SET_ADVERTISEMENT_ENABLED = 1 << 2,
};
typedef enum gattAction {
gattActionWrite,
gattActionSubscribe,
gattActionUnsubscribe,
gattActionServiceQuery,
gattActionCharacteristicQuery,
gattActionRead,
} gattAction_t;
static gattAction_t gattAction;
// btstack state
static int btstack_state;
static const uint8_t iBeaconAdvertisement01[] = { 0x02, 0x01 };
static const uint8_t iBeaconAdvertisement38[] = { 0x1a, 0xff, 0x4c, 0x00, 0x02, 0x15 };
static uint8_t adv_data[31];
static uint16_t adv_data_len = 0;
//static int gatt_is_characteristics_query;
static uint16_t le_peripheral_todos = 0;
static bool have_custom_addr;
static bd_addr_t public_bd_addr;
static btstack_timer_source_t connection_timer;
static void (*bleAdvertismentCallback)(BLEAdvertisement * bleAdvertisement) = NULL;
static void (*bleDeviceConnectedCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*bleDeviceDisconnectedCallback)(BLEDevice * device) = NULL;
static void (*gattServiceDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLEService * bleService) = NULL;
static void (*gattCharacteristicDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic) = NULL;
static void (*gattCharacteristicNotificationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
static void (*gattCharacteristicIndicationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
static void (*gattCharacteristicReadCallback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length) = NULL;
static void (*gattCharacteristicWrittenCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*gattCharacteristicSubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*gattCharacteristicUnsubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
// retarget printf to Serial
#ifdef ENERGIA
extern "C" int putchar(int c) {
Serial.write((uint8_t)c);
return c;
}
#else
#ifdef __AVR__
static FILE uartout = {0} ;
static int uart_putchar(char c, FILE *stream) {
Serial.write(c);
return 0;
}
#endif
// added for Arduino Zero. Arduino Due already has tis own _write(..) implementation
// in /Users/mringwal/Library/Arduino15/packages/arduino/hardware/sam/1.6.4/cores/arduino/syscalls_sam3.c
#if defined(__SAMD21G18A__)
// #ifdef __arm__
extern "C" int _write(int file, char *ptr, int len) {
int i;
for (i = 0; i < len; i++) {
if (ptr[i] == '\n') {
Serial.write((uint8_t)'\r');
}
Serial.write((uint8_t)ptr[i]);
}
return i;
}
#endif
#endif
// HAL CPU Implementation
extern "C" void hal_cpu_disable_irqs(void) { }
extern "C" void hal_cpu_enable_irqs(void) { }
extern "C" void hal_cpu_enable_irqs_and_sleep(void) { }
//
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
(void) channel;
(void) size;
hci_con_handle_t con_handle;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (packet[0]) {
case BTSTACK_EVENT_STATE:
btstack_state = packet[2];
// bt stack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
le_peripheral_todos |= SET_ADVERTISEMENT_PARAMS
| SET_ADVERTISEMENT_DATA
| SET_ADVERTISEMENT_ENABLED;
bd_addr_t addr;
gap_local_bd_addr(addr);
printf("BTstack up and running at %s\n", bd_addr_to_str(addr));
}
break;
case HCI_EVENT_DISCONNECTION_COMPLETE:
if (bleDeviceDisconnectedCallback) {
con_handle = little_endian_read_16(packet, 3);
BLEDevice device(con_handle);
(*bleDeviceDisconnectedCallback)(&device);
}
le_peripheral_todos |= SET_ADVERTISEMENT_ENABLED;
break;
case GAP_EVENT_ADVERTISING_REPORT: {
if (bleAdvertismentCallback) {
BLEAdvertisement advertisement(packet);
(*bleAdvertismentCallback)(&advertisement);
}
break;
}
case HCI_EVENT_LE_META:
switch (packet[2]) {
case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
con_handle = little_endian_read_16(packet, 4);
printf("Connection complete, con_handle 0x%04x\n", con_handle);
btstack_run_loop_remove_timer(&connection_timer);
if (!bleDeviceConnectedCallback) {
break;
}
if (packet[3]) {
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_ERROR, NULL);
} else {
BLEDevice device(con_handle);
(*bleDeviceConnectedCallback)(BLE_STATUS_OK, &device);
}
break;
default:
break;
}
break;
}
}
}
static void extract_service(gatt_client_service_t * service, uint8_t * packet) {
service->start_group_handle = little_endian_read_16(packet, 4);
service->end_group_handle = little_endian_read_16(packet, 6);
service->uuid16 = 0;
reverse_128(&packet[8], service->uuid128);
if (uuid_has_bluetooth_prefix(service->uuid128)) {
service->uuid16 = big_endian_read_32(service->uuid128, 0);
}
}
static void extract_characteristic(gatt_client_characteristic_t * characteristic, uint8_t * packet) {
characteristic->start_handle = little_endian_read_16(packet, 4);
characteristic->value_handle = little_endian_read_16(packet, 6);
characteristic->end_handle = little_endian_read_16(packet, 8);
characteristic->properties = little_endian_read_16(packet, 10);
characteristic->uuid16 = 0;
reverse_128(&packet[12], characteristic->uuid128);
if (uuid_has_bluetooth_prefix(characteristic->uuid128)) {
characteristic->uuid16 = big_endian_read_32(characteristic->uuid128, 0);
}
}
static void gatt_client_callback(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t size) {
(void) channel;
(void) packet_type;
(void) size;
// if (hci) event is not 4-byte aligned, event->handle causes crash
// workaround: check event type, assuming GATT event types are contagious
if (packet[0] < GATT_EVENT_QUERY_COMPLETE) {
return;
}
if (packet[0] > GATT_EVENT_MTU) {
return;
}
hci_con_handle_t con_handle = little_endian_read_16(packet, 2);
uint8_t status;
uint8_t * value;
uint16_t value_handle;
uint16_t value_length;
BLEDevice device(con_handle);
switch (hci_event_packet_get_type(packet)) {
case GATT_EVENT_SERVICE_QUERY_RESULT:
if (gattServiceDiscoveredCallback) {
gatt_client_service_t service;
extract_service(&service, packet);
BLEService bleService(service);
(*gattServiceDiscoveredCallback)(BLE_STATUS_OK, &device, &bleService);
}
break;
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
if (gattCharacteristicDiscoveredCallback) {
gatt_client_characteristic_t characteristic;
extract_characteristic(&characteristic, packet);
BLECharacteristic bleCharacteristic(characteristic);
(*gattCharacteristicDiscoveredCallback)(BLE_STATUS_OK, &device, &bleCharacteristic);
}
break;
case GATT_EVENT_QUERY_COMPLETE:
status = little_endian_read_16(packet, 4);
switch (gattAction) {
case gattActionWrite:
if (gattCharacteristicWrittenCallback) {
gattCharacteristicWrittenCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionSubscribe:
if (gattCharacteristicSubscribedCallback) {
gattCharacteristicSubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionUnsubscribe:
if (gattCharacteristicUnsubscribedCallback) {
gattCharacteristicUnsubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionServiceQuery:
if (gattServiceDiscoveredCallback) {
gattServiceDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
}
break;
case gattActionCharacteristicQuery:
if (gattCharacteristicDiscoveredCallback) {
gattCharacteristicDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
}
break;
default:
break;
};
break;
case GATT_EVENT_NOTIFICATION:
if (gattCharacteristicNotificationCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicNotificationCallback)(&device, value_handle, value, value_length);
}
break;
case GATT_EVENT_INDICATION:
if (gattCharacteristicIndicationCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicIndicationCallback)(&device, value_handle, value, value_length);
}
break;
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
if (gattCharacteristicReadCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicReadCallback)(BLE_STATUS_OK, &device, value, value_length);
}
break;
default:
break;
}
}
static void connection_timeout_handler(btstack_timer_source_t * timer) {
(void) timer;
// log_info("Cancel outgoing connection");
gap_connect_cancel();
if (!bleDeviceConnectedCallback) {
return;
}
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_TIMEOUT, NULL); // page timeout 0x04
}
//
/// UUID class
UUID::UUID(void) {
memset(uuid, 0, 16);
}
UUID::UUID(const uint8_t uuid[16]) {
memcpy(this->uuid, uuid, 16);
}
UUID::UUID(const char * uuidStr) {
memset(uuid, 0, 16);
int len = strlen(uuidStr);
if (len <= 4) {
// Handle 4 Bytes HEX
unsigned int uuid16;
int result = sscanf((char *) uuidStr, "%x", &uuid16);
if (result == 1) {
uuid_add_bluetooth_prefix(uuid, uuid16);
}
return;
}
// quick UUID parser, ignoring dashes
int i = 0;
int data = 0;
int have_nibble = 0;
while (*uuidStr && i < 16) {
const char c = *uuidStr++;
if (c == '-') {
continue;
}
data = data << 4 | nibble_for_char(c);
if (!have_nibble) {
have_nibble = 1;
continue;
}
uuid[i++] = data;
data = 0;
have_nibble = 0;
}
}
const uint8_t * UUID::getUuid(void) const {
return uuid;
}
static char uuid16_buffer[5];
const char * UUID::getUuidString() const {
// TODO: fix uuid_has_bluetooth_prefix call to use const
if (uuid_has_bluetooth_prefix((uint8_t*)uuid)) {
sprintf(uuid16_buffer, "%04x", (uint16_t) big_endian_read_32(uuid, 0));
return uuid16_buffer;
} else {
// TODO: fix uuid128_to_str
return uuid128_to_str((uint8_t*)uuid);
}
}
const char * UUID::getUuid128String() const {
return uuid128_to_str((uint8_t*)uuid);
}
bool UUID::matches(UUID *other) const {
return memcmp(this->uuid, other->uuid, 16) == 0;
}
// BD_ADDR class
BD_ADDR::BD_ADDR(void) {
}
BD_ADDR::BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type) : address_type(address_type) {
(void) address_string;
// TODO: implement
// log_error("BD_ADDR::BD_ADDR(const char *, BD_ADDR_TYPE) not implemented yet!");
}
BD_ADDR::BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type) : address_type(address_type) {
memcpy(this->address, address, 6);
}
const uint8_t * BD_ADDR::getAddress(void) {
return address;
}
const char * BD_ADDR::getAddressString(void) {
return bd_addr_to_str(address);
}
BD_ADDR_TYPE BD_ADDR::getAddressType(void) {
return address_type;
}
BLEAdvertisement::BLEAdvertisement(uint8_t * event_packet) :
advertising_event_type(event_packet[2]),
rssi(event_packet[10]),
data_length(event_packet[11]),
iBeacon_UUID(NULL) {
bd_addr_t addr;
reverse_bd_addr(&event_packet[4], addr);
bd_addr = BD_ADDR(addr, (BD_ADDR_TYPE)event_packet[3]);
memcpy(data, &event_packet[12], LE_ADVERTISING_DATA_SIZE);
}
BLEAdvertisement::~BLEAdvertisement() {
if (iBeacon_UUID) {
delete (iBeacon_UUID);
}
}
const uint8_t * BLEAdvertisement::getAdvData(void) {
return data;
}
BD_ADDR * BLEAdvertisement::getBdAddr(void) {
return &bd_addr;
}
int BLEAdvertisement::getRssi(void) {
return rssi > 127 ? rssi - 256 : rssi;
}
bool BLEAdvertisement::containsService(UUID * service) {
return ad_data_contains_uuid128(data_length, data, (uint8_t*) service->getUuid());
}
bool BLEAdvertisement::nameHasPrefix(const char * name_prefix) {
ad_context_t context;
int name_prefix_len = strlen(name_prefix);
for (ad_iterator_init(&context, data_length, data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) {
uint8_t data_type = ad_iterator_get_data_type(&context);
uint8_t data_len = ad_iterator_get_data_len(&context);
const uint8_t * data = ad_iterator_get_data(&context);
int compare_len = name_prefix_len;
switch (data_type) {
case 8: // shortented local name
case 9: // complete local name
if (compare_len > data_len) {
compare_len = data_len;
}
if (strncmp(name_prefix, (const char*) data, compare_len) == 0) {
return true;
}
break;
default:
break;
}
}
return false;
};
bool BLEAdvertisement::isIBeacon(void) {
return ((memcmp(iBeaconAdvertisement01, data, sizeof(iBeaconAdvertisement01)) == 0)
&& (memcmp(iBeaconAdvertisement38, &data[3], sizeof(iBeaconAdvertisement38)) == 0));
}
const UUID * BLEAdvertisement::getIBeaconUUID(void) {
if (!iBeacon_UUID) {
iBeacon_UUID = new UUID(&data[9]);
}
return iBeacon_UUID;
};
uint16_t BLEAdvertisement::getIBeaconMajorID(void) {
return big_endian_read_16(data, 25);
};
uint16_t BLEAdvertisement::getIBecaonMinorID(void) {
return big_endian_read_16(data, 27);
};
uint8_t BLEAdvertisement::getiBeaconMeasuredPower(void) {
return data[29];
}
BLECharacteristic::BLECharacteristic(void) {
}
BLECharacteristic::BLECharacteristic(gatt_client_characteristic_t characteristic)
: characteristic(characteristic), uuid(characteristic.uuid128) {
}
const UUID * BLECharacteristic::getUUID(void) {
return &uuid;
}
bool BLECharacteristic::matches(UUID * uuid) {
return this->uuid.matches(uuid);
}
bool BLECharacteristic::isValueHandle(uint16_t value_handle) {
return characteristic.value_handle == value_handle;
}
const gatt_client_characteristic_t * BLECharacteristic::getCharacteristic(void) {
return &characteristic;
}
BLEService::BLEService(void) {
}
BLEService::BLEService(gatt_client_service_t service)
: service(service), uuid(service.uuid128) {
}
const UUID * BLEService::getUUID(void) {
return &uuid;
}
bool BLEService::matches(UUID * uuid) {
return this->uuid.matches(uuid);
}
const gatt_client_service_t * BLEService::getService(void) {
return &service;
}
// discovery of services and characteristics
BLEDevice::BLEDevice(void) {
}
BLEDevice::BLEDevice(hci_con_handle_t handle)
: handle(handle) {
}
uint16_t BLEDevice::getHandle(void) {
return handle;
}
int BLEDevice::discoverGATTServices(void) {
return BTstack.discoverGATTServices(this);
}
int BLEDevice::discoverCharacteristicsForService(BLEService * service) {
return BTstack.discoverCharacteristicsForService(this, service);
}
int BLEDevice::readCharacteristic(BLECharacteristic * characteristic) {
return BTstack.readCharacteristic(this, characteristic);
}
int BLEDevice::writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return BTstack.writeCharacteristic(this, characteristic, data, size);
}
int BLEDevice::writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return BTstack.writeCharacteristicWithoutResponse(this, characteristic, data, size);
}
int BLEDevice::subscribeForNotifications(BLECharacteristic * characteristic) {
return BTstack.subscribeForNotifications(this, characteristic);
}
int BLEDevice::unsubscribeFromNotifications(BLECharacteristic * characteristic) {
return BTstack.unsubscribeFromNotifications(this, characteristic);
}
int BLEDevice::subscribeForIndications(BLECharacteristic * characteristic) {
return BTstack.subscribeForIndications(this, characteristic);
}
int BLEDevice::unsubscribeFromIndications(BLECharacteristic * characteristic) {
return BTstack.unsubscribeFromIndications(this, characteristic);
}
static uint16_t (*gattReadCallback)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size);
static int (*gattWriteCallback)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size);
// ATT Client Read Callback for Dynamic Data
// - if buffer == NULL, don't copy data, just return size of value
// - if buffer != NULL, copy data and return number bytes copied
// @param offset defines start of attribute value
static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size) {
(void) con_handle;
(void) offset;
if (gattReadCallback) {
return gattReadCallback(att_handle, buffer, buffer_size);
}
return 0;
}
/* LISTING_END */
/*
@section ATT Write
@text The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification
and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored and used
in the heartbeat handler to decide if a new value should be sent. See Listing attWrite.
*/
/* LISTING_START(attWrite): ATT Write */
static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) {
(void) con_handle;
(void) transaction_mode;
(void) offset;
if (gattWriteCallback) {
gattWriteCallback(att_handle, buffer, buffer_size);
}
return 0;
}
BTstackManager::BTstackManager(void) {
// client_packet_handler = NULL;
have_custom_addr = false;
// reset handler
bleAdvertismentCallback = NULL;
bleDeviceConnectedCallback = NULL;
bleDeviceDisconnectedCallback = NULL;
gattServiceDiscoveredCallback = NULL;
gattCharacteristicDiscoveredCallback = NULL;
gattCharacteristicNotificationCallback = NULL;
att_db_util_init();
// disable LOG_INFO messages
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 0);
#ifdef __AVR__
// configure stdout to go via Serial
fdev_setup_stream(&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
stdout = &uartout;
#endif
}
void BTstackManager::setBLEAdvertisementCallback(void (*callback)(BLEAdvertisement * bleAdvertisement)) {
bleAdvertismentCallback = callback;
}
void BTstackManager::setBLEDeviceConnectedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
bleDeviceConnectedCallback = callback;
}
void BTstackManager::setBLEDeviceDisconnectedCallback(void (*callback)(BLEDevice * device)) {
bleDeviceDisconnectedCallback = callback;
}
void BTstackManager::setGATTServiceDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLEService * bleService)) {
gattServiceDiscoveredCallback = callback;
}
void BTstackManager::setGATTCharacteristicDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic)) {
gattCharacteristicDiscoveredCallback = callback;
}
void BTstackManager::setGATTCharacteristicNotificationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
gattCharacteristicNotificationCallback = callback;
}
void BTstackManager::setGATTCharacteristicIndicationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
gattCharacteristicIndicationCallback = callback;
}
void BTstackManager::setGATTCharacteristicReadCallback(void (*callback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length)) {
gattCharacteristicReadCallback = callback;
}
void BTstackManager::setGATTCharacteristicWrittenCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicWrittenCallback = callback;
}
void BTstackManager::setGATTCharacteristicSubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicSubscribedCallback = callback;
}
void BTstackManager::setGATTCharacteristicUnsubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicUnsubscribedCallback = callback;
}
int BTstackManager::discoverGATTServices(BLEDevice * device) {
gattAction = gattActionServiceQuery;
return gatt_client_discover_primary_services(gatt_client_callback, device->getHandle());
}
int BTstackManager::discoverCharacteristicsForService(BLEDevice * device, BLEService * service) {
gattAction = gattActionCharacteristicQuery;
return gatt_client_discover_characteristics_for_service(gatt_client_callback, device->getHandle(), (gatt_client_service_t*) service->getService());
}
int BTstackManager::readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic) {
return gatt_client_read_value_of_characteristic(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic());
}
int BTstackManager::writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
gattAction = gattActionWrite;
return gatt_client_write_value_of_characteristic(gatt_client_callback, device->getHandle(), characteristic->getCharacteristic()->value_handle,
size, data);
}
int BTstackManager::writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return gatt_client_write_value_of_characteristic_without_response(device->getHandle(), characteristic->getCharacteristic()->value_handle,
size, data);
}
int BTstackManager::subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionSubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
}
int BTstackManager::subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionSubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION);
}
int BTstackManager::unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionUnsubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
}
int BTstackManager::unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionUnsubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
}
void BTstackManager::bleConnect(BLEAdvertisement * advertisement, int timeout_ms) {
bleConnect(advertisement->getBdAddr(), timeout_ms);
}
void BTstackManager::bleConnect(BD_ADDR * address, int timeout_ms) {
bleConnect(address->getAddressType(), address->getAddress(), timeout_ms);
}
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) {
(void) address_type;
(void) address;
(void) timeout_ms;
// TODO: implement
// log_error("BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) not implemented");
}
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms) {
gap_connect((uint8_t*)address, (bd_addr_type_t) address_type);
if (!timeout_ms) {
return;
}
btstack_run_loop_set_timer(&connection_timer, timeout_ms);
btstack_run_loop_set_timer_handler(&connection_timer, connection_timeout_handler);
btstack_run_loop_add_timer(&connection_timer);
}
void BTstackManager::bleDisconnect(BLEDevice * device) {
btstack_run_loop_remove_timer(&connection_timer);
gap_disconnect(device->getHandle());
}
void BTstackManager::setPublicBdAddr(bd_addr_t addr) {
have_custom_addr = true;
memcpy(public_bd_addr, addr, 6);
}
void bluetooth_hardware_error(uint8_t error) {
printf("Bluetooth Hardware Error event 0x%02x. Restarting...\n\n\n", error);
#ifdef __AVR__
wdt_enable(WDTO_15MS);
// wait for watchdog to trigger
#endif
#ifdef __arm__
// NVIC_SystemReset();
#endif
while (1);
}
#if 0
static hci_transport_config_uart_t config = {
HCI_TRANSPORT_CONFIG_UART,
115200,
0, // main baudrate
1, // flow control
NULL,
};
#endif
static btstack_packet_callback_registration_t hci_event_callback_registration;
void BTstackManager::setup(void) {
//#ifdef PIN_LED
// pinMode(PIN_LED, OUTPUT);
//#endif
// printf("BTstackManager::setup()\n");
#if 0
btstack_memory_init();
btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
const hci_transport_t * transport = hci_transport_h4_instance(btstack_uart_block_embedded_instance());
hci_init(transport, (void*) &config);
hci_set_chipset(btstack_chipset_em9301_instance());
if (have_custom_addr) {
hci_set_bd_addr(public_bd_addr);
}
hci_set_hardware_error_callback(&bluetooth_hardware_error);
#endif
// inform about BTstack state
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
l2cap_init();
// setup central device db
le_device_db_init();
sm_init();
att_server_init(att_db_util_get_address(), att_read_callback, att_write_callback);
gatt_client_init();
// setup advertisements params
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
uint8_t adv_type = 0;
bd_addr_t null_addr;
memset(null_addr, 0, 6);
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
// setup advertisements data
int pos = 0;
const uint8_t flags[] = { 0x02, 0x01, 0x02 };
memcpy(&adv_data[pos], flags, sizeof(flags));
pos += sizeof(flags);
const char * name = "BTstack LE Shield";
adv_data[pos++] = strlen(name) + 1;
adv_data[pos++] = 0x09;
memcpy(&adv_data[pos], name, strlen(name));
pos += strlen(name);
adv_data_len = pos;
gap_advertisements_set_data(adv_data_len, adv_data);
// turn on!
btstack_state = 0;
hci_power_control(HCI_POWER_ON);
}
void BTstackManager::enablePacketLogger(void) {
hci_dump_init(hci_dump_embedded_stdout_get_instance());
}
void BTstackManager::enableDebugLogger() {
// enable LOG_INFO messages
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 1);
}
void BTstackManager::loop(void) {
// process data from/to Bluetooth module
// hal_uart_dma_process();
// BTstack Run Loop
// btstack_run_loop_embedded_execute_once();
}
void BTstackManager::bleStartScanning(void) {
// printf("Start scanning\n");
gap_start_scan();
}
void BTstackManager::bleStopScanning(void) {
gap_stop_scan();
}
void BTstackManager::setGATTCharacteristicRead(uint16_t (*cb)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size)) {
gattReadCallback = cb;
}
void BTstackManager::setGATTCharacteristicWrite(int (*cb)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size)) {
gattWriteCallback = cb;
}
void BTstackManager::addGATTService(UUID * uuid) {
att_db_util_add_service_uuid128((uint8_t*)uuid->getUuid());
}
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text) {
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, (uint8_t*)text, strlen(text));
}
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len) {
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, data, data_len);
}
uint16_t BTstackManager::addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id) {
(void) characteristic_id;
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0);
}
void BTstackManager::setAdvData(uint16_t adv_data_len, const uint8_t * adv_data) {
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
}
void BTstackManager::startAdvertising() {
gap_advertisements_enable(1);
}
void BTstackManager::stopAdvertising() {
gap_advertisements_enable(0);
}
void BTstackManager::iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power) {
memcpy(adv_data, iBeaconAdvertisement01, sizeof(iBeaconAdvertisement01));
adv_data[2] = 0x06;
memcpy(&adv_data[3], iBeaconAdvertisement38, sizeof(iBeaconAdvertisement38));
memcpy(&adv_data[9], uuid->getUuid(), 16);
big_endian_store_16(adv_data, 25, major_id);
big_endian_store_16(adv_data, 27, minor_id);
adv_data[29] = measured_power;
adv_data_len = 30;
gap_advertisements_set_data(adv_data_len, adv_data);
}
// 02 01 06 1A FF 4C 00 02 15 -- F8 97 17 7B AE E8 47 67 8E CC CC 69 4F D5 FC EE -- 12 67 00 02 00 00
// 02 01 06 1a ff 4c 00 02 15 -- FB 0B 57 A2 82 28 44 CD 91 3A 94 A1 22 BA 12 06 -- 00 01 00 02 D1 00
BTstackManager BTstack;
+204
View File
@@ -0,0 +1,204 @@
/**
Arduino Wrapper for BTstack
*/
#include <_needsbt.h>
#ifndef __ARDUINO_BTSTACK_H
#define __ARDUINO_BTSTACK_H
#if defined __cplusplus
extern "C" {
#endif
#include "ble/att_db.h"
#include "btstack_util.h"
#include "ble/gatt_client.h"
#include "hci.h"
#include <stdint.h>
typedef enum BLEStatus {
BLE_STATUS_OK,
BLE_STATUS_DONE, // e.g. for service or characteristic discovery done
BLE_STATUS_CONNECTION_TIMEOUT,
BLE_STATUS_CONNECTION_ERROR,
BLE_STATUS_OTHER_ERROR
} BLEStatus;
typedef void (*btstack_packet_handler_t)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
class UUID {
private:
uint8_t uuid[16];
public:
UUID();
UUID(const uint8_t uuid[16]);
UUID(const char * uuidStr);
const char * getUuidString() const;
const char * getUuid128String() const;
const uint8_t * getUuid(void) const;
bool matches(UUID *uuid) const;
};
typedef enum BD_ADDR_TYPE {
PUBLIC_ADDRESS = 0,
PRIVAT_ADDRESS
} BD_ADDR_TYPE;
class BD_ADDR {
private:
uint8_t address[6];
BD_ADDR_TYPE address_type;
public:
BD_ADDR();
BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
const uint8_t * getAddress();
const char * getAddressString();
BD_ADDR_TYPE getAddressType();
};
class BLEAdvertisement {
private:
uint8_t advertising_event_type;
uint8_t rssi;
uint8_t data_length;
uint8_t data[10 + LE_ADVERTISING_DATA_SIZE];
BD_ADDR bd_addr;
UUID * iBeacon_UUID;
public:
BLEAdvertisement(uint8_t * event_packet);
~BLEAdvertisement();
BD_ADDR * getBdAddr();
BD_ADDR_TYPE getBdAddrType();
int getRssi();
bool containsService(UUID * service);
bool nameHasPrefix(const char * namePrefix);
const uint8_t * getAdvData();
bool isIBeacon();
const UUID * getIBeaconUUID();
uint16_t getIBeaconMajorID();
uint16_t getIBecaonMinorID();
uint8_t getiBeaconMeasuredPower();
};
class BLECharacteristic {
private:
gatt_client_characteristic_t characteristic;
UUID uuid;
public:
BLECharacteristic();
BLECharacteristic(gatt_client_characteristic_t characteristic);
const UUID * getUUID();
bool matches(UUID * uuid);
bool isValueHandle(uint16_t value_handle);
const gatt_client_characteristic_t * getCharacteristic();
};
class BLEService {
private:
gatt_client_service_t service;
UUID uuid;
public:
BLEService();
BLEService(gatt_client_service_t service);
const UUID * getUUID();
bool matches(UUID * uuid);
const gatt_client_service_t * getService();
};
class BLEDevice {
private:
hci_con_handle_t handle;
public:
BLEDevice();
BLEDevice(hci_con_handle_t handle);
hci_con_handle_t getHandle();
// discovery of services and characteristics
int discoverGATTServices();
int discoverCharacteristicsForService(BLEService * service);
// read/write
int readCharacteristic(BLECharacteristic * characteristic);
int writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
int writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
// subscribe/unsubscribe
int subscribeForNotifications(BLECharacteristic * characteristic);
int unsubscribeFromNotifications(BLECharacteristic * characteristic);
int subscribeForIndications(BLECharacteristic * characteristic);
int unsubscribeFromIndications(BLECharacteristic * characteristic);
};
class BTstackManager {
public:
BTstackManager(void);
void setup(void);
void loop(void);
void setPublicBdAddr(bd_addr_t addr);
void enablePacketLogger();
void enableDebugLogger();
void setAdvData(uint16_t size, const uint8_t * data);
void iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power = 0xc6);
void startAdvertising();
void stopAdvertising();
void bleStartScanning();
void bleStopScanning();
// connection management
void bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms);
void bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms);
void bleConnect(BD_ADDR * address, int timeout_ms);
void bleConnect(BLEAdvertisement * advertisement, int timeout_ms);
void bleDisconnect(BLEDevice * device);
// discovery of services and characteristics
int discoverGATTServices(BLEDevice * device);
int discoverCharacteristicsForService(BLEDevice * peripheral, BLEService * service);
// read/write
int readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic);
int writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
int writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
// subscribe/unsubscribe notification and indications
int subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic);
int unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic);
int subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic);
int unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic);
// Callbacks
void setBLEAdvertisementCallback(void (*)(BLEAdvertisement * bleAdvertisement));
void setBLEDeviceConnectedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setBLEDeviceDisconnectedCallback(void (*)(BLEDevice * device));
void setGATTServiceDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLEService * bleService));
void setGATTCharacteristicDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic));
void setGATTCharacteristicReadCallback(void (*)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length));
void setGATTCharacteristicNotificationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
void setGATTCharacteristicIndicationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
void setGATTDoneCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicWrittenCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicSubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicUnsubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicRead(uint16_t (*)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size));
void setGATTCharacteristicWrite(int (*)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size));
void addGATTService(UUID * uuid);
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text);
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len);
uint16_t addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id);
};
extern BTstackManager BTstack;
#if defined __cplusplus
}
#endif
#endif // __ARDUINO_BTSTACK_H
+4
View File
@@ -2,6 +2,8 @@
# https://arduino.github.io/arduino-cli/library-specification/#keywords
# Formatted by a single true tab (not spaces)
FreeRTOS KEYWORD1
# Datatypes (KEYWORD1)
StackType_t KEYWORD1
BaseType_t KEYWORD1
@@ -34,6 +36,7 @@ pcTaskGetName KEYWORD2
ulTaskNotifyTake KEYWORD2
vTaskNotifyGiveFromISR KEYWORD2
taskYIELD KEYWORD2
vTaskCoreAffinitySet KEYWORD2
vTaskSuspend KEYWORD2
vTaskResume KEYWORD2
xTaskResumeFromISR KEYWORD2
@@ -41,6 +44,7 @@ xTaskGetTickCount KEYWORD2
xTaskGetTickCountFromISR KEYWORD2
uxTaskGetNumberOfTasks KEYWORD2
uxTaskGetStackHighWaterMark KEYWORD2
uxTaskGetSystemState KEYWORD2
# Instances (KEYWORD2)
+1 -4
View File
@@ -29,10 +29,7 @@
#define configUSE_TASK_PREEMPTION_DISABLE 1
#define configUSE_NEWLIB_REENTRANT 1
#define configNEWLIB_REENTRANT_IS_DYNAMIC 0 /* Note that we have a different config option, portSET_IMPURE_PTR */
#include <reent.h>
extern void __register_impure_ptr(struct _reent *p);
#define portSET_IMPURE_PTR(x) __register_impure_ptr(x)
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1
/* Run time stats related definitions. */
void vMainConfigureTimerForRunTimeStats(void);
+13
View File
@@ -1 +1,14 @@
#include "../lib/FreeRTOS-Kernel/tasks.c"
//See https://github.com/FreeRTOS/FreeRTOS-Kernel/pull/496
struct _reent* __wrap___getreent(void) {
// No lock needed because if this changes, we won't be running anymore.
TCB_t *pxCurTask = xTaskGetCurrentTaskHandle();
if (pxCurTask == NULL) {
// No task running. Return global struct.
return _GLOBAL_REENT;
} else {
// We have a task; return its reentrant struct.
return &pxCurTask->xNewLib_reent;
}
}
+100 -52
View File
@@ -40,7 +40,57 @@
#include <pico.h>
#include <pico/time.h>
#include "_freertos.h"
#include <_freertos.h>
// Interfaces for the main core to use FreeRTOS mutexes
extern "C" {
extern volatile bool __otherCoreIdled;
SemaphoreHandle_t __freertos_mutex_create() {
return xSemaphoreCreateMutex();
}
SemaphoreHandle_t _freertos_recursive_mutex_create() {
return xSemaphoreCreateRecursiveMutex();
}
void __freertos_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTake(mtx, portMAX_DELAY);
}
void __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx) {
xSemaphoreTakeFromISR(mtx, NULL);
}
int __freertos_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTake(mtx, 0);
}
void __freertos_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGive(mtx);
}
void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx) {
xSemaphoreGiveFromISR(mtx, NULL);
}
void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
}
int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTakeRecursive(mtx, 0);
}
void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGiveRecursive(mtx);
}
bool __freertos_check_if_in_isr() {
return portCHECK_IF_IN_ISR();
}
}
/*-----------------------------------------------------------*/
@@ -108,6 +158,40 @@ extern "C" void yield() {
taskYIELD();
}
static TaskHandle_t __idleCoreTask[2];
static void __no_inline_not_in_flash_func(IdleThisCore)(void *param) {
(void) param;
while (true) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
vTaskPreemptionDisable(nullptr);
portDISABLE_INTERRUPTS();
__otherCoreIdled = true;
while (__otherCoreIdled) {
/* noop */
}
portENABLE_INTERRUPTS();
vTaskPreemptionEnable(nullptr);
}
}
extern "C" void __no_inline_not_in_flash_func(__freertos_idle_other_core)() {
vTaskPreemptionDisable(nullptr);
xTaskNotifyGive(__idleCoreTask[ 1 ^ sio_hw->cpuid ]);
while (!__otherCoreIdled) {
/* noop */
}
portDISABLE_INTERRUPTS();
vTaskSuspendAll();
}
extern "C" void __no_inline_not_in_flash_func(__freertos_resume_other_core)() {
__otherCoreIdled = false;
portENABLE_INTERRUPTS();
xTaskResumeAll();
vTaskPreemptionEnable(nullptr);
}
extern mutex_t __usb_mutex;
static TaskHandle_t __usbTask;
static void __usb(void *param);
@@ -124,6 +208,12 @@ void startFreeRTOS(void) {
vTaskCoreAffinitySet(c1, 1 << 1);
}
// Create the idle-other-core tasks (for when flash is being written)
xTaskCreate(IdleThisCore, "IdleCore0", 128, 0, configMAX_PRIORITIES - 1, __idleCoreTask + 0);
vTaskCoreAffinitySet(__idleCoreTask[0], 1 << 0);
xTaskCreate(IdleThisCore, "IdleCore1", 128, 0, configMAX_PRIORITIES - 1, __idleCoreTask + 1);
vTaskCoreAffinitySet(__idleCoreTask[1], 1 << 1);
// Initialise and run the freeRTOS scheduler. Execution should never return here.
__freeRTOSinitted = true;
vTaskStartScheduler();
@@ -216,16 +306,20 @@ void vApplicationTickHook(void) {
Private function to enable board led to use it in application hooks
*/
void prvSetMainLedOn(void) {
#ifdef LED_BUILTIN
gpio_init(LED_BUILTIN);
gpio_set_dir(LED_BUILTIN, true);
gpio_put(LED_BUILTIN, true);
#endif
}
/**
Private function to blink board led to use it in application hooks
*/
void prvBlinkMainLed(void) {
#ifdef LED_BUILTIN
gpio_put(LED_BUILTIN, !gpio_get(LED_BUILTIN));
#endif
}
#endif
@@ -376,9 +470,10 @@ static void __usb(void *param) {
__usbInitted = true;
while (true) {
if (mutex_try_enter(&__usb_mutex, NULL)) {
auto m = __get_freertos_mutex_for_ptr(&__usb_mutex);
if (xSemaphoreTake(m, 0)) {
tud_task();
mutex_exit(&__usb_mutex);
xSemaphoreGive(m);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
@@ -393,54 +488,7 @@ void __USBStart() {
__SetupDescHIDReport();
__SetupUSBDescriptor();
// Make highest prio and locked to core 0
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 1, &__usbTask);
// Make high prio and locked to core 0
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 2, &__usbTask);
vTaskCoreAffinitySet(__usbTask, 1 << 0);
}
// Interfaces for the main core to use FreeRTOS mutexes
extern "C" {
SemaphoreHandle_t __freertos_mutex_create() {
return xSemaphoreCreateMutex();
}
SemaphoreHandle_t _freertos_recursive_mutex_create() {
return xSemaphoreCreateRecursiveMutex();
}
void __freertos_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTake(mtx, portMAX_DELAY);
}
void __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx) {
xSemaphoreTakeFromISR(mtx, NULL);
}
int __freertos_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTake(mtx, 0);
}
void __freertos_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGive(mtx);
}
void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx) {
xSemaphoreGiveFromISR(mtx, NULL);
}
void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
}
int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTakeRecursive(mtx, 0);
}
void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGiveRecursive(mtx);
}
}
+30
View File
@@ -0,0 +1,30 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
PicoBluetoothHID KEYWORD1
PicoBluetoothBLEHID KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
setOpenedCB KEYWORD2
setClosedCB KEYWORD2
setCanSendNowCB KEYWORD2
startHID KEYWORD2
connected KEYWORD2
send KEYWORD2
lockBluetooth KEYWORD2
unlockBluetooth KEYWORD2
getCID KEYWORD2
setBattery KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
@@ -0,0 +1,9 @@
name=HID_Bluetooth
version=1.0.0
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
sentence=Implements a HID device base class for PicoW Bluetooth
paragraph=Implements a HID device base class for PicoW Bluetooth
category=Device Control
url=https://github.com/earlephilhower/arduino-pico
architectures=rp2040
@@ -0,0 +1,7 @@
#ifdef ENABLE_CLASSIC
#include "PicoBluetoothHID.h"
#endif
#ifdef ENABLE_BLE
#include "PicoBluetoothBLEHID.h"
#endif
@@ -0,0 +1,23 @@
/*
PicoBluetoothBLEHID.h - Simple wrapper for BT-HID objects like
keyboards, mice, gamepads.
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "PicoBluetoothBLEHID.h"
PicoBluetoothBLEHID_ PicoBluetoothBLEHID;
@@ -0,0 +1,485 @@
/*
PicoBluetoothBLEHID.h - Simple wrapper for BT-HID objects like
keyboards, mice, gamepads using Bluetooth LE mode
Based off of the BTStack HID main loop
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <_needsbt.h>
#include <Arduino.h>
#include <functional>
#include <pico/cyw43_arch.h>
#include <class/hid/hid_device.h>
// The BTStack has redefinitions of this USB enum (same values, just redefined), so hide it to allow easy compilation
#define HID_REPORT_TYPE_INPUT HID_REPORT_TYPE_INPUT_BT
#define HID_REPORT_TYPE_OUTPUT HID_REPORT_TYPE_OUTPUT_BT
#define HID_REPORT_TYPE_FEATURE HID_REPORT_TYPE_FEATURE_BT
#define hid_report_type_t hid_report_type_t_bt
#include <btstack.h>
#undef hid_report_type_t
#undef HID_REPORT_TYPE_FEATURE
#undef HID_REPORT_TYPE_OUTPUT
#undef HID_REPORT_TYPE_INPUT
#include <ble/sm.h>
#include <ble/att_server.h>
#include <btstack_event.h>
#include <ble/gatt-service/battery_service_server.h>
#include <ble/gatt-service/device_information_service_server.h>
#include <ble/gatt-service/hids_device.h>
class PicoBluetoothBLEHID_;
extern PicoBluetoothBLEHID_ PicoBluetoothBLEHID;
class PicoBluetoothBLEHID_ {
public:
PicoBluetoothBLEHID_() {
}
~PicoBluetoothBLEHID_() {
}
// Optional callback, not used presently
// Usage: PicoBluetoothHID.setCanSendNowCB(std::bind(&kbd::onCanSendNow, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
typedef std::function<void(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size)> BTCallback;
void setOpenedCB(BTCallback cb) {
_onOpened = cb;
}
void setClosedCB(BTCallback cb) {
_onClosed = cb;
}
void setCanSendNowCB(BTCallback cb) {
_onCanSendNow = cb;
}
bool startHID(const char *localName, const char *hidName, uint16_t appearance, const uint8_t *hidDescriptor, uint16_t hidDescriptorSize, int battery = 100) {
if (_running) {
return false;
}
_running = true;
_buildAdvData(localName, appearance);
_buildAttdb(hidName);
_battery = battery;
// Setup L2CAP
l2cap_init();
// Setup SM
sm_init();
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION | SM_AUTHREQ_BONDING);
// Setup ATT server
att_server_init(_attdb, NULL, NULL);
// Setup battery service
battery_service_server_init(battery);
// Setup device information service
device_information_service_server_init();
// Setup HID Device service
hids_device_init(0, hidDescriptor, hidDescriptorSize);
// Setup advertisements
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
uint8_t adv_type = 0;
bd_addr_t null_addr;
memset(null_addr, 0, 6);
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
gap_advertisements_set_data(_advDataLen, _advData);
gap_advertisements_enable(1);
// Register for HCI events
_hci_event_callback_registration.callback = PacketHandlerWrapper;
hci_add_event_handler(&_hci_event_callback_registration);
// Register for SM events
_sm_event_callback_registration.callback = PacketHandlerWrapper;
sm_add_event_handler(&_sm_event_callback_registration);
// Register for HIDS events
hids_device_register_packet_handler(PacketHandlerWrapper);
// GO!
hci_power_control(HCI_POWER_ON);
return true;
}
static void PacketHandlerWrapper(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t packet_size) {
PicoBluetoothBLEHID.packetHandler(packet_type, channel, packet, packet_size);
}
void packetHandler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) {
if (type != HCI_EVENT_PACKET) {
return;
}
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_DISCONNECTION_COMPLETE:
_con_handle = HCI_CON_HANDLE_INVALID;
if (_onClosed) {
_onClosed(type, channel, packet, size);
}
break;
case SM_EVENT_JUST_WORKS_REQUEST:
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
break;
case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
// printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
break;
case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
//printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
break;
case HCI_EVENT_HIDS_META:
switch (hci_event_hids_meta_get_subevent_code(packet)) {
case HIDS_SUBEVENT_INPUT_REPORT_ENABLE:
_con_handle = hids_subevent_input_report_enable_get_con_handle(packet);
if (_onOpened) {
_onOpened(type, channel, packet, size);
}
break;
case HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE:
_con_handle = hids_subevent_boot_keyboard_input_report_enable_get_con_handle(packet);
if (_onOpened) {
_onOpened(type, channel, packet, size);
}
break;
case HIDS_SUBEVENT_PROTOCOL_MODE:
_protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet);
break;
case HIDS_SUBEVENT_CAN_SEND_NOW:
switch (_protocol_mode) {
case 0:
hids_device_send_boot_keyboard_input_report(_con_handle, (const uint8_t *)_sendReport, _sendReportLen);
break;
case 1:
hids_device_send_input_report(_con_handle, (const uint8_t *)_sendReport, _sendReportLen);
break;
default:
break;
}
_needToSend = false;
if (_onCanSendNow) {
_onCanSendNow(type, channel, packet, size);
}
break;
}
break;
default:
break;
}
}
bool end() {
if (_running) {
hci_power_control(HCI_POWER_OFF);
}
_running = false;
_needToSend = false;
return true;
}
bool connected() {
return _con_handle != HCI_CON_HANDLE_INVALID;
}
bool send(void *rpt, int len) {
_needToSend = true;
_sendReport = rpt;
_sendReportLen = len;
lockBluetooth();
hids_device_request_can_send_now_event(_con_handle);
unlockBluetooth();
while (connected() && _needToSend) {
/* noop busy wait */
}
return connected();
}
bool setBattery(int level) {
if (!_running || (level < 0) || (level > 100)) {
return false;
}
battery_service_server_set_battery_value(level);
return true;
}
static void lockBluetooth() {
async_context_acquire_lock_blocking(cyw43_arch_async_context());
}
static void unlockBluetooth() {
async_context_release_lock(cyw43_arch_async_context());
}
private:
bool _running = false;
BTCallback _onOpened = nullptr;
BTCallback _onClosed = nullptr;
BTCallback _onCanSendNow = nullptr;
btstack_packet_callback_registration_t _hci_event_callback_registration;
btstack_packet_callback_registration_t _sm_event_callback_registration;
uint8_t _battery = 100;
hci_con_handle_t _con_handle = HCI_CON_HANDLE_INVALID;
uint8_t _protocol_mode = 1;
void _buildAdvData(const char *completeLocalName, uint16_t appearance) {
free(_advData);
_advDataLen = 13 + strlen(completeLocalName);
_advData = (uint8_t*) malloc(_advDataLen);
int i = 0;
// Flags general discoverable, BR/EDR not supported
// 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
_advData[i++] = 0x02;
_advData[i++] = BLUETOOTH_DATA_TYPE_FLAGS;
_advData[i++] = 0x06;
// Name
// 0x0d, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME,
_advData[i++] = 1 + strlen(completeLocalName);
_advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME;
memcpy(_advData + i, completeLocalName, strlen(completeLocalName));
i += strlen(completeLocalName);
// 16-bit Service UUIDs
// 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE & 0xff, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE >> 8,
_advData[i++] = 0x03;
_advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS;
_advData[i++] = ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE & 0xff;
_advData[i++] = ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE >> 8;
// Appearance HID - Keyboard (Category 15, Sub-Category 1)
// 0x03, BLUETOOTH_DATA_TYPE_APPEARANCE, 0xC1, 0x03,
_advData[i++] = 0x03;
_advData[i++] = BLUETOOTH_DATA_TYPE_APPEARANCE;
_advData[i++] = appearance & 0xff;
_advData[i++] = appearance >> 8;
}
uint8_t *_advData = nullptr;
uint8_t _advDataLen = 0;
void _buildAttdb(const char *hidName) {
free(_attdb);
_attdbLen = sizeof(_attdb_head) + 8 + strlen(hidName) + sizeof(_attdb_tail);
_attdb = (uint8_t *) malloc(_attdbLen);
memcpy(_attdb, _attdb_head, sizeof(_attdb_head));
// 0x0003 VALUE CHARACTERISTIC-GAP_DEVICE_NAME - READ -'HID Mouse'
// READ_ANYBODY
// 0x11, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x48, 0x49, 0x44, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65,
int i = sizeof(_attdb_head);
_attdb[i++] = 8 + strlen(hidName);
_attdb[i++] = 0x00;
_attdb[i++] = 0x02;
_attdb[i++] = 0x00;
_attdb[i++] = 0x03;
_attdb[i++] = 0x00;
_attdb[i++] = 0x00;
_attdb[i++] = 0x2a;
memcpy(_attdb + i, hidName, strlen(hidName));
i += strlen(hidName);
memcpy(_attdb + i, _attdb_tail, sizeof(_attdb_tail));
}
uint8_t *_attdb = nullptr;
int _attdbLen = 0;
static constexpr const uint8_t _attdb_head[] = {
// ATT DB Version
1,
// 0x0001 PRIMARY_SERVICE-GAP_SERVICE
0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18,
// 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME - READ
0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a,
};
static constexpr const uint8_t _attdb_tail[] = {
// #import <battery_service.gatt> -- BEGIN
// Specification Type org.bluetooth.service.battery_service
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.battery_service.xml
// Battery Service 180F
// 0x0004 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE
0x0a, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x28, 0x0f, 0x18,
// 0x0005 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY
0x0d, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x28, 0x12, 0x06, 0x00, 0x19, 0x2a,
// 0x0006 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x06, 0x00, 0x19, 0x2a,
// 0x0007 CLIENT_CHARACTERISTIC_CONFIGURATION
// READ_ANYBODY, WRITE_ANYBODY
0x0a, 0x00, 0x0e, 0x01, 0x07, 0x00, 0x02, 0x29, 0x00, 0x00,
// #import <battery_service.gatt> -- END
// add Device ID Service
// #import <device_information_service.gatt> -- BEGIN
// Specification Type org.bluetooth.service.device_information
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.device_information.xml
// Device Information 180A
// 0x0008 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION
0x0a, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x28, 0x0a, 0x18,
// 0x0009 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x09, 0x00, 0x03, 0x28, 0x02, 0x0a, 0x00, 0x29, 0x2a,
// 0x000a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x0a, 0x00, 0x29, 0x2a,
// 0x000b CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x28, 0x02, 0x0c, 0x00, 0x24, 0x2a,
// 0x000c VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x0c, 0x00, 0x24, 0x2a,
// 0x000d CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x03, 0x28, 0x02, 0x0e, 0x00, 0x25, 0x2a,
// 0x000e VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x0e, 0x00, 0x25, 0x2a,
// 0x000f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x03, 0x28, 0x02, 0x10, 0x00, 0x27, 0x2a,
// 0x0010 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x10, 0x00, 0x27, 0x2a,
// 0x0011 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x11, 0x00, 0x03, 0x28, 0x02, 0x12, 0x00, 0x26, 0x2a,
// 0x0012 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x26, 0x2a,
// 0x0013 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x13, 0x00, 0x03, 0x28, 0x02, 0x14, 0x00, 0x28, 0x2a,
// 0x0014 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x14, 0x00, 0x28, 0x2a,
// 0x0015 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x15, 0x00, 0x03, 0x28, 0x02, 0x16, 0x00, 0x23, 0x2a,
// 0x0016 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x16, 0x00, 0x23, 0x2a,
// 0x0017 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x17, 0x00, 0x03, 0x28, 0x02, 0x18, 0x00, 0x2a, 0x2a,
// 0x0018 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x18, 0x00, 0x2a, 0x2a,
// 0x0019 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x19, 0x00, 0x03, 0x28, 0x02, 0x1a, 0x00, 0x50, 0x2a,
// 0x001a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x1a, 0x00, 0x50, 0x2a,
// #import <device_information_service.gatt> -- END
// #import <hids.gatt> -- BEGIN
// Specification Type org.bluetooth.service.human_interface_device
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.human_interface_device.xml
// Human Interface Device 1812
// 0x001b PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE
0x0a, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x28, 0x12, 0x18,
// 0x001c CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE - DYNAMIC | READ | WRITE_WITHOUT_RESPONSE
0x0d, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x03, 0x28, 0x06, 0x1d, 0x00, 0x4e, 0x2a,
// 0x001d VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE - DYNAMIC | READ | WRITE_WITHOUT_RESPONSE
// READ_ANYBODY, WRITE_ANYBODY
0x08, 0x00, 0x06, 0x01, 0x1d, 0x00, 0x4e, 0x2a,
// 0x001e CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
0x0d, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x03, 0x28, 0x1a, 0x1f, 0x00, 0x4d, 0x2a,
// 0x001f VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
0x08, 0x00, 0x0b, 0xf5, 0x1f, 0x00, 0x4d, 0x2a,
// 0x0020 CLIENT_CHARACTERISTIC_CONFIGURATION
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
0x0a, 0x00, 0x0f, 0xf1, 0x20, 0x00, 0x02, 0x29, 0x00, 0x00,
// fixed report id = 1, type = Input (1)
// 0x0021 REPORT_REFERENCE-READ-1-1
0x0a, 0x00, 0x02, 0x00, 0x21, 0x00, 0x08, 0x29, 0x1, 0x1,
// 0x0022 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
0x0d, 0x00, 0x02, 0x00, 0x22, 0x00, 0x03, 0x28, 0x1a, 0x23, 0x00, 0x4d, 0x2a,
// 0x0023 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
0x08, 0x00, 0x0b, 0xf5, 0x23, 0x00, 0x4d, 0x2a,
// 0x0024 CLIENT_CHARACTERISTIC_CONFIGURATION
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
0x0a, 0x00, 0x0f, 0xf1, 0x24, 0x00, 0x02, 0x29, 0x00, 0x00,
// fixed report id = 2, type = Output (2)
// 0x0025 REPORT_REFERENCE-READ-2-2
0x0a, 0x00, 0x02, 0x00, 0x25, 0x00, 0x08, 0x29, 0x2, 0x2,
// 0x0026 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
0x0d, 0x00, 0x02, 0x00, 0x26, 0x00, 0x03, 0x28, 0x1a, 0x27, 0x00, 0x4d, 0x2a,
// 0x0027 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT - DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
0x08, 0x00, 0x0b, 0xf5, 0x27, 0x00, 0x4d, 0x2a,
// 0x0028 CLIENT_CHARACTERISTIC_CONFIGURATION
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
0x0a, 0x00, 0x0f, 0xf1, 0x28, 0x00, 0x02, 0x29, 0x00, 0x00,
// fixed report id = 3, type = Feature (3)
// 0x0029 REPORT_REFERENCE-READ-3-3
0x0a, 0x00, 0x02, 0x00, 0x29, 0x00, 0x08, 0x29, 0x3, 0x3,
// 0x002a CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP - DYNAMIC | READ
0x0d, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x03, 0x28, 0x02, 0x2b, 0x00, 0x4b, 0x2a,
// 0x002b VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP - DYNAMIC | READ
// READ_ANYBODY
0x08, 0x00, 0x02, 0x01, 0x2b, 0x00, 0x4b, 0x2a,
// 0x002c CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
0x0d, 0x00, 0x02, 0x00, 0x2c, 0x00, 0x03, 0x28, 0x1a, 0x2d, 0x00, 0x22, 0x2a,
// 0x002d VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
// READ_ANYBODY, WRITE_ANYBODY
0x08, 0x00, 0x0a, 0x01, 0x2d, 0x00, 0x22, 0x2a,
// 0x002e CLIENT_CHARACTERISTIC_CONFIGURATION
// READ_ANYBODY, WRITE_ANYBODY
0x0a, 0x00, 0x0e, 0x01, 0x2e, 0x00, 0x02, 0x29, 0x00, 0x00,
// 0x002f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT - DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE
0x0d, 0x00, 0x02, 0x00, 0x2f, 0x00, 0x03, 0x28, 0x0e, 0x30, 0x00, 0x32, 0x2a,
// 0x0030 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT - DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE
// READ_ANYBODY, WRITE_ANYBODY
0x08, 0x00, 0x0e, 0x01, 0x30, 0x00, 0x32, 0x2a,
// 0x0031 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
0x0d, 0x00, 0x02, 0x00, 0x31, 0x00, 0x03, 0x28, 0x1a, 0x32, 0x00, 0x33, 0x2a,
// 0x0032 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT - DYNAMIC | READ | WRITE | NOTIFY
// READ_ANYBODY, WRITE_ANYBODY
0x08, 0x00, 0x0a, 0x01, 0x32, 0x00, 0x33, 0x2a,
// 0x0033 CLIENT_CHARACTERISTIC_CONFIGURATION
// READ_ANYBODY, WRITE_ANYBODY
0x0a, 0x00, 0x0e, 0x01, 0x33, 0x00, 0x02, 0x29, 0x00, 0x00,
// bcdHID = 0x101 (v1.0.1), bCountryCode 0, remote wakeable = 0 | normally connectable 2
// 0x0034 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION - READ
0x0d, 0x00, 0x02, 0x00, 0x34, 0x00, 0x03, 0x28, 0x02, 0x35, 0x00, 0x4a, 0x2a,
// 0x0035 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION - READ -'01 01 00 02'
// READ_ANYBODY
0x0c, 0x00, 0x02, 0x00, 0x35, 0x00, 0x4a, 0x2a, 0x01, 0x01, 0x00, 0x02,
// 0x0036 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT - DYNAMIC | WRITE_WITHOUT_RESPONSE
0x0d, 0x00, 0x02, 0x00, 0x36, 0x00, 0x03, 0x28, 0x04, 0x37, 0x00, 0x4c, 0x2a,
// 0x0037 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT - DYNAMIC | WRITE_WITHOUT_RESPONSE
// WRITE_ANYBODY
0x08, 0x00, 0x04, 0x01, 0x37, 0x00, 0x4c, 0x2a,
// #import <hids.gatt> -- END
// 0x0038 PRIMARY_SERVICE-GATT_SERVICE
0x0a, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x28, 0x01, 0x18,
// 0x0039 CHARACTERISTIC-GATT_DATABASE_HASH - READ
0x0d, 0x00, 0x02, 0x00, 0x39, 0x00, 0x03, 0x28, 0x02, 0x3a, 0x00, 0x2a, 0x2b,
// 0x003a VALUE CHARACTERISTIC-GATT_DATABASE_HASH - READ -''
// READ_ANYBODY
0x18, 0x00, 0x02, 0x00, 0x3a, 0x00, 0x2a, 0x2b, 0xc2, 0x10, 0xf5, 0x75, 0xf3, 0x9f, 0x50, 0xb6, 0x83, 0xc7, 0xfa, 0xac, 0xa6, 0x1b, 0x7d, 0x32,
// END
0x00, 0x00
};
volatile bool _needToSend = false;
void *_sendReport;
int _sendReportLen;
};
@@ -0,0 +1,23 @@
/*
PicoBluetoothHID.h - Simple wrapper for BT-HID objects like
keyboards, mice, gamepads.
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "PicoBluetoothHID.h"
PicoBluetoothHID_ PicoBluetoothHID;
@@ -0,0 +1,256 @@
/*
PicoBluetoothHID.h - Simple wrapper for BT-HID objects like
keyboards, mice, gamepads.
Based off of the BTStack HID main loop
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <_needsbt.h>
#include <Arduino.h>
#include <functional>
#include <pico/cyw43_arch.h>
#include <class/hid/hid_device.h>
// The BTStack has redefinitions of this USB enum (same values, just redefined), so hide it to allow easy compilation
#define HID_REPORT_TYPE_INPUT HID_REPORT_TYPE_INPUT_BT
#define HID_REPORT_TYPE_OUTPUT HID_REPORT_TYPE_OUTPUT_BT
#define HID_REPORT_TYPE_FEATURE HID_REPORT_TYPE_FEATURE_BT
#define hid_report_type_t hid_report_type_t_bt
#include <btstack.h>
#undef hid_report_type_t
#undef HID_REPORT_TYPE_FEATURE
#undef HID_REPORT_TYPE_OUTPUT
#undef HID_REPORT_TYPE_INPUT
class PicoBluetoothHID_;
extern PicoBluetoothHID_ PicoBluetoothHID;
class PicoBluetoothHID_ {
public:
PicoBluetoothHID_() {
}
~PicoBluetoothHID_() {
}
// Optional callback, not used presently
// Usage: PicoBluetoothHID.setCanSendNowCB(std::bind(&kbd::onCanSendNow, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
typedef std::function<void(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size)> BTCallback;
void setOpenedCB(BTCallback cb) {
_onOpened = cb;
}
void setClosedCB(BTCallback cb) {
_onClosed = cb;
}
void setCanSendNowCB(BTCallback cb) {
_onCanSendNow = cb;
}
bool startHID(const char *localName, const char *hidName, uint16_t hidClass, uint8_t hidSubclass, const uint8_t *hidDescriptor, uint16_t hidDescriptorSize) {
if (_running) {
return false;
}
_running = true;
// Allow finding via inquiry
gap_discoverable_control(1);
// Use Limited Discoverable Mode; Peripheral; Keyboard as CoD
gap_set_class_of_device(hidClass);
// Set local name to be identified - zeroes will be replaced by actual BD ADDR
gap_set_local_name(localName);
// Allow for role switch in general and sniff mode
gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE);
// Allow for role switch on outgoing connections - this allow HID Host to become master when we re-connect to it
gap_set_allow_role_switch(true);
// L2CAP
l2cap_init();
#ifdef ENABLE_BLE
// Initialize LE Security Manager. Needed for cross-transport key derivation
sm_init();
#endif
// SDP Server
sdp_init();
bzero(_hid_service_buffer, sizeof(_hid_service_buffer));
const uint8_t hid_boot_device = 0;
const uint8_t hid_virtual_cable = 0;
const uint8_t hid_remote_wake = 1;
const uint8_t hid_reconnect_initiate = 1;
const uint8_t hid_normally_connectable = 1;
// When not set to 0xffff, sniff and sniff subrating are enabled
const uint16_t host_max_latency = 1600;
const uint16_t host_min_timeout = 3200;
hid_sdp_record_t hid_params = {
hidClass, hidSubclass,
hid_virtual_cable, hid_remote_wake,
hid_reconnect_initiate, (bool)hid_normally_connectable,
(bool)hid_boot_device,
host_max_latency, host_min_timeout,
3200,
hidDescriptor,
hidDescriptorSize,
hidName
};
hid_create_sdp_record(_hid_service_buffer, 0x10001, &hid_params);
sdp_register_service(_hid_service_buffer);
// See https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers if you don't have a USB Vendor ID and need a Bluetooth Vendor ID
// device info: BlueKitchen GmbH, product 1, version 1
device_id_create_sdp_record(_device_id_sdp_service_buffer, 0x10003, DEVICE_ID_VENDOR_ID_SOURCE_BLUETOOTH, BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH, 1, 1);
sdp_register_service(_device_id_sdp_service_buffer);
// HID Device
hid_device_init(hid_boot_device, hidDescriptorSize, hidDescriptor);
// register for HCI events
_hci_event_callback_registration.callback = PacketHandlerWrapper;
hci_add_event_handler(&_hci_event_callback_registration);
// register for HID events
hid_device_register_packet_handler(PacketHandlerWrapper);
hci_power_control(HCI_POWER_ON);
return true;
}
static void PacketHandlerWrapper(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t packet_size) {
PicoBluetoothHID.packetHandler(packet_type, channel, packet, packet_size);
}
void packetHandler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) {
uint8_t status;
if (type != HCI_EVENT_PACKET) {
return;
}
switch (hci_event_packet_get_type(packet)) {
case BTSTACK_EVENT_STATE:
if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) {
return;
}
_appState = APP_NOT_CONNECTED;
break;
case HCI_EVENT_USER_CONFIRMATION_REQUEST:
// ssp: inform about user confirmation request
//log_info("SSP User Confirmation Request with numeric value '%06" PRIu32 "'\n", hci_event_user_confirmation_request_get_numeric_value(packet));
//log_info("SSP User Confirmation Auto accept\n");
break;
case HCI_EVENT_HID_META:
switch (hci_event_hid_meta_get_subevent_code(packet)) {
case HID_SUBEVENT_CONNECTION_OPENED:
status = hid_subevent_connection_opened_get_status(packet);
if (status != ERROR_CODE_SUCCESS) {
// outgoing connection failed
_appState = APP_NOT_CONNECTED;
_hid_cid = 0;
return;
}
_appState = APP_CONNECTED;
_hid_cid = hid_subevent_connection_opened_get_hid_cid(packet);
if (_onOpened) {
_onOpened(type, channel, packet, size);
}
break;
case HID_SUBEVENT_CONNECTION_CLOSED:
_appState = APP_NOT_CONNECTED;
_hid_cid = 0;
if (_onClosed) {
_onClosed(type, channel, packet, size);
}
break;
case HID_SUBEVENT_CAN_SEND_NOW:
uint8_t report[2 + _sendReportLen];
report[0] = 0xa1;
report[1] = _sendReportID;
memcpy(report + 2, _sendReport, _sendReportLen);
hid_device_send_interrupt_message(getCID(), report, sizeof(report));
_needToSend = false;
if (_onCanSendNow) {
_onCanSendNow(type, channel, packet, size);
}
break;
}
}
}
bool end() {
if (_running) {
hci_power_control(HCI_POWER_OFF);
}
_running = false;
_needToSend = false;
return true;
}
bool connected() {
return _appState == APP_CONNECTED;
}
bool send(int id, void *rpt, int len) {
_needToSend = true;
_sendReportID = id;
_sendReport = rpt;
_sendReportLen = len;
lockBluetooth();
hid_device_request_can_send_now_event(getCID());
unlockBluetooth();
while (connected() && _needToSend) {
/* noop busy wait */
}
return connected();
}
static void lockBluetooth() {
async_context_acquire_lock_blocking(cyw43_arch_async_context());
}
static void unlockBluetooth() {
async_context_release_lock(cyw43_arch_async_context());
}
uint16_t getCID() {
return _hid_cid;
}
private:
bool _running = false;
enum {APP_BOOTING, APP_NOT_CONNECTED, APP_CONNECTING, APP_CONNECTED} _appState = APP_BOOTING;
BTCallback _onOpened = nullptr;
BTCallback _onClosed = nullptr;
BTCallback _onCanSendNow = nullptr;
uint8_t _hid_service_buffer[300];
uint8_t _device_id_sdp_service_buffer[100];
btstack_packet_callback_registration_t _hci_event_callback_registration;
uint16_t _hid_cid;
volatile bool _needToSend = false;
int _sendReportID;
void *_sendReport;
int _sendReportLen;
};
Submodule libraries/HID_Mouse added at 8bd9d96bd9
+21 -2
View File
@@ -10,6 +10,19 @@
+-- ------------------------------------ --+
left RPI | (1) GND (2) DIN (3) BCLK (4) LRCLK (5) 3.3V | AIY right
logo +---------------------------------------------+ logo
For an Adfruit I2S MEMS microphone (https://www.adafruit.com/product/3421),
connect the pins as follows:
DOUT -> GPIO0
BCLK <- GPIO1
LRCL <- GPIO2 # LRCLK = BCLK + 1
GND <-> GND
3V <-> 3V3OUT
The other idiosyncrasy of most modern MEMS microphones is that they
require a minimum clock rate to wake up. For example, the SPH0645
microphone needs at least 1MHz.
*/
#include <I2S.h>
@@ -20,15 +33,21 @@ void setup() {
Serial.begin(115200);
i2s.setDATA(0);
i2s.setBCLK(1); // LRCLK = GP2
i2s.setBCLK(1); // Note: LRCLK = BCLK + 1
i2s.setBitsPerSample(16);
i2s.setFrequency(22050);
// NOTE: The following values are known to work with the Adafruit microphone:
// i2s.setBitsPerSample(32);
// i2s.setFrequency(16000);
i2s.begin();
while (1) {
int16_t l, r;
i2s.read16(&l, &r);
Serial.printf("%d %d\n", l, r);
// NOTE: Adafruit microphone word size needs to match the BPS above.
// int32_t l, r;
// i2s.read32(&l, &r);
Serial.printf("%d %d\r\n", l, r);
}
}
+1
View File
@@ -20,6 +20,7 @@ setBitsPerSample KEYWORD2
setFrequency KEYWORD2
setBuffers KEYWORD2
setLSBJFormat KEYWORD2
swapClocks KEYWORD2
read8 KEYWORD2
read16 KEYWORD2
+21 -7
View File
@@ -52,6 +52,7 @@ I2S::I2S(PinMode direction) {
_bufferWords = 0;
_silenceSample = 0;
_isLSBJ = false;
_swapClocks = false;
}
I2S::~I2S() {
@@ -108,6 +109,14 @@ bool I2S::setLSBJFormat() {
return true;
}
bool I2S::swapClocks() {
if (_running || !_isOutput) {
return false;
}
_swapClocks = true;
return true;
}
void I2S::onTransmit(void(*fn)(void)) {
if (_isOutput) {
_cb = fn;
@@ -130,16 +139,20 @@ bool I2S::begin() {
_running = true;
_hasPeeked = false;
int off = 0;
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program) : &pio_i2s_in_program);
if (!_swapClocks) {
_i2s = new PIOProgram(_isOutput ? (_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->prepare(&_pio, &_sm, &off);
if (_isOutput) {
if (_isLSBJ) {
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
} else {
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
}
} else {
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
}
setFrequency(_freq);
if (_bps == 8) {
@@ -363,13 +376,14 @@ bool I2S::read32(int32_t *l, int32_t *r) {
size_t I2S::write(const uint8_t *buffer, size_t size) {
// We can only write 32-bit chunks here
if (size & 0x3) {
if (size & 0x3 || !_running || !_isOutput) {
return 0;
}
size_t writtenSize = 0;
int32_t *p = (int32_t *)buffer;
uint32_t *p = (uint32_t *)buffer;
while (size) {
if (!write((int32_t)*p)) {
if (!_arb->write(*p, false)) {
// Blocked, stop write here
return writtenSize;
} else {
+2
View File
@@ -34,6 +34,7 @@ public:
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0);
bool setFrequency(int newFreq);
bool setLSBJFormat();
bool swapClocks();
bool begin(long sampleRate) {
setFrequency(sampleRate);
@@ -107,6 +108,7 @@ private:
int32_t _silenceSample;
bool _isLSBJ;
bool _isOutput;
bool _swapClocks;
bool _running;
+74 -7
View File
@@ -41,6 +41,28 @@ right:
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
; Loop back to beginning...
.program pio_i2s_out_swap
.side_set 2 ; 0 = wclk, 1=bclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- BCLK
; |+---- WCLK
mov x, y side 0b10
left:
out pins, 1 side 0b00
jmp x--, left side 0b10
out pins, 1 side 0b01 ; Last bit of left has WCLK change per I2S spec
mov x, y side 0b11
right:
out pins, 1 side 0b01
jmp x--, right side 0b11
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
; Loop back to beginning...
.program pio_lsbj_out
.side_set 2 ; 0 = bclk, 1=wclk
@@ -64,6 +86,28 @@ right:
; Loop back to beginning...
.program pio_lsbj_out_swap
.side_set 2 ; 0 = wclk, 1=bclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- BCLK
; |+---- WCLK
mov x, y side 0b10
left:
out pins, 1 side 0b01
jmp x--, left side 0b11
out pins, 1 side 0b01
mov x, y side 0b11
right:
out pins, 1 side 0b00
jmp x--, right side 0b10
out pins, 1 side 0b00
; Loop back to beginning...
.program pio_i2s_in ; Note this is the same as _out, just "in" and not "out"
.side_set 2 ; 0 = bclk, 1=wclk
@@ -86,16 +130,39 @@ right:
in pins, 1 side 0b01 ; Last bit of right also has WCLK change
; Loop back to beginning...
.program pio_i2s_in_swap ; Note this is the same as _out, just "in" and not "out"
.side_set 2 ; 0 = wclk, 1=bclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- BCLK
; |+---- WCLK
mov x, y side 0b00
left:
in pins, 1 side 0b10
jmp x--, left side 0b00
in pins, 1 side 0b11 ; Last bit of left has WCLK change per I2S spec
mov x, y side 0b01
right:
in pins, 1 side 0b11
jmp x--, right side 0b01
in pins, 1 side 0b10 ; Last bit of right also has WCLK change
; Loop back to beginning...
% c-sdk {
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
static inline void pio_i2s_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);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_out_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_i2s_out_swap_program_get_default_config(offset) : pio_i2s_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);
@@ -111,12 +178,12 @@ 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_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
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);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_lsbj_out_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_lsbj_out_swap_program_get_default_config(offset) : pio_lsbj_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);
@@ -132,12 +199,12 @@ static inline void pio_lsbj_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_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
static inline void pio_i2s_in_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);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_in_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_i2s_in_swap_program_get_default_config(offset) : pio_i2s_in_program_get_default_config(offset);
sm_config_set_in_pins(&sm_config, data_pin);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
+112 -6
View File
@@ -43,6 +43,41 @@ static inline pio_sm_config pio_i2s_out_program_get_default_config(uint offset)
}
#endif
// ---------------- //
// pio_i2s_out_swap //
// ---------------- //
#define pio_i2s_out_swap_wrap_target 0
#define pio_i2s_out_swap_wrap 7
static const uint16_t pio_i2s_out_swap_program_instructions[] = {
// .wrap_target
0xb022, // 0: mov x, y side 2
0x6001, // 1: out pins, 1 side 0
0x1041, // 2: jmp x--, 1 side 2
0x6801, // 3: out pins, 1 side 1
0xb822, // 4: mov x, y side 3
0x6801, // 5: out pins, 1 side 1
0x1845, // 6: jmp x--, 5 side 3
0x6001, // 7: out pins, 1 side 0
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_i2s_out_swap_program = {
.instructions = pio_i2s_out_swap_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_i2s_out_swap_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_i2s_out_swap_wrap_target, offset + pio_i2s_out_swap_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
// ------------ //
// pio_lsbj_out //
// ------------ //
@@ -78,6 +113,41 @@ static inline pio_sm_config pio_lsbj_out_program_get_default_config(uint offset)
}
#endif
// ----------------- //
// pio_lsbj_out_swap //
// ----------------- //
#define pio_lsbj_out_swap_wrap_target 0
#define pio_lsbj_out_swap_wrap 7
static const uint16_t pio_lsbj_out_swap_program_instructions[] = {
// .wrap_target
0xb022, // 0: mov x, y side 2
0x6801, // 1: out pins, 1 side 1
0x1841, // 2: jmp x--, 1 side 3
0x6801, // 3: out pins, 1 side 1
0xb822, // 4: mov x, y side 3
0x6001, // 5: out pins, 1 side 0
0x1045, // 6: jmp x--, 5 side 2
0x6001, // 7: out pins, 1 side 0
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_lsbj_out_swap_program = {
.instructions = pio_lsbj_out_swap_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_lsbj_out_swap_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_lsbj_out_swap_wrap_target, offset + pio_lsbj_out_swap_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
// ---------- //
// pio_i2s_in //
// ---------- //
@@ -111,12 +181,47 @@ static inline pio_sm_config pio_i2s_in_program_get_default_config(uint offset) {
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
// --------------- //
// pio_i2s_in_swap //
// --------------- //
#define pio_i2s_in_swap_wrap_target 0
#define pio_i2s_in_swap_wrap 7
static const uint16_t pio_i2s_in_swap_program_instructions[] = {
// .wrap_target
0xa022, // 0: mov x, y side 0
0x5001, // 1: in pins, 1 side 2
0x0041, // 2: jmp x--, 1 side 0
0x5801, // 3: in pins, 1 side 3
0xa822, // 4: mov x, y side 1
0x5801, // 5: in pins, 1 side 3
0x0845, // 6: jmp x--, 5 side 1
0x5001, // 7: in pins, 1 side 2
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_i2s_in_swap_program = {
.instructions = pio_i2s_in_swap_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_i2s_in_swap_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_i2s_in_swap_wrap_target, offset + pio_i2s_in_swap_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
static inline void pio_i2s_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);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_out_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_i2s_out_swap_program_get_default_config(offset) : pio_i2s_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, (bits <= 16) ? 2 * bits : bits);
@@ -127,11 +232,11 @@ 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_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
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);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_lsbj_out_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_lsbj_out_swap_program_get_default_config(offset) : pio_lsbj_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, (bits <= 16) ? 2 * bits : bits);
@@ -142,11 +247,11 @@ static inline void pio_lsbj_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_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
static inline void pio_i2s_in_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);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_in_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_i2s_in_swap_program_get_default_config(offset) : pio_i2s_in_program_get_default_config(offset);
sm_config_set_in_pins(&sm_config, data_pin);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_in_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
@@ -159,3 +264,4 @@ static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint d
}
#endif
+32
View File
@@ -0,0 +1,32 @@
:repository-owner: arduino-libraries
:repository-name: Joystick
= {repository-name} Library for Arduino (RP2040 based boards) =
This library allows an RaspberryPi RP2040 board to act as a Joystick when
Earle F. Philhower`s [arduino-pico](https://github.com/earlephilhower/arduino-pico)
Core is used.
== Acknowledgements / Credits ==
* [arduino-pico](https://github.com/earlephilhower/arduino-pico) Earle F. Philhower providing the Arduino Core, on which this library is based on, available under LGPL.
* [tinyUSB] (https://github.com/hathach/tinyusb) Ha Thach for providing tinyUSB under MIT license, which covers most of the USB functionality.
== License ==
Copyright (c) Benjamin Aigner <beni@asterics-foundation.org> All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+435
View File
@@ -0,0 +1,435 @@
# Joystick library
## Methods
### `Joystick.begin()`
Must be called before starting using the Joystick emulation. To end control, use `Joystick.end()`.
#### Syntax
```
Joystick.begin()
```
#### Parameters
None.
#### Returns
None.
#### Example
```
#include <Joystick.h>
void setup() {
pinMode(2, INPUT_PULLUP);
}
void loop() {
// Initialize the Joystick library when button is pressed
if (digitalRead(2) == LOW) {
Joystick.begin();
}
}
```
#### See also
* [Joystick.button()](#joystickbutton)
* [Joystick.end()](#joystickend)
* [Joystick.send_now()](#joysticksend_now)
* [Joystick.position()](#joystickposition)
* [Joystick.X()](#joystickx)
* [Joystick.hat()](#joystickhat)
* [Joystick.use8bit()](#joystickuse8bit)
* [Joystick.useManualSend()](#joystickuseManualSend)
### `Joystick.button()`
Updates a button of the USB joystick.
#### Syntax
```
Joystick.button(1,true);
delay(250);
Joystick.button(1,false);
```
#### Parameters
* `button`: number of Joystick button, which status should be changed
* `val`: state of button, `true` for pressed, `false` for released
#### Returns
None.
#### Example
```
#include <Joystick.h>
void setup() {
pinMode(2, INPUT_PULLUP);
Joystick.begin();
}
void loop() {
uint8_t i = 1; //counter for the button number 1-32
if (digitalRead(2) == LOW) { //if button is pressed
Joystick.button(i,true); //"press" the Joystick button
delay(250); //wait for 0.25s
Joystick.button(i,false); //"release" the Joystick button
i = i + 1; //increment & use next Joystick button number
if(i > 32) i = 1; //we have 32 buttons available, wrap-around
}
}
```
#### Notes
* Up to 32 buttons are available, numbered as button 1 to button 32.
* If manual_send is active, call `Joystick.send_now()` to send an update to the host.
#### See also
* [Joystick.send_now()](#joysticksend_now)
* [Joystick.useManualSend()](#joystickuseManualSend)
* [Joystick.X()](#joystickx)
* [Joystick.hat()](#joystickhat)
### `Joystick.end()`
Stops emulating the Joystick connected to a computer. To start control, use `Joystick.begin()`.
#### Syntax
```
Joystick.end()
```
#### Parameters
None.
#### Returns
None.
#### Example
```
#include <Joystick.h>
void setup() {
pinMode(2, INPUT_PULLUP);
// Initiate the Joystick library
Joystick.begin();
}
void loop() {
// If the button is pressed, send a button 1 press / release
if (digitalRead(2) == LOW) {
Joystick.button(1,true);
delay(250);
Joystick.button(1,false);
// Then end the Joystick emulation
Joystick.end();
}
}
```
#### See also
* [Joystick.begin()](#joystickbegin)
### `Joystick.use8bit()`
Switch axis value range between 10bit and 8bit.
* Default: 10bit, range for an axis from 0 to 1023
* 8bit mode: range from -127 to 127.
__Note:__ due to the gamepad descriptor of tinyUSB, the maximum range is -127/127. 10bit mode enables mapping, not a higher resolution.
#### Syntax
```
Joystick.use8bit(true)
```
#### Parameters
* `mode`: true, if values from -127/127 are used. False to use a range from 0 to 1023.
#### Returns
None.
#### Example
```
#include <Joystick.h>
void setup() {
pinMode(2, INPUT_PULLUP);
Joystick.begin();
}
void loop() {
//send middle position in default 10bit mode
Joystick.position(512,512);
delay(250);
//enable 8bit mode
Joystick.use8bit(true);
//send middle position in 8bit mode
Joystick.position(0,0);
delay(250);
//send maximum left in 10bit mode
Joystick.use8bit(false);
Joystick.position(0,0);
delay(250);
//enable 8bit mode
Joystick.use8bit(true);
//send left position in 8bit mode
Joystick.position(-127,-127);
}
```
#### See also
* [Joystick.position()](#joystickposition)
* [Joystick.X()](#joystickx)
* [Joystick.Y()](#joysticky)
* [Joystick.Z()](#joystickz)
* [Joystick.Zrotate()](#joystickrotate)
* [Joystick.slider()](#joystickslider)
* [Joystick.sliderLeft()](#joysticksliderleft)
* [Joystick.sliderRight()](#joysticksliderright)
### `Joystick.useManualSend()`
To fully control transmitting the USB-HID reports, enable manual sending.
If disabled, each call to a function updating the Joystick status (buttons, all axis, hat)
will send a HID report. If you update in a loop, the time between updates (at least 1ms) is too short and something might be not transmitted correctly.
If enabled, update all your axis values, buttons, hat and then send one report via `Joystick.send_now()`.
#### Syntax
```
Joystick.useManualSend(true)
```
#### Parameters
* `mode`: false is sending report each Joystick update, true enables manual sending via send_now().
#### Returns
None.
#### Example
```
#include <Joystick.h>
void setup() {
pinMode(2, INPUT_PULLUP);
Joystick.begin();
}
void loop() {
if (digitalRead(2) == LOW) {
// send data in 4 different reports
Joystick.button(1,true);
Joystick.button(2,true);
Joystick.button(3,true);
Joystick.button(4,true);
//enable manual send
Joystick.useManualSend(true);
//send same data in one report
Joystick.button(1,false);
Joystick.button(2,false);
Joystick.button(3,false);
Joystick.button(4,false);
Joystick.send_now();
}
}
```
#### See also
* [Joystick.send_now()](#joysticksend_now)
### `Joystick.send_now()`
Send a HID report now. Used together with manual sending, see `Joystick.useManualSend()`.
#### Syntax
```
Joystick.send_now()
```
#### Parameters
None.
#### Returns
None.
#### Example
```
#include <Joystick.h>
void setup() {
pinMode(2, INPUT_PULLUP);
Joystick.begin();
//enable manual sending in setup
Joystick.useManualSend(true);
}
void loop() {
if (digitalRead(2) == LOW) {
// update all buttons, but nothing is sent to the host
for(uint8_t i = 1; i<=32; i++) Joystick.button(i,true);
Joystick.X(256);
Joystick.sliderLeft(0);
//now send in one HID report
Joystick.send_now();
}
}
```
#### See also
* [Joystick.useManualSend()](#joystickusemanualsend)
### `Joystick.X()`
Update X axis.
__Note:__ If [manual send](#joystickusemanualsend) is active, the value is sent to the host only after calling [send_now](#joysticksend_now).
__Note:__ If in 10bit mode (default), the parameter is interpreted from 0 to 1023.
In 8bit mode from -127 to 127. The internal resolution is always 8bit. Change setting with [use8bit](#joystickuse8bit).
#### Syntax
```
Joystick.X(0)
```
#### Parameters
* `val`: value from 0 to 1023 (default) or -127 to 127 (8bit mode)
#### Returns
None.
#### Example
```
#include <Joystick.h>
void setup() {
pinMode(2, INPUT_PULLUP);
Joystick.begin();
}
void loop() {
if (digitalRead(2) == LOW) {
Joystick.X(256);
delay(500);
Joystick.X(512);
}
}
```
#### See also
* [Joystick.Y()](#joysticky)
* [Joystick.Z()](#joystickz)
* [Joystick.Zrotate()](#joystickrotate)
* [Joystick.slider()](#joystickslider)
* [Joystick.sliderLeft()](#joysticksliderleft)
* [Joystick.sliderRight()](#joysticksliderright)
* [Joystick.send_now()](#joysticksend_now)
* [Joystick.position()](#joystickposition)
* [Joystick.hat()](#joystickhat)
* [Joystick.use8bit()](#joystickuse8bit)
* [Joystick.useManualSend()](#joystickuseManualSend)
### `Joystick.Y()`
Update Y axis. Please refer to [Joystick.X()](#joystickx).
### `Joystick.Z()`
Update Z axis. Please refer to [Joystick.X()](#joystickx).
### `Joystick.Zrotate()`
Update Z rotate axis. Please refer to [Joystick.X()](#joystickx).
### `Joystick.sliderLeft()`
Left slider value. Please refer to [Joystick.X()](#joystickx).
### `Joystick.sliderRight()`
Right slider value. Please refer to [Joystick.X()](#joystickx).
### `Joystick.slider()`
Same as [Joystick.sliderLeft()](#joysticksliderleft).
### `Joystick.position()`
Sets X and Y axis in one call. If autosending is active, one report is generated. Please refer to [Joystick.X()](#joystickx).
#### Syntax
```
Joystick.position(512,512)
```
#### Parameters
* `X`: value from 0 to 1023 (default) or -127 to 127 (8bit mode); for X axis
* `Y`: value from 0 to 1023 (default) or -127 to 127 (8bit mode); for Y axis
#### See also
* [Joystick.X()](#joystickx)
* [Joystick.Y()](#joysticky)
### `Joystick.hat()`
Set the hat value to selection angle or rest position.
#### Syntax
```
Joystick.hat(-1), // released/rest position
```
#### Parameters
* `angle` Angle value from 0-360 degrees or -1 for released resting position. Mapped to 8 different directions.
+14
View File
@@ -0,0 +1,14 @@
# Joystick library
The Joystick functions enable a RP2040 board to act as a HID game controller.
To use this library:
```
#include <Joystick.h>
```
## Examples
* [Joystick-AllFunctions] Includes example calls for each Joystick function
@@ -0,0 +1,110 @@
/* Benjamin Aigner, 2022 <beni@asterics-foundation.org> */
/* Public domain / CC 0 */
/** example code using all possibilities of the Joystick class
for the RP2040.
*/
#include <Joystick.h>
void setup() {
Serial.begin(115200);
Serial.println("Use BOOTSEL to start the Joystick demo.");
Joystick.begin();
}
void loop() {
if (BOOTSEL) {
Serial.println("Joystick buttons");
for (uint8_t i = 1; i <= 32; i++) {
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!
}
//alternativ with manual send:
Joystick.useManualSend(true);
Serial.println("Joystick buttons - manual send");
for (uint8_t i = 1; i <= 32; i++) {
Joystick.button(i, true);
Joystick.send_now();
delay(250);
Joystick.button(i, false);
}
Joystick.useManualSend(false);
//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);
delay(2);
} Joystick.X(512);
Serial.println("Joystick Y");
for (uint16_t i = 0; i < 1023; i++) {
Joystick.Y(i);
delay(2);
} Joystick.Y(512);
Serial.println("Joystick Z");
for (uint16_t i = 0; i < 1023; i++) {
Joystick.Z(i);
delay(2);
} Joystick.Z(512);
Serial.println("Joystick Zrotate");
for (uint16_t i = 0; i < 1023; i++) {
Joystick.Zrotate(i);
delay(2);
} Joystick.Zrotate(512);
Serial.println("Joystick sliderLeft");
for (uint16_t i = 0; i < 1023; i++) {
Joystick.sliderLeft(i);
delay(2);
} Joystick.sliderLeft(0);
Serial.println("Joystick sliderRight");
for (uint16_t i = 0; i < 1023; i++) {
Joystick.sliderRight(i);
delay(2);
} Joystick.sliderRight(0);
Serial.println("Joystick hat");
for (uint16_t i = 0; i < 360; i++) {
Joystick.hat(i);
delay(20);
} Joystick.hat(-1);
//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");
for (int16_t i = -127; i < 128; i++) {
Joystick.X(i);
delay(2);
} Joystick.X(0);
Serial.println("Joystick Y");
for (int16_t i = -127; i < 128; i++) {
Joystick.Y(i);
delay(2);
} Joystick.Y(0);
Serial.println("Joystick Z");
for (int16_t i = -127; i < 128; i++) {
Joystick.Z(i);
delay(2);
} Joystick.Z(0);
Serial.println("Joystick Zrotate");
for (int16_t i = -127; i < 128; i++) {
Joystick.Zrotate(i);
delay(2);
} Joystick.Zrotate(0);
Serial.println("Joystick sliderLeft");
for (int16_t i = -127; i < 128; i++) {
Joystick.sliderLeft(i);
delay(2);
} Joystick.sliderLeft(0);
Serial.println("Joystick sliderRight");
for (int16_t i = -127; i < 128; i++) {
Joystick.sliderRight(i);
delay(2);
} Joystick.sliderRight(0);
Joystick.use8bit(false);
}
}
+33
View File
@@ -0,0 +1,33 @@
#######################################
# Syntax Coloring Map For Joystick
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Joystick KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
end KEYWORD2
use8bit KEYWORD2
X KEYWORD2
Y KEYWORD2
button KEYWORD2
position KEYWORD2
Z KEYWORD2
Zrotate KEYWORD2
sliderLeft KEYWORD2
slider KEYWORD2
sliderRight KEYWORD2
hat KEYWORD2
useManualSend KEYWORD2
send_now KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
+9
View File
@@ -0,0 +1,9 @@
name=Joystick
version=1.0.1
author=Benjamin Aigner
maintainer=Benjamin Aigner <beni@asterics-foundation.org>
sentence=Allows any RP2040 board to act as a joystick/gamepad
paragraph=Allows the RP2040 to emulate a USB Joystick
category=Device Control
url=https://github.com/benjaminaigner/Joystick
architectures=rp2040
+49
View File
@@ -0,0 +1,49 @@
/*
Joystick.cpp
Copyright (c) 2022, Benjamin Aigner <beni@asterics-foundation.org>
Implementation loosely based on:
Mouse library from https://github.com/earlephilhower/arduino-pico
Joystick functions from Teensyduino https://github.com/PaulStoffregen
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 "Joystick.h"
#include "Arduino.h"
#include <RP2040USB.h>
#include "tusb.h"
#include "class/hid/hid_device.h"
// Weak function override to add our descriptor to the TinyUSB list
void __USBInstallJoystick() { /* noop */ }
Joystick_::Joystick_(void) {
// Everything set up in HID_Joystick constructor
}
//immediately send an HID report
void Joystick_::send_now(void) {
CoreMutex m(&__usb_mutex);
tud_task();
if (tud_hid_ready()) {
tud_hid_n_report(0, __USBGetJoystickReportID(), &data, sizeof(data));
}
tud_task();
}
Joystick_ Joystick;

Some files were not shown because too many files have changed in this diff Show More