Adding Pico W AIO and USB Host to BLE project code

Adding Arduino code for two projects: Pico W DVI AIO display and the USB host featherwing to BLE
This commit is contained in:
Liz 2024-04-09 15:30:28 -04:00
parent 56fd748b19
commit 1afc27f9b1
6 changed files with 666 additions and 0 deletions

View file

@ -0,0 +1,193 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/************************** Configuration ***********************************/
// edit the config.h tab and enter your Adafruit IO credentials
// and any additional configuration needed for WiFi, cellular,
// or ethernet clients.
#include "config.h"
#include "sprites.h"
#include <PicoDVI.h> // Core display & graphics library
#include <Fonts/FreeSansBold18pt7b.h> // A custom font
#include <Fonts/FreeSans9pt7b.h> // A custom font
// put your four feed names here!
AdafruitIO_Feed *temp = io.feed("temperature-feed");
AdafruitIO_Feed *humid = io.feed("humidity-feed");
AdafruitIO_Feed *bat = io.feed("battery-feed");
AdafruitIO_Feed *aqi = io.feed("aqi-feed");
#define IO_LOOP_DELAY 5000
unsigned long lastUpdate = 0;
float temp_data;
float humid_data;
int bat_data;
int aqi_data;
struct outline {
int16_t x, y; // Top-left corner
};
outline greenOutline = {159, 35};
outline yellowOutline = {204, 35};
outline redOutline = {250, 35};
DVIGFX8 display(DVI_RES_320x240p60, false, adafruit_dvibell_cfg);
void setup() {
// start the serial connection
Serial.begin(115200);
// wait for serial monitor to open
//while ( !Serial ) delay(10);
Serial.print("Connecting to Adafruit IO");
// start connection to io.adafruit.com
io.connect();
// set up a message handler for the count feed.
// the handleMessage function (defined below)
// will be called whenever a message is
// received from adafruit io.
temp->onMessage(tempMessage);
humid->onMessage(humidMessage);
bat->onMessage(batMessage);
aqi->onMessage(aqiMessage);
// wait for a connection
while(io.status() < AIO_CONNECTED) {
Serial.print(".");
delay(500);
}
// we are connected
Serial.println();
Serial.println(io.statusText());
// Because Adafruit IO doesn't support the MQTT retain flag, we can use the
// get() function to ask IO to resend the last value for this feed to just
// this MQTT client after the io client is connected.
temp->get();
humid->get();
bat->get();
aqi->get();
Serial.println("starting picodvi..");
if (!display.begin()) { // Blink LED if insufficient RAM
pinMode(LED_BUILTIN, OUTPUT);
for (;;) digitalWrite(LED_BUILTIN, (millis() / 500) & 1);
}
Serial.println("picodvi good to go");
// Set up color palette
display.setColor(0, 0x0000); // black
display.setColor(1, 0x057D); // blue
display.setColor(2, 0xB77F); // light blue
display.setColor(3, 0xE8E4); // red
display.setColor(4, 0x3DA9); // green
display.setColor(5, 0xFF80); // yellow
display.setColor(6, 0xFFFF); // white
}
void loop() {
// io.run(); is required for all sketches.
// it should always be present at the top of your loop
// function. it keeps the client connected to
// io.adafruit.com, and processes any incoming data.
io.run();
if (millis() > (lastUpdate + IO_LOOP_DELAY)) {
display.fillScreen(0);
display.drawBitmap(38, 35, airBitmap, airWidth, airHeight, 2);
display.drawBitmap(47, 132, tempBitmap, tempWidth, tempHeight, 3);
display.drawBitmap(145, 132, waterBitmap, waterWidth, waterHeight, 1);
display.drawBitmap(248, 132, batBitmap, batWidth, batHeight, 6);
drawBatterySquare(bat_data);
displayAQI(aqi_data);
display.setFont(&FreeSansBold18pt7b);
display.setTextColor(6);
display.setCursor(38 + airWidth + 15, 38 + airHeight - 10);
display.println(aqi_data);
display.setFont(&FreeSans9pt7b);
display.setCursor(47 - 9, 130 + tempHeight + 25);
display.print(temp_data, 2);
display.println(" F");
display.setCursor(145 - 9, 130 + tempHeight + 25);
display.print(humid_data, 2);
display.println("%");
display.setCursor(248 - 5, 130 + tempHeight + 25);
display.print(bat_data);
display.println("%");
// store the current time
lastUpdate = millis();
}
}
void humidMessage(AdafruitIO_Data *data) {
//Serial.print("received <- ");
Serial.println(data->value());
String h = data->value();
humid_data = h.toFloat();
}
void tempMessage(AdafruitIO_Data *data) {
//Serial.print("received <- ");
Serial.println(data->value());
String d = data->value();
temp_data = d.toFloat();
}
void batMessage(AdafruitIO_Data *data) {
//Serial.print("received <- ");
Serial.println(data->value());
String b = data->value();
bat_data = b.toInt();
}
void aqiMessage(AdafruitIO_Data *data) {
//Serial.print("received <- ");
Serial.println(data->value());
String a = data->value();
aqi_data = a.toInt();
}
void displayAQI(int data) {
display.fillRoundRect(164, 40, 30, 30, 4, 4);
display.fillRoundRect(209, 40, 30, 30, 4, 5);
display.fillRoundRect(255, 40, 30, 30, 4, 3);
if (data <= 12) { // Good
display.drawRoundRect(greenOutline.x, greenOutline.y, 40, 40, 4, 6);
} else if (data <= 35) { // Bad
display.drawRoundRect(yellowOutline.x, yellowOutline.y, 40, 40, 4, 6);
} else { // Dangerous
display.drawRoundRect(redOutline.x, redOutline.y, 40, 40, 4, 6);
}
}
void drawBatterySquare(int data) {
int BASE_SQUARE_X = 252; // Base X position
int BASE_SQUARE_Y = 140; // Base Y position
int SQUARE_WIDTH = 21; // Width is constant
int MAX_SQUARE_HEIGHT = 35; // Maximum height for 100% charge
// Map battery percentage to square height
int height = map(data, 0, 100, 0, MAX_SQUARE_HEIGHT);
// Choose color based on battery percentage
uint16_t color;
if (data >= 70) {
color = 4;
} else if (data >= 40) {
color = 5;
} else {
color = 3;
}
// Calculate Y position based on height to draw from bottom up
int yPos = BASE_SQUARE_Y + (MAX_SQUARE_HEIGHT - height);
// Draw the battery square
display.fillRect(BASE_SQUARE_X, yPos, SQUARE_WIDTH, height, color);
}

