From 1afc27f9b10e7d99029be9b36c02c9c0e3561d01 Mon Sep 17 00:00:00 2001 From: Liz Date: Tue, 9 Apr 2024 15:30:28 -0400 Subject: [PATCH] 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 --- PicoW_DVI_AIO_Display/.pico_rp2040.test.only | 0 .../PicoW_DVI_AIO_Display.ino | 193 +++++++++++ PicoW_DVI_AIO_Display/config.h | 76 +++++ PicoW_DVI_AIO_Display/sprites.h | 81 +++++ .../.feather_esp32s3_tft.test.only | 0 .../USB_Host_to_BLE_Arduino.ino | 316 ++++++++++++++++++ 6 files changed, 666 insertions(+) create mode 100644 PicoW_DVI_AIO_Display/.pico_rp2040.test.only create mode 100644 PicoW_DVI_AIO_Display/PicoW_DVI_AIO_Display.ino create mode 100644 PicoW_DVI_AIO_Display/config.h create mode 100644 PicoW_DVI_AIO_Display/sprites.h create mode 100644 USB_Host_to_BLE_Arduino/.feather_esp32s3_tft.test.only create mode 100644 USB_Host_to_BLE_Arduino/USB_Host_to_BLE_Arduino.ino diff --git a/PicoW_DVI_AIO_Display/.pico_rp2040.test.only b/PicoW_DVI_AIO_Display/.pico_rp2040.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/PicoW_DVI_AIO_Display/PicoW_DVI_AIO_Display.ino b/PicoW_DVI_AIO_Display/PicoW_DVI_AIO_Display.ino new file mode 100644 index 000000000..a66e8c644 --- /dev/null +++ b/PicoW_DVI_AIO_Display/PicoW_DVI_AIO_Display.ino @@ -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 // Core display & graphics library +#include // A custom font +#include // 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); +} diff --git a/PicoW_DVI_AIO_Display/config.h b/PicoW_DVI_AIO_Display/config.h new file mode 100644 index 000000000..a8ad8a069 --- /dev/null +++ b/PicoW_DVI_AIO_Display/config.h @@ -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); diff --git a/PicoW_DVI_AIO_Display/sprites.h b/PicoW_DVI_AIO_Display/sprites.h new file mode 100644 index 000000000..2ed8181fe --- /dev/null +++ b/PicoW_DVI_AIO_Display/sprites.h @@ -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 + }; diff --git a/USB_Host_to_BLE_Arduino/.feather_esp32s3_tft.test.only b/USB_Host_to_BLE_Arduino/.feather_esp32s3_tft.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/USB_Host_to_BLE_Arduino/USB_Host_to_BLE_Arduino.ino b/USB_Host_to_BLE_Arduino/USB_Host_to_BLE_Arduino.ino new file mode 100644 index 000000000..e6d2cf934 --- /dev/null +++ b/USB_Host_to_BLE_Arduino/USB_Host_to_BLE_Arduino.ino @@ -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 +#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 +#include + +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