compilable for esp32

This commit is contained in:
Seth Tunstall 2022-10-28 09:45:33 +01:00
parent 70202828e7
commit 65378456fc
28 changed files with 23 additions and 2562 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.pio/*
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View file

@ -1 +0,0 @@
82b9f8aaa58aa6491c6064e514b8151d88ade40b

View file

@ -1 +0,0 @@
{"type": "library", "name": "ESP32 BLE Keyboard", "version": "0.3.2", "spec": {"owner": "t-vk", "id": 6749, "name": "ESP32 BLE Keyboard", "requirements": null, "uri": null}}

View file

@ -1,546 +0,0 @@
#include "BleKeyboard.h"
#if defined(USE_NIMBLE)
#include <NimBLEDevice.h>
#include <NimBLEServer.h>
#include <NimBLEUtils.h>
#include <NimBLEHIDDevice.h>
#else
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include "BLE2902.h"
#include "BLEHIDDevice.h"
#endif // USE_NIMBLE
#include "HIDTypes.h"
#include <driver/adc.h>
#include "sdkconfig.h"
#if defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
#define LOG_TAG ""
#else
#include "esp_log.h"
static const char* LOG_TAG = "BLEDevice";
#endif
// Report IDs:
#define KEYBOARD_ID 0x01
#define MEDIA_KEYS_ID 0x02
static const uint8_t _hidReportDescriptor[] = {
USAGE_PAGE(1), 0x01, // USAGE_PAGE (Generic Desktop Ctrls)
USAGE(1), 0x06, // USAGE (Keyboard)
COLLECTION(1), 0x01, // COLLECTION (Application)
// ------------------------------------------------- Keyboard
REPORT_ID(1), KEYBOARD_ID, // REPORT_ID (1)
USAGE_PAGE(1), 0x07, // USAGE_PAGE (Kbrd/Keypad)
USAGE_MINIMUM(1), 0xE0, // USAGE_MINIMUM (0xE0)
USAGE_MAXIMUM(1), 0xE7, // USAGE_MAXIMUM (0xE7)
LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM(1), 0x01, // Logical Maximum (1)
REPORT_SIZE(1), 0x01, // REPORT_SIZE (1)
REPORT_COUNT(1), 0x08, // REPORT_COUNT (8)
HIDINPUT(1), 0x02, // INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
REPORT_COUNT(1), 0x01, // REPORT_COUNT (1) ; 1 byte (Reserved)
REPORT_SIZE(1), 0x08, // REPORT_SIZE (8)
HIDINPUT(1), 0x01, // INPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
REPORT_COUNT(1), 0x05, // REPORT_COUNT (5) ; 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
REPORT_SIZE(1), 0x01, // REPORT_SIZE (1)
USAGE_PAGE(1), 0x08, // USAGE_PAGE (LEDs)
USAGE_MINIMUM(1), 0x01, // USAGE_MINIMUM (0x01) ; Num Lock
USAGE_MAXIMUM(1), 0x05, // USAGE_MAXIMUM (0x05) ; Kana
HIDOUTPUT(1), 0x02, // OUTPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
REPORT_COUNT(1), 0x01, // REPORT_COUNT (1) ; 3 bits (Padding)
REPORT_SIZE(1), 0x03, // REPORT_SIZE (3)
HIDOUTPUT(1), 0x01, // OUTPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
REPORT_COUNT(1), 0x06, // REPORT_COUNT (6) ; 6 bytes (Keys)
REPORT_SIZE(1), 0x08, // REPORT_SIZE(8)
LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM(0)
LOGICAL_MAXIMUM(1), 0x65, // LOGICAL_MAXIMUM(0x65) ; 101 keys
USAGE_PAGE(1), 0x07, // USAGE_PAGE (Kbrd/Keypad)
USAGE_MINIMUM(1), 0x00, // USAGE_MINIMUM (0)
USAGE_MAXIMUM(1), 0x65, // USAGE_MAXIMUM (0x65)
HIDINPUT(1), 0x00, // INPUT (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
END_COLLECTION(0), // END_COLLECTION
// ------------------------------------------------- Media Keys
USAGE_PAGE(1), 0x0C, // USAGE_PAGE (Consumer)
USAGE(1), 0x01, // USAGE (Consumer Control)
COLLECTION(1), 0x01, // COLLECTION (Application)
REPORT_ID(1), MEDIA_KEYS_ID, // REPORT_ID (3)
USAGE_PAGE(1), 0x0C, // USAGE_PAGE (Consumer)
LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM(1), 0x01, // LOGICAL_MAXIMUM (1)
REPORT_SIZE(1), 0x01, // REPORT_SIZE (1)
REPORT_COUNT(1), 0x10, // REPORT_COUNT (16)
USAGE(1), 0xB5, // USAGE (Scan Next Track) ; bit 0: 1
USAGE(1), 0xB6, // USAGE (Scan Previous Track) ; bit 1: 2
USAGE(1), 0xB7, // USAGE (Stop) ; bit 2: 4
USAGE(1), 0xCD, // USAGE (Play/Pause) ; bit 3: 8
USAGE(1), 0xE2, // USAGE (Mute) ; bit 4: 16
USAGE(1), 0xE9, // USAGE (Volume Increment) ; bit 5: 32
USAGE(1), 0xEA, // USAGE (Volume Decrement) ; bit 6: 64
USAGE(2), 0x23, 0x02, // Usage (WWW Home) ; bit 7: 128
USAGE(2), 0x94, 0x01, // Usage (My Computer) ; bit 0: 1
USAGE(2), 0x92, 0x01, // Usage (Calculator) ; bit 1: 2
USAGE(2), 0x2A, 0x02, // Usage (WWW fav) ; bit 2: 4
USAGE(2), 0x21, 0x02, // Usage (WWW search) ; bit 3: 8
USAGE(2), 0x26, 0x02, // Usage (WWW stop) ; bit 4: 16
USAGE(2), 0x24, 0x02, // Usage (WWW back) ; bit 5: 32
USAGE(2), 0x83, 0x01, // Usage (Media sel) ; bit 6: 64
USAGE(2), 0x8A, 0x01, // Usage (Mail) ; bit 7: 128
HIDINPUT(1), 0x02, // INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
END_COLLECTION(0) // END_COLLECTION
};
BleKeyboard::BleKeyboard(std::string deviceName, std::string deviceManufacturer, uint8_t batteryLevel)
: hid(0)
, deviceName(std::string(deviceName).substr(0, 15))
, deviceManufacturer(std::string(deviceManufacturer).substr(0,15))
, batteryLevel(batteryLevel) {}
void BleKeyboard::begin(void)
{
BLEDevice::init(deviceName);
BLEServer* pServer = BLEDevice::createServer();
pServer->setCallbacks(this);
hid = new BLEHIDDevice(pServer);
inputKeyboard = hid->inputReport(KEYBOARD_ID); // <-- input REPORTID from report map
outputKeyboard = hid->outputReport(KEYBOARD_ID);
inputMediaKeys = hid->inputReport(MEDIA_KEYS_ID);
outputKeyboard->setCallbacks(this);
hid->manufacturer()->setValue(deviceManufacturer);
hid->pnp(0x02, vid, pid, version);
hid->hidInfo(0x00, 0x01);
#if defined(USE_NIMBLE)
BLEDevice::setSecurityAuth(true, true, true);
#else
BLESecurity* pSecurity = new BLESecurity();
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND);
#endif // USE_NIMBLE
hid->reportMap((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor));
hid->startServices();
onStarted(pServer);
advertising = pServer->getAdvertising();
advertising->setAppearance(HID_KEYBOARD);
advertising->addServiceUUID(hid->hidService()->getUUID());
advertising->setScanResponse(false);
advertising->start();
hid->setBatteryLevel(batteryLevel);
ESP_LOGD(LOG_TAG, "Advertising started!");
}
void BleKeyboard::end(void)
{
}
bool BleKeyboard::isConnected(void) {
return this->connected;
}
void BleKeyboard::setBatteryLevel(uint8_t level) {
this->batteryLevel = level;
if (hid != 0)
this->hid->setBatteryLevel(this->batteryLevel);
}
//must be called before begin in order to set the name
void BleKeyboard::setName(std::string deviceName) {
this->deviceName = deviceName;
}
/**
* @brief Sets the waiting time (in milliseconds) between multiple keystrokes in NimBLE mode.
*
* @param ms Time in milliseconds
*/
void BleKeyboard::setDelay(uint32_t ms) {
this->_delay_ms = ms;
}
void BleKeyboard::set_vendor_id(uint16_t vid) {
this->vid = vid;
}
void BleKeyboard::set_product_id(uint16_t pid) {
this->pid = pid;
}
void BleKeyboard::set_version(uint16_t version) {
this->version = version;
}
void BleKeyboard::sendReport(KeyReport* keys)
{
if (this->isConnected())
{
this->inputKeyboard->setValue((uint8_t*)keys, sizeof(KeyReport));
this->inputKeyboard->notify();
#if defined(USE_NIMBLE)
// vTaskDelay(delayTicks);
this->delay_ms(_delay_ms);
#endif // USE_NIMBLE
}
}
void BleKeyboard::sendReport(MediaKeyReport* keys)
{
if (this->isConnected())
{
this->inputMediaKeys->setValue((uint8_t*)keys, sizeof(MediaKeyReport));
this->inputMediaKeys->notify();
#if defined(USE_NIMBLE)
//vTaskDelay(delayTicks);
this->delay_ms(_delay_ms);
#endif // USE_NIMBLE
}
}
extern
const uint8_t _asciimap[128] PROGMEM;
#define SHIFT 0x80
const uint8_t _asciimap[128] =
{
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, // ~
0 // DEL
};
uint8_t USBPutChar(uint8_t c);
// 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 BleKeyboard::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 & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers |= 0x02; // the left shift modifier
k &= 0x7F;
}
}
// 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;
}
size_t BleKeyboard::press(const MediaKeyReport k)
{
uint16_t k_16 = k[1] | (k[0] << 8);
uint16_t mediaKeyReport_16 = _mediaKeyReport[1] | (_mediaKeyReport[0] << 8);
mediaKeyReport_16 |= k_16;
_mediaKeyReport[0] = (uint8_t)((mediaKeyReport_16 & 0xFF00) >> 8);
_mediaKeyReport[1] = (uint8_t)(mediaKeyReport_16 & 0x00FF);
sendReport(&_mediaKeyReport);
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 BleKeyboard::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 & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers &= ~(0x02); // the left shift modifier
k &= 0x7F;
}
}
// 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;
}
size_t BleKeyboard::release(const MediaKeyReport k)
{
uint16_t k_16 = k[1] | (k[0] << 8);
uint16_t mediaKeyReport_16 = _mediaKeyReport[1] | (_mediaKeyReport[0] << 8);
mediaKeyReport_16 &= ~k_16;
_mediaKeyReport[0] = (uint8_t)((mediaKeyReport_16 & 0xFF00) >> 8);
_mediaKeyReport[1] = (uint8_t)(mediaKeyReport_16 & 0x00FF);
sendReport(&_mediaKeyReport);
return 1;
}
void BleKeyboard::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;
_mediaKeyReport[0] = 0;
_mediaKeyReport[1] = 0;
sendReport(&_keyReport);
}
size_t BleKeyboard::write(uint8_t c)
{
uint8_t p = press(c); // Keydown
release(c); // Keyup
return p; // just return the result of press() since release() almost always returns 1
}
size_t BleKeyboard::write(const MediaKeyReport c)
{
uint16_t p = press(c); // Keydown
release(c); // Keyup
return p; // just return the result of press() since release() almost always returns 1
}
size_t BleKeyboard::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 BleKeyboard::onConnect(BLEServer* pServer) {
this->connected = true;
#if !defined(USE_NIMBLE)
BLE2902* desc = (BLE2902*)this->inputKeyboard->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
desc->setNotifications(true);
desc = (BLE2902*)this->inputMediaKeys->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
desc->setNotifications(true);
#endif // !USE_NIMBLE
}
void BleKeyboard::onDisconnect(BLEServer* pServer) {
this->connected = false;
#if !defined(USE_NIMBLE)
BLE2902* desc = (BLE2902*)this->inputKeyboard->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
desc->setNotifications(false);
desc = (BLE2902*)this->inputMediaKeys->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
desc->setNotifications(false);
advertising->start();
#endif // !USE_NIMBLE
}
void BleKeyboard::onWrite(BLECharacteristic* me) {
uint8_t* value = (uint8_t*)(me->getValue().c_str());
(void)value;
ESP_LOGI(LOG_TAG, "special keys: %d", *value);
}
void BleKeyboard::delay_ms(uint64_t ms) {
uint64_t m = esp_timer_get_time();
if(ms){
uint64_t e = (m + (ms * 1000));
if(m > e){ //overflow
while(esp_timer_get_time() > e) { }
}
while(esp_timer_get_time() < e) {}
}
}