View file

@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/************************ Adafruit IO Config *******************************/
// visit io.adafruit.com if you need to create an account,
// or if you need your Adafruit IO key.
#define IO_USERNAME "YOUR-AIO-USERNAME"
#define IO_KEY "YOUR-AIO-KEY"
/******************************* WIFI **************************************/
// the AdafruitIO_WiFi client will work with the following boards:
// - HUZZAH ESP8266 Breakout -> https://www.adafruit.com/products/2471
// - Feather HUZZAH ESP8266 -> https://www.adafruit.com/products/2821
// - Feather HUZZAH ESP32 -> https://www.adafruit.com/product/3405
// - Feather M0 WiFi -> https://www.adafruit.com/products/3010
// - Feather WICED -> https://www.adafruit.com/products/3056
// - Adafruit PyPortal -> https://www.adafruit.com/product/4116
// - Adafruit Metro M4 Express AirLift Lite ->
// https://www.adafruit.com/product/4000
// - Adafruit AirLift Breakout -> https://www.adafruit.com/product/4201
// - Adafruit AirLift Shield -> https://www.adafruit.com/product/4285
// - Adafruit AirLift FeatherWing -> https://www.adafruit.com/product/4264
#define WIFI_SSID "YOUR-WIFI-SSID"
#define WIFI_PASS "YOUR-WIFI-SSID-PASSWORD"
// uncomment the following line if you are using airlift
// #define USE_AIRLIFT
// uncomment the following line if you are using winc1500
// #define USE_WINC1500
// uncomment the following line if you are using mrk1010 or nano 33 iot
//#define ARDUINO_SAMD_MKR1010
// comment out the following lines if you are using fona or ethernet
#include "AdafruitIO_WiFi.h"
#if defined(USE_AIRLIFT) || defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE) || \
defined(ADAFRUIT_PYPORTAL)
// Configure the pins used for the ESP32 connection
#if !defined(SPIWIFI_SS) // if the wifi definition isnt in the board variant
// Don't change the names of these #define's! they match the variant ones
#define SPIWIFI SPI
#define SPIWIFI_SS 10 // Chip select pin
#define NINA_ACK 9 // a.k.a BUSY or READY pin
#define NINA_RESETN 6 // Reset pin
#define NINA_GPIO0 -1 // Not connected
#endif
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS, SPIWIFI_SS,
NINA_ACK, NINA_RESETN, NINA_GPIO0, &SPIWIFI);
#else
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);
#endif
/******************************* FONA **************************************/
// the AdafruitIO_FONA client will work with the following boards:
// - Feather 32u4 FONA -> https://www.adafruit.com/product/3027
// uncomment the following two lines for 32u4 FONA,
// and comment out the AdafruitIO_WiFi client in the WIFI section
// #include "AdafruitIO_FONA.h"
// AdafruitIO_FONA io(IO_USERNAME, IO_KEY);
/**************************** ETHERNET ************************************/
// the AdafruitIO_Ethernet client will work with the following boards:
// - Ethernet FeatherWing -> https://www.adafruit.com/products/3201
// uncomment the following two lines for ethernet,
// and comment out the AdafruitIO_WiFi client in the WIFI section
// #include "AdafruitIO_Ethernet.h"
// AdafruitIO_Ethernet io(IO_USERNAME, IO_KEY);

