Compare commits
@@ -217,3 +217,5 @@ jobs:
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/WiFi/examples/ScanNetworks/ScanNetworks.ino
|
||||
- name: Build Signed OTA Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino
|
||||
- name: Build Bluetooth Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH" libraries/MouseBLE/examples/BLECircle/BLECircle.ino
|
||||
|
||||
+6
-6
@@ -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
|
||||
|
||||
@@ -40,9 +40,11 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* 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 XIAO RP2040
|
||||
@@ -63,13 +65,33 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* Generic (configurable flash, I/O pins)
|
||||
|
||||
# Installing via Arduino Boards Manager
|
||||
**Windows Users**: Please do not use the Windows Store version of the actual Arduino application
|
||||
## Windows-specific Notes
|
||||
Please do not use the Windows Store version of the actual Arduino application
|
||||
because it has issues detecting attached Pico boards. Use the "Windows ZIP" or plain "Windows"
|
||||
executable (EXE) download direct from https://arduino.cc. and allow it to install any device
|
||||
drivers it suggests. Otherwise the Pico board may not be detected. Also, if trying out the
|
||||
2.0 beta Arduino please install the release 1.8 version beforehand to ensure needed device drivers
|
||||
are present. (See #20 for more details.)
|
||||
|
||||
## Linux-specific Notes
|
||||
Installing Arduino using flatpak (often used by "App Stores" in various Linux
|
||||
distributions) will mean it has restricted access to the host. This might cause uploads to fail
|
||||
with error messages such as the following:
|
||||
|
||||
```
|
||||
Scanning for RP2040 devices
|
||||
...
|
||||
No drive to deploy.
|
||||
```
|
||||
|
||||
If you encounter this, you will need to either install Arduino in a different manner, or override
|
||||
the flatpak sandboxing feature using the following command, then restarting Arduino.
|
||||
|
||||
```
|
||||
flatpak override --user --filesystem=host:ro cc.arduino.IDE2
|
||||
```
|
||||
|
||||
## Installation
|
||||
Open up the Arduino IDE and go to File->Preferences.
|
||||
|
||||
In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field:
|
||||
@@ -87,6 +109,12 @@ Type "pico" in the search box and select "Add":
|
||||

|
||||
|
||||
# Installing via GIT
|
||||
|
||||
**Windows Users:** Before installing via `git` on Windows, please read and follow the directions in
|
||||
[this link](https://arduino-pico.readthedocs.io/en/latest/platformio.html#important-steps-for-windows-users-before-installing).
|
||||
If Win32 long paths are not enabled, and `git` not configured to use them then there
|
||||
may be errors when attempting to clone the submodules.
|
||||
|
||||
To install via GIT (for latest and greatest versions):
|
||||
````
|
||||
mkdir -p ~/Arduino/hardware/pico
|
||||
|
||||
+1558
-110
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -43,5 +43,5 @@ public:
|
||||
private:
|
||||
mutex_t *_mutex;
|
||||
bool _acquired;
|
||||
u_int8_t _option;
|
||||
uint8_t _option;
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#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 +101,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 +109,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) {
|
||||
@@ -137,7 +138,7 @@ int __USBGetJoystickReportID() {
|
||||
if (__USBInstallKeyboard) {
|
||||
i++;
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
@@ -156,8 +157,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 +168,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 +198,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 +218,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 +250,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);
|
||||
|
||||
@@ -383,6 +398,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 +412,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;
|
||||
|
||||
@@ -23,9 +23,15 @@
|
||||
|
||||
// Weak function definitions for each type of endpoint
|
||||
extern void __USBInstallSerial() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallKeyboard() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallJoystick() __attribute__((weak));
|
||||
|
||||
// One or the other allowed, not both
|
||||
extern void __USBInstallMouse() __attribute__((weak));
|
||||
extern void __USBInstallAbsoluteMouse() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallMassStorage() __attribute__((weak));
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 3
|
||||
#define ARDUINO_PICO_MINOR 0
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "3.0.0"
|
||||
#define ARDUINO_PICO_MINOR 1
|
||||
#define ARDUINO_PICO_REVISION 1
|
||||
#define ARDUINO_PICO_VERSION_STR "3.1.1"
|
||||
|
||||
@@ -256,14 +256,10 @@ extern "C" {
|
||||
}
|
||||
|
||||
// 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
|
||||
auto_init_mutex(__sys_check_timeouts_mtx);
|
||||
extern void __real_sys_check_timeouts(void);
|
||||
extern void __real_sys_check_timeouts();
|
||||
void __wrap_sys_check_timeouts(void) {
|
||||
uint32_t owner;
|
||||
if (mutex_try_enter(&__sys_check_timeouts_mtx, &owner)) {
|
||||
__real_sys_check_timeouts();
|
||||
mutex_exit(&__sys_check_timeouts_mtx);
|
||||
}
|
||||
LWIPMutex m;
|
||||
__real_sys_check_timeouts();
|
||||
}
|
||||
|
||||
extern err_t __real_dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, void *callback_arg);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'3.0.0'
|
||||
version = u'3.1.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'3.0.0'
|
||||
release = u'3.1.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -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 |
+7
-1
@@ -143,7 +143,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::
|
||||
|
||||
|
||||
@@ -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
|
||||
----------------------------------
|
||||
|
||||
@@ -56,6 +56,9 @@ extern unsigned long get_rand_32(void);
|
||||
#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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This is the BTstack ports/arduino library, with compile errors fixed only.
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <BTstack.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 <BTstack.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 <BTstack.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 <BTstack.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,59 @@
|
||||
|
||||
#include <BTstack.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/* EXAMPLE_START(iBeaconScanner): iBeacon Scanner
|
||||
|
||||
@section Setup
|
||||
|
||||
@text After BTstack.setup(), BTstack is configured to call
|
||||
advertisementCallback whenever an Advertisement was received.
|
||||
Then, a device discovery is started
|
||||
*/
|
||||
|
||||
/* LISTING_START(iBeaconSetup): iBeacon Scanner Setup */
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
BTstack.setup();
|
||||
BTstack.setBLEAdvertisementCallback(advertisementCallback);
|
||||
BTstack.bleStartScanning();
|
||||
}
|
||||
/* LISTING_END(iBeaconSetup): iBeacon Scanner Setup */
|
||||
|
||||
void loop(void) {
|
||||
BTstack.loop();
|
||||
}
|
||||
|
||||
/*
|
||||
@section Advertisement Callback
|
||||
|
||||
@text Whenever an Advertisement is received, isIBeacon() checks if
|
||||
it contains an iBeacon. If yes, the Major ID, Minor ID, and UUID
|
||||
is printed.
|
||||
If it's not an iBeacon, only the BD_ADDR and the received signal strength
|
||||
(RSSI) is shown.
|
||||
*/
|
||||
/* LISTING_START(iBeaconCallback): iBeacon Scanner Callback */
|
||||
void advertisementCallback(BLEAdvertisement *adv) {
|
||||
if (adv->isIBeacon()) {
|
||||
Serial.print("iBeacon found ");
|
||||
Serial.print(adv->getBdAddr()->getAddressString());
|
||||
Serial.print(", RSSI ");
|
||||
Serial.print(adv->getRssi());
|
||||
Serial.print(", UUID ");
|
||||
Serial.print(adv->getIBeaconUUID()->getUuidString());
|
||||
Serial.print(", MajorID ");
|
||||
Serial.print(adv->getIBeaconMajorID());
|
||||
Serial.print(", MinorID ");
|
||||
Serial.print(adv->getIBecaonMinorID());
|
||||
Serial.print(", Measured Power ");
|
||||
Serial.println(adv->getiBeaconMeasuredPower());
|
||||
} else {
|
||||
Serial.print("Device discovered: ");
|
||||
Serial.print(adv->getBdAddr()->getAddressString());
|
||||
Serial.print(", RSSI ");
|
||||
Serial.println(adv->getRssi());
|
||||
}
|
||||
}
|
||||
/* LISTING_END(iBeaconCallback): iBeacon Scanner Callback */
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
BTstack KEYWORD3 RESERVED_WORD
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
BLEStatus KEYWORD2
|
||||
UUID KEYWORD2
|
||||
BD_ADDR_TYPE KEYWORD2
|
||||
BD_ADDR KEYWORD2
|
||||
BLEAdvertisement KEYWORD2
|
||||
BLECharacteristic KEYWORD2
|
||||
BLEService KEYWORD2
|
||||
BLEDevice KEYWORD2
|
||||
BTstackManager KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
getUuidString KEYWORD2
|
||||
getUuid128String KEYWORD2
|
||||
getUuid KEYWORD2
|
||||
matches KEYWORD2
|
||||
getAddress KEYWORD2
|
||||
getAddressString KEYWORD2
|
||||
getAddressType KEYWORD2
|
||||
getBdAddr KEYWORD2
|
||||
getBdAddrType KEYWORD2
|
||||
getRssi KEYWORD2
|
||||
containsService KEYWORD2
|
||||
nameHasPrefix KEYWORD2
|
||||
getAdvData KEYWORD2
|
||||
isIBeacon KEYWORD2
|
||||
getIBeaconUUID KEYWORD2
|
||||
getIBeaconMajorID KEYWORD2
|
||||
getIBecaonMinorID KEYWORD2
|
||||
getiBeaconMeasuredPower KEYWORD2
|
||||
isValueHandle KEYWORD2
|
||||
getCharacteristic KEYWORD2
|
||||
getUUID KEYWORD2
|
||||
getService KEYWORD2
|
||||
getHandle KEYWORD2
|
||||
discoverGATTServices KEYWORD2
|
||||
discoverCharacteristicsForService KEYWORD2
|
||||
readCharacteristic KEYWORD2
|
||||
writeCharacteristic KEYWORD2
|
||||
writeCharacteristicWithoutResponse KEYWORD2
|
||||
subscribeForNotifications KEYWORD2
|
||||
unsubscribeFromNotifications KEYWORD2
|
||||
subscribeForIndications KEYWORD2
|
||||
unsubscribeFromIndications KEYWORD2
|
||||
setPublicBdAddr KEYWORD2
|
||||
enablePacketLogger KEYWORD2
|
||||
enableDebugLogger KEYWORD2
|
||||
setAdvData KEYWORD2
|
||||
iBeaconConfigure KEYWORD2
|
||||
startAdvertising KEYWORD2
|
||||
stopAdvertising KEYWORD2
|
||||
bleStartScanning KEYWORD2
|
||||
bleStopScanning KEYWORD2
|
||||
bleConnect KEYWORD2
|
||||
bleDisconnect KEYWORD2
|
||||
setBLEAdvertisementCallback KEYWORD2
|
||||
setBLEDeviceConnectedCallback KEYWORD2
|
||||
setBLEDeviceDisconnectedCallback KEYWORD2
|
||||
setGATTServiceDiscoveredCallback KEYWORD2
|
||||
setGATTCharacteristicDiscoveredCallback KEYWORD2
|
||||
setGATTCharacteristicReadCallback KEYWORD2
|
||||
setGATTCharacteristicNotificationCallback KEYWORD2
|
||||
setGATTCharacteristicIndicationCallback KEYWORD2
|
||||
setGATTDoneCallback KEYWORD2
|
||||
setGATTCharacteristicWrittenCallback KEYWORD2
|
||||
setGATTCharacteristicSubscribedCallback KEYWORD2
|
||||
setGATTCharacteristicUnsubscribedCallback KEYWORD2
|
||||
setGATTCharacteristicRead KEYWORD2
|
||||
setGATTCharacteristicWrite KEYWORD2
|
||||
addGATTService KEYWORD2
|
||||
addGATTCharacteristic KEYWORD2
|
||||
addGATTCharacteristicDynamic KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
BLE_STATUS_OK LITERAL1
|
||||
BLE_STATUS_DONE LITERAL1
|
||||
BLE_STATUS_CONNECTION_TIMEOUT LITERAL1
|
||||
BLE_STATUS_CONNECTION_ERROR LITERAL1
|
||||
BLE_STATUS_OTHER_ERROR LITERAL1
|
||||
PUBLIC_ADDRESS LITERAL1
|
||||
PRIVAT_ADDRESS LITERAL1
|
||||
@@ -0,0 +1,10 @@
|
||||
name=BTstack
|
||||
version=1.0
|
||||
author=bluekitchen-gmbh
|
||||
maintainer=Earle Philhower
|
||||
sentence=BTstack Arduino library distributed with BTstack
|
||||
paragraph=
|
||||
category=Data Processing
|
||||
url=
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,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 "BTstack.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;
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
Arduino Wrapper for BTstack
|
||||
*/
|
||||
#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
|
||||
@@ -6,6 +6,7 @@
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
PicoBluetoothHID KEYWORD1
|
||||
PicoBluetoothBLEHID KEYWORD1
|
||||
|
||||
#######################################
|
||||
@@ -21,6 +22,7 @@ send KEYWORD2
|
||||
lockBluetooth KEYWORD2
|
||||
unlockBluetooth KEYWORD2
|
||||
getCID KEYWORD2
|
||||
setBattery KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
name=PicoBluetoothBLEHID
|
||||
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 BLE
|
||||
paragraph=Implements a HID device base class for PicoW Bluetooth BLE
|
||||
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
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
PicoBluetoothHID.h - Simple wrapper for BT-HID objects like
|
||||
PicoBluetoothBLEHID.h - Simple wrapper for BT-HID objects like
|
||||
keyboards, mice, gamepads.
|
||||
Copyright (c) 2023 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
+8
@@ -219,6 +219,14 @@ public:
|
||||
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());
|
||||
}
|
||||
Submodule
+1
Submodule libraries/HID_Joystick added at 4eb3e28dc2
Submodule
+1
Submodule libraries/HID_Keyboard added at c0f474695f
Submodule
+1
Submodule libraries/HID_Mouse added at 8bd9d96bd9
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ setBitsPerSample KEYWORD2
|
||||
setFrequency KEYWORD2
|
||||
setBuffers KEYWORD2
|
||||
setLSBJFormat KEYWORD2
|
||||
swapClocks KEYWORD2
|
||||
|
||||
read8 KEYWORD2
|
||||
read16 KEYWORD2
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Submodule libraries/Joystick deleted from af745f3e62
@@ -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
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
#######################################
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Keyboard_da_DK.h
|
||||
Joystick.h
|
||||
|
||||
Copyright (c) 2021, Peter John
|
||||
Copyright (c) 2022, Benjamin Aigner <beni@asterics-foundation.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
@@ -18,27 +18,18 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef KEYBOARD_DA_DK_h
|
||||
#define KEYBOARD_DA_DK_h
|
||||
#ifndef _JOYSTICK_h
|
||||
#define _JOYSTICK_h
|
||||
|
||||
#include "HID.h"
|
||||
#include <HID_Joystick.h>
|
||||
#include "class/hid/hid.h"
|
||||
|
||||
#if !defined(_USING_HID)
|
||||
//======================================================================
|
||||
class Joystick_ : public HID_Joystick {
|
||||
public:
|
||||
Joystick_(void);
|
||||
virtual void send_now(void) override;
|
||||
};
|
||||
extern Joystick_ Joystick;
|
||||
|
||||
#warning "Using legacy HID core (non pluggable)"
|
||||
|
||||
#else
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
// DA_DK keys
|
||||
#define KEY_A_RING (136+0x2f)
|
||||
#define KEY_SLASHED_O (136+0x34)
|
||||
#define KEY_ASH (136+0x33)
|
||||
#define KEY_UMLAUT (136+0x30)
|
||||
#define KEY_ACUTE_ACC (136+0x2e)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif /* _JOYSTICK_h */
|
||||
@@ -27,6 +27,7 @@ sliderRight KEYWORD2
|
||||
hat KEYWORD2
|
||||
useManualSend KEYWORD2
|
||||
send_now KEYWORD2
|
||||
setBattery KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
|
||||
@@ -32,152 +32,31 @@
|
||||
// Joystick/Gamepad
|
||||
|
||||
JoystickBLE_::JoystickBLE_(void) {
|
||||
_use8bit = false;
|
||||
_autosend = true;
|
||||
memset(&data, 0, sizeof(data));
|
||||
//_X_axis = _Y_axis = _Z_axis = _Zrotate = _sliderLeft = _sliderRight = _hat = data.buttons = 0;
|
||||
}
|
||||
|
||||
/** define the mapping of axes values
|
||||
default: axes methods are accepting values from 0-1023 (compatibility to other Joystick libraries)
|
||||
and are mapped internally to int8_t
|
||||
if use8bit(true) is called, -127 to 127 values are used.*/
|
||||
void JoystickBLE_::use8bit(bool mode) {
|
||||
_use8bit = mode;
|
||||
}
|
||||
|
||||
//if set, the gamepad report is not automatically sent after an update of axes/buttons; use send_now to update
|
||||
void JoystickBLE_::useManualSend(bool mode) {
|
||||
_autosend = !mode;
|
||||
}
|
||||
|
||||
/** Maps values from 8bit signed or 10bit unsigned to report values
|
||||
|
||||
Depending on the setting via use8bit(), either values from 0-1023 or -127 - 127
|
||||
are mapped.
|
||||
*/
|
||||
int JoystickBLE_::map8or10bit(int const value) {
|
||||
if (_use8bit) {
|
||||
if (value < -127) {
|
||||
return -127;
|
||||
} else if (value > 127) {
|
||||
return 127;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
if (value < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (value > 1023) {
|
||||
return 1023;
|
||||
}
|
||||
return map(value, 0, 1023, -127, 127);
|
||||
}
|
||||
// Member vars set in base constructor
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
static const uint8_t desc_joystick[] = {TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(REPORT_ID))};
|
||||
void JoystickBLE_::begin(void) {
|
||||
PicoBluetoothBLEHID.startHID("PicoW BLE Joystick", "PicoW BLE Joystick", 0x03c4, desc_joystick, sizeof(desc_joystick));
|
||||
|
||||
void JoystickBLE_::begin(const char *localName, const char *hidName) {
|
||||
if (!localName) {
|
||||
localName = "PicoW BLE Joystick";
|
||||
}
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, 0x03c4, desc_joystick, sizeof(desc_joystick));
|
||||
}
|
||||
|
||||
void JoystickBLE_::end(void) {
|
||||
void JoystickBLE_::end() {
|
||||
PicoBluetoothBLEHID.end();
|
||||
}
|
||||
|
||||
void JoystickBLE_::button(uint8_t button, bool val) {
|
||||
//I've no idea why, but without a second dword, it is not possible.
|
||||
//maybe something with the alignment when using bit set/clear?!?
|
||||
static uint32_t buttons_local = 0;
|
||||
|
||||
if (button >= 1 && button <= 32) {
|
||||
if (val) {
|
||||
buttons_local |= (1UL << (button - 1));
|
||||
} else {
|
||||
buttons_local &= ~(1UL << (button - 1));
|
||||
}
|
||||
|
||||
data.buttons = buttons_local;
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBLE_::setBattery(int lvl) {
|
||||
PicoBluetoothBLEHID.setBattery(lvl);
|
||||
}
|
||||
|
||||
void JoystickBLE_::X(int val) {
|
||||
data.x = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBLE_::Y(int val) {
|
||||
data.y = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBLE_::Z(int val) {
|
||||
data.z = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBLE_::Zrotate(int val) {
|
||||
data.rz = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBLE_::sliderLeft(int val) {
|
||||
data.rx = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBLE_::sliderRight(int val) {
|
||||
data.ry = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
void JoystickBLE_::slider(int val) {
|
||||
data.rx = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
void JoystickBLE_::position(int X, int Y) {
|
||||
data.x = map8or10bit(X);
|
||||
data.y = map8or10bit(Y);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
//compatibility: there is only one hat implemented, num parameter is ignored
|
||||
void JoystickBLE_::hat(unsigned int num, int angle) {
|
||||
(void) num;
|
||||
hat(angle);
|
||||
}
|
||||
|
||||
//set the hat value, from 0-360. -1 is rest position
|
||||
void JoystickBLE_::hat(int angle) {
|
||||
if (angle < 0) {
|
||||
data.hat = 0;
|
||||
}
|
||||
if (angle >= 0 && angle <= 360) {
|
||||
data.hat = map(angle, 0, 360, 1, 8);
|
||||
}
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
//immediately send an HID report
|
||||
void JoystickBLE_::send_now(void) {
|
||||
void JoystickBLE_::send_now() {
|
||||
PicoBluetoothBLEHID.send(&data, sizeof(data));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,48 +21,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HID_Joystick.h>
|
||||
#include "class/hid/hid.h"
|
||||
|
||||
//======================================================================
|
||||
class JoystickBLE_ {
|
||||
private:
|
||||
bool _autosend;
|
||||
bool _use8bit;
|
||||
hid_gamepad_report_t data;
|
||||
int map8or10bit(int const value);
|
||||
class JoystickBLE_ : public HID_Joystick {
|
||||
public:
|
||||
JoystickBLE_(void);
|
||||
void begin(void);
|
||||
void end(void);
|
||||
|
||||
//set a selected button to pressed/released
|
||||
void button(uint8_t button, bool val);
|
||||
//set axis values
|
||||
void X(int val);
|
||||
void Y(int val);
|
||||
void position(int X, int Y);
|
||||
void Z(int val);
|
||||
void Zrotate(int val);
|
||||
void sliderLeft(int val);
|
||||
void sliderRight(int val);
|
||||
//note: not implemented in TinyUSB gamepad, is mapped to sliderLeft.
|
||||
void slider(int val);
|
||||
|
||||
//set the hat value, from 0-360. -1 is rest position
|
||||
void hat(int angle);
|
||||
//compatibility: there is only one hat implemented, num parameter is ignored
|
||||
void hat(unsigned int num, int angle);
|
||||
|
||||
|
||||
//if set, the gamepad report is not automatically sent after an update of axes/buttons; use send_now to update
|
||||
void useManualSend(bool mode);
|
||||
//immediately send an HID report
|
||||
void send_now(void);
|
||||
//define the mapping of axes values
|
||||
//default: axes methods are accepting values from 0-1023 (compatibility to other Joystick libraries)
|
||||
// and are mapped internally to int8_t
|
||||
//if use8bit(true) is called, -127 to 127 values are used.
|
||||
void use8bit(bool mode);
|
||||
JoystickBLE_();
|
||||
void begin(const char *localName = nullptr, const char *hidName = nullptr);
|
||||
void end();
|
||||
virtual void send_now() override;
|
||||
void setBattery(int lvl);
|
||||
};
|
||||
extern JoystickBLE_ JoystickBLE;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Use BOOTSEL to start the Joystick demo.");
|
||||
JoystickBT.begin();
|
||||
JoystickBT.begin("PicoJoy Demo");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
@@ -31,153 +31,29 @@
|
||||
//================================================================================
|
||||
// Joystick/Gamepad
|
||||
|
||||
JoystickBT_::JoystickBT_(void) {
|
||||
_use8bit = false;
|
||||
_autosend = true;
|
||||
memset(&data, 0, sizeof(data));
|
||||
//_X_axis = _Y_axis = _Z_axis = _Zrotate = _sliderLeft = _sliderRight = _hat = data.buttons = 0;
|
||||
}
|
||||
|
||||
/** define the mapping of axes values
|
||||
default: axes methods are accepting values from 0-1023 (compatibility to other Joystick libraries)
|
||||
and are mapped internally to int8_t
|
||||
if use8bit(true) is called, -127 to 127 values are used.*/
|
||||
void JoystickBT_::use8bit(bool mode) {
|
||||
_use8bit = mode;
|
||||
}
|
||||
|
||||
//if set, the gamepad report is not automatically sent after an update of axes/buttons; use send_now to update
|
||||
void JoystickBT_::useManualSend(bool mode) {
|
||||
_autosend = !mode;
|
||||
}
|
||||
|
||||
/** Maps values from 8bit signed or 10bit unsigned to report values
|
||||
|
||||
Depending on the setting via use8bit(), either values from 0-1023 or -127 - 127
|
||||
are mapped.
|
||||
*/
|
||||
int JoystickBT_::map8or10bit(int const value) {
|
||||
if (_use8bit) {
|
||||
if (value < -127) {
|
||||
return -127;
|
||||
} else if (value > 127) {
|
||||
return 127;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
if (value < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (value > 1023) {
|
||||
return 1023;
|
||||
}
|
||||
return map(value, 0, 1023, -127, 127);
|
||||
}
|
||||
JoystickBT_::JoystickBT_() {
|
||||
// HID_Joystick sets up all the member vars
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
static const uint8_t desc_joystick[] = {TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(REPORT_ID))};
|
||||
void JoystickBT_::begin(void) {
|
||||
PicoBluetoothHID.startHID("PicoW Joystick 00:00:00:00:00:00", "PicoW HID Joystick", 0x2508, 33, desc_joystick, sizeof(desc_joystick));
|
||||
|
||||
void JoystickBT_::begin(const char *localName, const char *hidName) {
|
||||
if (!localName) {
|
||||
localName = "PicoW BT Joystick";
|
||||
}
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
PicoBluetoothHID.startHID(localName, hidName, 0x2508, 33, desc_joystick, sizeof(desc_joystick));
|
||||
}
|
||||
|
||||
void JoystickBT_::end(void) {
|
||||
void JoystickBT_::end() {
|
||||
PicoBluetoothHID.end();
|
||||
}
|
||||
|
||||
void JoystickBT_::button(uint8_t button, bool val) {
|
||||
//I've no idea why, but without a second dword, it is not possible.
|
||||
//maybe something with the alignment when using bit set/clear?!?
|
||||
static uint32_t buttons_local = 0;
|
||||
|
||||
if (button >= 1 && button <= 32) {
|
||||
if (val) {
|
||||
buttons_local |= (1UL << (button - 1));
|
||||
} else {
|
||||
buttons_local &= ~(1UL << (button - 1));
|
||||
}
|
||||
|
||||
data.buttons = buttons_local;
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JoystickBT_::X(int val) {
|
||||
data.x = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBT_::Y(int val) {
|
||||
data.y = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBT_::Z(int val) {
|
||||
data.z = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBT_::Zrotate(int val) {
|
||||
data.rz = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBT_::sliderLeft(int val) {
|
||||
data.rx = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
void JoystickBT_::sliderRight(int val) {
|
||||
data.ry = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
void JoystickBT_::slider(int val) {
|
||||
data.rx = map8or10bit(val);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
void JoystickBT_::position(int X, int Y) {
|
||||
data.x = map8or10bit(X);
|
||||
data.y = map8or10bit(Y);
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
//compatibility: there is only one hat implemented, num parameter is ignored
|
||||
void JoystickBT_::hat(unsigned int num, int angle) {
|
||||
(void) num;
|
||||
hat(angle);
|
||||
}
|
||||
|
||||
//set the hat value, from 0-360. -1 is rest position
|
||||
void JoystickBT_::hat(int angle) {
|
||||
if (angle < 0) {
|
||||
data.hat = 0;
|
||||
}
|
||||
if (angle >= 0 && angle <= 360) {
|
||||
data.hat = map(angle, 0, 360, 1, 8);
|
||||
}
|
||||
if (_autosend) {
|
||||
send_now();
|
||||
}
|
||||
}
|
||||
|
||||
//immediately send an HID report
|
||||
void JoystickBT_::send_now(void) {
|
||||
void JoystickBT_::send_now() {
|
||||
PicoBluetoothHID.send(REPORT_ID, &data, sizeof(data));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,48 +21,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HID_Joystick.h>
|
||||
#include "class/hid/hid.h"
|
||||
|
||||
//======================================================================
|
||||
class JoystickBT_ {
|
||||
private:
|
||||
bool _autosend;
|
||||
bool _use8bit;
|
||||
hid_gamepad_report_t data;
|
||||
int map8or10bit(int const value);
|
||||
class JoystickBT_ : public HID_Joystick {
|
||||
public:
|
||||
JoystickBT_(void);
|
||||
void begin(void);
|
||||
void end(void);
|
||||
|
||||
//set a selected button to pressed/released
|
||||
void button(uint8_t button, bool val);
|
||||
//set axis values
|
||||
void X(int val);
|
||||
void Y(int val);
|
||||
void position(int X, int Y);
|
||||
void Z(int val);
|
||||
void Zrotate(int val);
|
||||
void sliderLeft(int val);
|
||||
void sliderRight(int val);
|
||||
//note: not implemented in TinyUSB gamepad, is mapped to sliderLeft.
|
||||
void slider(int val);
|
||||
|
||||
//set the hat value, from 0-360. -1 is rest position
|
||||
void hat(int angle);
|
||||
//compatibility: there is only one hat implemented, num parameter is ignored
|
||||
void hat(unsigned int num, int angle);
|
||||
|
||||
|
||||
//if set, the gamepad report is not automatically sent after an update of axes/buttons; use send_now to update
|
||||
void useManualSend(bool mode);
|
||||
//immediately send an HID report
|
||||
void send_now(void);
|
||||
//define the mapping of axes values
|
||||
//default: axes methods are accepting values from 0-1023 (compatibility to other Joystick libraries)
|
||||
// and are mapped internally to int8_t
|
||||
//if use8bit(true) is called, -127 to 127 values are used.
|
||||
void use8bit(bool mode);
|
||||
JoystickBT_();
|
||||
void begin(const char *localName = nullptr, const char *hidName = nullptr);
|
||||
void end();
|
||||
virtual void send_now() override;
|
||||
};
|
||||
extern JoystickBT_ JoystickBT;
|
||||
|
||||
Submodule libraries/Keyboard deleted from a6498d6228
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -0,0 +1,31 @@
|
||||
:repository-owner: arduino-libraries
|
||||
:repository-name: Keyboard
|
||||
|
||||
= {repository-name} Library for Arduino =
|
||||
|
||||
image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml/badge.svg["Check Arduino status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml"]
|
||||
image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml/badge.svg["Compile Examples status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml"]
|
||||
image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"]
|
||||
|
||||
This library allows an Arduino board with USB capabilities to act as a keyboard.
|
||||
|
||||
For more information about this library please visit us at
|
||||
https://www.arduino.cc/reference/en/language/functions/usb/keyboard/
|
||||
|
||||
== License ==
|
||||
|
||||
Copyright (c) Arduino LLC. 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
|
||||
@@ -0,0 +1,44 @@
|
||||
/* Released into the public domain */
|
||||
/* Earle F. Philhower, III <earlephilhower@yahoo.com> */
|
||||
|
||||
#include <Keyboard.h>
|
||||
|
||||
void ledCB(bool numlock, bool capslock, bool scrolllock, bool compose, bool kana, void *cbData) {
|
||||
(void) numlock;
|
||||
(void) scrolllock;
|
||||
(void) compose;
|
||||
(void) kana;
|
||||
(void) cbData;
|
||||
digitalWrite(LED_BUILTIN, capslock ? HIGH : LOW);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
Keyboard.onLED(ledCB);
|
||||
Keyboard.begin();
|
||||
delay(5000);
|
||||
Serial.printf("Arduino USB Password Typer\n");
|
||||
Serial.printf("Press BOOTSEL to enter your super-secure(not!) password\n\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (BOOTSEL) {
|
||||
Serial.println("Typing password for you...shhhh....");
|
||||
|
||||
Serial.println("First, mute the computer to be extra quiet");
|
||||
Keyboard.consumerPress(KEY_MUTE);
|
||||
delay(100);
|
||||
Keyboard.consumerRelease();
|
||||
|
||||
Keyboard.print("ThisPasswordIsWeakLikeABaby");
|
||||
|
||||
Serial.println("OK, unmute the computer since our secret is done");
|
||||
Keyboard.consumerPress(KEY_MUTE);
|
||||
delay(100);
|
||||
Keyboard.consumerRelease();
|
||||
|
||||
while (BOOTSEL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Keyboard test
|
||||
|
||||
For the Arduino Leonardo, Micro or Due
|
||||
|
||||
Reads a byte from the serial port, sends a keystroke back.
|
||||
The sent keystroke is one higher than what's received, e.g. if you send a,
|
||||
you get b, send A you get B, and so forth.
|
||||
|
||||
The circuit:
|
||||
- none
|
||||
|
||||
created 21 Oct 2011
|
||||
modified 27 Mar 2012
|
||||
by Tom Igoe
|
||||
|
||||
This example code is in the public domain.
|
||||
|
||||
https://www.arduino.cc/en/Tutorial/BuiltInExamples/KeyboardSerial
|
||||
*/
|
||||
|
||||
#include "Keyboard.h"
|
||||
|
||||
void setup() {
|
||||
// open the serial port:
|
||||
Serial.begin(9600);
|
||||
// initialize control over the keyboard:
|
||||
Keyboard.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// check for incoming serial data:
|
||||
if (Serial.available() > 0) {
|
||||
// read incoming serial data:
|
||||
char inChar = Serial.read();
|
||||
// Type the next ASCII value from what you received:
|
||||
Keyboard.write(inChar + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Keyboard
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Keyboard KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
write KEYWORD2
|
||||
press KEYWORD2
|
||||
release KEYWORD2
|
||||
releaseAll KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
name=Keyboard
|
||||
version=1.0.3
|
||||
author=Arduino and EFP3
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Allows a RP2040 to act as a Keyboard.
|
||||
paragraph=Port of pure Arduino Keyboard
|
||||
category=Device Control
|
||||
url=https://www.arduino.cc/reference/en/language/functions/usb/keyboard/
|
||||
architectures=rp2040
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Keyboard.cpp
|
||||
|
||||
Modified by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
Main Arduino Library Copyright (c) 2015, Arduino LLC
|
||||
Original code (pre-library): Copyright (c) 2011, Peter Barrett
|
||||
|
||||
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 "Keyboard.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 __USBInstallKeyboard() { /* noop */ }
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
Keyboard_::Keyboard_(void) {
|
||||
// Base class clears the members we care about
|
||||
}
|
||||
|
||||
void Keyboard_::sendReport(KeyReport* keys) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
tud_task();
|
||||
if (tud_hid_ready()) {
|
||||
tud_hid_keyboard_report(__USBGetKeyboardReportID(), keys->modifiers, keys->keys);
|
||||
}
|
||||
tud_task();
|
||||
}
|
||||
|
||||
void Keyboard_::sendConsumerReport(uint16_t key) {
|
||||
CoreMutex m(&__usb_mutex);
|
||||
tud_task();
|
||||
if (tud_hid_ready()) {
|
||||
tud_hid_report(__USBGetKeyboardReportID() + 1, &key, sizeof(key));
|
||||
}
|
||||
tud_task();
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
(void) report_id;
|
||||
(void) instance;
|
||||
|
||||
if ((report_type == HID_REPORT_TYPE_OUTPUT) && (bufsize > 0) && (Keyboard._ledCB)) {
|
||||
uint8_t const kbd_leds = buffer[0];
|
||||
Keyboard._ledCB(kbd_leds & KEYBOARD_LED_NUMLOCK, kbd_leds & KEYBOARD_LED_CAPSLOCK, kbd_leds & KEYBOARD_LED_SCROLLLOCK, kbd_leds & KEYBOARD_LED_COMPOSE, kbd_leds & KEYBOARD_LED_KANA, Keyboard._ledCBdata);
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard_ Keyboard;
|
||||
@@ -1,7 +1,9 @@
|
||||
/*
|
||||
Keyboard_sv_SE.h
|
||||
Keyboard.h
|
||||
|
||||
Copyright (c) 2021, Peter John
|
||||
Modified by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
Main Arduino Library Copyright (c) 2015, Arduino LLC
|
||||
Original code (pre-library): Copyright (c) 2011, Peter Barrett
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
@@ -18,27 +20,19 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef KEYBOARD_SV_SE_h
|
||||
#define KEYBOARD_SV_SE_h
|
||||
#ifndef KEYBOARD_h
|
||||
#define KEYBOARD_h
|
||||
|
||||
#include "HID.h"
|
||||
#include <HID_Keyboard.h>
|
||||
|
||||
#if !defined(_USING_HID)
|
||||
class Keyboard_ : public HID_Keyboard {
|
||||
protected:
|
||||
virtual void sendReport(KeyReport* keys) override;
|
||||
virtual void sendConsumerReport(uint16_t key) override;
|
||||
|
||||
#warning "Using legacy HID core (non pluggable)"
|
||||
|
||||
#else
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
// SV_SE keys
|
||||
#define KEY_A_RING (136+0x2f)
|
||||
#define KEY_A_UMLAUT (136+0x34)
|
||||
#define KEY_O_UMLAUT (136+0x33)
|
||||
#define KEY_UMLAUT (136+0x30)
|
||||
#define KEY_ACUTE_ACC (136+0x2e)
|
||||
public:
|
||||
Keyboard_(void);
|
||||
};
|
||||
extern Keyboard_ Keyboard;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -15,6 +15,7 @@ void loop() {
|
||||
if (BOOTSEL) {
|
||||
Serial.println("Typing password for you...shhhh....");
|
||||
KeyboardBLE.print("ThisPasswordIsWeakLikeABaby");
|
||||
KeyboardBLE.setBattery(random(0, 101)); // Set between 0...100%
|
||||
while (BOOTSEL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ write KEYWORD2
|
||||
press KEYWORD2
|
||||
release KEYWORD2
|
||||
releaseAll KEYWORD2
|
||||
setBattery KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
|
||||
@@ -29,22 +29,32 @@
|
||||
// Keyboard
|
||||
|
||||
KeyboardBLE_::KeyboardBLE_(void) {
|
||||
bzero(&_keyReport, sizeof(_keyReport));
|
||||
_asciimap = KeyboardLayout_en_US;
|
||||
// Base class clears the members we care about
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
|
||||
static const uint8_t desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID))};
|
||||
void KeyboardBLE_::begin(const uint8_t *layout) {
|
||||
|
||||
void KeyboardBLE_::begin(const char *localName, const char *hidName, const uint8_t *layout) {
|
||||
if (!localName) {
|
||||
localName = "PicoW BLE Keyboard";
|
||||
}
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
_asciimap = layout;
|
||||
PicoBluetoothBLEHID.startHID("PicoW BLE Keyboard", "PicoW BLE Keyboard", 0x03c1, desc_keyboard, sizeof(desc_keyboard));
|
||||
PicoBluetoothBLEHID.startHID(localName, hidName, 0x03c1, desc_keyboard, sizeof(desc_keyboard));
|
||||
}
|
||||
|
||||
void KeyboardBLE_::end(void) {
|
||||
PicoBluetoothBLEHID.end();
|
||||
}
|
||||
|
||||
void KeyboardBLE_::setBattery(int lvl) {
|
||||
PicoBluetoothBLEHID.setBattery(lvl);
|
||||
}
|
||||
|
||||
void KeyboardBLE_::sendReport(KeyReport* keys) {
|
||||
hid_keyboard_report_t data;
|
||||
data.modifier = keys->modifiers;
|
||||
@@ -53,127 +63,9 @@ void KeyboardBLE_::sendReport(KeyReport* keys) {
|
||||
PicoBluetoothBLEHID.send(&data, sizeof(data));
|
||||
}
|
||||
|
||||
// press() adds the specified key (printing, non-printing, or modifier)
|
||||
// to the persistent key report and sends the report. Because of the way
|
||||
// USB HID works, the host acts like the key remains pressed until we
|
||||
// call release(), releaseAll(), or otherwise clear the report and resend.
|
||||
size_t KeyboardBLE_::press(uint8_t k) {
|
||||
uint8_t i;
|
||||
if (k >= 136) { // it's a non-printing key (not a modifier)
|
||||
k = k - 136;
|
||||
} else if (k >= 128) { // it's a modifier key
|
||||
_keyReport.modifiers |= (1 << (k - 128));
|
||||
k = 0;
|
||||
} else { // it's a printing key
|
||||
k = pgm_read_byte(_asciimap + k);
|
||||
if (!k) {
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
if ((k & ALT_GR) == ALT_GR) {
|
||||
_keyReport.modifiers |= 0x40; // AltGr = right Alt
|
||||
k &= 0x3F;
|
||||
} else if ((k & SHIFT) == SHIFT) {
|
||||
_keyReport.modifiers |= 0x02; // the left shift modifier
|
||||
k &= 0x7F;
|
||||
}
|
||||
if (k == ISO_REPLACEMENT) {
|
||||
k = ISO_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
// Add k to the key report only if it's not already present
|
||||
// and if there is an empty slot.
|
||||
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
|
||||
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
|
||||
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (_keyReport.keys[i] == 0x00) {
|
||||
_keyReport.keys[i] = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 6) {
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
sendReport(&_keyReport);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// release() takes the specified key out of the persistent key report and
|
||||
// sends the report. This tells the OS the key is no longer pressed and that
|
||||
// it shouldn't be repeated any more.
|
||||
size_t KeyboardBLE_::release(uint8_t k) {
|
||||
uint8_t i;
|
||||
if (k >= 136) { // it's a non-printing key (not a modifier)
|
||||
k = k - 136;
|
||||
} else if (k >= 128) { // it's a modifier key
|
||||
_keyReport.modifiers &= ~(1 << (k - 128));
|
||||
k = 0;
|
||||
} else { // it's a printing key
|
||||
k = pgm_read_byte(_asciimap + k);
|
||||
if (!k) {
|
||||
return 0;
|
||||
}
|
||||
if ((k & ALT_GR) == ALT_GR) {
|
||||
_keyReport.modifiers &= ~(0x40); // AltGr = right Alt
|
||||
k &= 0x3F;
|
||||
} else if ((k & SHIFT) == SHIFT) {
|
||||
_keyReport.modifiers &= ~(0x02); // the left shift modifier
|
||||
k &= 0x7F;
|
||||
}
|
||||
if (k == ISO_REPLACEMENT) {
|
||||
k = ISO_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
// Test the key report to see if k is present. Clear it if it exists.
|
||||
// Check all positions in case the key is present more than once (which it shouldn't be)
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (0 != k && _keyReport.keys[i] == k) {
|
||||
_keyReport.keys[i] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
sendReport(&_keyReport);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void KeyboardBLE_::releaseAll(void) {
|
||||
_keyReport.keys[0] = 0;
|
||||
_keyReport.keys[1] = 0;
|
||||
_keyReport.keys[2] = 0;
|
||||
_keyReport.keys[3] = 0;
|
||||
_keyReport.keys[4] = 0;
|
||||
_keyReport.keys[5] = 0;
|
||||
_keyReport.modifiers = 0;
|
||||
sendReport(&_keyReport);
|
||||
}
|
||||
|
||||
size_t KeyboardBLE_::write(uint8_t c) {
|
||||
uint8_t p = press(c); // Keydown
|
||||
delay(10);
|
||||
release(c); // Keyup
|
||||
delay(10);
|
||||
return p; // just return the result of press() since release() almost always returns 1
|
||||
}
|
||||
|
||||
size_t KeyboardBLE_::write(const uint8_t *buffer, size_t size) {
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
if (*buffer != '\r') {
|
||||
if (write(*buffer)) {
|
||||
n++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer++;
|
||||
}
|
||||
return n;
|
||||
void KeyboardBLE_::sendConsumerReport(uint16_t key) {
|
||||
(void) key;
|
||||
// TODO - Need some BLE-specific code to send 2nd report
|
||||
}
|
||||
|
||||
KeyboardBLE_ KeyboardBLE;
|
||||
|
||||
@@ -23,118 +23,17 @@
|
||||
#ifndef KEYBOARDBLE_h
|
||||
#define KEYBOARDBLE_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HID_Keyboard.h>
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
// Modifiers
|
||||
#define KEY_LEFT_CTRL 0x80
|
||||
#define KEY_LEFT_SHIFT 0x81
|
||||
#define KEY_LEFT_ALT 0x82
|
||||
#define KEY_LEFT_GUI 0x83
|
||||
#define KEY_RIGHT_CTRL 0x84
|
||||
#define KEY_RIGHT_SHIFT 0x85
|
||||
#define KEY_RIGHT_ALT 0x86
|
||||
#define KEY_RIGHT_GUI 0x87
|
||||
|
||||
// Misc keys
|
||||
#define KEY_UP_ARROW 0xDA
|
||||
#define KEY_DOWN_ARROW 0xD9
|
||||
#define KEY_LEFT_ARROW 0xD8
|
||||
#define KEY_RIGHT_ARROW 0xD7
|
||||
#define KEY_BACKSPACE 0xB2
|
||||
#define KEY_TAB 0xB3
|
||||
#define KEY_RETURN 0xB0
|
||||
#define KEY_MENU 0xED // "Keyboard Application" in USB standard
|
||||
#define KEY_ESC 0xB1
|
||||
#define KEY_INSERT 0xD1
|
||||
#define KEY_DELETE 0xD4
|
||||
#define KEY_PAGE_UP 0xD3
|
||||
#define KEY_PAGE_DOWN 0xD6
|
||||
#define KEY_HOME 0xD2
|
||||
#define KEY_END 0xD5
|
||||
#define KEY_CAPS_LOCK 0xC1
|
||||
#define KEY_PRINT_SCREEN 0xCE // Print Screen / SysRq
|
||||
#define KEY_SCROLL_LOCK 0xCF
|
||||
#define KEY_PAUSE 0xD0 // Pause / Break
|
||||
|
||||
// Numeric keypad
|
||||
#define KEY_NUM_LOCK 0xDB
|
||||
#define KEY_KP_SLASH 0xDC
|
||||
#define KEY_KP_ASTERISK 0xDD
|
||||
#define KEY_KP_MINUS 0xDE
|
||||
#define KEY_KP_PLUS 0xDF
|
||||
#define KEY_KP_ENTER 0xE0
|
||||
#define KEY_KP_1 0xE1
|
||||
#define KEY_KP_2 0xE2
|
||||
#define KEY_KP_3 0xE3
|
||||
#define KEY_KP_4 0xE4
|
||||
#define KEY_KP_5 0xE5
|
||||
#define KEY_KP_6 0xE6
|
||||
#define KEY_KP_7 0xE7
|
||||
#define KEY_KP_8 0xE8
|
||||
#define KEY_KP_9 0xE9
|
||||
#define KEY_KP_0 0xEA
|
||||
#define KEY_KP_DOT 0xEB
|
||||
|
||||
// Function keys
|
||||
#define KEY_F1 0xC2
|
||||
#define KEY_F2 0xC3
|
||||
#define KEY_F3 0xC4
|
||||
#define KEY_F4 0xC5
|
||||
#define KEY_F5 0xC6
|
||||
#define KEY_F6 0xC7
|
||||
#define KEY_F7 0xC8
|
||||
#define KEY_F8 0xC9
|
||||
#define KEY_F9 0xCA
|
||||
#define KEY_F10 0xCB
|
||||
#define KEY_F11 0xCC
|
||||
#define KEY_F12 0xCD
|
||||
#define KEY_F13 0xF0
|
||||
#define KEY_F14 0xF1
|
||||
#define KEY_F15 0xF2
|
||||
#define KEY_F16 0xF3
|
||||
#define KEY_F17 0xF4
|
||||
#define KEY_F18 0xF5
|
||||
#define KEY_F19 0xF6
|
||||
#define KEY_F20 0xF7
|
||||
#define KEY_F21 0xF8
|
||||
#define KEY_F22 0xF9
|
||||
#define KEY_F23 0xFA
|
||||
#define KEY_F24 0xFB
|
||||
|
||||
// Supported keyboard layouts
|
||||
extern const uint8_t KeyboardLayout_de_DE[];
|
||||
extern const uint8_t KeyboardLayout_en_US[];
|
||||
extern const uint8_t KeyboardLayout_es_ES[];
|
||||
extern const uint8_t KeyboardLayout_fr_FR[];
|
||||
extern const uint8_t KeyboardLayout_it_IT[];
|
||||
extern const uint8_t KeyboardLayout_sv_SE[];
|
||||
extern const uint8_t KeyboardLayout_da_DK[];
|
||||
|
||||
// Low level key report: up to 6 keys and shift, ctrl etc at once
|
||||
typedef struct {
|
||||
uint8_t modifiers;
|
||||
uint8_t reserved;
|
||||
uint8_t keys[6];
|
||||
} KeyReport;
|
||||
|
||||
class KeyboardBLE_ : public Print {
|
||||
class KeyboardBLE_ : public HID_Keyboard {
|
||||
private:
|
||||
KeyReport _keyReport;
|
||||
const uint8_t *_asciimap;
|
||||
void sendReport(KeyReport* keys);
|
||||
virtual void sendReport(KeyReport* keys) override;
|
||||
virtual void sendConsumerReport(uint16_t key) override;
|
||||
public:
|
||||
KeyboardBLE_(void);
|
||||
void begin(const uint8_t *layout = KeyboardLayout_en_US);
|
||||
void begin(const char *localName = nullptr, const char *hidName = nullptr, const uint8_t *layout = KeyboardLayout_en_US);
|
||||
void end(void);
|
||||
size_t write(uint8_t k);
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
size_t press(uint8_t k);
|
||||
size_t release(uint8_t k);
|
||||
void releaseAll(void);
|
||||
void setBattery(int lvl);
|
||||
};
|
||||
extern KeyboardBLE_ KeyboardBLE;
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
KeyboardLayout.h
|
||||
|
||||
This file is not part of the public API. It is meant to be included
|
||||
only in Keyboard.cpp and the keyboard layout files. Layout files map
|
||||
ASCII character codes to keyboard scan codes (technically, to USB HID
|
||||
Usage codes), possibly altered by the SHIFT or ALT_GR modifiers.
|
||||
|
||||
== Creating your own layout ==
|
||||
|
||||
In order to create your own layout file, copy an existing layout that
|
||||
is similar to yours, then modify it to use the correct keys. The
|
||||
layout is an array in ASCII order. Each entry contains a scan code,
|
||||
possibly modified by "|SHIFT" or "|ALT_GR", as in this excerpt from
|
||||
the Italian layout:
|
||||
|
||||
0x35, // bslash
|
||||
0x30|ALT_GR, // ]
|
||||
0x2e|SHIFT, // ^
|
||||
|
||||
Do not change the control characters (those before scan code 0x2c,
|
||||
corresponding to space). Do not attempt to grow the table past DEL. Do
|
||||
not use both SHIFT and ALT_GR on the same character: this is not
|
||||
supported. Unsupported characters should have 0x00 as scan code.
|
||||
|
||||
For a keyboard with an ISO physical layout, use the scan codes below:
|
||||
|
||||
+---+---+---+---+---+---+---+---+---+---+---+---+---+-------+
|
||||
|35 |1e |1f |20 |21 |22 |23 |24 |25 |26 |27 |2d |2e |BackSp |
|
||||
+---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-----+
|
||||
| Tab |14 |1a |08 |15 |17 |1c |18 |0c |12 |13 |2f |30 | Ret |
|
||||
+-----++--++--++--++--++--++--++--++--++--++--++--++--++ |
|
||||
|CapsL |04 |16 |07 |09 |0a |0b |0d |0e |0f |33 |34 |31 | |
|
||||
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+---+----+
|
||||
|Shi.|32 |1d |1b |06 |19 |05 |11 |10 |36 |37 |38 | Shift |
|
||||
+----+---++--+-+-+---+---+---+---+---+--++---+---++----+----+
|
||||
|Ctrl|Win |Alt | |AlGr|Win |Menu|Ctrl|
|
||||
+----+----+----+------------------------+----+----+----+----+
|
||||
|
||||
The ANSI layout is identical except that key 0x31 is above (rather
|
||||
than next to) Return, and there is not key 0x32.
|
||||
|
||||
Give a unique name to the layout array, then declare it in Keyboard.h
|
||||
with a line of the form:
|
||||
|
||||
extern const uint8_t KeyboardLayout_xx_YY[];
|
||||
|
||||
== Encoding details ==
|
||||
|
||||
All scan codes are less than 0x80, which makes bit 7 available to
|
||||
signal that a modifier (Shift or AltGr) is needed to generate the
|
||||
character. With only one exception, keys that are used with modifiers
|
||||
have scan codes that are less than 0x40. This makes bit 6 available
|
||||
to signal whether the modifier is Shift or AltGr. The exception is
|
||||
0x64, the key next next to Left Shift on the ISO layout (and absent
|
||||
from the ANSI layout). We handle it by replacing its value by 0x32 in
|
||||
the layout arrays.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define SHIFT 0x80
|
||||
#define ALT_GR 0xc0
|
||||
#define ISO_KEY 0x64
|
||||
#define ISO_REPLACEMENT 0x32
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Danish keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_da_DK[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x20 | SHIFT, // #
|
||||
0x21 | ALT_GR, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x31, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x31 | SHIFT, // *
|
||||
0x2d, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x1f | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x25 | ALT_GR, // [
|
||||
0x32 | ALT_GR, // bslash
|
||||
0x26 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x24 | ALT_GR, // {
|
||||
0x2e | ALT_GR, // |
|
||||
0x27 | ALT_GR, // }
|
||||
0x00, // ~ not supported (requires dead key + space)
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
German keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_de_DE[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x31, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x31 | SHIFT, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x30 | SHIFT, // *
|
||||
0x30, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x14 | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1d | SHIFT, // Y
|
||||
0x1c | SHIFT, // Z
|
||||
0x25 | ALT_GR, // [
|
||||
0x2d | ALT_GR, // bslash
|
||||
0x26 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1d, // y
|
||||
0x1c, // z
|
||||
0x24 | ALT_GR, // {
|
||||
0x32 | ALT_GR, // |
|
||||
0x27 | ALT_GR, // }
|
||||
0x30 | ALT_GR, // ~
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Standard US keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_en_US[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x34 | SHIFT, // "
|
||||
0x20 | SHIFT, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x24 | SHIFT, // &
|
||||
0x34, // '
|
||||
0x26 | SHIFT, // (
|
||||
0x27 | SHIFT, // )
|
||||
0x25 | SHIFT, // *
|
||||
0x2e | SHIFT, // +
|
||||
0x36, // ,
|
||||
0x2d, // -
|
||||
0x37, // .
|
||||
0x38, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x33 | SHIFT, // :
|
||||
0x33, // ;
|
||||
0x36 | SHIFT, // <
|
||||
0x2e, // =
|
||||
0x37 | SHIFT, // >
|
||||
0x38 | SHIFT, // ?
|
||||
0x1f | SHIFT, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x2f, // [
|
||||
0x31, // bslash
|
||||
0x30, // ]
|
||||
0x23 | SHIFT, // ^
|
||||
0x2d | SHIFT, // _
|
||||
0x35, // `
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x2f | SHIFT, // {
|
||||
0x31 | SHIFT, // |
|
||||
0x30 | SHIFT, // }
|
||||
0x35 | SHIFT, // ~
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Spanish keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_es_ES[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x20 | ALT_GR, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x2d, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x30 | SHIFT, // *
|
||||
0x30, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x1f | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x2f | ALT_GR, // [
|
||||
0x35 | ALT_GR, // bslash
|
||||
0x30 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x34 | ALT_GR, // {
|
||||
0x1e | ALT_GR, // |
|
||||
0x31 | ALT_GR, // }
|
||||
0x00, // ~ not supported (requires dead key + space)
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Traditional (not AFNOR) French keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_fr_FR[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x38, // !
|
||||
0x20, // "
|
||||
0x20 | ALT_GR, // #
|
||||
0x30, // $
|
||||
0x34 | SHIFT, // %
|
||||
0x1E, // &
|
||||
0x21, // '
|
||||
0x22, // (
|
||||
0x2d, // )
|
||||
0x31, // *
|
||||
0x2e | SHIFT, // +
|
||||
0x10, // ,
|
||||
0x23, // -
|
||||
0x36 | SHIFT, // .
|
||||
0x37 | SHIFT, // /
|
||||
0x27 | SHIFT, // 0
|
||||
0x1e | SHIFT, // 1
|
||||
0x1f | SHIFT, // 2
|
||||
0x20 | SHIFT, // 3
|
||||
0x21 | SHIFT, // 4
|
||||
0x22 | SHIFT, // 5
|
||||
0x23 | SHIFT, // 6
|
||||
0x24 | SHIFT, // 7
|
||||
0x25 | SHIFT, // 8
|
||||
0x26 | SHIFT, // 9
|
||||
0x37, // :
|
||||
0x36, // ;
|
||||
0x32, // <
|
||||
0x2e, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x10 | SHIFT, // ?
|
||||
0x27 | ALT_GR, // @
|
||||
0x14 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x33 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x04 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1d | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1a | SHIFT, // Z
|
||||
0x22 | ALT_GR, // [
|
||||
0x25 | ALT_GR, // bslash
|
||||
0x2d | ALT_GR, // ]
|
||||
0x26 | ALT_GR, // ^
|
||||
0x25, // _
|
||||
0x24 | ALT_GR, // `
|
||||
0x14, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x33, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x04, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1d, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1a, // z
|
||||
0x21 | ALT_GR, // {
|
||||
0x23 | ALT_GR, // |
|
||||
0x2e | ALT_GR, // }
|
||||
0x1f | ALT_GR, // ~
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Italian keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_it_IT[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x34 | ALT_GR, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x2d, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x30 | SHIFT, // *
|
||||
0x30, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x33 | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x2f | ALT_GR, // [
|
||||
0x35, // bslash
|
||||
0x30 | ALT_GR, // ]
|
||||
0x2e | SHIFT, // ^
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not in this layout
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x00, // { not supported (requires AltGr+Shift)
|
||||
0x35 | SHIFT, // |
|
||||
0x00, // } not supported (requires AltGr+Shift)
|
||||
0x00, // ~ not in this layout
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Swedish keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_sv_SE[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x20 | SHIFT, // #
|
||||
0x21 | ALT_GR, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x31, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x31 | SHIFT, // *
|
||||
0x2d, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x1f | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x25 | ALT_GR, // [
|
||||
0x2d | ALT_GR, // bslash
|
||||
0x26 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x24 | ALT_GR, // {
|
||||
0x32 | ALT_GR, // |
|
||||
0x27 | ALT_GR, // }
|
||||
0x00, // ~ not supported (requires dead key + space)
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -3,8 +3,21 @@
|
||||
|
||||
#include <KeyboardBT.h>
|
||||
|
||||
void ledCB(bool numlock, bool capslock, bool scrolllock, bool compose, bool kana, void *cbData) {
|
||||
(void) numlock;
|
||||
(void) scrolllock;
|
||||
(void) compose;
|
||||
(void) kana;
|
||||
(void) cbData;
|
||||
digitalWrite(LED_BUILTIN, capslock ? HIGH : LOW);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
KeyboardBT.begin("PicoW Password");
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
KeyboardBT.onLED(ledCB);
|
||||
KeyboardBT.begin();
|
||||
delay(5000);
|
||||
Serial.printf("Arduino USB Password Typer\n");
|
||||
|
||||
@@ -29,16 +29,34 @@
|
||||
// Keyboard
|
||||
|
||||
KeyboardBT_::KeyboardBT_(void) {
|
||||
bzero(&_keyReport, sizeof(_keyReport));
|
||||
_asciimap = KeyboardLayout_en_US;
|
||||
// Base class clears the members we care about
|
||||
}
|
||||
|
||||
#define REPORT_ID 0x01
|
||||
|
||||
static const uint8_t desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID))};
|
||||
void KeyboardBT_::begin(const uint8_t *layout) {
|
||||
static const uint8_t desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(REPORT_ID + 1))};
|
||||
|
||||
static void _hidReportCB(uint16_t cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t *report) {
|
||||
(void) cid;
|
||||
(void) report_id;
|
||||
if ((report_type == HID_REPORT_TYPE_OUTPUT) && (report_size > 0) && (KeyboardBT._ledCB)) {
|
||||
uint8_t const kbd_leds = report[0];
|
||||
KeyboardBT._ledCB(kbd_leds & KEYBOARD_LED_NUMLOCK, kbd_leds & KEYBOARD_LED_CAPSLOCK, kbd_leds & KEYBOARD_LED_SCROLLLOCK, kbd_leds & KEYBOARD_LED_COMPOSE, kbd_leds & KEYBOARD_LED_KANA, KeyboardBT._ledCBdata);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardBT_::begin(const char *localName, const char *hidName, const uint8_t *layout) {
|
||||
if (!localName) {
|
||||
localName = "PicoW BT Keyboard";
|
||||
}
|
||||
if (!hidName) {
|
||||
hidName = localName;
|
||||
}
|
||||
_asciimap = layout;
|
||||
PicoBluetoothHID.startHID("PicoW Keyboard 00:00:00:00:00:00", "PicoW HID Keyboard", 0x2540, 33, desc_keyboard, sizeof(desc_keyboard));
|
||||
// Required because the hid_report_type_t overlap in BTStack and TUSB
|
||||
auto *fcn = (void (*)(short unsigned int, hid_report_type_t_bt, short unsigned int, int, unsigned char*))_hidReportCB;
|
||||
hid_device_register_report_data_callback(fcn);
|
||||
PicoBluetoothHID.startHID(localName, hidName, 0x2540, 33, desc_keyboard, sizeof(desc_keyboard));
|
||||
}
|
||||
|
||||
void KeyboardBT_::end(void) {
|
||||
@@ -53,127 +71,8 @@ void KeyboardBT_::sendReport(KeyReport* keys) {
|
||||
PicoBluetoothHID.send(REPORT_ID, &data, sizeof(data));
|
||||
}
|
||||
|
||||
// press() adds the specified key (printing, non-printing, or modifier)
|
||||
// to the persistent key report and sends the report. Because of the way
|
||||
// USB HID works, the host acts like the key remains pressed until we
|
||||
// call release(), releaseAll(), or otherwise clear the report and resend.
|
||||
size_t KeyboardBT_::press(uint8_t k) {
|
||||
uint8_t i;
|
||||
if (k >= 136) { // it's a non-printing key (not a modifier)
|
||||
k = k - 136;
|
||||
} else if (k >= 128) { // it's a modifier key
|
||||
_keyReport.modifiers |= (1 << (k - 128));
|
||||
k = 0;
|
||||
} else { // it's a printing key
|
||||
k = pgm_read_byte(_asciimap + k);
|
||||
if (!k) {
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
if ((k & ALT_GR) == ALT_GR) {
|
||||
_keyReport.modifiers |= 0x40; // AltGr = right Alt
|
||||
k &= 0x3F;
|
||||
} else if ((k & SHIFT) == SHIFT) {
|
||||
_keyReport.modifiers |= 0x02; // the left shift modifier
|
||||
k &= 0x7F;
|
||||
}
|
||||
if (k == ISO_REPLACEMENT) {
|
||||
k = ISO_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
// Add k to the key report only if it's not already present
|
||||
// and if there is an empty slot.
|
||||
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
|
||||
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
|
||||
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (_keyReport.keys[i] == 0x00) {
|
||||
_keyReport.keys[i] = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 6) {
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
sendReport(&_keyReport);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// release() takes the specified key out of the persistent key report and
|
||||
// sends the report. This tells the OS the key is no longer pressed and that
|
||||
// it shouldn't be repeated any more.
|
||||
size_t KeyboardBT_::release(uint8_t k) {
|
||||
uint8_t i;
|
||||
if (k >= 136) { // it's a non-printing key (not a modifier)
|
||||
k = k - 136;
|
||||
} else if (k >= 128) { // it's a modifier key
|
||||
_keyReport.modifiers &= ~(1 << (k - 128));
|
||||
k = 0;
|
||||
} else { // it's a printing key
|
||||
k = pgm_read_byte(_asciimap + k);
|
||||
if (!k) {
|
||||
return 0;
|
||||
}
|
||||
if ((k & ALT_GR) == ALT_GR) {
|
||||
_keyReport.modifiers &= ~(0x40); // AltGr = right Alt
|
||||
k &= 0x3F;
|
||||
} else if ((k & SHIFT) == SHIFT) {
|
||||
_keyReport.modifiers &= ~(0x02); // the left shift modifier
|
||||
k &= 0x7F;
|
||||
}
|
||||
if (k == ISO_REPLACEMENT) {
|
||||
k = ISO_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
// Test the key report to see if k is present. Clear it if it exists.
|
||||
// Check all positions in case the key is present more than once (which it shouldn't be)
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (0 != k && _keyReport.keys[i] == k) {
|
||||
_keyReport.keys[i] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
sendReport(&_keyReport);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void KeyboardBT_::releaseAll(void) {
|
||||
_keyReport.keys[0] = 0;
|
||||
_keyReport.keys[1] = 0;
|
||||
_keyReport.keys[2] = 0;
|
||||
_keyReport.keys[3] = 0;
|
||||
_keyReport.keys[4] = 0;
|
||||
_keyReport.keys[5] = 0;
|
||||
_keyReport.modifiers = 0;
|
||||
sendReport(&_keyReport);
|
||||
}
|
||||
|
||||
size_t KeyboardBT_::write(uint8_t c) {
|
||||
uint8_t p = press(c); // Keydown
|
||||
delay(10);
|
||||
release(c); // Keyup
|
||||
delay(10);
|
||||
return p; // just return the result of press() since release() almost always returns 1
|
||||
}
|
||||
|
||||
size_t KeyboardBT_::write(const uint8_t *buffer, size_t size) {
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
if (*buffer != '\r') {
|
||||
if (write(*buffer)) {
|
||||
n++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer++;
|
||||
}
|
||||
return n;
|
||||
void KeyboardBT_::sendConsumerReport(uint16_t key) {
|
||||
PicoBluetoothHID.send(REPORT_ID + 1, &key, sizeof(key));
|
||||
}
|
||||
|
||||
KeyboardBT_ KeyboardBT;
|
||||
|
||||
@@ -23,118 +23,16 @@
|
||||
#ifndef KEYBOARDBT_h
|
||||
#define KEYBOARDBT_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HID_Keyboard.h>
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
// Modifiers
|
||||
#define KEY_LEFT_CTRL 0x80
|
||||
#define KEY_LEFT_SHIFT 0x81
|
||||
#define KEY_LEFT_ALT 0x82
|
||||
#define KEY_LEFT_GUI 0x83
|
||||
#define KEY_RIGHT_CTRL 0x84
|
||||
#define KEY_RIGHT_SHIFT 0x85
|
||||
#define KEY_RIGHT_ALT 0x86
|
||||
#define KEY_RIGHT_GUI 0x87
|
||||
|
||||
// Misc keys
|
||||
#define KEY_UP_ARROW 0xDA
|
||||
#define KEY_DOWN_ARROW 0xD9
|
||||
#define KEY_LEFT_ARROW 0xD8
|
||||
#define KEY_RIGHT_ARROW 0xD7
|
||||
#define KEY_BACKSPACE 0xB2
|
||||
#define KEY_TAB 0xB3
|
||||
#define KEY_RETURN 0xB0
|
||||
#define KEY_MENU 0xED // "Keyboard Application" in USB standard
|
||||
#define KEY_ESC 0xB1
|
||||
#define KEY_INSERT 0xD1
|
||||
#define KEY_DELETE 0xD4
|
||||
#define KEY_PAGE_UP 0xD3
|
||||
#define KEY_PAGE_DOWN 0xD6
|
||||
#define KEY_HOME 0xD2
|
||||
#define KEY_END 0xD5
|
||||
#define KEY_CAPS_LOCK 0xC1
|
||||
#define KEY_PRINT_SCREEN 0xCE // Print Screen / SysRq
|
||||
#define KEY_SCROLL_LOCK 0xCF
|
||||
#define KEY_PAUSE 0xD0 // Pause / Break
|
||||
|
||||
// Numeric keypad
|
||||
#define KEY_NUM_LOCK 0xDB
|
||||
#define KEY_KP_SLASH 0xDC
|
||||
#define KEY_KP_ASTERISK 0xDD
|
||||
#define KEY_KP_MINUS 0xDE
|
||||
#define KEY_KP_PLUS 0xDF
|
||||
#define KEY_KP_ENTER 0xE0
|
||||
#define KEY_KP_1 0xE1
|
||||
#define KEY_KP_2 0xE2
|
||||
#define KEY_KP_3 0xE3
|
||||
#define KEY_KP_4 0xE4
|
||||
#define KEY_KP_5 0xE5
|
||||
#define KEY_KP_6 0xE6
|
||||
#define KEY_KP_7 0xE7
|
||||
#define KEY_KP_8 0xE8
|
||||
#define KEY_KP_9 0xE9
|
||||
#define KEY_KP_0 0xEA
|
||||
#define KEY_KP_DOT 0xEB
|
||||
|
||||
// Function keys
|
||||
#define KEY_F1 0xC2
|
||||
#define KEY_F2 0xC3
|
||||
#define KEY_F3 0xC4
|
||||
#define KEY_F4 0xC5
|
||||
#define KEY_F5 0xC6
|
||||
#define KEY_F6 0xC7
|
||||
#define KEY_F7 0xC8
|
||||
#define KEY_F8 0xC9
|
||||
#define KEY_F9 0xCA
|
||||
#define KEY_F10 0xCB
|
||||
#define KEY_F11 0xCC
|
||||
#define KEY_F12 0xCD
|
||||
#define KEY_F13 0xF0
|
||||
#define KEY_F14 0xF1
|
||||
#define KEY_F15 0xF2
|
||||
#define KEY_F16 0xF3
|
||||
#define KEY_F17 0xF4
|
||||
#define KEY_F18 0xF5
|
||||
#define KEY_F19 0xF6
|
||||
#define KEY_F20 0xF7
|
||||
#define KEY_F21 0xF8
|
||||
#define KEY_F22 0xF9
|
||||
#define KEY_F23 0xFA
|
||||
#define KEY_F24 0xFB
|
||||
|
||||
// Supported keyboard layouts
|
||||
extern const uint8_t KeyboardLayout_de_DE[];
|
||||
extern const uint8_t KeyboardLayout_en_US[];
|
||||
extern const uint8_t KeyboardLayout_es_ES[];
|
||||
extern const uint8_t KeyboardLayout_fr_FR[];
|
||||
extern const uint8_t KeyboardLayout_it_IT[];
|
||||
extern const uint8_t KeyboardLayout_sv_SE[];
|
||||
extern const uint8_t KeyboardLayout_da_DK[];
|
||||
|
||||
// Low level key report: up to 6 keys and shift, ctrl etc at once
|
||||
typedef struct {
|
||||
uint8_t modifiers;
|
||||
uint8_t reserved;
|
||||
uint8_t keys[6];
|
||||
} KeyReport;
|
||||
|
||||
class KeyboardBT_ : public Print {
|
||||
private:
|
||||
KeyReport _keyReport;
|
||||
const uint8_t *_asciimap;
|
||||
void sendReport(KeyReport* keys);
|
||||
class KeyboardBT_ : public HID_Keyboard {
|
||||
protected:
|
||||
virtual void sendReport(KeyReport* keys) override;
|
||||
virtual void sendConsumerReport(uint16_t key) override;
|
||||
public:
|
||||
KeyboardBT_(void);
|
||||
void begin(const uint8_t *layout = KeyboardLayout_en_US);
|
||||
void begin(const char *localName = nullptr, const char *hidName = nullptr, const uint8_t *layout = KeyboardLayout_en_US);
|
||||
void end(void);
|
||||
size_t write(uint8_t k);
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
size_t press(uint8_t k);
|
||||
size_t release(uint8_t k);
|
||||
void releaseAll(void);
|
||||
};
|
||||
extern KeyboardBT_ KeyboardBT;
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
KeyboardLayout.h
|
||||
|
||||
This file is not part of the public API. It is meant to be included
|
||||
only in Keyboard.cpp and the keyboard layout files. Layout files map
|
||||
ASCII character codes to keyboard scan codes (technically, to USB HID
|
||||
Usage codes), possibly altered by the SHIFT or ALT_GR modifiers.
|
||||
|
||||
== Creating your own layout ==
|
||||
|
||||
In order to create your own layout file, copy an existing layout that
|
||||
is similar to yours, then modify it to use the correct keys. The
|
||||
layout is an array in ASCII order. Each entry contains a scan code,
|
||||
possibly modified by "|SHIFT" or "|ALT_GR", as in this excerpt from
|
||||
the Italian layout:
|
||||
|
||||
0x35, // bslash
|
||||
0x30|ALT_GR, // ]
|
||||
0x2e|SHIFT, // ^
|
||||
|
||||
Do not change the control characters (those before scan code 0x2c,
|
||||
corresponding to space). Do not attempt to grow the table past DEL. Do
|
||||
not use both SHIFT and ALT_GR on the same character: this is not
|
||||
supported. Unsupported characters should have 0x00 as scan code.
|
||||
|
||||
For a keyboard with an ISO physical layout, use the scan codes below:
|
||||
|
||||
+---+---+---+---+---+---+---+---+---+---+---+---+---+-------+
|
||||
|35 |1e |1f |20 |21 |22 |23 |24 |25 |26 |27 |2d |2e |BackSp |
|
||||
+---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-----+
|
||||
| Tab |14 |1a |08 |15 |17 |1c |18 |0c |12 |13 |2f |30 | Ret |
|
||||
+-----++--++--++--++--++--++--++--++--++--++--++--++--++ |
|
||||
|CapsL |04 |16 |07 |09 |0a |0b |0d |0e |0f |33 |34 |31 | |
|
||||
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+---+----+
|
||||
|Shi.|32 |1d |1b |06 |19 |05 |11 |10 |36 |37 |38 | Shift |
|
||||
+----+---++--+-+-+---+---+---+---+---+--++---+---++----+----+
|
||||
|Ctrl|Win |Alt | |AlGr|Win |Menu|Ctrl|
|
||||
+----+----+----+------------------------+----+----+----+----+
|
||||
|
||||
The ANSI layout is identical except that key 0x31 is above (rather
|
||||
than next to) Return, and there is not key 0x32.
|
||||
|
||||
Give a unique name to the layout array, then declare it in Keyboard.h
|
||||
with a line of the form:
|
||||
|
||||
extern const uint8_t KeyboardLayout_xx_YY[];
|
||||
|
||||
== Encoding details ==
|
||||
|
||||
All scan codes are less than 0x80, which makes bit 7 available to
|
||||
signal that a modifier (Shift or AltGr) is needed to generate the
|
||||
character. With only one exception, keys that are used with modifiers
|
||||
have scan codes that are less than 0x40. This makes bit 6 available
|
||||
to signal whether the modifier is Shift or AltGr. The exception is
|
||||
0x64, the key next next to Left Shift on the ISO layout (and absent
|
||||
from the ANSI layout). We handle it by replacing its value by 0x32 in
|
||||
the layout arrays.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define SHIFT 0x80
|
||||
#define ALT_GR 0xc0
|
||||
#define ISO_KEY 0x64
|
||||
#define ISO_REPLACEMENT 0x32
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Danish keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_da_DK[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x20 | SHIFT, // #
|
||||
0x21 | ALT_GR, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x31, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x31 | SHIFT, // *
|
||||
0x2d, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x1f | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x25 | ALT_GR, // [
|
||||
0x32 | ALT_GR, // bslash
|
||||
0x26 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x24 | ALT_GR, // {
|
||||
0x2e | ALT_GR, // |
|
||||
0x27 | ALT_GR, // }
|
||||
0x00, // ~ not supported (requires dead key + space)
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
German keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_de_DE[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x31, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x31 | SHIFT, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x30 | SHIFT, // *
|
||||
0x30, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x14 | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1d | SHIFT, // Y
|
||||
0x1c | SHIFT, // Z
|
||||
0x25 | ALT_GR, // [
|
||||
0x2d | ALT_GR, // bslash
|
||||
0x26 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1d, // y
|
||||
0x1c, // z
|
||||
0x24 | ALT_GR, // {
|
||||
0x32 | ALT_GR, // |
|
||||
0x27 | ALT_GR, // }
|
||||
0x30 | ALT_GR, // ~
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Standard US keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_en_US[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x34 | SHIFT, // "
|
||||
0x20 | SHIFT, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x24 | SHIFT, // &
|
||||
0x34, // '
|
||||
0x26 | SHIFT, // (
|
||||
0x27 | SHIFT, // )
|
||||
0x25 | SHIFT, // *
|
||||
0x2e | SHIFT, // +
|
||||
0x36, // ,
|
||||
0x2d, // -
|
||||
0x37, // .
|
||||
0x38, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x33 | SHIFT, // :
|
||||
0x33, // ;
|
||||
0x36 | SHIFT, // <
|
||||
0x2e, // =
|
||||
0x37 | SHIFT, // >
|
||||
0x38 | SHIFT, // ?
|
||||
0x1f | SHIFT, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x2f, // [
|
||||
0x31, // bslash
|
||||
0x30, // ]
|
||||
0x23 | SHIFT, // ^
|
||||
0x2d | SHIFT, // _
|
||||
0x35, // `
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x2f | SHIFT, // {
|
||||
0x31 | SHIFT, // |
|
||||
0x30 | SHIFT, // }
|
||||
0x35 | SHIFT, // ~
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Spanish keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_es_ES[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x20 | ALT_GR, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x2d, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x30 | SHIFT, // *
|
||||
0x30, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x1f | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x2f | ALT_GR, // [
|
||||
0x35 | ALT_GR, // bslash
|
||||
0x30 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x34 | ALT_GR, // {
|
||||
0x1e | ALT_GR, // |
|
||||
0x31 | ALT_GR, // }
|
||||
0x00, // ~ not supported (requires dead key + space)
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Traditional (not AFNOR) French keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_fr_FR[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x38, // !
|
||||
0x20, // "
|
||||
0x20 | ALT_GR, // #
|
||||
0x30, // $
|
||||
0x34 | SHIFT, // %
|
||||
0x1E, // &
|
||||
0x21, // '
|
||||
0x22, // (
|
||||
0x2d, // )
|
||||
0x31, // *
|
||||
0x2e | SHIFT, // +
|
||||
0x10, // ,
|
||||
0x23, // -
|
||||
0x36 | SHIFT, // .
|
||||
0x37 | SHIFT, // /
|
||||
0x27 | SHIFT, // 0
|
||||
0x1e | SHIFT, // 1
|
||||
0x1f | SHIFT, // 2
|
||||
0x20 | SHIFT, // 3
|
||||
0x21 | SHIFT, // 4
|
||||
0x22 | SHIFT, // 5
|
||||
0x23 | SHIFT, // 6
|
||||
0x24 | SHIFT, // 7
|
||||
0x25 | SHIFT, // 8
|
||||
0x26 | SHIFT, // 9
|
||||
0x37, // :
|
||||
0x36, // ;
|
||||
0x32, // <
|
||||
0x2e, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x10 | SHIFT, // ?
|
||||
0x27 | ALT_GR, // @
|
||||
0x14 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x33 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x04 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1d | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1a | SHIFT, // Z
|
||||
0x22 | ALT_GR, // [
|
||||
0x25 | ALT_GR, // bslash
|
||||
0x2d | ALT_GR, // ]
|
||||
0x26 | ALT_GR, // ^
|
||||
0x25, // _
|
||||
0x24 | ALT_GR, // `
|
||||
0x14, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x33, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x04, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1d, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1a, // z
|
||||
0x21 | ALT_GR, // {
|
||||
0x23 | ALT_GR, // |
|
||||
0x2e | ALT_GR, // }
|
||||
0x1f | ALT_GR, // ~
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Italian keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_it_IT[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x34 | ALT_GR, // #
|
||||
0x21 | SHIFT, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x2d, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x30 | SHIFT, // *
|
||||
0x30, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x33 | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x2f | ALT_GR, // [
|
||||
0x35, // bslash
|
||||
0x30 | ALT_GR, // ]
|
||||
0x2e | SHIFT, // ^
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not in this layout
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x00, // { not supported (requires AltGr+Shift)
|
||||
0x35 | SHIFT, // |
|
||||
0x00, // } not supported (requires AltGr+Shift)
|
||||
0x00, // ~ not in this layout
|
||||
0x00 // DEL
|
||||
};
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Swedish keyboard layout.
|
||||
*/
|
||||
|
||||
#include "KeyboardLayout.h"
|
||||
|
||||
extern const uint8_t KeyboardLayout_sv_SE[128] PROGMEM = {
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e | SHIFT, // !
|
||||
0x1f | SHIFT, // "
|
||||
0x20 | SHIFT, // #
|
||||
0x21 | ALT_GR, // $
|
||||
0x22 | SHIFT, // %
|
||||
0x23 | SHIFT, // &
|
||||
0x31, // '
|
||||
0x25 | SHIFT, // (
|
||||
0x26 | SHIFT, // )
|
||||
0x31 | SHIFT, // *
|
||||
0x2d, // +
|
||||
0x36, // ,
|
||||
0x38, // -
|
||||
0x37, // .
|
||||
0x24 | SHIFT, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x37 | SHIFT, // :
|
||||
0x36 | SHIFT, // ;
|
||||
0x32, // <
|
||||
0x27 | SHIFT, // =
|
||||
0x32 | SHIFT, // >
|
||||
0x2d | SHIFT, // ?
|
||||
0x1f | ALT_GR, // @
|
||||
0x04 | SHIFT, // A
|
||||
0x05 | SHIFT, // B
|
||||
0x06 | SHIFT, // C
|
||||
0x07 | SHIFT, // D
|
||||
0x08 | SHIFT, // E
|
||||
0x09 | SHIFT, // F
|
||||
0x0a | SHIFT, // G
|
||||
0x0b | SHIFT, // H
|
||||
0x0c | SHIFT, // I
|
||||
0x0d | SHIFT, // J
|
||||
0x0e | SHIFT, // K
|
||||
0x0f | SHIFT, // L
|
||||
0x10 | SHIFT, // M
|
||||
0x11 | SHIFT, // N
|
||||
0x12 | SHIFT, // O
|
||||
0x13 | SHIFT, // P
|
||||
0x14 | SHIFT, // Q
|
||||
0x15 | SHIFT, // R
|
||||
0x16 | SHIFT, // S
|
||||
0x17 | SHIFT, // T
|
||||
0x18 | SHIFT, // U
|
||||
0x19 | SHIFT, // V
|
||||
0x1a | SHIFT, // W
|
||||
0x1b | SHIFT, // X
|
||||
0x1c | SHIFT, // Y
|
||||
0x1d | SHIFT, // Z
|
||||
0x25 | ALT_GR, // [
|
||||
0x2d | ALT_GR, // bslash
|
||||
0x26 | ALT_GR, // ]
|
||||
0x00, // ^ not supported (requires dead key + space)
|
||||
0x38 | SHIFT, // _
|
||||
0x00, // ` not supported (requires dead key + space)
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x24 | ALT_GR, // {
|
||||
0x32 | ALT_GR, // |
|
||||
0x27 | ALT_GR, // }
|
||||
0x00, // ~ not supported (requires dead key + space)
|
||||
0x00 // DEL
|
||||
};
|
||||
Submodule libraries/Mouse deleted from be7395a597
@@ -0,0 +1,31 @@
|
||||
:repository-owner: arduino-libraries
|
||||
:repository-name: Mouse
|
||||
|
||||
= {repository-name} Library for Arduino =
|
||||
|
||||
image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml/badge.svg["Check Arduino status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml"]
|
||||
image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml/badge.svg["Compile Examples status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml"]
|
||||
image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"]
|
||||
|
||||
This library allows an Arduino board with USB capabilities to act as a Mouse.
|
||||
|
||||
For more information about this library please visit us at
|
||||
https://www.arduino.cc/reference/en/language/functions/usb/mouse/
|
||||
|
||||
== License ==
|
||||
|
||||
Copyright (c) Arduino LLC. 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
|
||||
@@ -0,0 +1,428 @@
|
||||
# Mouse library
|
||||
|
||||
## Methods
|
||||
|
||||
### `Mouse.begin()`
|
||||
|
||||
Begins emulating the mouse connected to a computer. `begin()` must be called before controlling the computer. To end control, use `Mouse.end()`.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
Mouse.begin()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None.
|
||||
|
||||
#### Returns
|
||||
|
||||
None.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
|
||||
void setup() {
|
||||
pinMode(2, INPUT);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Initialize the Mouse library when button is pressed
|
||||
if (digitalRead(2) == HIGH) {
|
||||
Mouse.begin();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### See also
|
||||
|
||||
* [Mouse.click()](#mouseclick)
|
||||
* [Mouse.end()](#mouseend)
|
||||
* [Mouse.move()](#mousemove)
|
||||
* [Mouse.press()](#mousepress)
|
||||
* [Mouse.release()](#mouserelease)
|
||||
* [Mouse.isPressed()](#mouseispressed)
|
||||
|
||||
### `Mouse.click()`
|
||||
|
||||
Sends a momentary click to the computer at the location of the cursor. This is the same as pressing and immediately releasing the mouse button.
|
||||
|
||||
`Mouse.click()` defaults to the left mouse button.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
Mouse.click()
|
||||
Mouse.click(button)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `button`: which mouse button to press (MOUSE_LEFT, MOUSE_RIGHT or MOUSE_MIDDLE, default is MOUSE_LEFT).
|
||||
|
||||
#### Returns
|
||||
|
||||
None.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
|
||||
void setup() {
|
||||
pinMode(2, INPUT);
|
||||
// Initialize the Mouse library
|
||||
Mouse.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// If the button is pressed, send a left mouse click
|
||||
if (digitalRead(2) == HIGH) {
|
||||
Mouse.click();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Notes and warnings
|
||||
|
||||
When you use the `Mouse.click()` command, the Arduino takes over your mouse! Make sure you have control before you use the command. A pushbutton to toggle the mouse control state is effective.
|
||||
|
||||
#### See also
|
||||
|
||||
* [Mouse.begin()](#mousebegin)
|
||||
* [Mouse.end()](#mouseend)
|
||||
* [Mouse.move()](#mousemove)
|
||||
* [Mouse.press()](#mousepress)
|
||||
* [Mouse.release()](#mouserelease)
|
||||
* [Mouse.isPressed()](#mouseispressed)
|
||||
|
||||
### `Mouse.end()`
|
||||
|
||||
Stops emulating the mouse connected to a computer. To start control, use `Mouse.begin()`.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
Mouse.end()
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None.
|
||||
|
||||
#### Returns
|
||||
|
||||
None.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
|
||||
void setup() {
|
||||
pinMode(2, INPUT);
|
||||
// Initiate the Mouse library
|
||||
Mouse.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// If the button is pressed, send a left mouse click
|
||||
if (digitalRead(2) == HIGH) {
|
||||
Mouse.click();
|
||||
// Then end the Mouse emulation
|
||||
Mouse.end();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### See also
|
||||
|
||||
* [Mouse.begin()](#mousebegin)
|
||||
* [Mouse.click()](#mouseclick)
|
||||
* [Mouse.move()](#mousemove)
|
||||
* [Mouse.press()](#mousepress)
|
||||
* [Mouse.release()](#mouserelease)
|
||||
* [Mouse.isPressed()](#mouseispressed)
|
||||
|
||||
### `Mouse.move()`
|
||||
|
||||
Moves the cursor on a connected computer. The motion onscreen is always relative to the cursor’s current location. Before using `Mouse.move()` you must call `Mouse.begin()`.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
Mouse.move(xVal, yVal, wheel)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `xVal`: amount to move along the x-axis. Allowed data types: signed char.
|
||||
* `yVal`: amount to move along the y-axis. Allowed data types: signed char.
|
||||
* `wheel`: amount to move scroll wheel. Allowed data types: signed char.
|
||||
|
||||
#### Returns
|
||||
|
||||
None.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
|
||||
const int xAxis = A1; // Analog sensor for X axis
|
||||
const int yAxis = A2; // Analog sensor for Y axis
|
||||
|
||||
int range = 12; // Output range of X or Y movement
|
||||
int responseDelay = 2; // Response delay of the mouse, in ms
|
||||
int threshold = range / 4; // Resting threshold
|
||||
int center = range / 2; // Resting position value
|
||||
int minima[] = {1023, 1023}; // Actual analogRead minima for (x, y)
|
||||
int maxima[] = {0, 0}; // Actual analogRead maxima for (x, y)
|
||||
int axis[] = {xAxis, yAxis}; // Pin numbers for (x, y)
|
||||
int mouseReading[2]; // Final mouse readings for (x, y)
|
||||
|
||||
void setup() {
|
||||
// Initialize the Mouse library
|
||||
Mouse.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Read and scale the two axes
|
||||
int xReading = readAxis(0);
|
||||
int yReading = readAxis(1);
|
||||
|
||||
// Move the mouse
|
||||
Mouse.move(xReading, yReading, 0);
|
||||
delay(responseDelay);
|
||||
}
|
||||
|
||||
/*
|
||||
Reads an axis (0 or 1 for x or y) and scales the
|
||||
analog input range to a range from 0 to <range>
|
||||
*/
|
||||
int readAxis(int axisNumber) {
|
||||
int distance = 0; // Distance from center of the output range
|
||||
|
||||
// Read the analog input
|
||||
int reading = analogRead(axis[axisNumber]);
|
||||
|
||||
// Of the current reading exceeds the max or min for this axis, reset the max or min
|
||||
if (reading < minima[axisNumber]) {
|
||||
minima[axisNumber] = reading;
|
||||
}
|
||||
if (reading > maxima[axisNumber]) {
|
||||
maxima[axisNumber] = reading;
|
||||
}
|
||||
|
||||
// Map the reading from the analog input range to the output range
|
||||
reading = map(reading, minima[axisNumber], maxima[axisNumber], 0, range);
|
||||
|
||||
// If the output reading is outside from the rest position threshold, use it
|
||||
if (abs(reading - center) > threshold) {
|
||||
distance = (reading - center);
|
||||
}
|
||||
|
||||
// The Y axis needs to be inverted in order to map the movement correctly
|
||||
if (axisNumber == 1) {
|
||||
distance = -distance;
|
||||
}
|
||||
|
||||
// Return the distance for this axis
|
||||
return distance;
|
||||
}
|
||||
```
|
||||
|
||||
#### Notes and warnings
|
||||
|
||||
When you use the `Mouse.move()` command, the Arduino takes over your mouse! Make sure you have control before you use the command. A pushbutton to toggle the mouse control state is effective.
|
||||
|
||||
#### See also
|
||||
|
||||
* [Mouse.begin()](#mousebegin)
|
||||
* [Mouse.click()](#mouseclick)
|
||||
* [Mouse.end()](#mouseend)
|
||||
* [Mouse.press()](#mousepress)
|
||||
* [Mouse.release()](#mouserelease)
|
||||
* [Mouse.isPressed()](#mouseispressed)
|
||||
|
||||
### `Mouse.press()`
|
||||
|
||||
Sends a button press to a connected computer. A press is the equivalent of clicking and continuously holding the mouse button. A press is cancelled with `Mouse.release()`. Before using `Mouse.press()`, you need to start communication with `Mouse.begin()`. `Mouse.press()` defaults to a left button press.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
Mouse.press()
|
||||
Mouse.press(button)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `button`: which mouse button to press (MOUSE_LEFT, MOUSE_RIGHT or MOUSE_MIDDLE, default is MOUSE_LEFT).
|
||||
|
||||
#### Returns
|
||||
|
||||
None.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
|
||||
void setup() {
|
||||
// The switch that will initiate the Mouse press
|
||||
pinMode(2, INPUT);
|
||||
// The switch that will terminate the Mouse press
|
||||
pinMode(3, INPUT);
|
||||
// Initialize the Mouse library
|
||||
Mouse.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// If the switch attached to pin 2 is closed, press and hold the left mouse button
|
||||
if (digitalRead(2) == HIGH) {
|
||||
Mouse.press();
|
||||
}
|
||||
// If the switch attached to pin 3 is closed, release the left mouse button
|
||||
if (digitalRead(3) == HIGH) {
|
||||
Mouse.release();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Notes and warnings
|
||||
|
||||
When you use the `Mouse.press()` command, the Arduino takes over your mouse! Make sure you have control before you use the command. A pushbutton to toggle the mouse control state is effective.
|
||||
|
||||
#### See also
|
||||
|
||||
* [Mouse.begin()](#mousebegin)
|
||||
* [Mouse.click()](#mouseclick)
|
||||
* [Mouse.end()](#mouseend)
|
||||
* [Mouse.move()](#mousemove)
|
||||
* [Mouse.release()](#mouserelease)
|
||||
* [Mouse.isPressed()](#mouseispressed)
|
||||
|
||||
### `Mouse.release()`
|
||||
|
||||
Sends a message that a previously pressed button (invoked through `Mouse.press()`) is released. `Mouse.release()` defaults to the left button.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
Mouse.press()
|
||||
Mouse.press(button)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `button`: which mouse button was released (MOUSE_LEFT, MOUSE_RIGHT or MOUSE_MIDDLE, default is MOUSE_LEFT).
|
||||
|
||||
#### Returns
|
||||
|
||||
None.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
|
||||
void setup() {
|
||||
// The switch that will initiate the Mouse press
|
||||
pinMode(2, INPUT);
|
||||
// The switch that will terminate the Mouse press
|
||||
pinMode(3, INPUT);
|
||||
// Initialize the Mouse library
|
||||
Mouse.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// If the switch attached to pin 2 is closed, press and hold the left mouse button
|
||||
if (digitalRead(2) == HIGH) {
|
||||
Mouse.press();
|
||||
}
|
||||
// If the switch attached to pin 3 is closed, release the left mouse button
|
||||
if (digitalRead(3) == HIGH) {
|
||||
Mouse.release();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Notes and warnings
|
||||
|
||||
When you use the `Mouse.release()` command, the Arduino takes over your mouse! Make sure you have control before you use the command. A pushbutton to toggle the mouse control state is effective.
|
||||
|
||||
#### See also
|
||||
|
||||
* [Mouse.begin()](#mousebegin)
|
||||
* [Mouse.click()](#mouseclick)
|
||||
* [Mouse.end()](#mouseend)
|
||||
* [Mouse.move()](#mousemove)
|
||||
* [Mouse.press()](#mousepress)
|
||||
* [Mouse.isPressed()](#mouseispressed)
|
||||
|
||||
### `Mouse.isPressed()`
|
||||
|
||||
Checks the current status of all mouse buttons, and reports if any are pressed or not. By default, it checks the status of the left mouse button.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```
|
||||
Mouse.isPressed();
|
||||
Mouse.isPressed(button);
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `button`: which mouse button was released (MOUSE_LEFT, MOUSE_RIGHT or MOUSE_MIDDLE, default is MOUSE_LEFT).
|
||||
|
||||
#### Returns
|
||||
|
||||
1 if a button was pressed, 0 if a not.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
|
||||
void setup() {
|
||||
// The switch that will initiate the Mouse press
|
||||
pinMode(2, INPUT);
|
||||
// The switch that will terminate the Mouse press
|
||||
pinMode(3, INPUT);
|
||||
// Start serial communication with the computer
|
||||
Serial.begin(9600);
|
||||
// Initialize the Mouse library
|
||||
Mouse.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// A variable for checking the button's state
|
||||
int mouseState = 0;
|
||||
// If the switch attached to pin 2 is closed, press and hold the left mouse button and save the state ina variable
|
||||
if (digitalRead(2) == HIGH) {
|
||||
Mouse.press();
|
||||
mouseState = Mouse.isPressed();
|
||||
}
|
||||
// If the switch attached to pin 3 is closed, release the left mouse button and save the state in a variable
|
||||
if (digitalRead(3) == HIGH) {
|
||||
Mouse.release();
|
||||
mouseState = Mouse.isPressed();
|
||||
}
|
||||
// Print out the current mouse button state
|
||||
Serial.println(mouseState);
|
||||
delay(10);
|
||||
}
|
||||
```
|
||||
|
||||
#### See also
|
||||
|
||||
* [Mouse.begin()](#mousebegin)
|
||||
* [Mouse.click()](#mouseclick)
|
||||
* [Mouse.end()](#mouseend)
|
||||
* [Mouse.move()](#mousemove)
|
||||
* [Mouse.press()](#mousepress)
|
||||
* [Mouse.release()](#mouserelease)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Mouse library
|
||||
|
||||
The mouse functions enable 32u4 or SAMD micro based boards to control cursor movement on a connected computer through their micro’s native USB port. When updating the cursor position, it is always relative to the cursor’s previous location.
|
||||
|
||||
To use this library:
|
||||
|
||||
```
|
||||
#include <Mouse.h>
|
||||
```
|
||||
|
||||
## Notes and warnings
|
||||
|
||||
These core libraries allow the 32u4 and SAMD based boards (Leonardo, Esplora, Zero, Due and MKR Family) to appear as a native Mouse and/or Keyboard to a connected computer.
|
||||
|
||||
**A word of caution on using the Mouse and Keyboard libraries**: if the Mouse or Keyboard library is constantly running, it will be difficult to program your board. Functions such as `Mouse.move()` and `Keyboard.print()` will move your cursor or send keystrokes to a connected computer and should only be called when you are ready to handle them. It is recommended to use a control system to turn this functionality on, like a physical switch or only responding to specific input you can control. Refer to the Mouse and Keyboard examples for some ways to handle this.
|
||||
|
||||
When using the Mouse or Keyboard library, it may be best to test your output first using `Serial.print()`. This way, you can be sure you know what values are being reported.
|
||||
|
||||
## Examples
|
||||
|
||||
* [KeyboardAndMouseControl](https://www.arduino.cc/en/Tutorial/BuiltInExamples/KeyboardAndMouseControl): Demonstrates the Mouse and Keyboard commands in one program.
|
||||
* [ButtonMouseControl](https://www.arduino.cc/en/Tutorial/BuiltInExamples/ButtonMouseControl): Control cursor movement with 5 pushbuttons.
|
||||
* [JoystickMouseControl](https://www.arduino.cc/en/Tutorial/BuiltInExamples/JoystickMouseControl): Controls a computer’s cursor movement with a Joystick when a button is pressed.
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Earle F. Philhower, III <earlephilhower@yahoo.com> */
|
||||
/* Released to the public domain */
|
||||
|
||||
|
||||
#include <Mouse.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Mouse.begin();
|
||||
delay(5000);
|
||||
Serial.printf("Press BOOTSEL to move the mouse in a circle\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (BOOTSEL) {
|
||||
Serial.println("BARREL ROLL!!!");
|
||||
float r = 100;
|
||||
float ox = 0.0;
|
||||
float oy = 0.0;
|
||||
for (float a = 0; a < 2.0 * 3.14159; a += 0.1) {
|
||||
float ax = r * cos(a);
|
||||
float ay = r * sin(a);
|
||||
float dx = ax - ox;
|
||||
float dy = ay - oy;
|
||||
Mouse.move(dx, dy, 0);
|
||||
ox = ax;
|
||||
oy = ay;
|
||||
delay(10);
|
||||
}
|
||||
while (BOOTSEL) {
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user