View file

@ -1,183 +0,0 @@
// uncomment the following line to use NimBLE library
//#define USE_NIMBLE
#ifndef ESP32_BLE_KEYBOARD_H
#define ESP32_BLE_KEYBOARD_H
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(USE_NIMBLE)
#include "NimBLECharacteristic.h"
#include "NimBLEHIDDevice.h"
#define BLEDevice NimBLEDevice
#define BLEServerCallbacks NimBLEServerCallbacks
#define BLECharacteristicCallbacks NimBLECharacteristicCallbacks
#define BLEHIDDevice NimBLEHIDDevice
#define BLECharacteristic NimBLECharacteristic
#define BLEAdvertising NimBLEAdvertising
#define BLEServer NimBLEServer
#else
#include "BLEHIDDevice.h"
#include "BLECharacteristic.h"
#endif // USE_NIMBLE
#include "Print.h"
#define BLE_KEYBOARD_VERSION "0.0.4"
#define BLE_KEYBOARD_VERSION_MAJOR 0
#define BLE_KEYBOARD_VERSION_MINOR 0
#define BLE_KEYBOARD_VERSION_REVISION 4
const uint8_t KEY_LEFT_CTRL = 0x80;
const uint8_t KEY_LEFT_SHIFT = 0x81;
const uint8_t KEY_LEFT_ALT = 0x82;
const uint8_t KEY_LEFT_GUI = 0x83;
const uint8_t KEY_RIGHT_CTRL = 0x84;
const uint8_t KEY_RIGHT_SHIFT = 0x85;
const uint8_t KEY_RIGHT_ALT = 0x86;
const uint8_t KEY_RIGHT_GUI = 0x87;
const uint8_t KEY_UP_ARROW = 0xDA;
const uint8_t KEY_DOWN_ARROW = 0xD9;
const uint8_t KEY_LEFT_ARROW = 0xD8;
const uint8_t KEY_RIGHT_ARROW = 0xD7;
const uint8_t KEY_BACKSPACE = 0xB2;
const uint8_t KEY_TAB = 0xB3;
const uint8_t KEY_RETURN = 0xB0;
const uint8_t KEY_ESC = 0xB1;
const uint8_t KEY_INSERT = 0xD1;
const uint8_t KEY_PRTSC = 0xCE;
const uint8_t KEY_DELETE = 0xD4;
const uint8_t KEY_PAGE_UP = 0xD3;
const uint8_t KEY_PAGE_DOWN = 0xD6;
const uint8_t KEY_HOME = 0xD2;
const uint8_t KEY_END = 0xD5;
const uint8_t KEY_CAPS_LOCK = 0xC1;
const uint8_t KEY_F1 = 0xC2;
const uint8_t KEY_F2 = 0xC3;
const uint8_t KEY_F3 = 0xC4;
const uint8_t KEY_F4 = 0xC5;
const uint8_t KEY_F5 = 0xC6;
const uint8_t KEY_F6 = 0xC7;
const uint8_t KEY_F7 = 0xC8;
const uint8_t KEY_F8 = 0xC9;
const uint8_t KEY_F9 = 0xCA;
const uint8_t KEY_F10 = 0xCB;
const uint8_t KEY_F11 = 0xCC;
const uint8_t KEY_F12 = 0xCD;
const uint8_t KEY_F13 = 0xF0;
const uint8_t KEY_F14 = 0xF1;
const uint8_t KEY_F15 = 0xF2;
const uint8_t KEY_F16 = 0xF3;
const uint8_t KEY_F17 = 0xF4;
const uint8_t KEY_F18 = 0xF5;
const uint8_t KEY_F19 = 0xF6;
const uint8_t KEY_F20 = 0xF7;
const uint8_t KEY_F21 = 0xF8;
const uint8_t KEY_F22 = 0xF9;
const uint8_t KEY_F23 = 0xFA;
const uint8_t KEY_F24 = 0xFB;
const uint8_t KEY_NUM_0 = 0xEA;
const uint8_t KEY_NUM_1 = 0xE1;
const uint8_t KEY_NUM_2 = 0xE2;
const uint8_t KEY_NUM_3 = 0xE3;
const uint8_t KEY_NUM_4 = 0xE4;
const uint8_t KEY_NUM_5 = 0xE5;
const uint8_t KEY_NUM_6 = 0xE6;
const uint8_t KEY_NUM_7 = 0xE7;
const uint8_t KEY_NUM_8 = 0xE8;
const uint8_t KEY_NUM_9 = 0xE9;
const uint8_t KEY_NUM_SLASH = 0xDC;
const uint8_t KEY_NUM_ASTERISK = 0xDD;
const uint8_t KEY_NUM_MINUS = 0xDE;
const uint8_t KEY_NUM_PLUS = 0xDF;
const uint8_t KEY_NUM_ENTER = 0xE0;
const uint8_t KEY_NUM_PERIOD = 0xEB;
typedef uint8_t MediaKeyReport[2];
const MediaKeyReport KEY_MEDIA_NEXT_TRACK = {1, 0};
const MediaKeyReport KEY_MEDIA_PREVIOUS_TRACK = {2, 0};
const MediaKeyReport KEY_MEDIA_STOP = {4, 0};
const MediaKeyReport KEY_MEDIA_PLAY_PAUSE = {8, 0};
const MediaKeyReport KEY_MEDIA_MUTE = {16, 0};
const MediaKeyReport KEY_MEDIA_VOLUME_UP = {32, 0};
const MediaKeyReport KEY_MEDIA_VOLUME_DOWN = {64, 0};
const MediaKeyReport KEY_MEDIA_WWW_HOME = {128, 0};
const MediaKeyReport KEY_MEDIA_LOCAL_MACHINE_BROWSER = {0, 1}; // Opens "My Computer" on Windows
const MediaKeyReport KEY_MEDIA_CALCULATOR = {0, 2};
const MediaKeyReport KEY_MEDIA_WWW_BOOKMARKS = {0, 4};
const MediaKeyReport KEY_MEDIA_WWW_SEARCH = {0, 8};
const MediaKeyReport KEY_MEDIA_WWW_STOP = {0, 16};
const MediaKeyReport KEY_MEDIA_WWW_BACK = {0, 32};
const MediaKeyReport KEY_MEDIA_CONSUMER_CONTROL_CONFIGURATION = {0, 64}; // Media Selection
const MediaKeyReport KEY_MEDIA_EMAIL_READER = {0, 128};
// 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 BleKeyboard : public Print, public BLEServerCallbacks, public BLECharacteristicCallbacks
{
private:
BLEHIDDevice* hid;
BLECharacteristic* inputKeyboard;
BLECharacteristic* outputKeyboard;
BLECharacteristic* inputMediaKeys;
BLEAdvertising* advertising;
KeyReport _keyReport;
MediaKeyReport _mediaKeyReport;
std::string deviceName;
std::string deviceManufacturer;
uint8_t batteryLevel;
bool connected = false;
uint32_t _delay_ms = 7;
void delay_ms(uint64_t ms);
uint16_t vid = 0x05ac;
uint16_t pid = 0x820a;
uint16_t version = 0x0210;
public:
BleKeyboard(std::string deviceName = "ESP32 Keyboard", std::string deviceManufacturer = "Espressif", uint8_t batteryLevel = 100);
void begin(void);
void end(void);
void sendReport(KeyReport* keys);
void sendReport(MediaKeyReport* keys);
size_t press(uint8_t k);
size_t press(const MediaKeyReport k);
size_t release(uint8_t k);
size_t release(const MediaKeyReport k);
size_t write(uint8_t c);
size_t write(const MediaKeyReport c);
size_t write(const uint8_t *buffer, size_t size);
void releaseAll(void);
bool isConnected(void);
void setBatteryLevel(uint8_t level);
void setName(std::string deviceName);
void setDelay(uint32_t ms);
void set_vendor_id(uint16_t vid);
void set_product_id(uint16_t pid);
void set_version(uint16_t version);
protected:
virtual void onStarted(BLEServer *pServer) { };
virtual void onConnect(BLEServer* pServer) override;
virtual void onDisconnect(BLEServer* pServer) override;
virtual void onWrite(BLECharacteristic* me) override;
};
#endif // CONFIG_BT_ENABLED
#endif // ESP32_BLE_KEYBOARD_H