View file

@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#define airWidth 31
#define airHeight 38
const uint8_t PROGMEM airBitmap[] = {
0x00, 0x00, 0x01, 0x80, 0x00, 0x38, 0x03, 0xC0, 0x00, 0x10, 0x07, 0xC0, 0x07,
0x00, 0xC7, 0xC0, 0x0F, 0x81, 0xE3, 0xC0, 0x0F, 0x81, 0xE0, 0x00, 0x0F, 0x81,
0xE0, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0F,
0x00, 0x00, 0x1E, 0x1F, 0x80, 0x00, 0x3E, 0x3F, 0xC0, 0x00, 0x1E, 0x3F, 0xC0,
0x00, 0x1C, 0x3F, 0xC0, 0x18, 0x00, 0x3F, 0xC0, 0x38, 0x00, 0x1F, 0xC0, 0x10,
0x00, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3F,
0xE0, 0x38, 0x00, 0xFF, 0xF8, 0x38, 0x01, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFC,
0x00, 0x03, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xE0,
0x1F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xFC, 0x7F,
0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF,
0xF8, 0x1F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0
};
#define batWidth 29
#define batHeight 46
const uint8_t PROGMEM batBitmap[] = {
0x00, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x01,
0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF,
0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xF0, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00,
0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70,
0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70,
0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00,
0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00,
0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70,
0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70,
0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00,
0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00,
0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70,
0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x70, 0x70,
0x00, 0x00, 0x70, 0x7F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF,
0xFF, 0xC0
};
#define tempWidth 29
#define tempHeight 47
const uint8_t PROGMEM tempBitmap[] = {
0x00, 0x0F, 0x80, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x01,
0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x07, 0xFF,
0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF,
0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00,
0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07,
0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF,
0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF,
0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0x00,
0x0F, 0xFF, 0xFF, 0x80, 0x1F, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xE0, 0x3F,
0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF,
0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF,
0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xC0, 0x0F,
0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x00, 0x7F,
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00
};
#define waterWidth 31
#define waterHeight 49
const uint8_t PROGMEM waterBitmap[] = {
0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00,
0x0F, 0xE0, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x3F,
0xF0, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x7F, 0xFC,
0x00, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFE, 0x00,
0x01, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x03,
0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF,
0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF,
0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xF8,
0x7F, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xFC, 0xFF,
0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF,
0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xFC,
0x7F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F,
0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF,
0xFF, 0x80, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x07, 0xC0,
0x00
};

View file