View file

@ -1,162 +0,0 @@
# ESP32 BLE Keyboard library
This library allows you to make the ESP32 act as a Bluetooth Keyboard and control what it does.
You might also be interested in:
- [ESP32-BLE-Mouse](https://github.com/T-vK/ESP32-BLE-Mouse)
- [ESP32-BLE-Gamepad](https://github.com/lemmingDev/ESP32-BLE-Gamepad)
## Features
- [x] Send key strokes
- [x] Send text
- [x] Press/release individual keys
- [x] Media keys are supported
- [ ] Read Numlock/Capslock/Scrolllock state
- [x] Set battery level (basically works, but doesn't show up in Android's status bar)
- [x] Compatible with Android
- [x] Compatible with Windows
- [x] Compatible with Linux
- [x] Compatible with MacOS X (not stable, some people have issues, doesn't work with old devices)
- [x] Compatible with iOS (not stable, some people have issues, doesn't work with old devices)
## Installation
- (Make sure you can use the ESP32 with the Arduino IDE. [Instructions can be found here.](https://github.com/espressif/arduino-esp32#installation-instructions))
- [Download the latest release of this library from the release page.](https://github.com/T-vK/ESP32-BLE-Keyboard/releases)
- In the Arduino IDE go to "Sketch" -> "Include Library" -> "Add .ZIP Library..." and select the file you just downloaded.
- You can now go to "File" -> "Examples" -> "ESP32 BLE Keyboard" and select any of the examples to get started.
## Example
``` C++
/**
* This example turns the ESP32 into a Bluetooth LE keyboard that writes the words, presses Enter, presses a media key and then Ctrl+Alt+Delete
*/
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE work!");
bleKeyboard.begin();
}
void loop() {
if(bleKeyboard.isConnected()) {
Serial.println("Sending 'Hello world'...");
bleKeyboard.print("Hello world");
delay(1000);
Serial.println("Sending Enter key...");
bleKeyboard.write(KEY_RETURN);
delay(1000);
Serial.println("Sending Play/Pause media key...");
bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE);
delay(1000);
//
// Below is an example of pressing multiple keyboard modifiers
// which by default is commented out.
//
/* Serial.println("Sending Ctrl+Alt+Delete...");
bleKeyboard.press(KEY_LEFT_CTRL);
bleKeyboard.press(KEY_LEFT_ALT);
bleKeyboard.press(KEY_DELETE);
delay(100);
bleKeyboard.releaseAll();
*/
}
Serial.println("Waiting 5 seconds...");
delay(5000);
}
```
## API docs
The BleKeyboard interface is almost identical to the Keyboard Interface, so you can use documentation right here:
https://www.arduino.cc/reference/en/language/functions/usb/keyboard/
Just remember that you have to use `bleKeyboard` instead of just `Keyboard` and you need these two lines at the top of your script:
```
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;
```
In addition to that you can send media keys (which is not possible with the USB keyboard library). Supported are the following:
- KEY_MEDIA_NEXT_TRACK
- KEY_MEDIA_PREVIOUS_TRACK
- KEY_MEDIA_STOP
- KEY_MEDIA_PLAY_PAUSE
- KEY_MEDIA_MUTE
- KEY_MEDIA_VOLUME_UP
- KEY_MEDIA_VOLUME_DOWN
- KEY_MEDIA_WWW_HOME
- KEY_MEDIA_LOCAL_MACHINE_BROWSER // Opens "My Computer" on Windows
- KEY_MEDIA_CALCULATOR
- KEY_MEDIA_WWW_BOOKMARKS
- KEY_MEDIA_WWW_SEARCH
- KEY_MEDIA_WWW_STOP
- KEY_MEDIA_WWW_BACK
- KEY_MEDIA_CONSUMER_CONTROL_CONFIGURATION // Media Selection
- KEY_MEDIA_EMAIL_READER
There is also Bluetooth specific information that you can set (optional):
Instead of `BleKeyboard bleKeyboard;` you can do `BleKeyboard bleKeyboard("Bluetooth Device Name", "Bluetooth Device Manufacturer", 100);`. (Max lenght is 15 characters, anything beyond that will be truncated.)
The third parameter is the initial battery level of your device. To adjust the battery level later on you can simply call e.g. `bleKeyboard.setBatteryLevel(50)` (set battery level to 50%).
By default the battery level will be set to 100%, the device name will be `ESP32 Bluetooth Keyboard` and the manufacturer will be `Espressif`.
There is also a `setDelay` method to set a delay between each key event. E.g. `bleKeyboard.setDelay(10)` (10 milliseconds). The default is `8`.
This feature is meant to compensate for some applications and devices that can't handle fast input and will skip letters if too many keys are sent in a small time frame.
## NimBLE-Mode
The NimBLE mode enables a significant saving of RAM and FLASH memory.
### Comparison (SendKeyStrokes.ino at compile-time)
**Standard**
```
RAM: [= ] 9.3% (used 30548 bytes from 327680 bytes)
Flash: [======== ] 75.8% (used 994120 bytes from 1310720 bytes)
```
**NimBLE mode**
```
RAM: [= ] 8.3% (used 27180 bytes from 327680 bytes)
Flash: [==== ] 44.2% (used 579158 bytes from 1310720 bytes)
```
### Comparison (SendKeyStrokes.ino at run-time)
| | Standard | NimBLE mode | difference
|---|--:|--:|--:|
| `ESP.getHeapSize()` | 296.804 | 321.252 | **+ 24.448** |
| `ESP.getFreeHeap()` | 143.572 | 260.764 | **+ 117.192** |
| `ESP.getSketchSize()` | 994.224 | 579.264 | **- 414.960** |
## How to activate NimBLE mode?
### ArduinoIDE:
Uncomment the first line in BleKeyboard.h
```C++
#define USE_NIMBLE
```
### PlatformIO:
Change your `platformio.ini` to the following settings
```ini
lib_deps =
NimBLE-Arduino
build_flags =
-D USE_NIMBLE
```
## Credits
Credits to [chegewara](https://github.com/chegewara) and [the authors of the USB keyboard library](https://github.com/arduino-libraries/Keyboard/) as this project is heavily based on their work!
Also, credits to [duke2421](https://github.com/T-vK/ESP32-BLE-Keyboard/issues/1) who helped a lot with testing, debugging and fixing the device descriptor!
And credits to [sivar2311](https://github.com/sivar2311) for adding NimBLE support, greatly reducing the memory footprint, fixing advertising issues and for adding the `setDelay` method.

View file

@ -1,46 +0,0 @@
/**
* This example turns the ESP32 into a Bluetooth LE keyboard that writes the words, presses Enter, presses a media key and then Ctrl+Alt+Delete
*/
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE work!");
bleKeyboard.begin();
}
void loop() {
if(bleKeyboard.isConnected()) {
Serial.println("Sending 'Hello world'...");
bleKeyboard.print("Hello world");
delay(1000);
Serial.println("Sending Enter key...");
bleKeyboard.write(KEY_RETURN);
delay(1000);
Serial.println("Sending Play/Pause media key...");
bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE);
delay(1000);
//
// Below is an example of pressing multiple keyboard modifiers
// which by default is commented out.
/*
Serial.println("Sending Ctrl+Alt+Delete...");
bleKeyboard.press(KEY_LEFT_CTRL);
bleKeyboard.press(KEY_LEFT_ALT);
bleKeyboard.press(KEY_DELETE);
delay(100);
bleKeyboard.releaseAll();
*/
}
Serial.println("Waiting 5 seconds...");
delay(5000);
}

View file

@ -1,24 +0,0 @@
#######################################
# Syntax Coloring Map For ESP32 BLE Keyboard
#######################################
# Class
#######################################
BleKeyboard KEYWORD1
#######################################
# Methods and Functions
#######################################
begin KEYWORD2
end KEYWORD2
write KEYWORD2
press KEYWORD2
release KEYWORD2
releaseAll KEYWORD2
setBatteryLevel KEYWORD2
isConnected KEYWORD2
#######################################
# Constants
#######################################

View file

@ -1,9 +0,0 @@
name=ESP32 BLE Keyboard
version=0.3.2
author=T-vK
maintainer=T-vK
sentence=Bluetooth LE Keyboard library for the ESP32.
paragraph=Bluetooth LE Keyboard library for the ESP32.
category=Communication
url=https://github.com/T-vK/ESP32-BLE-Keyboard
architectures=esp32

View file

@ -1 +0,0 @@
{"type": "library", "name": "SoftwareSerial", "version": "1.0.0", "spec": {"owner": "featherfly", "id": 7212, "name": "SoftwareSerial", "requirements": null, "uri": null}}

View file

@ -1,677 +0,0 @@
/*
SoftwareSerial.cpp (formerly NewSoftSerial.cpp) -
Multi-instance software serial library for Arduino/Wiring
-- Interrupt-driven receive and other improvements by ladyada
(http://ladyada.net)
-- Tuning, circular buffer, derivation from class Print/Stream,
multi-instance support, porting to 8MHz processors,
various optimizations, PROGMEM delay tables, inverse logic and
direct port writing by Mikal Hart (http://www.arduiniana.org)
-- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com)
-- 20MHz processor support by Garrett Mace (http://www.macetech.com)
-- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.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
The latest version of this library can always be found at
http://arduiniana.org.
*/
// When set, _DEBUG co-opts pins 11 and 13 for debugging with an
// oscilloscope or logic analyzer. Beware: it also slightly modifies
// the bit times, so don't rely on it too much at high baud rates
#define _DEBUG 0
#define _DEBUG_PIN1 11
#define _DEBUG_PIN2 13
//
// Includes
//
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <Arduino.h>
#include <SoftwareSerial.h>
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1052__) || defined(__IMXRT1062__)
SoftwareSerial::SoftwareSerial(uint8_t rxPin, uint8_t txPin, bool inverse_logic /* = false */)
{
buffer_overflow = false;
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
if (rxPin == 0 && txPin == 1) {
port = &Serial1;
return;
} else if (rxPin == 6 && txPin == 7) {
port = &Serial2;
return;
} else if (rxPin == 14 && txPin == 15) {
port = &Serial3;
return;
} else if (rxPin == 16 && txPin == 17) {
port = &Serial4;
return;
} else if (rxPin == 21 && txPin == 20) {
port = &Serial5;
return;
} else if (rxPin == 25 && txPin == 24) {
port = &Serial6;
return;
} else if (rxPin == 28 && txPin == 29) {
port = &Serial7;
return;
}
#else
if (rxPin == 0 && txPin == 1) {
port = &Serial1;
return;
} else if (rxPin == 9 && txPin == 10) {
port = &Serial2;
return;
} else if (rxPin == 7 && txPin == 8) {
port = &Serial3;
return;
}
#endif
port = NULL;
pinMode(txPin, OUTPUT);
pinMode(rxPin, INPUT_PULLUP);
txpin = txPin;
rxpin = rxPin;
txreg = portOutputRegister(digitalPinToPort(txPin));
rxreg = portInputRegister(digitalPinToPort(rxPin));
cycles_per_bit = 0;
}
void SoftwareSerial::begin(unsigned long speed)
{
if (port) {
port->begin(speed);
} else {
cycles_per_bit = (uint32_t)(F_CPU + speed / 2) / speed;
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
}
}
void SoftwareSerial::end()
{
if (port) {
port->end();
port = NULL;
} else {
pinMode(txpin, INPUT);
pinMode(rxpin, INPUT);
}
cycles_per_bit = 0;
}
// The worst case expected length of any interrupt routines. If an
// interrupt runs longer than this number of cycles, it can disrupt
// the transmit waveform. Increasing this number causes SoftwareSerial
// to hog the CPU longer, delaying all interrupt response for other
// libraries, so this should be made as small as possible but still
// ensure accurate transmit waveforms.
#define WORST_INTERRUPT_CYCLES 360
static void wait_for_target(uint32_t begin, uint32_t target)
{
if (target - (ARM_DWT_CYCCNT - begin) > WORST_INTERRUPT_CYCLES+20) {
uint32_t pretarget = target - WORST_INTERRUPT_CYCLES;
//digitalWriteFast(12, HIGH);
interrupts();
while (ARM_DWT_CYCCNT - begin < pretarget) ; // wait
noInterrupts();
//digitalWriteFast(12, LOW);
}
while (ARM_DWT_CYCCNT - begin < target) ; // wait
}
size_t SoftwareSerial::write(uint8_t b)
{
elapsedMicros elapsed;
uint32_t target;
uint8_t mask;
uint32_t begin_cycle;
// use hardware serial, if possible
if (port) return port->write(b);
if (cycles_per_bit == 0) return 0;
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
// start bit
target = cycles_per_bit;
noInterrupts();
begin_cycle = ARM_DWT_CYCCNT;
*txreg = 0;
wait_for_target(begin_cycle, target);
// 8 data bits
for (mask = 1; mask; mask <<= 1) {
*txreg = (b & mask) ? 1 : 0;
target += cycles_per_bit;
wait_for_target(begin_cycle, target);
}
// stop bit
*txreg = 1;
interrupts();
target += cycles_per_bit;
while (ARM_DWT_CYCCNT - begin_cycle < target) ; // wait
return 1;
}
void SoftwareSerial::flush()
{
if (port) port->flush();
}
// TODO implement reception using pin change DMA capturing
// ARM_DWT_CYCCNT and the bitband mapped GPIO_PDIR register
// to a circular buffer (8 bytes per event... memory intensive)
int SoftwareSerial::available()
{
if (port) return port->available();
return 0;
}
int SoftwareSerial::peek()
{
if (port) return port->peek();
return -1;
}
int SoftwareSerial::read()
{
if (port) return port->read();
return -1;
}
#else
//
// Lookup table
//
typedef struct _DELAY_TABLE
{
long baud;
unsigned short rx_delay_centering;
unsigned short rx_delay_intrabit;
unsigned short rx_delay_stopbit;
unsigned short tx_delay;
} DELAY_TABLE;
#if F_CPU == 16000000
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 17, 17, 12, },
{ 57600, 10, 37, 37, 33, },
{ 38400, 25, 57, 57, 54, },
{ 31250, 31, 70, 70, 68, },
{ 28800, 34, 77, 77, 74, },
{ 19200, 54, 117, 117, 114, },
{ 14400, 74, 156, 156, 153, },
{ 9600, 114, 236, 236, 233, },
{ 4800, 233, 474, 474, 471, },
{ 2400, 471, 950, 950, 947, },
{ 1200, 947, 1902, 1902, 1899, },
{ 600, 1902, 3804, 3804, 3800, },
{ 300, 3804, 7617, 7617, 7614, },
};
const int XMIT_START_ADJUSTMENT = 5;
#elif F_CPU == 8000000
static const DELAY_TABLE table[] PROGMEM =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 5, 5, 3, },
{ 57600, 1, 15, 15, 13, },
{ 38400, 2, 25, 26, 23, },
{ 31250, 7, 32, 33, 29, },
{ 28800, 11, 35, 35, 32, },
{ 19200, 20, 55, 55, 52, },
{ 14400, 30, 75, 75, 72, },
{ 9600, 50, 114, 114, 112, },
{ 4800, 110, 233, 233, 230, },
{ 2400, 229, 472, 472, 469, },
{ 1200, 467, 948, 948, 945, },
{ 600, 948, 1895, 1895, 1890, },
{ 300, 1895, 3805, 3805, 3802, },
};
const int XMIT_START_ADJUSTMENT = 4;
#elif F_CPU == 20000000
// 20MHz support courtesy of the good people at macegr.com.
// Thanks, Garrett!
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 3, 21, 21, 18, },
{ 57600, 20, 43, 43, 41, },
{ 38400, 37, 73, 73, 70, },
{ 31250, 45, 89, 89, 88, },
{ 28800, 46, 98, 98, 95, },
{ 19200, 71, 148, 148, 145, },
{ 14400, 96, 197, 197, 194, },
{ 9600, 146, 297, 297, 294, },
{ 4800, 296, 595, 595, 592, },
{ 2400, 592, 1189, 1189, 1186, },
{ 1200, 1187, 2379, 2379, 2376, },
{ 600, 2379, 4759, 4759, 4755, },
{ 300, 4759, 9523, 9523, 9520, },
};
const int XMIT_START_ADJUSTMENT = 6;
#else
#error This version of SoftwareSerial supports only 20, 16 and 8MHz processors
#endif
//
// Statics
//
SoftwareSerial *SoftwareSerial::active_object = 0;
char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF];
volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0;
volatile uint8_t SoftwareSerial::_receive_buffer_head = 0;
//
// Debugging
//
// This function generates a brief pulse
// for debugging or measuring on an oscilloscope.
inline void DebugPulse(uint8_t pin, uint8_t count)
{
#if _DEBUG
volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin));
uint8_t val = *pport;
while (count--)
{
*pport = val | digitalPinToBitMask(pin);
*pport = val;
}
#endif
}
//
// Private methods
//
/* static */
inline void SoftwareSerial::tunedDelay(uint16_t delay) {
uint8_t tmp=0;
asm volatile("sbiw %0, 0x01 \n\t"
"ldi %1, 0xFF \n\t"
"cpi %A0, 0xFF \n\t"
"cpc %B0, %1 \n\t"
"brne .-10 \n\t"
: "+r" (delay), "+a" (tmp)
: "0" (delay)
);
}
// This function sets the current object as the "listening"
// one and returns true if it replaces another
bool SoftwareSerial::listen()
{
if (active_object != this)
{
_buffer_overflow = false;
uint8_t oldSREG = SREG;
cli();
_receive_buffer_head = _receive_buffer_tail = 0;
active_object = this;
SREG = oldSREG;
return true;
}
return false;
}
//
// The receive routine called by the interrupt handler
//
void SoftwareSerial::recv()
{
#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Preserve the registers that the compiler misses
// (courtesy of Arduino forum user *etracer*)
asm volatile(
"push r18 \n\t"
"push r19 \n\t"
"push r20 \n\t"
"push r21 \n\t"
"push r22 \n\t"
"push r23 \n\t"
"push r26 \n\t"
"push r27 \n\t"
::);
#endif
uint8_t d = 0;
// If RX line is high, then we don't see any start bit
// so interrupt is probably not for us
if (_inverse_logic ? rx_pin_read() : !rx_pin_read())
{
// Wait approximately 1/2 of a bit width to "center" the sample
tunedDelay(_rx_delay_centering);
DebugPulse(_DEBUG_PIN2, 1);
// Read each of the 8 bits
for (uint8_t i=0x1; i; i <<= 1)
{
tunedDelay(_rx_delay_intrabit);
DebugPulse(_DEBUG_PIN2, 1);
uint8_t noti = ~i;
if (rx_pin_read())
d |= i;
else // else clause added to ensure function timing is ~balanced
d &= noti;
}
// skip the stop bit
tunedDelay(_rx_delay_stopbit);
DebugPulse(_DEBUG_PIN2, 1);
if (_inverse_logic)
d = ~d;
// if buffer full, set the overflow flag and return
if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head)
{
// save new data in buffer: tail points to where byte goes
_receive_buffer[_receive_buffer_tail] = d; // save new byte
_receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
}
else
{
#if _DEBUG // for scope: pulse pin as overflow indictator
DebugPulse(_DEBUG_PIN1, 1);
#endif
_buffer_overflow = true;
}
}
#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Restore the registers that the compiler misses
asm volatile(
"pop r27 \n\t"
"pop r26 \n\t"
"pop r23 \n\t"
"pop r22 \n\t"
"pop r21 \n\t"
"pop r20 \n\t"
"pop r19 \n\t"
"pop r18 \n\t"
::);
#endif
}
void SoftwareSerial::tx_pin_write(uint8_t pin_state)
{
if (pin_state == LOW)
*_transmitPortRegister &= ~_transmitBitMask;
else
*_transmitPortRegister |= _transmitBitMask;
}
uint8_t SoftwareSerial::rx_pin_read()
{
return *_receivePortRegister & _receiveBitMask;
}
//
// Interrupt handling
//
/* static */
inline void SoftwareSerial::handle_interrupt()
{
if (active_object)
{
active_object->recv();
}
}
#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
SoftwareSerial::handle_interrupt();
}
#endif
#if defined(PCINT1_vect)
ISR(PCINT1_vect)
{
SoftwareSerial::handle_interrupt();
}
#endif
#if defined(PCINT2_vect)
ISR(PCINT2_vect)
{
SoftwareSerial::handle_interrupt();
}
#endif
#if defined(PCINT3_vect)
ISR(PCINT3_vect)
{
SoftwareSerial::handle_interrupt();
}
#endif
//
// Constructor
//
SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) :
_rx_delay_centering(0),
_rx_delay_intrabit(0),
_rx_delay_stopbit(0),
_tx_delay(0),
_buffer_overflow(false),
_inverse_logic(inverse_logic)
{
setTX(transmitPin);
setRX(receivePin);
}
//
// Destructor
//
SoftwareSerial::~SoftwareSerial()
{
end();
}
void SoftwareSerial::setTX(uint8_t tx)
{
pinMode(tx, OUTPUT);
digitalWrite(tx, HIGH);
_transmitBitMask = digitalPinToBitMask(tx);
uint8_t port = digitalPinToPort(tx);
_transmitPortRegister = portOutputRegister(port);
}
void SoftwareSerial::setRX(uint8_t rx)
{
pinMode(rx, INPUT);
if (!_inverse_logic)
digitalWrite(rx, HIGH); // pullup for normal logic!
_receivePin = rx;
_receiveBitMask = digitalPinToBitMask(rx);
uint8_t port = digitalPinToPort(rx);
_receivePortRegister = portInputRegister(port);
}
//
// Public methods
//
void SoftwareSerial::begin(long speed)
{
_rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0;
for (unsigned i=0; i<sizeof(table)/sizeof(table[0]); ++i)
{
long baud = pgm_read_dword(&table[i].baud);
if (baud == speed)
{
_rx_delay_centering = pgm_read_word(&table[i].rx_delay_centering);
_rx_delay_intrabit = pgm_read_word(&table[i].rx_delay_intrabit);
_rx_delay_stopbit = pgm_read_word(&table[i].rx_delay_stopbit);
_tx_delay = pgm_read_word(&table[i].tx_delay);
break;
}
}
// Set up RX interrupts, but only if we have a valid RX baud rate
if (_rx_delay_stopbit)
{
if (digitalPinToPCICR(_receivePin))
{
*digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin));
*digitalPinToPCMSK(_receivePin) |= _BV(digitalPinToPCMSKbit(_receivePin));
}
tunedDelay(_tx_delay); // if we were low this establishes the end
}
#if _DEBUG
pinMode(_DEBUG_PIN1, OUTPUT);
pinMode(_DEBUG_PIN2, OUTPUT);
#endif
listen();
}
void SoftwareSerial::end()
{
if (digitalPinToPCMSK(_receivePin))
*digitalPinToPCMSK(_receivePin) &= ~_BV(digitalPinToPCMSKbit(_receivePin));
}
// Read data from buffer
int SoftwareSerial::read()
{
if (!isListening())
return -1;
// Empty buffer?
if (_receive_buffer_head == _receive_buffer_tail)
return -1;
// Read from "head"
uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
_receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF;
return d;
}
int SoftwareSerial::available()
{
if (!isListening())
return 0;
return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF;
}
size_t SoftwareSerial::write(uint8_t b)
{
if (_tx_delay == 0) {
setWriteError();
return 0;
}
uint8_t oldSREG = SREG;
cli(); // turn off interrupts for a clean txmit
// Write the start bit
tx_pin_write(_inverse_logic ? HIGH : LOW);
tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
// Write each of the 8 bits
if (_inverse_logic)
{
for (byte mask = 0x01; mask; mask <<= 1)
{
if (b & mask) // choose bit
tx_pin_write(LOW); // send 1
else
tx_pin_write(HIGH); // send 0
tunedDelay(_tx_delay);
}
tx_pin_write(LOW); // restore pin to natural state
}
else
{
for (byte mask = 0x01; mask; mask <<= 1)
{
if (b & mask) // choose bit
tx_pin_write(HIGH); // send 1
else
tx_pin_write(LOW); // send 0
tunedDelay(_tx_delay);
}
tx_pin_write(HIGH); // restore pin to natural state
}
SREG = oldSREG; // turn interrupts back on
tunedDelay(_tx_delay);
return 1;
}
void SoftwareSerial::flush()
{
if (!isListening())
return;
uint8_t oldSREG = SREG;
cli();
_receive_buffer_head = _receive_buffer_tail = 0;
SREG = oldSREG;
}
int SoftwareSerial::peek()
{
if (!isListening())
return -1;
// Empty buffer?
if (_receive_buffer_head == _receive_buffer_tail)
return -1;
// Read from "head"
return _receive_buffer[_receive_buffer_head];
}
#endif

View file

@ -1,150 +0,0 @@
/*
SoftwareSerial.h (formerly NewSoftSerial.h) -
Multi-instance software serial library for Arduino/Wiring
-- Interrupt-driven receive and other improvements by ladyada
(http://ladyada.net)
-- Tuning, circular buffer, derivation from class Print/Stream,
multi-instance support, porting to 8MHz processors,
various optimizations, PROGMEM delay tables, inverse logic and
direct port writing by Mikal Hart (http://www.arduiniana.org)
-- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com)
-- 20MHz processor support by Garrett Mace (http://www.macetech.com)
-- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.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
The latest version of this library can always be found at
http://arduiniana.org.
*/
#ifndef SoftwareSerial_h
#define SoftwareSerial_h
#include <inttypes.h>
#include <Stream.h>
#include <HardwareSerial.h>
/******************************************************************************
* Definitions
******************************************************************************/
#define _SS_MAX_RX_BUFF 64 // RX buffer size
#ifndef GCC_VERSION
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#endif
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1052__) || defined(__IMXRT1062__)
class SoftwareSerial : public Stream
{
public:
SoftwareSerial(uint8_t rxPin, uint8_t txPin, bool inverse_logic = false);
~SoftwareSerial() { end(); }
void begin(unsigned long speed);
void end();
bool listen() { return true; }
bool isListening() { return true; }
bool overflow() { bool ret = buffer_overflow; buffer_overflow = false; return ret; }
virtual int available();
virtual int read();
int peek();
virtual void flush();
virtual size_t write(uint8_t byte);
using Print::write;
private:
HardwareSerial *port;
uint32_t cycles_per_bit;
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
volatile uint32_t *txreg;
volatile uint32_t *rxreg;
#else
volatile uint8_t *txreg;
volatile uint8_t *rxreg;
#endif
bool buffer_overflow;
uint8_t txpin;
uint8_t rxpin;
};
#else
class SoftwareSerial : public Stream
{
private:
// per object data
uint8_t _receivePin;
uint8_t _receiveBitMask;
volatile uint8_t *_receivePortRegister;
uint8_t _transmitBitMask;
volatile uint8_t *_transmitPortRegister;
uint16_t _rx_delay_centering;
uint16_t _rx_delay_intrabit;
uint16_t _rx_delay_stopbit;
uint16_t _tx_delay;
uint16_t _buffer_overflow:1;
uint16_t _inverse_logic:1;
// static data
static char _receive_buffer[_SS_MAX_RX_BUFF];
static volatile uint8_t _receive_buffer_tail;
static volatile uint8_t _receive_buffer_head;
static SoftwareSerial *active_object;
// private methods
void recv();
uint8_t rx_pin_read();
void tx_pin_write(uint8_t pin_state);
void setTX(uint8_t transmitPin);
void setRX(uint8_t receivePin);
// private static method for timing
static inline void tunedDelay(uint16_t delay);
public:
// public methods
SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);
~SoftwareSerial();
void begin(long speed);
bool listen();
void end();
bool isListening() { return this == active_object; }
bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; }
int peek();
virtual size_t write(uint8_t byte);
virtual int read();
virtual int available();
virtual void flush();
using Print::write;
// public only for easy access by interrupt handlers
static inline void handle_interrupt();
};
// Arduino 0012 workaround
#undef int
#undef char
#undef long
#undef byte
#undef float
#undef abs
#undef round
#endif
#endif