@ -0,0 +1,316 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// Uses the Adafruit ESP32-S3 TFT with the MAX3421E FeatherWing
// Acts as a USB keyboard to BLE converter
#include <Arduino.h>
#include "Adafruit_TinyUSB.h"
#include "BLEDevice.h"
#include "BLEHIDDevice.h"
#include "HIDTypes.h"
#include "HIDKeyboardTypes.h"
#include "Adafruit_MAX1704X.h"
#include "Adafruit_LC709203F.h"
#include <Adafruit_ST7789.h>
#include <Fonts/FreeSans12pt7b.h>
Adafruit_USBH_Host USBHost(&SPI, 10, 9);
#define US_KEYBOARD 1
#define DEVICE_NAME "ESP32 Keyboard"
#define BLE_MANUFACTURER "TinyUSB"
Adafruit_LC709203F lc_bat;
Adafruit_MAX17048 max_bat;
Adafruit_ST7789 display = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
BLEHIDDevice* hid;
BLECharacteristic* input;
BLECharacteristic* output;
String keyInput = "";
GFXcanvas16 canvas(240, 135);
bool maxfound = false;
bool lcfound = false;
bool isBleConnected = false;
unsigned long previousMillis = 0;
const long interval = 500;
void setup() {
Serial.begin(115200);
// while ( !Serial ) delay(10); // wait for native usb
// turn on the TFT / I2C power supply
pinMode(TFT_I2C_POWER, OUTPUT);
digitalWrite(TFT_I2C_POWER, HIGH);
pinMode(TFT_BACKLITE, OUTPUT);
digitalWrite(TFT_BACKLITE, HIGH);
display.init(135, 240); // Init ST7789 240x135
display.setRotation(3);
canvas.setFont(&FreeSans12pt7b);
canvas.setTextColor(ST77XX_WHITE);
canvas.fillScreen(ST77XX_BLACK);
canvas.setCursor(0, 25);
canvas.println("Connecting to BLE..");
display.drawRGBBitmap(0, 0, canvas.getBuffer(), 240, 135);
// start Bluetooth task
xTaskCreate(bluetoothTask, "bluetooth", 20000, NULL, 5, NULL);
canvas.fillScreen(ST77XX_BLACK);
canvas.setCursor(0, 25);
canvas.println("BLE Connected!");
canvas.println("Finding USB device..");
display.drawRGBBitmap(0, 0, canvas.getBuffer(), 240, 135);
// init host stack on controller (rhport) 1
USBHost.begin(1);
Serial.println("TinyUSB Dual: HID Device to ESP BLE Keyboard");
canvas.fillScreen(ST77XX_BLACK);
canvas.setCursor(0, 25);
canvas.println("BLE Connected!");
canvas.println("USB Connected!");
display.drawRGBBitmap(0, 0, canvas.getBuffer(), 240, 135);
if (lc_bat.begin()) {
Serial.println("Found LC709203F");
Serial.print("Version: 0x"); Serial.println(lc_bat.getICversion(), HEX);
lc_bat.setPackSize(LC709203F_APA_500MAH);
lcfound = true;
}
else {
Serial.println(F("Couldnt find Adafruit LC709203F?\nChecking for Adafruit MAX1704X.."));
delay(200);
if (!max_bat.begin()) {
Serial.println(F("Couldnt find Adafruit MAX1704X?\nMake sure a battery is plugged in!"));
while (1) delay(10);
}
Serial.print(F("Found MAX17048"));
Serial.print(F(" with Chip ID: 0x"));
Serial.println(max_bat.getChipID(), HEX);
maxfound = true;
}
}
void loop() {
unsigned long currentMillis = millis();
USBHost.task();
//Serial.flush();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
canvas.fillScreen(ST77XX_BLACK);
canvas.setCursor(0, 25);
canvas.setTextColor(ST77XX_RED);
canvas.println("Adafruit Feather");
canvas.setTextColor(ST77XX_YELLOW);
canvas.println("USB Host -> BLE");
canvas.setTextColor(ST77XX_GREEN);
canvas.print("Battery: ");
canvas.setTextColor(ST77XX_WHITE);
if (lcfound == true) {
canvas.print(lc_bat.cellVoltage(), 1);
canvas.print(" V / ");
canvas.print(lc_bat.cellPercent(), 0);
canvas.println("%");
hid->setBatteryLevel(lc_bat.cellPercent());
} else {
canvas.print(max_bat.cellVoltage(), 1);
canvas.print(" V / ");
canvas.print(max_bat.cellPercent(), 0);
canvas.println("%");
hid->setBatteryLevel(max_bat.cellPercent());
}
canvas.setTextColor(ST77XX_BLUE);
canvas.print("Sent: ");
canvas.setTextColor(ST77XX_WHITE);
canvas.println(keyInput);
display.drawRGBBitmap(0, 0, canvas.getBuffer(), 240, 135);
}
}
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *msg, uint16_t len);
// Message (report) sent when a key is pressed or released
struct InputReport {
uint8_t modifiers; // bitmask: CTRL = 1, SHIFT = 2, ALT = 4
uint8_t reserved; // must be 0
uint8_t pressedKeys[6]; // up to six concurrenlty pressed keys
};
// The report map describes the HID device (a keyboard in this case) and
// the messages (reports in HID terms) sent and received.
static const uint8_t REPORT_MAP[] = {
USAGE_PAGE(1), 0x01, // Generic Desktop Controls
USAGE(1), 0x06, // Keyboard
COLLECTION(1), 0x01, // Application
REPORT_ID(1), 0x01, // Report ID (1)
USAGE_PAGE(1), 0x07, // Keyboard/Keypad
USAGE_MINIMUM(1), 0xE0, // Keyboard Left Control
USAGE_MAXIMUM(1), 0xE7, // Keyboard Right Control
LOGICAL_MINIMUM(1), 0x00, // Each bit is either 0 or 1
LOGICAL_MAXIMUM(1), 0x01,
REPORT_COUNT(1), 0x08, // 8 bits for the modifier keys
REPORT_SIZE(1), 0x01,
HIDINPUT(1), 0x02, // Data, Var, Abs
REPORT_COUNT(1), 0x01, // 1 byte (unused)
REPORT_SIZE(1), 0x08,
HIDINPUT(1), 0x01, // Const, Array, Abs
REPORT_COUNT(1), 0x06, // 6 bytes (for up to 6 concurrently pressed keys)
REPORT_SIZE(1), 0x08,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x65, // 101 keys
USAGE_MINIMUM(1), 0x00,
USAGE_MAXIMUM(1), 0x65,
HIDINPUT(1), 0x00, // Data, Array, Abs
REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
REPORT_SIZE(1), 0x01,
USAGE_PAGE(1), 0x08, // LEDs
USAGE_MINIMUM(1), 0x01, // Num Lock
USAGE_MAXIMUM(1), 0x05, // Kana
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
HIDOUTPUT(1), 0x02, // Data, Var, Abs
REPORT_COUNT(1), 0x01, // 3 bits (Padding)
REPORT_SIZE(1), 0x03,
HIDOUTPUT(1), 0x01, // Const, Array, Abs
END_COLLECTION(0) // End application collection
};
const InputReport NO_KEY_PRESSED = { };
/*
* Callbacks related to BLE connection
*/
class BleKeyboardCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* server) {
isBleConnected = true;
// Allow notifications for characteristics
BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
cccDesc->setNotifications(true);
Serial.println("Client has connected");
}
void onDisconnect(BLEServer* server) {
isBleConnected = false;
// Disallow notifications for characteristics
BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
cccDesc->setNotifications(false);
Serial.println("Client has disconnected");
}
};
void bluetoothTask(void*) {
BLEDevice::init(DEVICE_NAME);
BLEServer* server = BLEDevice::createServer();
server->setCallbacks(new BleKeyboardCallbacks());
// create an HID device
hid = new BLEHIDDevice(server);
input = hid->inputReport(1); // report ID
// set manufacturer name
hid->manufacturer()->setValue(BLE_MANUFACTURER);
// set USB vendor and product ID
hid->pnp(0x02, 0xe502, 0xa111, 0x0210);
// information about HID device: device is not localized, device can be connected
hid->hidInfo(0x00, 0x02);
// Security: device requires bonding
BLESecurity* security = new BLESecurity();
security->setAuthenticationMode(ESP_LE_AUTH_BOND);
// set report map
hid->reportMap((uint8_t*)REPORT_MAP, sizeof(REPORT_MAP));
hid->startServices();
// set battery level to 100%
hid->setBatteryLevel(100);
// advertise the services
BLEAdvertising* advertising = server->getAdvertising();
advertising->setAppearance(HID_KEYBOARD);
advertising->addServiceUUID(hid->hidService()->getUUID());
advertising->addServiceUUID(hid->deviceInfo()->getUUID());
advertising->addServiceUUID(hid->batteryService()->getUUID());
advertising->start();
Serial.println("BLE ready");
delay(portMAX_DELAY);
};
extern "C" {
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use.
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
(void) desc_report;
(void) desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
hid->pnp(0x02, vid, 0xa111, pid);
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *msg, uint16_t len) {
// continue to request to receive report
//unsigned long a = millis();
if (!isBleConnected) return;
//Serial.println(messageLength);
if (msg[2] != 0) {
for (uint8_t i = 2; i < len; i++) {
// translate character to key combination
// Assuming your USB message fits the format required by the BLE keyboard
// You might need to adjust this depending on your actual USB message format
InputReport report = {
.modifiers = msg[0], // No modifier for now
.reserved = 0,
.pressedKeys = {msg[i], 0, 0, 0, 0, 0}
};
input->setValue((uint8_t*)&report, sizeof(report));
input->notify();
delay(1);
// release all keys between two characters; otherwise two identical
// consecutive characters are treated as just one key press
input->setValue((uint8_t*)&NO_KEY_PRESSED, sizeof(NO_KEY_PRESSED));
input->notify();
delay(1);
}
char formattedString[6]; // Large enough for "0x" + 2 hex digits + null terminator
sprintf(formattedString, "0x%02X", msg[2]);
//if (strcmp(formattedString, "0x00") != 0) {
keyInput = "";
keyInput = formattedString;
//Serial.println(keyInput);
//}
}
//sendUSBMessageOverBLE(report, len);
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
//unsigned long b = millis();
//Serial.println(b-a);
}
} // extern C