View file

@ -1,55 +0,0 @@
/*
Software serial multple serial test
Receives from the hardware serial, sends to software serial.
Receives from software serial, sends to hardware serial.
The circuit:
* RX is digital pin 10 (connect to TX of other device)
* TX is digital pin 11 (connect to RX of other device)
Note:
Not all pins on the Mega and Mega 2560 support change interrupts,
so only the following can be used for RX:
10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69
Not all pins on the Leonardo support change interrupts,
so only the following can be used for RX:
8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).
created back in the mists of time
modified 25 May 2012
by Tom Igoe
based on Mikal Hart's example
This example code is in the public domain.
*/
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(57600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.println("Goodnight moon!");
// set the data rate for the SoftwareSerial port
mySerial.begin(4800);
mySerial.println("Hello, world?");
}
void loop() // run over and over
{
if (mySerial.available())
Serial.write(mySerial.read());
if (Serial.available())
mySerial.write(Serial.read());
}

View file

@ -1,93 +0,0 @@
/*
Software serial multple serial test
Receives from the two software serial ports,
sends to the hardware serial port.
In order to listen on a software port, you call port.listen().
When using two software serial ports, you have to switch ports
by listen()ing on each one in turn. Pick a logical time to switch
ports, like the end of an expected transmission, or when the
buffer is empty. This example switches ports when there is nothing
more to read from a port
The circuit:
Two devices which communicate serially are needed.
* First serial device's TX attached to digital pin 2, RX to pin 3
* Second serial device's TX attached to digital pin 4, RX to pin 5
Note:
Not all pins on the Mega and Mega 2560 support change interrupts,
so only the following can be used for RX:
10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69
Not all pins on the Leonardo support change interrupts,
so only the following can be used for RX:
8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).
created 18 Apr. 2011
modified 25 May 2012
by Tom Igoe
based on Mikal Hart's twoPortRXExample
This example code is in the public domain.
*/
#include <SoftwareSerial.h>
// software serial #1: TX = digital pin 10, RX = digital pin 11
SoftwareSerial portOne(10,11);
// software serial #2: TX = digital pin 8, RX = digital pin 9
// on the Mega, use other pins instead, since 8 and 9 don't work on the Mega
SoftwareSerial portTwo(8,9);
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
// Start each software serial port
portOne.begin(9600);
portTwo.begin(9600);
}
void loop()
{
// By default, the last intialized port is listening.
// when you want to listen on a port, explicitly select it:
portOne.listen();
Serial.println("Data from port one:");
// while there is data coming in, read it
// and send to the hardware serial port:
while (portOne.available() > 0) {
char inByte = portOne.read();
Serial.write(inByte);
}
// blank line to separate data from the two ports:
Serial.println();
// Now listen on the second port
portTwo.listen();
// while there is data coming in, read it
// and send to the hardware serial port:
Serial.println("Data from port two:");
while (portTwo.available() > 0) {
char inByte = portTwo.read();
Serial.write(inByte);
}
// blank line to separate data from the two ports:
Serial.println();
}

View file

@ -1,30 +0,0 @@
#######################################
# Syntax Coloring Map for SoftwareSerial
# (formerly NewSoftSerial)
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SoftwareSerial KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
end KEYWORD2
read KEYWORD2
write KEYWORD2
available KEYWORD2
isListening KEYWORD2
overflow KEYWORD2
flush KEYWORD2
listen KEYWORD2
peek KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -1,31 +0,0 @@
{
"name": "SoftwareSerial",
"keywords": "SoftwareSerial",
"description": "The SoftwareSerial library has been developed to allow serial communication, using software to replicate the functionality of the hardware UART. It is possible to have multiple software serial ports with speeds up to 115200 bps. On 32 bit Teensy boards, SoftwareSerial uses the real hardware serial ports (and is restricted to only those pins), but allows compatibility with programs that depend on SoftwareSerial.",
"repository":
{
"type": "git",
"url": "https://github.com/featherfly/SoftwareSerial.git"
},
"authors":
[
{
"name": "Arduino",
"maintainer": true,
"url": "https://www.arduino.cc"
},
{
"name": "Paul Stoffregen",
"maintainer": true
},
{
"name": "yufei",
"email": "featherfly@foxmail.com"
}
],
"dependencies": [
],
"version": "1.0",
"frameworks": "arduino",
"platforms": "*"
}

View file

@ -1,10 +0,0 @@
name=SoftwareSerial
version=1.0
author=Arduino
maintainer=Paul Stoffregen
sentence=Enables serial communication on any digital pin.
paragraph=The SoftwareSerial library has been developed to allow serial communication, using software to replicate the functionality of the hardware UART. It is possible to have multiple software serial ports with speeds up to 115200 bps. On 32 bit Teensy boards, SoftwareSerial uses the real hardware serial ports (and is restricted to only those pins), but allows compatibility with programs that depend on SoftwareSerial.
category=Communication
url=http://www.arduino.cc/en/Reference/SoftwareSerial
architectures=*

View file

@ -1,31 +0,0 @@
{
"name": "SoftwareSerial",
"keywords": "SoftwareSerial",
"description": "The SoftwareSerial library has been developed to allow serial communication, using software to replicate the functionality of the hardware UART. It is possible to have multiple software serial ports with speeds up to 115200 bps. On 32 bit Teensy boards, SoftwareSerial uses the real hardware serial ports (and is restricted to only those pins), but allows compatibility with programs that depend on SoftwareSerial.",
"repository":
{
"type": "git",
"url": "https://github.com/featherfly/SoftwareSerial.git"
},
"authors":
[
{
"name": "Arduino",
"maintainer": true,
"url": "https://www.arduino.cc"
},
{
"name": "Paul Stoffregen",
"maintainer": true
},
{
"name": "yufei",
"email": "featherfly@foxmail.com"
}
],
"dependencies": [
],
"version": "1.0",
"frameworks": "arduino",
"platforms": "*"
}

View file

@ -1 +0,0 @@
{"type": "library", "name": "TinyPICO Helper Library", "version": "1.4.0", "spec": {"owner": "tinypico", "id": 6813, "name": "TinyPICO Helper Library", "requirements": null, "uri": null}}

View file

@ -1,97 +0,0 @@
TinyPICO Arduino Helper
=======================
This library adds some helper functions and useful pin assignments to make coding with TinyPICO & Arduino easier
We will be adding this library to the Arduino IDE library manager once we get closer to shipping the TinyPICOs.
TinyPICO Hardare Pin Assingments
--------------------------------
.. code-block:: c++
// APA102 Dotstar
#define DOTSTAR_PWR 13
#define DOTSTAR_DATA 2
#define DOTSTAR_CLK 12
// Battery
#define BAT_CHARGE 34
#define BAT_VOLTAGE 35
..
Helper functions
----------------
.. code-block:: c++
// Class constructor
TinyPICO();
// Get a *rough* estimate of the current battery voltage
// If the battery is not present, the charge IC will still report it's trying to charge at X voltage
// so it will still show a voltage.
float GetBatteryVoltage();
// Return the current charge state of the battery - we need to read the value multiple times
// to eliminate false negatives due to the charge IC not knowing the difference between no battery
// and a full battery not charging - This is why the charge LED flashes
bool IsChargingBattery();
// Power to the on-oard Dotstar is controlled by a PNP transistor, so low is ON and high is OFF
// We also need to set the Dotstar clock and data pins to be inputs to prevent power leakage when power is off
// The reason we have power control for the Dotstar is that it has a quiescent current of around 1mA, so we
// need to be able to cut power to it to minimise power consumption during deep sleep or with general battery powered use
// to minimse un-needed battery drain
void DotStar_SetPower( bool state );
// On-board Dotstar control
void DotStar_Clear();
void DotStar_SetBrightness( uint8_t );
void DotStar_SetPixelColor( uint32_t c );
void DotStar_SetPixelColor( uint8_t r, uint8_t g, uint8_t b );
void DotStar_Show( void );
void DotStar_CycleColor();
void DotStar_CycleColor( unsigned long wait );
void DotStar_CycleColor();
void DotStar_CycleColor( unsigned long wait );
// Convert R,G,B values to uint32_t
uint32_t Color( uint8_t r, uint8_t g, uint8_t b );
..
Example Usage
-------------
.. code-block:: c++
#include <TinyPICO.h>
// Interval between internal temperature reads
unsigned long next_temp_read = 0; // Next time step in milliseconds
uint8_t temp_read_interval = 1000; // This is in milliseconds
// Initialise the TinyPICO library
TinyPICO tp = TinyPICO();
void setup()
{
// Not used
}
void loop()
{
// Cycle the DotStar colour every 25 miliseconds
tp.DotStar_CycleColor(25);
// You can set the DotStar colour directly using r,g,b values
// tp.DotStar_SetPixelColor( 255, 128, 0 );
// You can set the DotStar colour directly using a uint32_t value
// tp.DotStar_SetPixelColor( 0xFFC900 );
// You can aclear the DotStar too
// tp.DotStar_Clear();
// To power down the DotStar for deep sleep you call this
// tp.DotStar_SetPower( false );
}
..

View file

@ -1,27 +0,0 @@
#include <TinyPICO.h>
// Initialise the TinyPICO library
TinyPICO tp = TinyPICO();
void setup()
{
// Not used
}
void loop()
{
// Cycle the DotStar colour every 25 milliseconds
tp.DotStar_CycleColor(25);
// You can set the DotStar colour directly using r,g,b values
// tp.DotStar_SetPixelColor( 255, 128, 0 );
// You can set the DotStar colour directly using a uint32_t value
// tp.DotStar_SetPixelColor( 0xFFC900 );
// You can clear the DotStar too
// tp.DotStar_Clear();
// To power down the DotStar for deep sleep you call this
// tp.DotStar_SetPower( false );
}

View file

@ -1,17 +0,0 @@
###################################
# Syntax Coloring Map For Laser7Segment
###################################
###################################
# Datatypes (KEYWORD1)
###################################
###################################
# Methods and Functions (KEYWORD2)
###################################
Laser7Segment KEYWORD2
###################################
# Constants (LITERAL1)
###################################

View file

@ -1,9 +0,0 @@
name=TinyPICO Helper Library
version=1.4.0
author=UnexpectedMaker
maintainer=UnexpectedMaker
sentence=A TinyPICO Helper Library
paragraph=A TinyPICO Helper Library
category=Uncategorized
url=https://github.com/inyPICO
architectures=*

View file

@ -1,254 +0,0 @@
// ---------------------------------------------------------------------------
// TinyPICO Helper Library - v1.4 - 18/10/2019
//
// Created by Seon Rozenblum - seon@unexpectedmaker.com
// Copyright 2019 License: MIT https://github.com/tinypico/tinypico-arduino/blob/master/LICENSE
//
// See "TinyPICO.h" for purpose, syntax, version history, links, and more.
//
// v1.4 - Support for esp32 calibrated battery voltage conversion ( @joey232 )
// - Removed temperature senser functions - This has been depreciated by Espressif
// - See https://github.com/espressif/esp-idf/issues/146
// v1.3 - Code cleanup for SWSPI bit-banging and fixed single set color not working the first time
// v1.2 - Fixed incorrect attenuation calc in the battery voltage method
// v1.1 - Fixed folder structure to be compliant with the Arduino Library Manager requirements
// v1.0 - Initial Release
// ---------------------------------------------------------------------------
#include "TinyPICO.h"
#include <SPI.h>
#include "driver/adc.h"
#include "esp_adc_cal.h"
// Battery divider resistor values
#define UPPER_DIVIDER 442
#define LOWER_DIVIDER 160
#define DEFAULT_VREF 1100 // Default referance voltage in mv
#define BATT_CHANNEL ADC1_CHANNEL_7 // Battery voltage ADC input
TinyPICO::TinyPICO()
{
pinMode( DOTSTAR_PWR, OUTPUT );
pinMode( BAT_CHARGE, INPUT );
pinMode( BAT_VOLTAGE, INPUT );
DotStar_SetPower( false );
nextVoltage = millis();
for (int i = 0; i < 3; i++ )
pixel[i] = 0;
isInit = false;
brightness = 128;
colorRotation = 0;
nextRotation = 0;
}
TinyPICO::~TinyPICO()
{
isInit = false;
DotStar_SetPower( false );
}
void TinyPICO::DotStar_SetBrightness(uint8_t b)
{
// Stored brightness value is different than what's passed. This
// optimizes the actual scaling math later, allowing a fast 8x8-bit
// multiply and taking the MSB. 'brightness' is a uint8_t, adding 1
// here may (intentionally) roll over...so 0 = max brightness (color
// values are interpreted literally; no scaling), 1 = min brightness
// (off), 255 = just below max brightness.
brightness = b + 1;
}
// Convert separate R,G,B to packed value
uint32_t TinyPICO::Color(uint8_t r, uint8_t g, uint8_t b)
{
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
void TinyPICO::DotStar_Show(void)
{
if ( !isInit )
{
isInit = true;
swspi_init();
delay(10);
}
uint16_t b16 = (uint16_t)brightness; // Type-convert for fixed-point math
// Start-frame marker
for( int i=0; i<4; i++) swspi_out(0x00);
// Pixel start
swspi_out(0xFF);
for( int i=0; i<3; i++)
{
if( brightness > 0)
swspi_out((pixel[i] * b16) >> 8); // Scale, write - Scaling pixel brightness on output
else
swspi_out(pixel[i]); // R,G,B @Full brightness (no scaling)
}
// // End frame marker
swspi_out(0xFF);
}
void TinyPICO::swspi_out(uint8_t n)
{
for(uint8_t i=8; i--; n <<= 1)
{
if (n & 0x80)
digitalWrite(DOTSTAR_DATA, HIGH);
else
digitalWrite(DOTSTAR_DATA, LOW);
digitalWrite(DOTSTAR_CLK, HIGH);
digitalWrite(DOTSTAR_CLK, LOW);
}
delay(1);
}
void TinyPICO::DotStar_Clear() { // Write 0s (off) to full pixel buffer
for (int i = 0; i < 3; i++ )
pixel[i] = 0;
DotStar_Show();
}
// Set pixel color, separate R,G,B values (0-255 ea.)
void TinyPICO::DotStar_SetPixelColor(uint8_t r, uint8_t g, uint8_t b)
{
pixel[0] = b;
pixel[1] = g;
pixel[2] = r;
DotStar_Show();
}
// Set pixel color, 'packed' RGB value (0x000000 - 0xFFFFFF)
void TinyPICO::DotStar_SetPixelColor(uint32_t c)
{
pixel[0] = (uint8_t)c;
pixel[1] = (uint8_t)(c >> 8);
pixel[2] = (uint8_t)(c >> 16);
DotStar_Show();
}
void TinyPICO::swspi_init(void)
{
DotStar_SetPower( true );
digitalWrite(DOTSTAR_DATA , LOW);
digitalWrite(DOTSTAR_CLK, LOW);
}
void TinyPICO::swspi_end()
{
DotStar_SetPower( false );
}
// Switch the DotStar power
void TinyPICO::DotStar_SetPower( bool state )
{
digitalWrite( DOTSTAR_PWR, !state );
pinMode( DOTSTAR_DATA, state ? OUTPUT : INPUT_PULLDOWN );
pinMode( DOTSTAR_CLK, state ? OUTPUT : INPUT_PULLDOWN );
}
void TinyPICO::DotStar_CycleColor()
{
DotStar_CycleColor(0);
}
void TinyPICO::DotStar_CycleColor( unsigned long wait = 0 )
{
if ( millis() > nextRotation + wait )
{
nextRotation = millis();
colorRotation++;
byte WheelPos = 255 - colorRotation;
if(WheelPos < 85)
{
DotStar_SetPixelColor(255 - WheelPos * 3, 0, WheelPos * 3);
}
else if(WheelPos < 170)
{
WheelPos -= 85;
DotStar_SetPixelColor(0, WheelPos * 3, 255 - WheelPos * 3);
}
else
{
WheelPos -= 170;
DotStar_SetPixelColor(WheelPos * 3, 255 - WheelPos * 3, 0);
}
DotStar_Show();
}
}
// Return the current charge state of the battery
bool TinyPICO::IsChargingBattery()
{
int measuredVal = 0;
for ( int i = 0; i < 10; i++ )
{
int v = digitalRead( BAT_CHARGE );
measuredVal += v;
}
return ( measuredVal == 0);
}
// Return a *rough* estimate of the current battery voltage
float TinyPICO::GetBatteryVoltage()
{
uint32_t raw, mv;
esp_adc_cal_characteristics_t chars;
// only check voltage every 1 second
if ( nextVoltage - millis() > 0 )
{
nextVoltage = millis() + 1000;
// grab latest voltage
analogRead(BAT_VOLTAGE); // Just to get the ADC setup
raw = adc1_get_raw(BATT_CHANNEL); // Read of raw ADC value
// Get ADC calibration values
esp_adc_cal_characterize(ADC_UNIT_1,ADC_ATTEN_11db ,ADC_WIDTH_BIT_12,DEFAULT_VREF,&chars);
// Convert to calibrated mv then volts
mv = esp_adc_cal_raw_to_voltage(raw, &chars) * (LOWER_DIVIDER+UPPER_DIVIDER) / LOWER_DIVIDER;
lastMeasuredVoltage = (float)mv / 1000.0;
}
return ( lastMeasuredVoltage );
}
// Tone - Sound wrapper
void TinyPICO::Tone( uint8_t pin, uint32_t freq )
{
if ( !isToneInit )
{
pinMode( pin, OUTPUT);
ledcSetup(0, freq, 8); // Channel 0, resolution 8
ledcAttachPin( pin , 0 );
isToneInit = true;
}
ledcWriteTone( 0, freq );
}
void TinyPICO::NoTone( uint8_t pin )
{
if ( isToneInit )
{
ledcWriteTone(0, 0);
pinMode( pin, INPUT_PULLDOWN);
isToneInit = false;
}
}

View file

@ -1,97 +0,0 @@
// ---------------------------------------------------------------------------
// TinyPICO Helper Library - v1.4 - 18/10/2019
//
// AUTHOR/LICENSE:
// Created by Seon Rozenblum - seon@unexpectedmaker.com
// Copyright 2016 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html
//
// LINKS:
// Project home: http://tinypico.com
// Blog: http://tinypico.com
//
// DISCLAIMER:
// This software is furnished "as is", without technical support, and with no
// warranty, express or implied, as to its usefulness for any purpose.
//
// PURPOSE:
// Helper Library for the TinyPICO http://tinypico.com
//
// HISTORY:
//
// v1.4 - Support for esp32 calibrated battery voltage conversion ( @joey232 )
// - Removed temperature senser functions - This has been depreciated by Espressif
// - See https://github.com/espressif/esp-idf/issues/146
// v1.3 - Code cleanup for SWSPI bit-banging and fixed single set color not working the first time
// v1.2 - Fixed incorrect attenuation calc in the battery voltage method
// v1.1 - Fixed folder structure to be compliant with the Arduino Library Manager requirements
// v1.0 - Initial Release
//
// ---------------------------------------------------------------------------
#ifndef TinyPICO_h
#define TinyPICO_h
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#include <pins_arduino.h>
#endif
#include <SPI.h>
#define DOTSTAR_PWR 13
#define DOTSTAR_DATA 2
#define DOTSTAR_CLK 12
#define BAT_CHARGE 34
#define BAT_VOLTAGE 35
class TinyPICO
{
public:
TinyPICO();
~TinyPICO();
// TinyPICO Features
void DotStar_SetPower( bool state );
float GetBatteryVoltage();
bool IsChargingBattery();
// Dotstar
void DotStar_Clear(); // Set all pixel data to zero
void DotStar_SetBrightness( uint8_t ); // Set global brightness 0-255
void DotStar_SetPixelColor( uint32_t c );
void DotStar_SetPixelColor( uint8_t r, uint8_t g, uint8_t b );
void DotStar_Show( void ); // Issue color data to strip
void DotStar_CycleColor();
void DotStar_CycleColor( unsigned long wait );
uint32_t Color( uint8_t r, uint8_t g, uint8_t b ); // R,G,B to 32-bit color
// Tone for making sound on any ESP32 - just using channel 0
void Tone( uint8_t, uint32_t );
void NoTone( uint8_t );
protected:
void swspi_init(void); // Start bitbang SPI
void swspi_out(uint8_t n); // Bitbang SPI write
void swspi_end(void); // Stop bitbang SPI
private:
unsigned long nextVoltage;
float lastMeasuredVoltage;
byte colorRotation;
unsigned long nextRotation;
uint8_t brightness; // Global brightness setting
uint8_t pixel[ 3 ]; // LED RGB values (3 bytes ea.)
bool isInit;
bool isToneInit;
};
#endif

View file

@ -1,3 +0,0 @@
featherfly/SoftwareSerial@^1.0
tinypico/TinyPICO Helper Library@^1.4.0
t-vk/ESP32 BLE Keyboard@^0.3.2

View file

@ -1,10 +1,20 @@
[platformio]
default_envs = esp32
[env]
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:az-delivery-devkit-v4]
platform = espressif32
#board = esp32cam
board = az-delivery-devkit-v4
framework = arduino
lib_deps =
featherfly/SoftwareSerial@^1.0
plerup/EspSoftwareSerial@^6.16.1
tinypico/TinyPICO Helper Library@^1.4.0
t-vk/ESP32 BLE Keyboard@^0.3.2
[env:esp32]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip
board = az-delivery-devkit-v4