Modular approach - first version for main branch (#60)

* first version of "modular-approach"

* changed keyboard commands for HOME and BACK

* Update README.md

* only some typos in comments

* readability

* comment for what the mqtt keyboard is used

* removed numbering of tab variables. not needed anymore

* changed the default keyboard from "´mqtt keyboard" to "BLE keyboard"

* updated to latest version of keypad library from Github, changes for inverted logic are explicitely marked

* added comment for key repeatModes

* added comment for MQTT keyboard

* setting timout via GUI now works, not only dropdown without functionality

* BLE indicator added; separation of BLE/WiFi activation from activation of devices using it

* report battery level to BLE device

* Dynamic keyboard commands, so you can safely deactivate BLE and/or WiFi and not break the example code

* reorganized files into folders

* moved lv_conf.h into the gui folder

* added devices for appleTV and smarthome

* assets.c broken up and placed them where they are used

* added support for IR RC5

* reorganization of files and folder

* added comment

* renamed assets files

* introduction of gui_registry

* removed unnecessary functions from sleep.h

* use gui_registry.h only once

* some files renamed for better understandability

* minor renaming

* some more renaming

* check if WiFi label was instantiated before using it

* introduction of a scene registry

* save prefs directly after timeout is changed

* renaming of preferencesStorage

* comment added

* only readability

* detailled definition of key layout for the diferrent scenes

* made code compile for "device_smarthome" when WiFi is deactivated

* fixed access violation when no scene was active

* added support for IR DENON commands

* increased lvgl heap from 32K to 48K
This commit is contained in:
Klaus Musch 2024-02-12 19:57:51 +01:00 committed by GitHub
parent d97d263620
commit 7ba79d5c92
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 4884 additions and 1156 deletions

View file

@ -1,5 +1,7 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/extensions.json
.vscode/launch.json
.vscode/ipch
src/secrets_override.h

View file

@ -2,7 +2,8 @@
"cmake.configureOnOpen": false,
"files.associations": {
"random": "cpp",
"array": "cpp"
"array": "cpp",
"lvgl.h": "c"
},
"cmake.sourceDirectory": "${workspaceFolder}/.pio/libdeps/esp32/Adafruit BusIO"
}

View file

@ -0,0 +1 @@
{"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

@ -0,0 +1,552 @@
#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
// original from BLE-Keyboard
// USAGE(2), 0x92, 0x01, // Usage (Calculator) ; bit 1: 2
// changed for usage in OMOTE
USAGE(1), 0xB3, // USAGE (Fast Forward); 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
// original from BLE-Keyboard
// USAGE(2), 0x8A, 0x01, // Usage (Mail) ; bit 7: 128
// changed for usage in OMOTE
USAGE(1), 0xB4, // USAGE (Rewind) ; 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

@ -0,0 +1,189 @@
// 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
// original from BLE-Keyboard
// const MediaKeyReport KEY_MEDIA_CALCULATOR = {0, 2};
// changed for usage in OMOTE
const MediaKeyReport KEY_MEDIA_FASTFORWARD = {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
// original from BLE-Keyboard
// const MediaKeyReport KEY_MEDIA_EMAIL_READER = {0, 128};
// changed for usage in OMOTE
const MediaKeyReport KEY_MEDIA_REWIND = {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

@ -0,0 +1,162 @@
# 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

@ -0,0 +1,46 @@
/**
* 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

@ -0,0 +1,24 @@
#######################################
# 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

@ -0,0 +1,9 @@
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

@ -15,7 +15,7 @@ byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
unsigned long loopCount = 0;
unsigned long timer_t = 0;
unsigned long timer_ms = 0;
void setup(){
Serial.begin(9600);
@ -33,12 +33,12 @@ void loop(){
// you a relative idea of just how much the debounceTime has changed the
// speed of your code. If you set a high debounceTime your loopCount will
// look good but your keypresses will start to feel sluggish.
if ((millis() - timer_t) > 1000) {
if ((millis() - timer_ms) > 1000) {
Serial.print("Your loop code ran ");
Serial.print(loopCount);
Serial.println(" times over the last second");
loopCount = 0;
timer_t = millis();
timer_ms = millis();
}
loopCount++;
if(key)

View file

@ -83,17 +83,30 @@ bool Keypad::getKeys() {
void Keypad::scanKeys() {
// Re-intialize the row pins. Allows sharing these pins with other hardware.
for (byte r=0; r<sizeKpd.rows; r++) {
// Logic needs to be inverted. This way the ESP32s EXT1 wakeup can be used to detect if the accelerometer pin or any button pin goes high
// original from Keypad
// pin_mode(rowPins[r],INPUT_PULLUP);
// changed for usage in OMOTE
pin_mode(rowPins[r],INPUT);
}
// bitMap stores ALL the keys that are being pressed.
for (byte c=0; c<sizeKpd.columns; c++) {
pin_mode(columnPins[c],OUTPUT);
// original from Keypad
// pin_write(columnPins[c], LOW); // Begin column pulse output.
// changed for usage in OMOTE
pin_write(columnPins[c], HIGH); // Begin column pulse output.
for (byte r=0; r<sizeKpd.rows; r++) {
// original from Keypad
// bitWrite(bitMap[r], c, !pin_read(rowPins[r])); // keypress is active low so invert to high.
// changed for usage in OMOTE
bitWrite(bitMap[r], c, pin_read(rowPins[r])); // keypress is active low so invert to high.
}
// Set pin to high impedance input. Effectively ends column pulse.
// original from Keypad
// pin_write(columnPins[c],HIGH);
// changed for usage in OMOTE
pin_write(columnPins[c], LOW);
pin_mode(columnPins[c],INPUT);
}

View file

@ -35,6 +35,22 @@
#include "Key.h"
// bperrybap - Thanks for a well reasoned argument and the following macro(s).
// See http://arduino.cc/forum/index.php/topic,142041.msg1069480.html#msg1069480
#ifndef INPUT_PULLUP
#warning "Using pinMode() INPUT_PULLUP AVR emulation"
#define INPUT_PULLUP 0x2
#define pinMode(_pin, _mode) _mypinMode(_pin, _mode)
#define _mypinMode(_pin, _mode) \
do { \
if(_mode == INPUT_PULLUP) \
pinMode(_pin, INPUT); \
digitalWrite(_pin, 1); \
if(_mode != INPUT_PULLUP) \
pinMode(_pin, _mode); \
}while(0)
#endif
#define OPEN LOW
#define CLOSED HIGH

View file

@ -24,9 +24,13 @@ lib_deps =
lvgl/lvgl@^8.3.4
bodmer/TFT_eSPI@^2.5.23
knolleary/PubSubClient@^2.8
h2zero/NimBLE-Arduino@^1.4.1
;t-vk/ESP32 BLE Keyboard@^0.3.2
build_flags =
-D DISABLE_ALL_LIBRARY_WARNINGS=1 ; for TFT_eSPI
-D USE_NIMBLE=1 ; for BLE Keyboard
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-I include ; Include the folder containing lv_conf.h
-D LV_CONF_PATH=../../../../src/gui_general_and_keys/lv_conf.h
; The following lines replace the TFT_eSPI User_setup.h-file
-D USER_SETUP_LOADED=1
-D ILI9341_DRIVER=1

View file

@ -0,0 +1,190 @@
#include <string>
#include <sstream>
#include "hardware/infrared_sender.h"
#include "hardware/mqtt.h"
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "device_keyboard_mqtt/device_keyboard_mqtt.h"
#include "device_keyboard_ble/device_keyboard_ble.h"
#include "commandHandler.h"
#include "scenes/sceneHandler.h"
std::map<std::string, commandData> commands;
commandData makeCommandData(commandHandlers a, std::list<std::string> b) {
commandData c = {a, b};
return c;
}
void register_specialCommands() {
// put SPECIAL commands here if you want
commands[MY_SPECIAL_COMMAND] = makeCommandData(SPECIAL, {""});
}
void executeCommandWithData(std::string command, commandData commandData, std::string additionalPayload = "") {
switch (commandData.commandHandler) {
case IR_GC: {
auto current = commandData.commandPayloads.begin();
std::string arrayStr = *current;
// first create array of needed size
std::string::difference_type size = std::count(arrayStr.begin(), arrayStr.end(), ',');
size += 1;
uint16_t *buf = new uint16_t[size];
// now get comma separated values and fill array
int pos = 0;
std::stringstream ss(arrayStr);
while(ss.good()) {
std::string dataStr;
std::getline(ss, dataStr, ',');
// https://cplusplus.com/reference/string/stoull/
std::string::size_type sz = 0; // alias of size_t
const uint64_t data = std::stoull(dataStr, &sz, 0);
// Serial.printf(" next string value %s (%" PRIu64 ")\r\n", dataStr.c_str(), data);
buf[pos] = data;
pos += 1;
}
Serial.printf("execute: will send IR GC, array size %d\r\n", size);
IrSender.sendGC(buf, size);
delete [] buf;
break;
}
case IR_NEC: {
auto current = commandData.commandPayloads.begin();
std::string dataStr = *current;
// https://cplusplus.com/reference/string/stoull/
std::string::size_type sz = 0; // alias of size_t
const uint64_t data = std::stoull(dataStr, &sz, 0);
Serial.printf("execute: will send IR NEC, data %s (%" PRIu64 ")\r\n", dataStr.c_str(), data);
IrSender.sendNEC(data);
break;
}
case IR_SAMSUNG: {
auto current = commandData.commandPayloads.begin();
std::string dataStr = *current;
// https://cplusplus.com/reference/string/stoull/
std::string::size_type sz = 0; // alias of size_t
const uint64_t data = std::stoull(dataStr, &sz, 0);
Serial.printf("execute: will send IR SAMSUNG, data %s (%" PRIu64 ")\r\n", dataStr.c_str(), data);
IrSender.sendSAMSUNG(data);
break;
}
case IR_SONY: {
std::string::size_type sz = 0; // alias of size_t
uint64_t data;
if (commandData.commandPayloads.empty() && (additionalPayload == "")) {
Serial.printf("execute: cannot send IR SONY, because both data and payload are empty\r\n");
} else {
if (additionalPayload != "") {
data = std::stoull(additionalPayload, &sz, 0);
} else {
auto current = commandData.commandPayloads.begin();
data = std::stoull(*current, &sz, 0);
}
Serial.printf("execute: will send IR SONY 15 bit, data (%" PRIu64 ")\r\n", data);
IrSender.sendSony(data, 15);
}
break;
}
case IR_RC5: {
std::string::size_type sz = 0; // alias of size_t
uint64_t data;
if (commandData.commandPayloads.empty() && (additionalPayload == "")) {
Serial.printf("execute: cannot send IR RC5, because both data and payload are empty\r\n");
} else {
if (additionalPayload != "") {
data = std::stoull(additionalPayload, &sz, 0);
} else {
auto current = commandData.commandPayloads.begin();
data = std::stoull(*current, &sz, 0);
}
Serial.printf("execute: will send IR RC5, data (%" PRIu64 ")\r\n", data);
IrSender.sendRC5(IrSender.encodeRC5X(0x00, data));
}
break;
}
case IR_DENON: {
std::string::size_type sz = 0; // alias of size_t
uint64_t data;
if (commandData.commandPayloads.empty() && (additionalPayload == "")) {
Serial.printf("execute: cannot send IR DENON 48 bit, because both data and payload are empty\r\n");
} else {
if (additionalPayload != "") {
data = std::stoull(additionalPayload, &sz, 0);
} else {
auto current = commandData.commandPayloads.begin();
data = std::stoull(*current, &sz, 0);
}
Serial.printf("execute: will send IR DENON 48 bit, data (%" PRIu64 ")\r\n", data);
IrSender.sendDenon(data, 48);
}
break;
}
#ifdef ENABLE_WIFI_AND_MQTT
case MQTT: {
auto current = commandData.commandPayloads.begin();
std::string topic = *current;
std::string payload;
if (additionalPayload == "") {
current = std::next(current, 1);
payload = *current;
} else {
payload = additionalPayload;
}
Serial.printf("execute: will send MQTT, topic '%s', payload '%s'\r\n", topic.c_str(), payload.c_str());
publishMQTTMessage(topic.c_str(), payload.c_str());
break;
}
#endif
#ifdef ENABLE_KEYBOARD_BLE
case BLE_KEYBOARD: {
auto current = commandData.commandPayloads.begin();
std::string command = *current;
std::string payload = "";
if (additionalPayload != "") {
payload = additionalPayload;
}
Serial.printf("execute: will send BLE keyboard command, command '%s', payload '%s'\r\n", command.c_str(), payload.c_str());
keyboard_ble_executeCommand(command, payload);
break;
}
#endif
case SCENE: {
// let the sceneHandler do the scene stuff
Serial.printf("execute: will send scene command to the sceneHandler\r\n");
handleScene(command, commandData, additionalPayload);
break;
}
case SPECIAL: {
if (command == MY_SPECIAL_COMMAND) {
// do your special command here
Serial.printf("execute: could execute a special command here, if you define one\r\n");
}
break;
}
}
}
void executeCommand(std::string command, std::string additionalPayload) {
try {
if (commands.count(command) > 0) {
Serial.printf("command: will execute command '%s'\r\n", command.c_str());
executeCommandWithData(command, commands.at(command), additionalPayload);
} else {
Serial.printf("command: command '%s' not found\r\n", command.c_str());
}
}
catch (const std::out_of_range& oor) {
Serial.printf("executeCommand: internal error, command not registered\r\n");
}
}

View file

@ -0,0 +1,127 @@
#ifndef __COMMANDHANDLER_H__
#define __COMMANDHANDLER_H__
#define ENABLE_WIFI_AND_MQTT // Comment out to disable WiFi
#define ENABLE_BLUETOOTH // Comment out to disable Bluetooth
#include <Arduino.h>
#include <string>
#include <list>
#include <map>
#include "device_keyboard_mqtt/device_keyboard_mqtt.h"
#include "device_keyboard_ble/device_keyboard_ble.h"
/*
Depending on which keyboard is enabled (BLE or MQTT), we define KEYBOARD_UP, KEYBOARD_DOWN and so on.
These defines are used in keys.cpp, gui*.cpp and commandHandler.cpp
Example:
If BLE is enabled, then KEYBOARD_UP will be the same as KEYBOARD_BLE_UP
If MQTT is enabled, then KEYBOARD_UP will be the same as KEYBOARD_MQTT_UP
If none of them is enabled, then KEYBOARD_UP will be the same as KEYBOARD_UP
Doing so you can switch between the keyboards without changing the UI code (keys.cpp, gui*.cpp and commandHandler.cpp)
If you need something different than this behaviour, then you can change the defines below for KEYBOARD_UP, KEYBOARD_DOWN and so on,
or you can of course change keys.cpp, gui*.cpp and commandHandler.cpp so that they directly use KEYBOARD_BLE_UP or KEYBOARD_MQTT_UP etc.
*/
#define KEYBOARD_DUMMY_UP "Keyboard_dummy_up"
#define KEYBOARD_DUMMY_DOWN "Keyboard_dummy_down"
#define KEYBOARD_DUMMY_RIGHT "Keyboard_dummy_right"
#define KEYBOARD_DUMMY_LEFT "Keyboard_dummy_left"
#define KEYBOARD_DUMMY_SELECT "Keyboard_dummy_select"
#define KEYBOARD_DUMMY_SENDSTRING "Keyboard_dummy_sendstring"
#define KEYBOARD_DUMMY_BACK "Keyboard_dummy_back"
#define KEYBOARD_DUMMY_HOME "Keyboard_dummy_home"
#define KEYBOARD_DUMMY_MENU "Keyboard_dummy_menu"
#define KEYBOARD_DUMMY_SCAN_PREVIOUS_TRACK "Keyboard_dummy_scan_previous_track"
#define KEYBOARD_DUMMY_REWIND_LONG "Keyboard_dummy_rewind_long"
#define KEYBOARD_DUMMY_REWIND "Keyboard_dummy_rewind"
#define KEYBOARD_DUMMY_PLAYPAUSE "Keyboard_dummy_playpause"
#define KEYBOARD_DUMMY_FASTFORWARD "Keyboard_dummy_fastforward"
#define KEYBOARD_DUMMY_FASTFORWARD_LONG "Keyboard_dummy_fastforward_long"
#define KEYBOARD_DUMMY_SCAN_NEXT_TRACK "Keyboard_dummy_scan_next_track"
#define KEYBOARD_DUMMY_MUTE "Keyboard_dummy_mute"
#define KEYBOARD_DUMMY_VOLUME_INCREMENT "Keyboard_dummy_volume_increment"
#define KEYBOARD_DUMMY_VOLUME_DECREMENT "Keyboard_dummy_volume_decrement"
#if defined(ENABLE_KEYBOARD_BLE)
#define KEYBOARD_PREFIX KEYBOARD_BLE_
#elif defined(ENABLE_KEYBOARD_MQTT)
#define KEYBOARD_PREFIX KEYBOARD_MQTT_
#else
// Of course keyboard commands will not work if neither BLE nor MQTT keyboard is enabled, but at least code will compile.
// But you have to change keys.cpp, gui_numpad.cpp and commandHandler.cpp where keyboard commands are used so that a command can be executed successfully.
// Search for "executeCommand(Key" to find them.
#define KEYBOARD_PREFIX KEYBOARD_DUMMY_
#endif
/*
* Concatenate preprocessor tokens A and B without expanding macro definitions
* (however, if invoked from a macro, macro arguments are expanded).
*/
#define PPCAT_NX(A, B) A ## B
/*
* Concatenate preprocessor tokens A and B after macro-expanding them.
*/
#define PPCAT(A, B) PPCAT_NX(A, B)
#define KEYBOARD_UP PPCAT(KEYBOARD_PREFIX, UP)
#define KEYBOARD_DOWN PPCAT(KEYBOARD_PREFIX, DOWN)
#define KEYBOARD_RIGHT PPCAT(KEYBOARD_PREFIX, RIGHT)
#define KEYBOARD_LEFT PPCAT(KEYBOARD_PREFIX, LEFT)
#define KEYBOARD_SELECT PPCAT(KEYBOARD_PREFIX, SELECT)
#define KEYBOARD_SENDSTRING PPCAT(KEYBOARD_PREFIX, SENDSTRING)
#define KEYBOARD_BACK PPCAT(KEYBOARD_PREFIX, BACK)
#define KEYBOARD_HOME PPCAT(KEYBOARD_PREFIX, HOME)
#define KEYBOARD_MENU PPCAT(KEYBOARD_PREFIX, MENU)
#define KEYBOARD_SCAN_PREVIOUS_TRACK PPCAT(KEYBOARD_PREFIX, SCAN_PREVIOUS_TRACK)
#define KEYBOARD_REWIND_LONG PPCAT(KEYBOARD_PREFIX, REWIND_LONG)
#define KEYBOARD_REWIND PPCAT(KEYBOARD_PREFIX, REWIND)
#define KEYBOARD_PLAYPAUSE PPCAT(KEYBOARD_PREFIX, PLAYPAUSE)
#define KEYBOARD_FASTFORWARD PPCAT(KEYBOARD_PREFIX, FASTFORWARD)
#define KEYBOARD_FASTFORWARD_LONG PPCAT(KEYBOARD_PREFIX, FASTFORWARD_LONG)
#define KEYBOARD_SCAN_NEXT_TRACK PPCAT(KEYBOARD_PREFIX, SCAN_NEXT_TRACK)
#define KEYBOARD_MUTE PPCAT(KEYBOARD_PREFIX, MUTE)
#define KEYBOARD_VOLUME_INCREMENT PPCAT(KEYBOARD_PREFIX, VOLUME_INCREMENT)
#define KEYBOARD_VOLUME_DECREMENT PPCAT(KEYBOARD_PREFIX, VOLUME_DECREMENT)
// Test
// https://stackoverflow.com/questions/5256313/c-c-macro-string-concatenation
// #define STR(x) #x
// #define XSTR(x) STR(x)
// #pragma message "1 The value is: " XSTR(KEYBOARD_BLE_UP)
// #pragma message "2 The value is: " XSTR(KEYBOARD_MQTT_UP)
// #pragma message "3 The value is: " XSTR(KEYBOARD_UP)
#define MY_SPECIAL_COMMAND "My_special_command"
enum commandHandlers {
SPECIAL,
SCENE,
IR_GC,
IR_NEC,
IR_SAMSUNG,
IR_SONY,
IR_RC5,
IR_DENON,
#ifdef ENABLE_WIFI_AND_MQTT
MQTT,
#endif
#ifdef ENABLE_KEYBOARD_BLE
BLE_KEYBOARD,
#endif
};
struct commandData {
commandHandlers commandHandler;
std::list<std::string> commandPayloads;
};
commandData makeCommandData(commandHandlers a, std::list<std::string> b);
extern std::map<std::string, commandData> commands;
void register_specialCommands();
void executeCommand(std::string command, std::string additionalPayload = "");
#endif /*__COMMANDHANDLER_H__*/

View file

@ -0,0 +1,6 @@
#include "commandHandler.h"
#include "device_appleTV/device_appleTV.h"
void register_device_appleTV() {
commands[APPLETV_GUI_EVENT_USER_DATA] = makeCommandData(IR_SONY, {}); // payload must be set when calling commandHandler
}

View file

@ -0,0 +1,8 @@
#ifndef __DEVICE_APPLETV_H__
#define __DEVICE_APPLETV_H__
#define APPLETV_GUI_EVENT_USER_DATA "AppleTV_gui_event_user_data"
void register_device_appleTV();
#endif /*__DEVICE_APPLETV_H__*/

View file

@ -0,0 +1,73 @@
#include <lvgl.h>
#include "device_appleTV/device_appleTV.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "hardware/tft.h"
#include "commandHandler.h"
// LVGL declarations
LV_IMG_DECLARE(appleTvIcon);
LV_IMG_DECLARE(appleDisplayIcon);
LV_IMG_DECLARE(appleBackIcon);
// Apple Key Event handler
static void appleKey_event_cb(lv_event_t* e) {
// Send IR command based on the event user data
executeCommand(APPLETV_GUI_EVENT_USER_DATA, std::to_string(50 + (int)e->user_data));
Serial.println(50 + (int)e->user_data);
}
void init_gui_tab_appleTV(lv_obj_t* tabview) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Apple TV");
// Add content to the Apple TV tab (3)
// Add a nice apple tv logo
lv_obj_t* appleImg = lv_img_create(tab);
lv_img_set_src(appleImg, &appleTvIcon);
lv_obj_align(appleImg, LV_ALIGN_CENTER, 0, -60);
// create two buttons and add their icons accordingly
lv_obj_t* button = lv_btn_create(tab);
lv_obj_align(button, LV_ALIGN_BOTTOM_LEFT, 10, 0);
lv_obj_set_size(button, 60, 60);
lv_obj_set_style_radius(button, 30, LV_PART_MAIN);
lv_obj_set_style_bg_color(button, color_primary, LV_PART_MAIN);
lv_obj_add_event_cb(button, appleKey_event_cb, LV_EVENT_CLICKED, (void*)1);
appleImg = lv_img_create(button);
lv_img_set_src(appleImg, &appleBackIcon);
lv_obj_set_style_img_recolor(appleImg, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_img_recolor_opa(appleImg, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_align(appleImg, LV_ALIGN_CENTER, -3, 0);
button = lv_btn_create(tab);
lv_obj_align(button, LV_ALIGN_BOTTOM_RIGHT, -10, 0);
lv_obj_set_size(button, 60, 60);
lv_obj_set_style_radius(button, 30, LV_PART_MAIN);
lv_obj_set_style_bg_color(button, color_primary, LV_PART_MAIN);
lv_obj_add_event_cb(button, appleKey_event_cb, LV_EVENT_CLICKED, (void*)2);
appleImg = lv_img_create(button);
lv_img_set_src(appleImg, &appleDisplayIcon);
lv_obj_set_style_img_recolor(appleImg, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_img_recolor_opa(appleImg, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_align(appleImg, LV_ALIGN_CENTER, 0, 0);
}
void init_gui_pageIndicator_appleTV(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Apple TV");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
void register_gui_appleTV(void){
register_gui(& init_gui_tab_appleTV, & init_gui_pageIndicator_appleTV);
}

View file

@ -0,0 +1,8 @@
#ifndef __GUI_APPLETV_H__
#define __GUI_APPLETV_H__
#include <lvgl.h>
void register_gui_appleTV(void);
#endif /*__GUI_APPLETV_H__*/

View file

@ -5,44 +5,6 @@
#define LV_ATTRIBUTE_MEM_ALIGN
#endif
#ifndef LV_ATTRIBUTE_IMG_GRADIENTLEFT
#define LV_ATTRIBUTE_IMG_GRADIENTLEFT
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_GRADIENTLEFT uint8_t gradientLeft_map[] = {
0xfa, 0xf2, 0xea, 0xe2, 0xda, 0xd1, 0xc7, 0xbe, 0xb7, 0xae, 0xa6, 0x9e, 0x95, 0x8d, 0x84, 0x7d, 0x74, 0x6c, 0x62, 0x5a, 0x51, 0x48, 0x41, 0x38, 0x2f, 0x28, 0x1f, 0x17, 0x0f, 0x07,
};
const lv_img_dsc_t gradientLeft = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 30,
.header.h = 1,
.data_size = 30,
.data = gradientLeft_map,
};
#ifndef LV_ATTRIBUTE_IMG_GRADIENTRIGHT
#define LV_ATTRIBUTE_IMG_GRADIENTRIGHT
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_GRADIENTRIGHT uint8_t gradientRight_map[] = {
0x07, 0x0f, 0x17, 0x1f, 0x28, 0x2f, 0x38, 0x41, 0x48, 0x51, 0x5a, 0x62, 0x6c, 0x74, 0x7d, 0x84, 0x8d, 0x95, 0x9e, 0xa6, 0xae, 0xb7, 0xbe, 0xc7, 0xd1, 0xda, 0xe2, 0xea, 0xf2, 0xfa,
};
const lv_img_dsc_t gradientRight = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 30,
.header.h = 1,
.data_size = 30,
.data = gradientRight_map,
};
#ifndef LV_ATTRIBUTE_IMG_APPLETVICON
#define LV_ATTRIBUTE_IMG_APPLETVICON
#endif
@ -183,111 +145,3 @@ const lv_img_dsc_t appleBackIcon = {
.data_size = 325,
.data = appleBackIcon_map,
};
#ifndef LV_ATTRIBUTE_IMG_HIGH_BRIGHTNESS
#define LV_ATTRIBUTE_IMG_HIGH_BRIGHTNESS
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_HIGH_BRIGHTNESS uint8_t high_brightness_map[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5c, 0x8e, 0x04, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x04, 0x8e, 0x5c, 0x00, 0x00,
0x00, 0x00, 0x8c, 0xff, 0xa8, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0xa8, 0xff, 0x8c, 0x00, 0x00,
0x00, 0x00, 0x04, 0xa9, 0xf5, 0x0d, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x0d, 0xf5, 0xa9, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x42, 0xd4, 0xff, 0xff, 0xd4, 0x41, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00,
0xbd, 0xcc, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x12, 0x0d, 0xbd, 0xcc, 0xbd,
0xbd, 0xcc, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x10, 0x0d, 0xbd, 0xcc, 0xbd,
0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x40, 0xd3, 0xff, 0xfe, 0xd2, 0x3f, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0xa9, 0xf5, 0x0d, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x0d, 0xf5, 0xa9, 0x04, 0x00, 0x00,
0x00, 0x00, 0x8c, 0xff, 0xa8, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0xa8, 0xff, 0x8c, 0x00, 0x00,
0x00, 0x00, 0x5c, 0x8e, 0x04, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x04, 0x8e, 0x5c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const lv_img_dsc_t high_brightness = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 18,
.header.h = 18,
.data_size = 324,
.data = high_brightness_map,
};
#ifndef LV_ATTRIBUTE_IMG_LOW_BRIGHTNESS
#define LV_ATTRIBUTE_IMG_LOW_BRIGHTNESS
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LOW_BRIGHTNESS uint8_t low_brightness_map[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x27, 0x72, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0x72, 0x28, 0x00, 0x00,
0x00, 0x00, 0x71, 0xf5, 0x0f, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x0d, 0xf5, 0x73, 0x00, 0x00,
0x00, 0x00, 0x01, 0x0b, 0x00, 0x42, 0xd4, 0xff, 0xff, 0xd4, 0x41, 0x00, 0x0a, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x42, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x00, 0x00, 0x00, 0x00,
0x43, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x12, 0x0d, 0xbc, 0x44,
0x43, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x11, 0x0d, 0xbc, 0x44,
0x00, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x41, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x0b, 0x00, 0x40, 0xd3, 0xfe, 0xff, 0xd2, 0x3f, 0x00, 0x0a, 0x01, 0x00, 0x00,
0x00, 0x00, 0x71, 0xf5, 0x0f, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x0d, 0xf5, 0x73, 0x00, 0x00,
0x00, 0x00, 0x27, 0x72, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0x72, 0x28, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const lv_img_dsc_t low_brightness = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 16,
.header.h = 16,
.data_size = 256,
.data = low_brightness_map,
};
#ifndef LV_ATTRIBUTE_IMG_LIGHTBULB
#define LV_ATTRIBUTE_IMG_LIGHTBULB
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LIGHTBULB uint8_t lightbulb_map[] = {
0x00, 0x00, 0x00, 0x00, 0x04, 0x1c, 0x1c, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x16, 0x95, 0xee, 0xff, 0xff, 0xee, 0x94, 0x15, 0x00, 0x00,
0x00, 0x27, 0xe3, 0xff, 0xcc, 0x8d, 0x8d, 0xcd, 0xff, 0xe1, 0x26, 0x00,
0x07, 0xd9, 0xfa, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x5f, 0xfa, 0xd7, 0x06,
0x65, 0xff, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0xff, 0x63,
0xb1, 0xf8, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xf8, 0xaf,
0xcc, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xcd,
0xb1, 0xf5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf1, 0xbd,
0x73, 0xff, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xff, 0x74,
0x0b, 0xd5, 0xfb, 0x40, 0x00, 0x00, 0x00, 0x00, 0x41, 0xfb, 0xd9, 0x0b,
0x00, 0x24, 0xef, 0xdc, 0x01, 0x00, 0x00, 0x01, 0xdd, 0xee, 0x24, 0x00,
0x00, 0x00, 0x83, 0xff, 0x30, 0x00, 0x00, 0x30, 0xff, 0x81, 0x00, 0x00,
0x00, 0x00, 0x12, 0x6c, 0x06, 0x00, 0x00, 0x06, 0x6c, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x25, 0xc7, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, 0x25, 0x00, 0x00,
0x00, 0x00, 0x25, 0xc7, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, 0x25, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1c, 0x76, 0x77, 0x77, 0x76, 0x1c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x69, 0xff, 0xff, 0xff, 0xff, 0x69, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x21, 0x22, 0x22, 0x21, 0x01, 0x00, 0x00, 0x00,
};
const lv_img_dsc_t lightbulb = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 12,
.header.h = 20,
.data_size = 240,
.data = lightbulb_map,
};

View file

@ -0,0 +1,230 @@
#include <BleKeyboard.h>
#include "commandHandler.h"
#include "device_keyboard_ble/device_keyboard_ble.h"
#include "gui_general_and_keys/guiBase.h"
#include "hardware/battery.h"
#ifdef ENABLE_KEYBOARD_BLE
BleKeyboard bleKeyboard("OMOTE Keyboard", "CoretechR");
void register_device_keyboard_ble() {
commands[KEYBOARD_BLE_UP] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_UP});
commands[KEYBOARD_BLE_DOWN] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_DOWN});
commands[KEYBOARD_BLE_RIGHT] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_RIGHT});
commands[KEYBOARD_BLE_LEFT] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_LEFT});
commands[KEYBOARD_BLE_SELECT] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_SELECT});
commands[KEYBOARD_BLE_SENDSTRING] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_SENDSTRING}); // payload must be set when calling commandHandler
commands[KEYBOARD_BLE_BACK] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_BACK});
commands[KEYBOARD_BLE_HOME] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_HOME});
commands[KEYBOARD_BLE_MENU] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_MENU});
commands[KEYBOARD_BLE_SCAN_PREVIOUS_TRACK] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_SCAN_PREVIOUS_TRACK});
commands[KEYBOARD_BLE_REWIND_LONG] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_REWIND_LONG});
commands[KEYBOARD_BLE_REWIND] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_REWIND});
commands[KEYBOARD_BLE_PLAYPAUSE] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_PLAYPAUSE});
commands[KEYBOARD_BLE_FASTFORWARD] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_FASTFORWARD});
commands[KEYBOARD_BLE_FASTFORWARD_LONG] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_FASTFORWARD_LONG});
commands[KEYBOARD_BLE_SCAN_NEXT_TRACK] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_SCAN_NEXT_TRACK});
commands[KEYBOARD_BLE_MUTE] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_MUTE});
commands[KEYBOARD_BLE_VOLUME_INCREMENT] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_VOLUME_INCREMENT});
commands[KEYBOARD_BLE_VOLUME_DECREMENT] = makeCommandData(BLE_KEYBOARD, {KEYBOARD_BLE_VOLUME_DECREMENT});
bleKeyboard.begin();
}
// if bluetooth is in pairing mode (pairing mode is always on, if not connected), but not connected, then blink
unsigned long blinkBluetoothLabelLastChange = millis();
bool blinkBluetoothLabelIsOn = false;
void update_keyboard_ble_status() {
if (bleKeyboard.isConnected()) {
lv_label_set_text(BluetoothLabel, LV_SYMBOL_BLUETOOTH);
bleKeyboard.setBatteryLevel(battery_percentage);
} else {
if(millis() - blinkBluetoothLabelLastChange >= 1000){
blinkBluetoothLabelIsOn = !blinkBluetoothLabelIsOn;
if (blinkBluetoothLabelIsOn) {
lv_label_set_text(BluetoothLabel, LV_SYMBOL_BLUETOOTH);
} else {
lv_label_set_text(BluetoothLabel, "");
}
blinkBluetoothLabelLastChange = millis();
}
}
}
void keyboard_ble_write(uint8_t c) {
bleKeyboard.write(c);
}
void keyboard_ble_press(uint8_t c) {
bleKeyboard.press(c);
bleKeyboard.release(c);
}
void keyboard_ble_longpress(uint8_t c) {
bleKeyboard.press(c);
delay(1000);
bleKeyboard.release(c);
}
void keyboard_ble_home() {
bleKeyboard.press(KEY_LEFT_ALT);
bleKeyboard.press(KEY_ESC);
bleKeyboard.releaseAll();
}
void keyboard_ble_sendString(const String &s) {
bleKeyboard.print(s);
}
void consumerControl_ble_write(const MediaKeyReport value) {
bleKeyboard.write(value);
}
void consumerControl_ble_press(const MediaKeyReport value) {
bleKeyboard.press(value);
bleKeyboard.release(value);
}
void consumerControl_ble_longpress(const MediaKeyReport value) {
bleKeyboard.press(value);
delay(1000);
bleKeyboard.release(value);
}
void keyboard_ble_executeCommand(std::string command, std::string additionalPayload) {
bool doLog = false;
if (doLog) {
if (bleKeyboard.isConnected()) {
Serial.println("BLE keyboard connected, could send key");
} else {
Serial.println("BLE keyboard NOT connected, cannot send key");
}
}
if (command == KEYBOARD_BLE_UP) {
if (doLog) {Serial.printf("UP received\r\n");}
keyboard_ble_write(KEY_UP_ARROW);
} else if (command == KEYBOARD_BLE_DOWN) {
if (doLog) {Serial.printf("DOWN received\r\n");}
keyboard_ble_write(KEY_DOWN_ARROW);
} else if (command == KEYBOARD_BLE_RIGHT) {
if (doLog) {Serial.printf("RIGHT received\r\n");}
keyboard_ble_write(KEY_RIGHT_ARROW);
} else if (command == KEYBOARD_BLE_LEFT) {
if (doLog) {Serial.printf("LEFT received\r\n");}
keyboard_ble_write(KEY_LEFT_ARROW);
} else if (command == KEYBOARD_BLE_SELECT) {
if (doLog) {Serial.printf("SELECT received\r\n");}
keyboard_ble_write(KEY_RETURN);
} else if (command == KEYBOARD_BLE_SENDSTRING) {
if (doLog) {Serial.printf("SENDSTRING received\r\n");}
if (additionalPayload != "") {
keyboard_ble_sendString(additionalPayload.c_str());
}
} else if (command == KEYBOARD_BLE_BACK) {
if (doLog) {Serial.printf("BACK received\r\n");}
// test which one works best for your device
// keyboard_ble_write(KEY_ESC);
consumerControl_ble_write(KEY_MEDIA_WWW_BACK);
} else if (command == KEYBOARD_BLE_HOME) {
if (doLog) {Serial.printf("HOME received\r\n");}
// test which one works best for your device
// keyboard_ble_home();
consumerControl_ble_write(KEY_MEDIA_WWW_HOME);
} else if (command == KEYBOARD_BLE_MENU) {
if (doLog) {Serial.printf("MENU received\r\n");}
keyboard_ble_write(0xED); // 0xDA + 13 = 0xED
// for more consumerControl codes see
// https://github.com/espressif/arduino-esp32/blob/master/libraries/USB/src/USBHIDConsumerControl.h
// https://github.com/adafruit/Adafruit_CircuitPython_HID/blob/main/adafruit_hid/consumer_control_code.py
} else if (command == KEYBOARD_BLE_SCAN_PREVIOUS_TRACK) {
if (doLog) {Serial.printf("SCAN_PREVIOUS_TRACK received\r\n");}
consumerControl_ble_write(KEY_MEDIA_PREVIOUS_TRACK);
} else if (command == KEYBOARD_BLE_REWIND_LONG) {
if (doLog) {Serial.printf("REWIND_LONG received\r\n");}
//keyboard_ble_longpress(KEY_LEFT_ARROW);
consumerControl_ble_longpress(KEY_MEDIA_REWIND);
} else if (command == KEYBOARD_BLE_REWIND) {
if (doLog) {Serial.printf("REWIND received\r\n");}
//keyboard_ble_write(KEY_LEFT_ARROW);
consumerControl_ble_write(KEY_MEDIA_REWIND);
} else if (command == KEYBOARD_BLE_PLAYPAUSE) {
if (doLog) {Serial.printf("PLAYPAUSE received\r\n");}
consumerControl_ble_write(KEY_MEDIA_PLAY_PAUSE);
} else if (command == KEYBOARD_BLE_FASTFORWARD) {
if (doLog) {Serial.printf("FASTFORWARD received\r\n");}
//keyboard_ble_write(KEY_RIGHT_ARROW);
consumerControl_ble_write(KEY_MEDIA_FASTFORWARD);
} else if (command == KEYBOARD_BLE_FASTFORWARD_LONG) {
if (doLog) {Serial.printf("FASTFORWARD_LONG received\r\n");}
//keyboard_ble_longpress(KEY_RIGHT_ARROW);
consumerControl_ble_longpress(KEY_MEDIA_FASTFORWARD);
} else if (command == KEYBOARD_BLE_SCAN_NEXT_TRACK) {
if (doLog) {Serial.printf("SCAN_NEXT_TRACK received\r\n");}
consumerControl_ble_write(KEY_MEDIA_NEXT_TRACK);
} else if (command == KEYBOARD_BLE_MUTE) {
if (doLog) {Serial.printf("MUTE received\r\n");}
consumerControl_ble_write(KEY_MEDIA_MUTE);
} else if (command == KEYBOARD_BLE_VOLUME_INCREMENT) {
if (doLog) {Serial.printf("VOLUME_INCREMENT received\r\n");}
consumerControl_ble_write(KEY_MEDIA_VOLUME_UP);
} else if (command == KEYBOARD_BLE_VOLUME_DECREMENT) {
if (doLog) {Serial.printf("VOLUME_DECREMENT received\r\n");}
consumerControl_ble_write(KEY_MEDIA_VOLUME_DOWN);
}
}
#endif
/*
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
// original from BLE-Keyboard
// const MediaKeyReport KEY_MEDIA_CALCULATOR = {0, 2};
// changed for usage in OMOTE
const MediaKeyReport KEY_MEDIA_FASTFORWARD = {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
// original from BLE-Keyboard
// const MediaKeyReport KEY_MEDIA_EMAIL_READER = {0, 128};
// changed for usage in OMOTE
const MediaKeyReport KEY_MEDIA_REWIND = {0, 128};
*/

View file

@ -0,0 +1,47 @@
#ifndef __DEVICE_KEYBOARD_BLE_H__
#define __DEVICE_KEYBOARD_BLE_H__
// Advertising is started automatically.
// As soon as a device is connected, a small indicator in the top left corner of the screen will appear
#define ENABLE_KEYBOARD_BLE // Comment out to disable BLE
#ifdef ENABLE_KEYBOARD_BLE
#include "commandHandler.h"
#if defined(ENABLE_KEYBOARD_BLE) && !defined(ENABLE_BLUETOOTH)
static_assert(false, "You have to use \"#define ENABLE_BLUETOOTH\" in \"commandHandler.h\" when having \"#define ENABLE_KEYBOARD_BLE\"");
#endif
#include <BleKeyboard.h>
#define KEYBOARD_BLE_UP "Keyboard_ble_up"
#define KEYBOARD_BLE_DOWN "Keyboard_ble_down"
#define KEYBOARD_BLE_RIGHT "Keyboard_ble_right"
#define KEYBOARD_BLE_LEFT "Keyboard_ble_left"
#define KEYBOARD_BLE_SELECT "Keyboard_ble_select"
#define KEYBOARD_BLE_SENDSTRING "Keyboard_ble_sendstring"
#define KEYBOARD_BLE_BACK "Keyboard_ble_back"
#define KEYBOARD_BLE_HOME "Keyboard_ble_home"
#define KEYBOARD_BLE_MENU "Keyboard_ble_menu"
#define KEYBOARD_BLE_SCAN_PREVIOUS_TRACK "Keyboard_ble_scan_previous_track"
#define KEYBOARD_BLE_REWIND_LONG "Keyboard_ble_rewind_long"
#define KEYBOARD_BLE_REWIND "Keyboard_ble_rewind"
#define KEYBOARD_BLE_PLAYPAUSE "Keyboard_ble_playpause"
#define KEYBOARD_BLE_FASTFORWARD "Keyboard_ble_fastforward"
#define KEYBOARD_BLE_FASTFORWARD_LONG "Keyboard_ble_fastforward_long"
#define KEYBOARD_BLE_SCAN_NEXT_TRACK "Keyboard_ble_scan_next_track"
#define KEYBOARD_BLE_MUTE "Keyboard_ble_mute"
#define KEYBOARD_BLE_VOLUME_INCREMENT "Keyboard_ble_volume_increment"
#define KEYBOARD_BLE_VOLUME_DECREMENT "Keyboard_ble_volume_decrement"
extern BleKeyboard bleKeyboard;
void register_device_keyboard_ble();
void keyboard_ble_executeCommand(std::string command, std::string additionalPayload = "");
void update_keyboard_ble_status();
#endif
#endif /*__DEVICE_KEYBOARD_BLE_H__*/

View file

@ -0,0 +1,28 @@
#include "commandHandler.h"
#include "device_keyboard_mqtt/device_keyboard_mqtt.h"
#ifdef ENABLE_KEYBOARD_MQTT
void register_device_keyboard_mqtt() {
commands[KEYBOARD_MQTT_UP] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/UP", "PRESS"});
commands[KEYBOARD_MQTT_DOWN] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/DOWN", "PRESS"});
commands[KEYBOARD_MQTT_RIGHT] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/RIGHT", "PRESS"});
commands[KEYBOARD_MQTT_LEFT] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/LEFT", "PRESS"});
commands[KEYBOARD_MQTT_SELECT] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/SELECT", "PRESS"});
commands[KEYBOARD_MQTT_SENDSTRING] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/SENDSTRING" }); // payload must be set when calling commandHandler
commands[KEYBOARD_MQTT_BACK] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/BACK", "PRESS"});
commands[KEYBOARD_MQTT_HOME] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/HOME", "PRESS"});
commands[KEYBOARD_MQTT_MENU] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/MENU", "PRESS"});
commands[KEYBOARD_MQTT_SCAN_PREVIOUS_TRACK] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/SCAN_PREVIOUS_TRACK", "PRESS"});
commands[KEYBOARD_MQTT_REWIND_LONG] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/REWIND_LONG", "PRESS"});
commands[KEYBOARD_MQTT_REWIND] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/REWIND", "PRESS"});
commands[KEYBOARD_MQTT_PLAYPAUSE] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/PLAYPAUSE", "PRESS"});
commands[KEYBOARD_MQTT_FASTFORWARD] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/FASTFORWARD", "PRESS"});
commands[KEYBOARD_MQTT_FASTFORWARD_LONG] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/FASTFORWARD_LONG", "PRESS"});
commands[KEYBOARD_MQTT_SCAN_NEXT_TRACK] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/SCAN_NEXT_TRACK", "PRESS"});
commands[KEYBOARD_MQTT_MUTE] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/MUTE", "PRESS"});
commands[KEYBOARD_MQTT_VOLUME_INCREMENT] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/VOLUME_INCREMENT", "PRESS"});
commands[KEYBOARD_MQTT_VOLUME_DECREMENT] = makeCommandData(MQTT, {"esp32_keyboard_firetv/cmnd/VOLUME_DECREMENT", "PRESS"});
}
#endif

View file

@ -0,0 +1,43 @@
#ifndef __DEVICE_KEYBOARD_MQTT_H__
#define __DEVICE_KEYBOARD_MQTT_H__
// The "MQTT keyboard" simply sends MQTT commands to a remote keyboard, which is connected via USB to a device
// https://github.com/KlausMu/esp32-mqtt-keyboard
#define ENABLE_KEYBOARD_MQTT // Comment out to disable WiFi and MQTT
#ifdef ENABLE_KEYBOARD_MQTT
// if you activate the MQTT keyboard, consider changing the mapping of the keyboard commands to the MQTT keyboard in file "commandHandler.h"
#include "commandHandler.h"
#if defined(ENABLE_KEYBOARD_MQTT) && !defined(ENABLE_WIFI_AND_MQTT)
static_assert(false, "You have to use \"#define ENABLE_WIFI_AND_MQTT\" in \"commandHandler.h\" when having \"#define ENABLE_KEYBOARD_MQTT\"");
#endif
#define KEYBOARD_MQTT_UP "Keyboard_mqtt_up"
#define KEYBOARD_MQTT_DOWN "Keyboard_mqtt_down"
#define KEYBOARD_MQTT_RIGHT "Keyboard_mqtt_right"
#define KEYBOARD_MQTT_LEFT "Keyboard_mqtt_left"
#define KEYBOARD_MQTT_SELECT "Keyboard_mqtt_select"
#define KEYBOARD_MQTT_SENDSTRING "Keyboard_mqtt_sendstring"
#define KEYBOARD_MQTT_BACK "Keyboard_mqtt_back"
#define KEYBOARD_MQTT_HOME "Keyboard_mqtt_home"
#define KEYBOARD_MQTT_MENU "Keyboard_mqtt_menu"
#define KEYBOARD_MQTT_SCAN_PREVIOUS_TRACK "Keyboard_mqtt_scan_previous_track"
#define KEYBOARD_MQTT_REWIND_LONG "Keyboard_mqtt_rewind_long"
#define KEYBOARD_MQTT_REWIND "Keyboard_mqtt_rewind"
#define KEYBOARD_MQTT_PLAYPAUSE "Keyboard_mqtt_playpause"
#define KEYBOARD_MQTT_FASTFORWARD "Keyboard_mqtt_fastforward"
#define KEYBOARD_MQTT_FASTFORWARD_LONG "Keyboard_mqtt_fastforward_long"
#define KEYBOARD_MQTT_SCAN_NEXT_TRACK "Keyboard_mqtt_scan_next_track"
#define KEYBOARD_MQTT_MUTE "Keyboard_mqtt_mute"
#define KEYBOARD_MQTT_VOLUME_INCREMENT "Keyboard_mqtt_volume_increment"
#define KEYBOARD_MQTT_VOLUME_DECREMENT "Keyboard_mqtt_volume_decrement"
void register_device_keyboard_mqtt();
#endif
#endif /*__DEVICE_KEYBOARD_MQTT_H__*/

View file

@ -0,0 +1,76 @@
#include "commandHandler.h"
#include "device_samsungTV/device_samsungTV.h"
void register_device_samsung() {
// both GC and SAMSUNG work well
// https://github.com/natcl/studioimaginaire/blob/master/arduino_remote/ircodes.py
commands[SAMSUNG_POWER_TOGGLE] = makeCommandData(IR_SAMSUNG, {"0xE0E040BF"});
commands[SAMSUNG_SOURCE] = makeCommandData(IR_SAMSUNG, {"0xE0E0807F"});
commands[SAMSUNG_HDMI] = makeCommandData(IR_SAMSUNG, {"0xE0E0D12E"});
commands[SAMSUNG_NUM_1] = makeCommandData(IR_SAMSUNG, {"0xE0E020DF"});
commands[SAMSUNG_NUM_2] = makeCommandData(IR_SAMSUNG, {"0xE0E0A05F"});
commands[SAMSUNG_NUM_3] = makeCommandData(IR_SAMSUNG, {"0xE0E0609F"});
commands[SAMSUNG_NUM_4] = makeCommandData(IR_SAMSUNG, {"0xE0E010EF"});
commands[SAMSUNG_NUM_5] = makeCommandData(IR_SAMSUNG, {"0xE0E0906F"});
commands[SAMSUNG_NUM_6] = makeCommandData(IR_SAMSUNG, {"0xE0E050AF"});
commands[SAMSUNG_NUM_7] = makeCommandData(IR_SAMSUNG, {"0xE0E030CF"});
commands[SAMSUNG_NUM_8] = makeCommandData(IR_SAMSUNG, {"0xE0E0B04F"});
commands[SAMSUNG_NUM_9] = makeCommandData(IR_SAMSUNG, {"0xE0E0708F"});
commands[SAMSUNG_NUM_0] = makeCommandData(IR_SAMSUNG, {"0xE0E08877"});
commands[SAMSUNG_TTXMIX] = makeCommandData(IR_SAMSUNG, {"0xE0E034CB"});
commands[SAMSUNG_PRECH] = makeCommandData(IR_SAMSUNG, {"0xE0E0C837"});
commands[SAMSUNG_VOL_MINUS] = makeCommandData(IR_SAMSUNG, {"0xE0E0D02F"});
commands[SAMSUNG_VOL_PLUS] = makeCommandData(IR_SAMSUNG, {"0xE0E0E01F"});
commands[SAMSUNG_MUTE_TOGGLE] = makeCommandData(IR_SAMSUNG, {"0xE0E0F00F"});
commands[SAMSUNG_CHLIST] = makeCommandData(IR_SAMSUNG, {"0xE0E0D629"});
commands[SAMSUNG_CHANNEL_UP] = makeCommandData(IR_SAMSUNG, {"0xE0E048B7"});
commands[SAMSUNG_CHANNEL_DOWN] = makeCommandData(IR_SAMSUNG, {"0xE0E008F7"});
commands[SAMSUNG_MENU] = makeCommandData(IR_SAMSUNG, {"0xE0E058A7"});
commands[SAMSUNG_APPS] = makeCommandData(IR_SAMSUNG, {"0xE0E09E61"});
commands[SAMSUNG_GUIDE] = makeCommandData(IR_SAMSUNG, {"0xE0E0F20D"});
commands[SAMSUNG_TOOLS] = makeCommandData(IR_SAMSUNG, {"0xE0E0D22D"});
commands[SAMSUNG_INFO] = makeCommandData(IR_SAMSUNG, {"0xE0E0F807"});
commands[SAMSUNG_UP] = makeCommandData(IR_SAMSUNG, {"0xE0E006F9"});
commands[SAMSUNG_DOWN] = makeCommandData(IR_SAMSUNG, {"0xE0E08679"});
commands[SAMSUNG_LEFT] = makeCommandData(IR_SAMSUNG, {"0xE0E0A659"});
commands[SAMSUNG_RIGHT] = makeCommandData(IR_SAMSUNG, {"0xE0E046B9"});
commands[SAMSUNG_SELECT] = makeCommandData(IR_SAMSUNG, {"0xE0E016E9"});
commands[SAMSUNG_RETURN] = makeCommandData(IR_SAMSUNG, {"0xE0E01AE5"});
commands[SAMSUNG_EXIT] = makeCommandData(IR_SAMSUNG, {"0xE0E0B44B"});
commands[SAMSUNG_KEY_A] = makeCommandData(IR_SAMSUNG, {"0xE0E036C9"});
commands[SAMSUNG_KEY_B] = makeCommandData(IR_SAMSUNG, {"0xE0E028D7"});
commands[SAMSUNG_KEY_C] = makeCommandData(IR_SAMSUNG, {"0xE0E0A857"});
commands[SAMSUNG_KEY_D] = makeCommandData(IR_SAMSUNG, {"0xE0E06897"});
commands[SAMSUNG_FAMILYSTORY] = makeCommandData(IR_SAMSUNG, {"0xE0E0639C"});
commands[SAMSUNG_SEARCH] = makeCommandData(IR_SAMSUNG, {"0xE0E0CE31"});
commands[SAMSUNG_DUALI_II] = makeCommandData(IR_SAMSUNG, {"0xE0E000FF"});
commands[SAMSUNG_SUPPORT] = makeCommandData(IR_SAMSUNG, {"0xE0E0FC03"});
commands[SAMSUNG_PSIZE] = makeCommandData(IR_SAMSUNG, {"0xE0E07C83"});
commands[SAMSUNG_ADSUBT] = makeCommandData(IR_SAMSUNG, {"0xE0E0A45B"});
commands[SAMSUNG_REWIND] = makeCommandData(IR_SAMSUNG, {"0xE0E0A25D"});
commands[SAMSUNG_PAUSE] = makeCommandData(IR_SAMSUNG, {"0xE0E052AD"});
commands[SAMSUNG_FASTFORWARD] = makeCommandData(IR_SAMSUNG, {"0xE0E012ED"});
commands[SAMSUNG_RECORD] = makeCommandData(IR_SAMSUNG, {"0xE0E0926D"});
commands[SAMSUNG_PLAY] = makeCommandData(IR_SAMSUNG, {"0xE0E0E21D"});
commands[SAMSUNG_STOP] = makeCommandData(IR_SAMSUNG, {"0xE0E0629D"});
commands[SAMSUNG_POWER_OFF] = makeCommandData(IR_SAMSUNG, {"0xE0E019E6"});
commands[SAMSUNG_POWER_ON] = makeCommandData(IR_SAMSUNG, {"0xE0E09966"});
commands[SAMSUNG_INPUT_HDMI_1] = makeCommandData(IR_SAMSUNG, {"0xE0E09768"});
commands[SAMSUNG_INPUT_HDMI_2] = makeCommandData(IR_SAMSUNG, {"0xE0E07D82"});
commands[SAMSUNG_INPUT_HDMI_3] = makeCommandData(IR_SAMSUNG, {"0xE0E043BC"});
commands[SAMSUNG_INPUT_HDMI_4] = makeCommandData(IR_SAMSUNG, {"0xE0E0A35C"});
commands[SAMSUNG_INPUT_COMPONENT] = makeCommandData(IR_SAMSUNG, {"0xE0E0619E"});
commands[SAMSUNG_INPUT_TV] = makeCommandData(IR_SAMSUNG, {"0xE0E0D827"});
// unknown commands. Not on my remote
// commands[-] = makeCommandData(IR_SAMSUNG, {"0xE0E0C43B"});
// commands[favorite_channel] = makeCommandData(IR_SAMSUNG, {"0xE0E022DD"});
// GC also works well
//commands[SAMSUNG_POWER_TOGGLE] = makeCommandData(IR_GC, {"38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20,63,20,63,20,63,20,63,20,1798"});
//commands[SAMSUNG_POWER_OFF] = makeCommandData(IR_GC, {"38000,1,1,173,173,21,65,21,65,21,65,21,21,21,21,21,21,21,21,21,21,21,65,21,65,21,65,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,65,21,65,21,21,21,21,21,65,21,65,21,65,21,65,21,21,21,21,21,65,21,65,21,21,21,1832"});
//commands[SAMSUNG_POWER_ON] = makeCommandData(IR_GC, {"38000,1,1,172,172,22,64,22,64,22,64,22,21,22,21,22,21,22,21,22,21,22,64,22,64,22,64,22,21,22,21,22,21,22,21,22,21,22,64,22,21,22,21,22,64,22,64,22,21,22,21,22,64,22,21,22,64,22,64,22,21,22,21,22,64,22,64,22,21,22,1820"});
//commands[SAMSUNG_INPUT_HDMI_1] = makeCommandData(IR_GC, {"38000,1,1,173,173,21,65,21,65,21,65,21,21,21,21,21,21,21,21,21,21,21,65,21,65,21,65,21,21,21,21,21,21,21,21,21,21,21,65,21,21,21,21,21,65,21,21,21,65,21,65,21,65,21,21,21,65,21,65,21,21,21,65,21,21,21,21,21,21,21,1832"});
//commands[SAMSUNG_INPUT_HDMI_2] = makeCommandData(IR_GC, {"38000,1,1,173,173,21,65,21,65,21,65,21,21,21,21,21,21,21,21,21,21,21,65,21,65,21,65,21,21,21,21,21,21,21,21,21,21,21,21,21,65,21,65,21,65,21,65,21,65,21,21,21,65,21,65,21,21,21,21,21,21,21,21,21,21,21,65,21,21,21,1832"});
//commands[SAMSUNG_TV] = makeCommandData(IR_GC, {"38000,1,1,172,172,21,64,21,64,21,64,21,21,21,21,21,21,21,21,21,21,21,64,21,64,21,64,21,21,21,21,21,21,21,21,21,21,21,64,21,64,21,21,21,64,21,64,21,21,21,21,21,21,21,21,21,21,21,64,21,21,21,21,21,64,21,64,21,64,21,1673"});
}

View file

@ -0,0 +1,64 @@
#ifndef __DEVICE_SAMSUNG_H__
#define __DEVICE_SAMSUNG_H__
#define SAMSUNG_POWER_TOGGLE "Samsung_power_toggle"
#define SAMSUNG_SOURCE "Samsung_source"
#define SAMSUNG_HDMI "Samsung_hdmi"
#define SAMSUNG_NUM_1 "Samsung_num_1"
#define SAMSUNG_NUM_2 "Samsung_num_2"
#define SAMSUNG_NUM_3 "Samsung_num_3"
#define SAMSUNG_NUM_4 "Samsung_num_4"
#define SAMSUNG_NUM_5 "Samsung_num_5"
#define SAMSUNG_NUM_6 "Samsung_num_6"
#define SAMSUNG_NUM_7 "Samsung_num_7"
#define SAMSUNG_NUM_8 "Samsung_num_8"
#define SAMSUNG_NUM_9 "Samsung_num_9"
#define SAMSUNG_NUM_0 "Samsung_num_0"
#define SAMSUNG_TTXMIX "Samsung_ttxmix"
#define SAMSUNG_PRECH "Samsung_prech"
#define SAMSUNG_VOL_MINUS "Samsung_vol_minus"
#define SAMSUNG_VOL_PLUS "Samsung_vol_plus"
#define SAMSUNG_MUTE_TOGGLE "Samsung_mute_toggle"
#define SAMSUNG_CHLIST "Samsung_chlist"
#define SAMSUNG_CHANNEL_UP "Samsung_channel_up"
#define SAMSUNG_CHANNEL_DOWN "Samsung_channel_down"
#define SAMSUNG_MENU "Samsung_menu"
#define SAMSUNG_APPS "Samsung_apps"
#define SAMSUNG_GUIDE "Samsung_guide"
#define SAMSUNG_TOOLS "Samsung_tools"
#define SAMSUNG_INFO "Samsung_info"
#define SAMSUNG_UP "Samsung_up"
#define SAMSUNG_DOWN "Samsung_down"
#define SAMSUNG_LEFT "Samsung_left"
#define SAMSUNG_RIGHT "Samsung_right"
#define SAMSUNG_SELECT "Samsung_select"
#define SAMSUNG_RETURN "Samsung_return"
#define SAMSUNG_EXIT "Samsung_exit"
#define SAMSUNG_KEY_A "Samsung_key_a"
#define SAMSUNG_KEY_B "Samsung_key_b"
#define SAMSUNG_KEY_C "Samsung_key_c"
#define SAMSUNG_KEY_D "Samsung_key_d"
#define SAMSUNG_FAMILYSTORY "Samsung_familystory"
#define SAMSUNG_SEARCH "Samsung_search"
#define SAMSUNG_DUALI_II "Samsung_duali-ii"
#define SAMSUNG_SUPPORT "Samsung_support"
#define SAMSUNG_PSIZE "Samsung_psize"
#define SAMSUNG_ADSUBT "Samsung_adsubt"
#define SAMSUNG_REWIND "Samsung_rewind"
#define SAMSUNG_PAUSE "Samsung_pause"
#define SAMSUNG_FASTFORWARD "Samsung_fastforward"
#define SAMSUNG_RECORD "Samsung_record"
#define SAMSUNG_PLAY "Samsung_play"
#define SAMSUNG_STOP "Samsung_stop"
#define SAMSUNG_POWER_OFF "Samsung_power_off"
#define SAMSUNG_POWER_ON "Samsung_power_on"
#define SAMSUNG_INPUT_HDMI_1 "Samsung_input_hdmi_1"
#define SAMSUNG_INPUT_HDMI_2 "Samsung_input_hdmi_2"
#define SAMSUNG_INPUT_HDMI_3 "Samsung_input_hdmi_3"
#define SAMSUNG_INPUT_HDMI_4 "Samsung_input_hdmi_4"
#define SAMSUNG_INPUT_COMPONENT "Samsung_input_component"
#define SAMSUNG_INPUT_TV "Samsung_input_tv"
void register_device_samsung();
#endif /*__DEVICE_SAMSUNG_H__*/

View file

@ -0,0 +1,11 @@
#include "commandHandler.h"
#include "device_smarthome/device_smarthome.h"
void register_device_smarthome() {
#ifdef ENABLE_KEYBOARD_MQTT
commands[SMARTHOME_MQTT_BULB1_SET] = makeCommandData(MQTT, {"bulb1_set" }); // payload must be set when calling commandHandler
commands[SMARTHOME_MQTT_BULB2_SET] = makeCommandData(MQTT, {"bulb2_set" }); // payload must be set when calling commandHandler
commands[SMARTHOME_MQTT_BULB1_BRIGHTNESS_SET] = makeCommandData(MQTT, {"bulb1_setbrightness" }); // payload must be set when calling commandHandler
commands[SMARTHOME_MQTT_BULB2_BRIGHTNESS_SET] = makeCommandData(MQTT, {"bulb2_setbrightness" }); // payload must be set when calling commandHandler
#endif
}

View file

@ -0,0 +1,11 @@
#ifndef __DEVICE_SMARTHOME_H__
#define __DEVICE_SMARTHOME_H__
#define SMARTHOME_MQTT_BULB1_SET "Smarthome_mqtt_bulb1_set"
#define SMARTHOME_MQTT_BULB2_SET "Smarthome_mqtt_bulb2_set"
#define SMARTHOME_MQTT_BULB1_BRIGHTNESS_SET "Smarthome_mqtt_bulb1_brightness_set"
#define SMARTHOME_MQTT_BULB2_BRIGHTNESS_SET "Smarthome_mqtt_bulb2_brightness_set"
void register_device_smarthome();
#endif /*__DEVICE_SMARTHOME_H__*/

View file

@ -0,0 +1,146 @@
#include <string>
#include <lvgl.h>
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "hardware/tft.h"
#include "device_smarthome/device_smarthome.h"
#include "commandHandler.h"
// LVGL declarations
LV_IMG_DECLARE(lightbulb);
// Smart Home Toggle Event handler
static void smartHomeToggle_event_cb(lv_event_t * e){
std::string payload;
if (lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED)) payload = "true";
else payload = "false";
// Publish an MQTT message based on the event user data
#ifdef ENABLE_WIFI_AND_MQTT
if((int)e->user_data == 1) executeCommand(SMARTHOME_MQTT_BULB1_SET, payload);
if((int)e->user_data == 2) executeCommand(SMARTHOME_MQTT_BULB2_SET, payload);
#endif
}
// Smart Home Slider Event handler
static void smartHomeSlider_event_cb(lv_event_t * e){
lv_obj_t * slider = lv_event_get_target(e);
char payload[8];
dtostrf(lv_slider_get_value(slider), 1, 2, payload);
std::string payload_str(payload);
// Publish an MQTT message based on the event user data
#ifdef ENABLE_WIFI_AND_MQTT
if((int)e->user_data == 1) executeCommand(SMARTHOME_MQTT_BULB1_BRIGHTNESS_SET, payload);
if((int)e->user_data == 2) executeCommand(SMARTHOME_MQTT_BULB2_BRIGHTNESS_SET, payload);
#endif
}
void init_gui_tab_smarthome(lv_obj_t* tabview) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Smart Home");
// Add content to the smart home tab (4)
lv_obj_set_layout(tab, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scrollbar_mode(tab, LV_SCROLLBAR_MODE_ACTIVE);
// Add a label, then a box for the light controls
lv_obj_t* menuLabel = lv_label_create(tab);
lv_label_set_text(menuLabel, "Living Room");
lv_obj_t* menuBox = lv_obj_create(tab);
lv_obj_set_size(menuBox, lv_pct(100), 79);
lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN);
lv_obj_t* bulbIcon = lv_img_create(menuBox);
lv_img_set_src(bulbIcon, &lightbulb);
lv_obj_set_style_img_recolor(bulbIcon, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_img_recolor_opa(bulbIcon, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_align(bulbIcon, LV_ALIGN_TOP_LEFT, 0, 0);
menuLabel = lv_label_create(menuBox);
lv_label_set_text(menuLabel, "Floor Lamp");
lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 22, 3);
lv_obj_t* lightToggleA = lv_switch_create(menuBox);
lv_obj_set_size(lightToggleA, 40, 22);
lv_obj_align(lightToggleA, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_style_bg_color(lightToggleA, lv_color_lighten(color_primary, 50), LV_PART_MAIN);
lv_obj_set_style_bg_color(lightToggleA, color_primary, LV_PART_INDICATOR);
lv_obj_add_event_cb(lightToggleA, smartHomeToggle_event_cb, LV_EVENT_VALUE_CHANGED, (void*)1);
lv_obj_t* slider = lv_slider_create(menuBox);
lv_slider_set_range(slider, 0, 100);
lv_obj_set_style_bg_color(slider, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_color(slider, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_dir(slider, LV_GRAD_DIR_HOR, LV_PART_INDICATOR);
lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB);
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
lv_obj_set_style_bg_color(slider, lv_color_lighten(color_primary, 50), LV_PART_MAIN);
lv_slider_set_value(slider, 255, LV_ANIM_OFF);
lv_obj_set_size(slider, lv_pct(90), 10);
lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 37);
lv_obj_add_event_cb(slider, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)1);
// Add another menu box for a second appliance
menuBox = lv_obj_create(tab);
lv_obj_set_size(menuBox, lv_pct(100), 79);
lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN);
bulbIcon = lv_img_create(menuBox);
lv_img_set_src(bulbIcon, &lightbulb);
lv_obj_set_style_img_recolor(bulbIcon, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_img_recolor_opa(bulbIcon, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_align(bulbIcon, LV_ALIGN_TOP_LEFT, 0, 0);
menuLabel = lv_label_create(menuBox);
lv_label_set_text(menuLabel, "Ceiling Light");
lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 22, 3);
lv_obj_t* lightToggleB = lv_switch_create(menuBox);
lv_obj_set_size(lightToggleB, 40, 22);
lv_obj_align(lightToggleB, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_style_bg_color(lightToggleB, lv_color_lighten(color_primary, 50), LV_PART_MAIN);
lv_obj_set_style_bg_color(lightToggleB, color_primary, LV_PART_INDICATOR);
lv_obj_add_event_cb(lightToggleB, smartHomeToggle_event_cb, LV_EVENT_VALUE_CHANGED, (void*)2);
slider = lv_slider_create(menuBox);
lv_slider_set_range(slider, 0, 100);
lv_obj_set_style_bg_color(slider, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_color(slider, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_dir(slider, LV_GRAD_DIR_HOR, LV_PART_INDICATOR);
lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB);
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
lv_obj_set_style_bg_color(slider, lv_color_lighten(color_primary, 50), LV_PART_MAIN);
lv_slider_set_value(slider, 255, LV_ANIM_OFF);
lv_obj_set_size(slider, lv_pct(90), 10);
lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 37);
lv_obj_add_event_cb(slider, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)2);
// Add another room (empty for now)
menuLabel = lv_label_create(tab);
lv_label_set_text(menuLabel, "Kitchen");
menuBox = lv_obj_create(tab);
lv_obj_set_size(menuBox, lv_pct(100), 79);
lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN);
}
void init_gui_pageIndicator_smarthome(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Smart Home");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
void register_gui_smarthome(void){
register_gui(& init_gui_tab_smarthome, & init_gui_pageIndicator_smarthome);
}

View file

@ -0,0 +1,8 @@
#ifndef __GUI_SMARTHOME_H__
#define __GUI_SMARTHOME_H__
#include <lvgl.h>
void register_gui_smarthome(void);
#endif /*__GUI_SMARTHOME_H__*/

View file

@ -0,0 +1,43 @@
#include <lvgl.h>
#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif
#ifndef LV_ATTRIBUTE_IMG_LIGHTBULB
#define LV_ATTRIBUTE_IMG_LIGHTBULB
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LIGHTBULB uint8_t lightbulb_map[] = {
0x00, 0x00, 0x00, 0x00, 0x04, 0x1c, 0x1c, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x16, 0x95, 0xee, 0xff, 0xff, 0xee, 0x94, 0x15, 0x00, 0x00,
0x00, 0x27, 0xe3, 0xff, 0xcc, 0x8d, 0x8d, 0xcd, 0xff, 0xe1, 0x26, 0x00,
0x07, 0xd9, 0xfa, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x5f, 0xfa, 0xd7, 0x06,
0x65, 0xff, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0xff, 0x63,
0xb1, 0xf8, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xf8, 0xaf,
0xcc, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xcd,
0xb1, 0xf5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf1, 0xbd,
0x73, 0xff, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xff, 0x74,
0x0b, 0xd5, 0xfb, 0x40, 0x00, 0x00, 0x00, 0x00, 0x41, 0xfb, 0xd9, 0x0b,
0x00, 0x24, 0xef, 0xdc, 0x01, 0x00, 0x00, 0x01, 0xdd, 0xee, 0x24, 0x00,
0x00, 0x00, 0x83, 0xff, 0x30, 0x00, 0x00, 0x30, 0xff, 0x81, 0x00, 0x00,
0x00, 0x00, 0x12, 0x6c, 0x06, 0x00, 0x00, 0x06, 0x6c, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x25, 0xc7, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, 0x25, 0x00, 0x00,
0x00, 0x00, 0x25, 0xc7, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, 0x25, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1c, 0x76, 0x77, 0x77, 0x76, 0x1c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x69, 0xff, 0xff, 0xff, 0xff, 0x69, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x21, 0x22, 0x22, 0x21, 0x01, 0x00, 0x00, 0x00,
};
const lv_img_dsc_t lightbulb = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 12,
.header.h = 20,
.data_size = 240,
.data = lightbulb_map,
};

View file

@ -0,0 +1,44 @@
#include "commandHandler.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
void register_device_yamaha() {
commands[YAMAHA_INPUT_DVD] = makeCommandData(IR_NEC, {"0x5EA1837C"});
commands[YAMAHA_INPUT_DTV] = makeCommandData(IR_NEC, {"0x5EA12AD5"});
commands[YAMAHA_INPUT_VCR] = makeCommandData(IR_NEC, {"0x5EA1F00F"});
commands[YAMAHA_POWER_TOGGLE] = makeCommandData(IR_NEC, {"0x5EA1F807"});
commands[YAMAHA_INPUT_CD] = makeCommandData(IR_NEC, {"0x5EA1A857"});
commands[YAMAHA_INPUT_MD] = makeCommandData(IR_NEC, {"0x5EA1936C"});
commands[YAMAHA_INPUT_VAUX] = makeCommandData(IR_NEC, {"0x5EA1AA55"});
commands[YAMAHA_MULTICHANNEL] = makeCommandData(IR_NEC, {"0x5EA1E11E"});
commands[YAMAHA_INPUT_TUNER] = makeCommandData(IR_NEC, {"0x5EA16897"});
commands[YAMAHA_PRESETGROUP] = makeCommandData(IR_NEC, {"0x5EA148B7"});
commands[YAMAHA_PRESETSTATION_MINUS] = makeCommandData(IR_NEC, {"0x5EA18877"});
commands[YAMAHA_PRESETSTATION_PLUS] = makeCommandData(IR_NEC, {"0x5EA108F7"});
commands[YAMAHA_STANDARD] = makeCommandData(IR_NEC, {"0x5EA109F6"});
commands[YAMAHA_5CHSTEREO] = makeCommandData(IR_NEC, {"0x5EA1E916"});
commands[YAMAHA_NIGHT] = makeCommandData(IR_NEC, {"0x5EA1A956"});
commands[YAMAHA_SLEEP] = makeCommandData(IR_NEC, {"0x5EA1EA15"});
commands[YAMAHA_TEST] = makeCommandData(IR_NEC, {"0x5EA1A15E"});
commands[YAMAHA_STRAIGHT] = makeCommandData(IR_NEC, {"0x5EA16A95"});
commands[YAMAHA_VOL_MINUS] = makeCommandData(IR_NEC, {"0x5EA1D827"});
commands[YAMAHA_VOL_PLUS] = makeCommandData(IR_NEC, {"0x5EA158A7"});
commands[YAMAHA_PROG_MINUS] = makeCommandData(IR_NEC, {"0x5EA19A65"});
commands[YAMAHA_PROG_PLUS] = makeCommandData(IR_NEC, {"0x5EA11AE5"});
commands[YAMAHA_MUTE_TOGGLE] = makeCommandData(IR_NEC, {"0x5EA138C7"});
commands[YAMAHA_LEVEL] = makeCommandData(IR_NEC, {"0x5EA1619E"});
commands[YAMAHA_SETMENU] = makeCommandData(IR_NEC, {"0x5EA139C6"});
commands[YAMAHA_SETMENU_UP] = makeCommandData(IR_NEC, {"0x5EA119E6"});
commands[YAMAHA_SETMENU_DOWN] = makeCommandData(IR_NEC, {"0x5EA19966"});
commands[YAMAHA_SETMENU_MINUS] = makeCommandData(IR_NEC, {"0x5EA1CA35"});
commands[YAMAHA_SETMENU_PLUS] = makeCommandData(IR_NEC, {"0x5EA14AB5"});
commands[YAMAHA_POWER_OFF] = makeCommandData(IR_NEC, {"0x5EA17887"});
commands[YAMAHA_POWER_ON] = makeCommandData(IR_NEC, {"0x5EA1B847"});
// GC seems not to work
//commands[YAMAHA_POWER_TOGGLE] = makeCommandData(IR_GC, {"38000,1,69,341,170,21,21,21,64,21,64,21,64,21,64,21,64,21,64,21,21,21,64,21,21,21,21,21,21,21,21,21,21,21,21,21,64,21,21,21,64,21,21,21,64,21,21,21,64,21,21,21,21,21,64,21,21,21,64,21,21,21,64,21,21,21,64,21,64,21,1517,341,85,21,3655"});
//commands[YAMAHA_POWER_OFF] = makeCommandData(IR_GC, {"38000,1,69,341,170,21,21,21,64,21,64,21,64,21,64,21,64,21,64,21,21,21,64,21,21,21,21,21,21,21,21,21,21,21,21,21,64,21,64,21,64,21,64,21,64,21,64,21,64,21,64,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,64,21,1517,341,85,21,3655"});
//commands[YAMAHA_POWER_ON] = makeCommandData(IR_GC, {"38000,1,69,341,170,21,21,21,64,21,64,21,64,21,64,21,64,21,64,21,21,21,64,21,21,21,21,21,21,21,21,21,21,21,21,21,64,21,21,21,64,21,64,21,64,21,64,21,64,21,64,21,21,21,64,21,21,21,21,21,21,21,21,21,21,21,21,21,64,21,1517,341,85,21,3655"});
//commands[YAMAHA_INPUT_DVD] = makeCommandData(IR_GC, {"38000,1,69,341,170,21,21,21,64,21,21,21,64,21,64,21,64,21,64,21,21,21,64,21,21,21,64,21,21,21,21,21,21,21,21,21,64,21,64,21,21,21,21,21,21,21,21,21,21,21,64,21,64,21,21,21,64,21,64,21,64,21,64,21,64,21,21,21,21,21,1517,341,85,21,3655"});
//commands[YAMAHA_INPUT_DTV] = makeCommandData(IR_GC, {"38000,1,69,341,170,21,21,21,64,21,21,21,64,21,64,21,64,21,64,21,21,21,64,21,21,21,64,21,21,21,21,21,21,21,21,21,64,21,21,21,21,21,64,21,21,21,64,21,21,21,64,21,21,21,64,21,64,21,21,21,64,21,21,21,64,21,21,21,64,21,1517,341,85,21,3655"});
//commands[YAMAHA_STANDARD] = makeCommandData(IR_GC, {"38000,1,69,341,170,21,21,21,64,21,64,21,64,21,64,21,64,21,64,21,21,21,64,21,21,21,21,21,21,21,21,21,21,21,21,21,64,21,21,21,64,21,64,21,64,21,64,21,64,21,64,21,64,21,64,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,1517,341,85,21,3655"});
}

View file

@ -0,0 +1,48 @@
#ifndef __DEVICE_YAMAHA_H__
#define __DEVICE_YAMAHA_H__
#define YAMAHA_INPUT_DVD "Yamaha_input_dvd"
#define YAMAHA_INPUT_DTV "Yamaha_input_dtv"
#define YAMAHA_INPUT_VCR "Yamaha_input_vcr"
#define YAMAHA_POWER_TOGGLE "Yamaha_power_toggle"
#define YAMAHA_INPUT_CD "Yamaha_input_cd"
#define YAMAHA_INPUT_MD "Yamaha_input_md"
#define YAMAHA_INPUT_VAUX "Yamaha_input_vaux"
#define YAMAHA_MULTICHANNEL "Yamaha_multichannel"
#define YAMAHA_INPUT_TUNER "Yamaha_input_tuner"
#define YAMAHA_PRESETGROUP "Yamaha_presetgroup"
#define YAMAHA_PRESETSTATION_MINUS "Yamaha_presetstation-"
#define YAMAHA_PRESETSTATION_PLUS "Yamaha_presetstation+"
#define YAMAHA_STANDARD "Yamaha_standard"
#define YAMAHA_5CHSTEREO "Yamaha_5chstereo"
#define YAMAHA_NIGHT "Yamaha_night"
#define YAMAHA_SLEEP "Yamaha_sleep"
#define YAMAHA_TEST "Yamaha_test"
#define YAMAHA_STRAIGHT "Yamaha_straight"
#define YAMAHA_VOL_MINUS "Yamaha_vol-"
#define YAMAHA_VOL_PLUS "Yamaha_vol+"
#define YAMAHA_PROG_MINUS "Yamaha_prog-"
#define YAMAHA_PROG_PLUS "Yamaha_prog+"
#define YAMAHA_MUTE_TOGGLE "Yamaha_mute_toggle"
#define YAMAHA_LEVEL "Yamaha_level"
#define YAMAHA_SETMENU "Yamaha_setmenu"
#define YAMAHA_SETMENU_UP "Yamaha_setmenu_up"
#define YAMAHA_SETMENU_DOWN "Yamaha_setmenu_down"
#define YAMAHA_SETMENU_MINUS "Yamaha_setmenu_-"
#define YAMAHA_SETMENU_PLUS "Yamaha_setmenu_+"
#define YAMAHA_POWER_OFF "Yamaha_power_off"
#define YAMAHA_POWER_ON "Yamaha_power_on"
void register_device_yamaha();
#endif /*__DEVICE_YAMAHA_H__*/

View file

@ -0,0 +1,219 @@
#include <lvgl.h>
#include "hardware/tft.h"
#include "hardware/sleep.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
lv_obj_t* panel;
lv_color_t color_primary = lv_color_hex(0x303030); // gray
lv_obj_t* WifiLabel = NULL;
lv_obj_t* BluetoothLabel;
lv_obj_t* objBattPercentage;
lv_obj_t* objBattIcon;
lv_obj_t* SceneLabel;
byte currentScreen = 1; // Current Device to control (allows switching mappings between devices)
// LVGL declarations
LV_IMG_DECLARE(gradientLeft);
LV_IMG_DECLARE(gradientRight);
// Helper Functions -----------------------------------------------------------------------------------------------------------------------
// Set the page indicator scroll position relative to the tabview scroll position
static void store_scroll_value_event_cb(lv_event_t* e){
float bias = (150.0 + 8.0) / 240.0;
int offset = 240 / 2 - 150 / 2 - 8 - 50 - 3;
lv_obj_t* screen = lv_event_get_target(e);
lv_obj_scroll_to_x(panel, lv_obj_get_scroll_x(screen) * bias - offset, LV_ANIM_OFF);
}
// Update current screen when the tabview page is changes
static void tabview_device_event_cb(lv_event_t* e){
currentScreen = lv_tabview_get_tab_act(lv_event_get_target(e));
}
// Display flushing
void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ){
uint32_t w = ( area->x2 - area->x1 + 1 );
uint32_t h = ( area->y2 - area->y1 + 1 );
tft.startWrite();
tft.setAddrWindow( area->x1, area->y1, w, h );
tft.pushPixelsDMA( ( uint16_t * )&color_p->full, w * h);
tft.endWrite();
lv_disp_flush_ready( disp );
}
// Read the touchpad
void my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data){
// int16_t touchX, touchY;
touchPoint = touch.getPoint();
int16_t touchX = touchPoint.x;
int16_t touchY = touchPoint.y;
bool touched = false;
if ((touchX > 0) || (touchY > 0)) {
touched = true;
resetStandbyTimer();
}
if( !touched ){
data->state = LV_INDEV_STATE_REL;
}
else{
data->state = LV_INDEV_STATE_PR;
// Set the coordinates
data->point.x = screenWidth - touchX;
data->point.y = screenHeight - touchY;
//Serial.print( "touchpoint: x" );
//Serial.print( touchX );
//Serial.print( " y" );
//Serial.println( touchY );
//tft.drawFastHLine(0, screenHeight - touchY, screenWidth, TFT_RED);
//tft.drawFastVLine(screenWidth - touchX, 0, screenHeight, TFT_RED);
}
}
static lv_disp_draw_buf_t draw_buf;
// static lv_color_t bufA[ screenWidth * screenHeight / 10 ];
// static lv_color_t bufB[ screenWidth * screenHeight / 10 ];
void init_gui(void) {
// Setup LVGL ---------------------------------------------------------------------------------------------
lv_init();
lv_color_t * bufA = (lv_color_t *) malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 10);
lv_color_t * bufB = (lv_color_t *) malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 10);
lv_disp_draw_buf_init( &draw_buf, bufA, bufB, screenWidth * screenHeight / 10 );
// Initialize the display driver --------------------------------------------------------------------------
static lv_disp_drv_t disp_drv;
lv_disp_drv_init( &disp_drv );
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register( &disp_drv );
// Initialize the touchscreen driver
static lv_indev_drv_t indev_drv;
lv_indev_drv_init( &indev_drv );
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register( &indev_drv );
// --- LVGL UI Configuration ---
// Set the background color -------------------------------------------------------------------------------
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN);
// Setup a scrollable tabview for devices and settings ----------------------------------------------------
lv_obj_t* tabview;
tabview = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, 0); // Hide tab labels by setting their height to 0
lv_obj_set_style_bg_color(tabview, lv_color_black(), LV_PART_MAIN);
lv_obj_set_size(tabview, screenWidth, 270); // 270 = screenHeight(320) - panel(30) - statusbar(20)
lv_obj_align(tabview, LV_ALIGN_TOP_MID, 0, 20);
// Add all the tabs here
create_gui_tabs_from_gui_registry(tabview);
// Set current page according to the current screen
lv_tabview_set_act(tabview, currentScreen, LV_ANIM_OFF);
// Create a page indicator at the bottom ------------------------------------------------------------------
panel = lv_obj_create(lv_scr_act());
lv_obj_clear_flag(panel, LV_OBJ_FLAG_CLICKABLE); // This indicator will not be clickable
lv_obj_set_size(panel, screenWidth, 30);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
lv_obj_align(panel, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_set_scrollbar_mode(panel, LV_SCROLLBAR_MODE_OFF);
// This small hidden button enables the page indicator to scroll further
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_set_size(btn, 50, lv_pct(100));
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_opa(btn, LV_OPA_TRANSP, LV_PART_MAIN);
// Create actual (non-clickable) buttons for every tab
create_gui_pageIndicators_from_gui_registry(panel);
// This small hidden button enables the page indicator to scroll further
btn = lv_btn_create(panel);
lv_obj_set_size(btn, 50, lv_pct(100));
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_opa(btn, LV_OPA_TRANSP, LV_PART_MAIN);
// Make the indicator scroll together with the tabs by creating a scroll event
lv_obj_add_event_cb(lv_tabview_get_content(tabview), store_scroll_value_event_cb, LV_EVENT_SCROLL, NULL);
lv_obj_add_event_cb(tabview, tabview_device_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
// Initialize scroll position for the indicator
lv_event_send(lv_tabview_get_content(tabview), LV_EVENT_SCROLL, NULL);
// Style the panel background
static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_pad_all(&style_btn, 3);
lv_style_set_border_width(&style_btn, 0);
lv_style_set_bg_opa(&style_btn, LV_OPA_TRANSP);
lv_obj_add_style(panel, &style_btn, 0);
// Make the indicator fade out at the sides using gradient bitmaps
lv_obj_t* img1 = lv_img_create(lv_scr_act());
lv_img_set_src(img1, &gradientLeft);
lv_obj_align(img1, LV_ALIGN_BOTTOM_LEFT, 0, 0);
lv_obj_set_size(img1, 30, 30); // stretch the 1-pixel high image to 30px
lv_obj_t* img2 = lv_img_create(lv_scr_act());
lv_img_set_src(img2, &gradientRight);
lv_obj_align(img2, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
lv_obj_set_size(img2, 30, 30);
// Create a status bar at the top -------------------------------------------------------------------------
lv_obj_t* statusbar = lv_btn_create(lv_scr_act());
lv_obj_set_size(statusbar, 240, 20);
lv_obj_set_style_shadow_width(statusbar, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(statusbar, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_radius(statusbar, 0, LV_PART_MAIN);
lv_obj_align(statusbar, LV_ALIGN_TOP_MID, 0, 0);
// WiFi -------------------------------------------------------------------------
WifiLabel = lv_label_create(statusbar);
lv_label_set_text(WifiLabel, "");
lv_obj_align(WifiLabel, LV_ALIGN_LEFT_MID, -8, 0);
lv_obj_set_style_text_font(WifiLabel, &lv_font_montserrat_12, LV_PART_MAIN);
// Bluetooth --------------------------------------------------------------------
BluetoothLabel = lv_label_create(statusbar);
lv_label_set_text(BluetoothLabel, "");
lv_obj_align(BluetoothLabel, LV_ALIGN_LEFT_MID, 12, 0);
lv_obj_set_style_text_font(BluetoothLabel, &lv_font_montserrat_12, LV_PART_MAIN);
// Scene ------------------------------------------------------------------------
SceneLabel = lv_label_create(statusbar);
lv_label_set_text(SceneLabel, "");
lv_obj_align(SceneLabel, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_font(SceneLabel, &lv_font_montserrat_12, LV_PART_MAIN);
// Battery ----------------------------------------------------------------------
objBattPercentage = lv_label_create(statusbar);
lv_label_set_text(objBattPercentage, "");
// lv_obj_align(objBattPercentage, LV_ALIGN_RIGHT_MID, -16, 0);
lv_obj_align(objBattPercentage, LV_ALIGN_RIGHT_MID, -20, 0);
lv_obj_set_style_text_font(objBattPercentage, &lv_font_montserrat_12, LV_PART_MAIN);
objBattIcon = lv_label_create(statusbar);
lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_EMPTY);
lv_obj_align(objBattIcon, LV_ALIGN_RIGHT_MID, 8, 0);
lv_obj_set_style_text_font(objBattIcon, &lv_font_montserrat_16, LV_PART_MAIN);
// --- End of LVGL configuration ---
}
void gui_loop(void) {
lv_timer_handler();
}

View file

@ -0,0 +1,23 @@
#ifndef __GUIBASE_H__
#define __GUIBASE_H__
#include <lvgl.h>
#include "../hardware/tft.h"
// LVGL declarations
LV_IMG_DECLARE(high_brightness);
LV_IMG_DECLARE(low_brightness);
extern lv_obj_t* objBattPercentage;
extern lv_obj_t* objBattIcon;
extern lv_obj_t* SceneLabel;
extern lv_color_t color_primary;
extern lv_obj_t* WifiLabel;
extern lv_obj_t* BluetoothLabel;
extern byte currentScreen; // Current screen that is shown
void init_gui(void);
void gui_loop(void);
#endif /*__GUIBASE_H__*/

View file

@ -0,0 +1,113 @@
#include <lvgl.h>
#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif
#ifndef LV_ATTRIBUTE_IMG_GRADIENTLEFT
#define LV_ATTRIBUTE_IMG_GRADIENTLEFT
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_GRADIENTLEFT uint8_t gradientLeft_map[] = {
0xfa, 0xf2, 0xea, 0xe2, 0xda, 0xd1, 0xc7, 0xbe, 0xb7, 0xae, 0xa6, 0x9e, 0x95, 0x8d, 0x84, 0x7d, 0x74, 0x6c, 0x62, 0x5a, 0x51, 0x48, 0x41, 0x38, 0x2f, 0x28, 0x1f, 0x17, 0x0f, 0x07,
};
const lv_img_dsc_t gradientLeft = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 30,
.header.h = 1,
.data_size = 30,
.data = gradientLeft_map,
};
#ifndef LV_ATTRIBUTE_IMG_GRADIENTRIGHT
#define LV_ATTRIBUTE_IMG_GRADIENTRIGHT
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_GRADIENTRIGHT uint8_t gradientRight_map[] = {
0x07, 0x0f, 0x17, 0x1f, 0x28, 0x2f, 0x38, 0x41, 0x48, 0x51, 0x5a, 0x62, 0x6c, 0x74, 0x7d, 0x84, 0x8d, 0x95, 0x9e, 0xa6, 0xae, 0xb7, 0xbe, 0xc7, 0xd1, 0xda, 0xe2, 0xea, 0xf2, 0xfa,
};
const lv_img_dsc_t gradientRight = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 30,
.header.h = 1,
.data_size = 30,
.data = gradientRight_map,
};
#ifndef LV_ATTRIBUTE_IMG_HIGH_BRIGHTNESS
#define LV_ATTRIBUTE_IMG_HIGH_BRIGHTNESS
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_HIGH_BRIGHTNESS uint8_t high_brightness_map[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5c, 0x8e, 0x04, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x04, 0x8e, 0x5c, 0x00, 0x00,
0x00, 0x00, 0x8c, 0xff, 0xa8, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0xa8, 0xff, 0x8c, 0x00, 0x00,
0x00, 0x00, 0x04, 0xa9, 0xf5, 0x0d, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x0d, 0xf5, 0xa9, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x42, 0xd4, 0xff, 0xff, 0xd4, 0x41, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00,
0xbd, 0xcc, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x12, 0x0d, 0xbd, 0xcc, 0xbd,
0xbd, 0xcc, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x10, 0x0d, 0xbd, 0xcc, 0xbd,
0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x40, 0xd3, 0xff, 0xfe, 0xd2, 0x3f, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0xa9, 0xf5, 0x0d, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x0d, 0xf5, 0xa9, 0x04, 0x00, 0x00,
0x00, 0x00, 0x8c, 0xff, 0xa8, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0xa8, 0xff, 0x8c, 0x00, 0x00,
0x00, 0x00, 0x5c, 0x8e, 0x04, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x04, 0x8e, 0x5c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const lv_img_dsc_t high_brightness = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 18,
.header.h = 18,
.data_size = 324,
.data = high_brightness_map,
};
#ifndef LV_ATTRIBUTE_IMG_LOW_BRIGHTNESS
#define LV_ATTRIBUTE_IMG_LOW_BRIGHTNESS
#endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LOW_BRIGHTNESS uint8_t low_brightness_map[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x27, 0x72, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0x72, 0x28, 0x00, 0x00,
0x00, 0x00, 0x71, 0xf5, 0x0f, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x0d, 0xf5, 0x73, 0x00, 0x00,
0x00, 0x00, 0x01, 0x0b, 0x00, 0x42, 0xd4, 0xff, 0xff, 0xd4, 0x41, 0x00, 0x0a, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x42, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x00, 0x00, 0x00, 0x00,
0x43, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x12, 0x0d, 0xbc, 0x44,
0x43, 0xbd, 0x0d, 0x11, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x11, 0x0d, 0xbc, 0x44,
0x00, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x41, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x0b, 0x00, 0x40, 0xd3, 0xfe, 0xff, 0xd2, 0x3f, 0x00, 0x0a, 0x01, 0x00, 0x00,
0x00, 0x00, 0x71, 0xf5, 0x0f, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x0d, 0xf5, 0x73, 0x00, 0x00,
0x00, 0x00, 0x27, 0x72, 0x01, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x01, 0x72, 0x28, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const lv_img_dsc_t low_brightness = {
.header.cf = LV_IMG_CF_ALPHA_8BIT,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = 16,
.header.h = 16,
.data_size = 256,
.data = low_brightness_map,
};

View file

@ -0,0 +1,31 @@
#include <string>
#include <list>
#include <lvgl.h>
#include "guiregistry.h"
// https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work
struct gui_definition {
init_gui_tab this_init_gui_tab;
init_gui_pageIndicator this_init_gui_pageIndicator;
};
std::list<gui_definition> registered_guis;
void register_gui(init_gui_tab a_init_gui_tab, init_gui_pageIndicator a_gui_pageIndicator) {
registered_guis.push_back(gui_definition{a_init_gui_tab, a_gui_pageIndicator});
}
void create_gui_tabs_from_gui_registry(lv_obj_t* tabview) {
std::list<gui_definition>::iterator it;
for (it = registered_guis.begin(); it != registered_guis.end(); ++it) {
it->this_init_gui_tab(tabview);
}
}
void create_gui_pageIndicators_from_gui_registry(lv_obj_t* panel) {
std::list<gui_definition>::iterator it;
for (it = registered_guis.begin(); it != registered_guis.end(); ++it) {
it->this_init_gui_pageIndicator(panel);
}
}

View file

@ -0,0 +1,29 @@
#ifndef __GUIREGISTRY_H__
#define __GUIREGISTRY_H__
/*
If you want to create a new GUI for the touch screen, then
- copy one of the existing GUIs, e.g. gui_numpad.cpp and gui_numpad.h
- place the files in the right folder:
- in folder "gui_general_and_keys" if they are of general purpose, not only for a specific device
- in the device folder, if the GUI is specific for a certain device
- in folder "scenes" if the GUI is intended for a specific scene
It does not really matter where the files are placed, but doing so helps to keep a clean structure
- rename the functions, they must not have the same name as the ones in the file you copied from
- call "register_gui_<nameOfGUI>();" in main.cpp
Technically spoken, to register a GUI, you need to provide two function pointers:
- one for creating the tab
- one for creating the page indicator at the bottom of the tab
*/
#include <string>
#include <lvgl.h>
typedef void (*init_gui_tab)(lv_obj_t* tabview);
typedef void (*init_gui_pageIndicator)(lv_obj_t* tabview);
void register_gui(init_gui_tab a_init_gui_tab, init_gui_pageIndicator a_gui_pageIndicator);
void create_gui_tabs_from_gui_registry(lv_obj_t* tabview);
void create_gui_pageIndicators_from_gui_registry(lv_obj_t* panel);
#endif /*__GUIREGISTRY_H__*/

View file

@ -0,0 +1,139 @@
#include <lvgl.h>
#include <string>
#include <iostream>
#include "hardware/tft.h"
#include "hardware/sleep.h"
#include "hardware/infrared_receiver.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
lv_obj_t* menuBoxToggle;
lv_obj_t* menuBoxMessages;
int16_t boxHeightDeactivated = 0;
int16_t boxHeightActivated = 200;
const int maxCountMessages = 15;
lv_obj_t* irReceivedMessage[maxCountMessages];
String IRmessages[maxCountMessages];
int messagePos = 0;
int messageCount = 0;
void printReceivedMessages(bool clearMessages = false) {
//Serial.println("");
int messagePosLoop;
if (messageCount < maxCountMessages) {
messagePosLoop = 0;
} else {
messagePosLoop = messagePos;
}
//Serial.printf("will start printing messages, beginning at position %d\r\n", messagePosLoop);
for (int i=0; i<maxCountMessages; i++) {
if (clearMessages) {
IRmessages[messagePosLoop] = "";
}
//Serial.printf("will print at line %d the message at position %d: %s\r\n", i, messagePosLoop, IRmessages[messagePosLoop].c_str());
lv_label_set_text(irReceivedMessage[i], IRmessages[messagePosLoop].c_str());
messagePosLoop += 1;
if (messagePosLoop == maxCountMessages) {
messagePosLoop = 0;
}
}
if (clearMessages) {
lv_obj_set_size(menuBoxMessages, lv_pct(100), boxHeightDeactivated);
}
}
void showNewIRmessage(String message) {
resetStandbyTimer(); // Reset the sleep timer when a IR message is received
// Serial.printf(" new IR message received: %s\r\n", message.c_str());
// const char *a = message.c_str();
std::string messageStr;
messageStr.append(message.c_str());
// std::string aMessage = s(a);
messageStr.erase(std::remove(messageStr.begin(), messageStr.end(), '\n'), messageStr.cend());
//Serial.printf(" will put message %s to list\r\n", messageStr.c_str());
messageCount += 1;
IRmessages[messagePos] = (std::to_string(messageCount) + ": " + messageStr).c_str();
//Serial.printf(" this is the message at position %d: %s\r\n", messagePos, IRmessages[messagePos].c_str());
messagePos += 1;
if (messagePos == maxCountMessages) {
messagePos = 0;
}
printReceivedMessages();
}
// IR receiver on Switch Event handler
static void IRReceiverOnSetting_event_cb(lv_event_t * e){
irReceiverEnabled = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
if (irReceiverEnabled) {
Serial.println("will turn on IR receiver");
init_infraredReceiver();
lv_obj_set_size(menuBoxMessages, lv_pct(100), boxHeightActivated);
messageCount = 0;
printReceivedMessages();
} else {
Serial.println("will turn off IR receiver");
shutdown_infraredReceiver();
printReceivedMessages(true);
messagePos = 0;
messageCount = 0;
lv_obj_set_size(menuBoxMessages, lv_pct(100), boxHeightDeactivated);
}
}
void init_gui_tab_irReceiver(lv_obj_t* tabview) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "IR Receiver");
// Add content to the settings tab
// With a flex layout, setting groups/boxes will position themselves automatically
lv_obj_set_layout(tab, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scrollbar_mode(tab, LV_SCROLLBAR_MODE_ACTIVE);
menuBoxToggle = lv_obj_create(tab);
lv_obj_set_size(menuBoxToggle, lv_pct(100), 48);
lv_obj_set_style_bg_color(menuBoxToggle, color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(menuBoxToggle, 0, LV_PART_MAIN);
lv_obj_t* menuLabel = lv_label_create(menuBoxToggle);
lv_label_set_text(menuLabel, "Turn on IR receiver");
lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 3);
lv_obj_t* irReceiverToggle = lv_switch_create(menuBoxToggle);
lv_obj_set_size(irReceiverToggle, 40, 22);
lv_obj_align(irReceiverToggle, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_style_bg_color(irReceiverToggle, lv_color_hex(0x505050), LV_PART_MAIN);
lv_obj_add_event_cb(irReceiverToggle, IRReceiverOnSetting_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
// lv_obj_add_state(irReceiverToggle, LV_STATE_CHECKED); // set default state
menuBoxMessages = lv_obj_create(tab);
lv_obj_set_size(menuBoxMessages, lv_pct(100), 77); // 125
lv_obj_set_style_bg_color(menuBoxMessages, color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(menuBoxMessages, 0, LV_PART_MAIN);
for (int i=0; i<maxCountMessages; i++) {
irReceivedMessage[i] = lv_label_create(menuBoxMessages);
lv_obj_set_style_text_font(irReceivedMessage[i], &lv_font_montserrat_10, LV_PART_MAIN);
lv_obj_align(irReceivedMessage[i], LV_ALIGN_TOP_LEFT, 0, 0 + i*11);
}
printReceivedMessages(true);
}
void init_gui_pageIndicator_irReceiver(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text_fmt(label, "IR Receiver");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
void register_gui_irReceiver(void){
register_gui(& init_gui_tab_irReceiver, & init_gui_pageIndicator_irReceiver);
}

View file

@ -0,0 +1,10 @@
#ifndef __GUI_IRRECEIVER_H__
#define __GUI_IRRECEIVER_H__
#include <lvgl.h>
#include <string>
void register_gui_irReceiver(void);
void showNewIRmessage(String);
#endif /*__GUI_IRRECEIVER_H__*/

View file

@ -0,0 +1,101 @@
#include <lvgl.h>
#include "hardware/tft.h"
#include "device_samsungTV/device_samsungTV.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "commandHandler.h"
#include "scenes/sceneHandler.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
// Virtual Keypad Event handler
static void virtualKeypad_event_cb(lv_event_t* e) {
lv_obj_t* target = lv_event_get_target(e);
lv_obj_t* cont = lv_event_get_current_target(e);
if (target == cont) return; // stop if container was clicked
// send corrensponding number
if (currentScene == scene_name_TV) {
std::string virtualKeyMapTVNumbers[10] = {SAMSUNG_NUM_1, SAMSUNG_NUM_2, SAMSUNG_NUM_3, SAMSUNG_NUM_4, SAMSUNG_NUM_5, SAMSUNG_NUM_6, SAMSUNG_NUM_7, SAMSUNG_NUM_8, SAMSUNG_NUM_9, SAMSUNG_NUM_0};
std::string command = virtualKeyMapTVNumbers[(int)target->user_data];
executeCommand(command);
} else if (currentScene == scene_name_fireTV) {
byte virtualKeyMapFireTVNumbers[10] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0};
int number = virtualKeyMapFireTVNumbers[(int)target->user_data];
std::string numberStr = std::to_string(number);
executeCommand(KEYBOARD_SENDSTRING, numberStr);
}
}
void init_gui_tab_numpad(lv_obj_t* tabview) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Numpad");
// Configure number button grid
static lv_coord_t col_dsc[] = { LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST }; // equal x distribution
static lv_coord_t row_dsc[] = { 52, 52, 52, 52, LV_GRID_TEMPLATE_LAST }; // manual y distribution to compress the grid a bit
// Create a container with grid for tab
lv_obj_set_style_pad_all(tab, 0, LV_PART_MAIN);
lv_obj_t* cont = lv_obj_create(tab);
lv_obj_set_style_shadow_width(cont, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(cont, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_border_width(cont, 0, LV_PART_MAIN);
lv_obj_set_style_grid_column_dsc_array(cont, col_dsc, 0);
lv_obj_set_style_grid_row_dsc_array(cont, row_dsc, 0);
lv_obj_set_size(cont, 240, 270);
lv_obj_set_layout(cont, LV_LAYOUT_GRID);
lv_obj_align(cont, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_set_style_radius(cont, 0, LV_PART_MAIN);
lv_obj_t* buttonLabel;
lv_obj_t* obj;
// Iterate through grid buttons configure them
for (int i = 0; i < 12; i++) {
uint8_t col = i % 3;
uint8_t row = i / 3;
// Create the button object
if ((row == 3) && ((col == 0) || (col == 2))) continue; // Do not create a complete fourth row, only a 0 button
obj = lv_btn_create(cont);
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1, LV_GRID_ALIGN_STRETCH, row, 1);
lv_obj_set_style_bg_color(obj, color_primary, LV_PART_MAIN);
lv_obj_set_style_radius(obj, 14, LV_PART_MAIN);
lv_obj_set_style_shadow_color(obj, lv_color_hex(0x404040), LV_PART_MAIN);
lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); // Clicking a button causes a event in its container
// Create Labels for each button
buttonLabel = lv_label_create(obj);
if(i < 9){
lv_label_set_text_fmt(buttonLabel, std::to_string(i+1).c_str(), col, row);
lv_obj_set_user_data(obj, (void*)i); // Add user data so we can identify which button caused the container event
}
else{
lv_label_set_text_fmt(buttonLabel, "0", col, row);
lv_obj_set_user_data(obj, (void*)9);
}
lv_obj_set_style_text_font(buttonLabel, &lv_font_montserrat_24, LV_PART_MAIN);
lv_obj_center(buttonLabel);
}
// Create a shared event for all button inside container
lv_obj_add_event_cb(cont, virtualKeypad_event_cb, LV_EVENT_CLICKED, NULL);
}
void init_gui_pageIndicator_numpad(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Numpad");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
void register_gui_numpad(void){
register_gui(& init_gui_tab_numpad, & init_gui_pageIndicator_numpad);
}

View file

@ -0,0 +1,8 @@
#ifndef __GUI_NUMPAD_H__
#define __GUI_NUMPAD_H__
#include <lvgl.h>
void register_gui_numpad(void);
#endif /*__GUI_NUMPAD_H__*/

View file

@ -0,0 +1,178 @@
#include <lvgl.h>
#include "preferencesStorage.h"
#include "hardware/tft.h"
#include "hardware/sleep.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
lv_obj_t* objBattSettingsVoltage;
lv_obj_t* objBattSettingsPercentage;
//lv_obj_t* objBattSettingsIscharging;
// Slider Event handler
static void bl_slider_event_cb(lv_event_t * e){
lv_obj_t * slider = lv_event_get_target(e);
backlight_brightness = constrain(lv_slider_get_value(slider), 60, 255);
}
// Wakeup by IMU Switch Event handler
static void WakeEnableSetting_event_cb(lv_event_t * e){
wakeupByIMUEnabled = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
}
// timout event handler
static void timout_event_cb(lv_event_t * e){
lv_obj_t * drop = lv_event_get_target(e);
uint16_t selected = lv_dropdown_get_selected(drop);
switch (selected) {
case 0: {actualSleepTimeout = 10000; break;}
case 1: {actualSleepTimeout = 20000; break;}
case 2: {actualSleepTimeout = 40000; break;}
case 3: {actualSleepTimeout = 60000; break;}
case 4: {actualSleepTimeout = 180000; break;}
case 5: {actualSleepTimeout = 600000; break;}
case 6: {actualSleepTimeout = 3600000; break;}
}
// Serial.printf("New timeout: %lu ms\r\n", actualSleepTimeout);
resetStandbyTimer();
// save preferences now, otherwise if you set a very big timeout and upload your firmware again, it never got saved
save_preferences();
}
void init_gui_tab_settings(lv_obj_t* tabview) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Settings");
// Add content to the settings tab
// With a flex layout, setting groups/boxes will position themselves automatically
lv_obj_set_layout(tab, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scrollbar_mode(tab, LV_SCROLLBAR_MODE_ACTIVE);
// Add a label, then a box for the display settings
lv_obj_t* menuLabel = lv_label_create(tab);
lv_label_set_text(menuLabel, "Display");
lv_obj_t* menuBox = lv_obj_create(tab);
lv_obj_set_size(menuBox, lv_pct(100), 109);
lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN);
lv_obj_t* brightnessIcon = lv_img_create(menuBox);
lv_img_set_src(brightnessIcon, &low_brightness);
lv_obj_set_style_img_recolor(brightnessIcon, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_img_recolor_opa(brightnessIcon, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_align(brightnessIcon, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_t* slider = lv_slider_create(menuBox);
lv_slider_set_range(slider, 60, 255);
lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB);
lv_obj_set_style_bg_opa(slider, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_bg_color(slider, lv_color_lighten(color_primary, 50), LV_PART_MAIN);
lv_slider_set_value(slider, backlight_brightness, LV_ANIM_OFF);
lv_obj_set_size(slider, lv_pct(66), 10);
lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 3);
brightnessIcon = lv_img_create(menuBox);
lv_img_set_src(brightnessIcon, &high_brightness);
lv_obj_set_style_img_recolor(brightnessIcon, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_img_recolor_opa(brightnessIcon, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_align(brightnessIcon, LV_ALIGN_TOP_RIGHT, 0, -1);
lv_obj_add_event_cb(slider, bl_slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
menuLabel = lv_label_create(menuBox);
lv_label_set_text(menuLabel, "Lift to Wake");
lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 32);
lv_obj_t* wakeToggle = lv_switch_create(menuBox);
lv_obj_set_size(wakeToggle, 40, 22);
lv_obj_align(wakeToggle, LV_ALIGN_TOP_RIGHT, 0, 29);
lv_obj_set_style_bg_color(wakeToggle, lv_color_hex(0x505050), LV_PART_MAIN);
lv_obj_add_event_cb(wakeToggle, WakeEnableSetting_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
if(wakeupByIMUEnabled) lv_obj_add_state(wakeToggle, LV_STATE_CHECKED); // set default state
menuLabel = lv_label_create(menuBox);
lv_label_set_text(menuLabel, "Timeout");
lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 64);
lv_obj_t* drop = lv_dropdown_create(menuBox);
lv_dropdown_set_options(drop, "10s\n"
"20s\n"
"40s\n"
"1m\n"
"3m\n"
"10m\n"
"1h"); // 1h for debug purposes, if you don't want the device to go to slepp
// if you add more options here, do the same in timout_event_cb()
switch (actualSleepTimeout) {
case 10000: {lv_dropdown_set_selected(drop, 0); break;}
case 20000: {lv_dropdown_set_selected(drop, 1); break;}
case 40000: {lv_dropdown_set_selected(drop, 2); break;}
case 60000: {lv_dropdown_set_selected(drop, 3); break;}
case 180000: {lv_dropdown_set_selected(drop, 4); break;}
case 600000: {lv_dropdown_set_selected(drop, 5); break;}
case 3600000: {lv_dropdown_set_selected(drop, 6); break;}
}
lv_dropdown_set_selected_highlight(drop, true);
lv_obj_align(drop, LV_ALIGN_TOP_RIGHT, 0, 61);
lv_obj_set_size(drop, 70, 22);
//lv_obj_set_style_text_font(drop, &lv_font_montserrat_12, LV_PART_MAIN);
//lv_obj_set_style_text_font(lv_dropdown_get_list(drop), &lv_font_montserrat_12, LV_PART_MAIN);
lv_obj_set_style_pad_top(drop, 1, LV_PART_MAIN);
lv_obj_set_style_bg_color(drop, color_primary, LV_PART_MAIN);
lv_obj_set_style_bg_color(lv_dropdown_get_list(drop), color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(lv_dropdown_get_list(drop), 1, LV_PART_MAIN);
lv_obj_set_style_border_color(lv_dropdown_get_list(drop), lv_color_hex(0x505050), LV_PART_MAIN);
lv_obj_add_event_cb(drop, timout_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
// // Add another label, then a settings box for WiFi
// menuLabel = lv_label_create(tab);
// lv_label_set_text(menuLabel, "Wi-Fi");
// menuBox = lv_obj_create(tab);
// lv_obj_set_size(menuBox, lv_pct(100), 80);
// lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN);
// lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN);
// menuLabel = lv_label_create(menuBox);
// lv_label_set_text(menuLabel, "Network");
// menuLabel = lv_label_create(menuBox);
// lv_label_set_text(menuLabel, LV_SYMBOL_RIGHT);
// lv_obj_align(menuLabel, LV_ALIGN_TOP_RIGHT, 0, 0);
// menuLabel = lv_label_create(menuBox);
// lv_label_set_text(menuLabel, "Password");
// lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 32);
// menuLabel = lv_label_create(menuBox);
// lv_label_set_text(menuLabel, LV_SYMBOL_RIGHT);
// lv_obj_align(menuLabel, LV_ALIGN_TOP_RIGHT, 0, 32);
// Another setting for the battery
menuLabel = lv_label_create(tab);
lv_label_set_text(menuLabel, "Battery");
menuBox = lv_obj_create(tab);
lv_obj_set_size(menuBox, lv_pct(100), 77); // 125
lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN);
lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN);
objBattSettingsVoltage = lv_label_create(menuBox);
lv_label_set_text(objBattSettingsVoltage, "Voltage:");
lv_obj_align(objBattSettingsVoltage, LV_ALIGN_TOP_LEFT, 0, 0);
objBattSettingsPercentage = lv_label_create(menuBox);
lv_label_set_text(objBattSettingsPercentage, "Percentage:");
lv_obj_align(objBattSettingsPercentage, LV_ALIGN_TOP_LEFT, 0, 32);
// objBattSettingsIscharging = lv_label_create(menuBox);
// lv_label_set_text(objBattSettingsIscharging, "Is charging:");
// lv_obj_align(objBattSettingsIscharging, LV_ALIGN_TOP_LEFT, 0, 64);
}
void init_gui_pageIndicator_settings(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Settings");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
void register_gui_settings(void){
register_gui(& init_gui_tab_settings, & init_gui_pageIndicator_settings);
}

View file

@ -0,0 +1,12 @@
#ifndef __GUI_SETTINGS_H__
#define __GUI_SETTINGS_H__
#include <lvgl.h>
extern lv_obj_t* objBattSettingsVoltage;
extern lv_obj_t* objBattSettingsPercentage;
//extern lv_obj_t* objBattSettingsIscharging;
void register_gui_settings(void);
#endif /*__GUI_SETTINGS_H__*/

View file

@ -0,0 +1,176 @@
#include <map>
#include <string.h>
#include "gui_general_and_keys/keys.h"
#include "hardware/sleep.h"
#include "hardware/infrared_sender.h"
#include "hardware/mqtt.h"
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "scenes/sceneRegistry.h"
#include "scenes/sceneHandler.h"
#include "commandHandler.h"
const byte ROWS = 5; //five rows
const byte COLS = 5; //five columns
// Keypad declarations
// define the symbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
{'s','^','-','m','e'}, // source, channel+, Volume-, mute, record
{'i','r','+','k','d'}, // info, right, Volume+, OK, down
{'4','v','1','3','2'}, // blue, channel-, red, yellow, green
{'>','o','b','u','l'}, // forward, off, back, up, left
{'?','p','c','<','='} // ?, play, config, rewind, stop
};
/*
off o
stop = rewind < play p forward >
config c info i
up u
left l OK k right r
down d
back b source s
Volume+ + mute m channel+ ^
Volume- - record e channel- v
red 1 green 2 yellow 3 blue 4
*/
byte rowPins[ROWS] = {SW_A, SW_B, SW_C, SW_D, SW_E}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {SW_1, SW_2, SW_3, SW_4, SW_5}; //connect to the column pinouts of the keypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
void init_keys(void) {
// Button Pin Definition
pinMode(SW_1, OUTPUT);
pinMode(SW_2, OUTPUT);
pinMode(SW_3, OUTPUT);
pinMode(SW_4, OUTPUT);
pinMode(SW_5, OUTPUT);
pinMode(SW_A, INPUT);
pinMode(SW_B, INPUT);
pinMode(SW_C, INPUT);
pinMode(SW_D, INPUT);
pinMode(SW_E, INPUT);
}
KeyState lastKeyState[ROWS][COLS] = {
{IDLE,IDLE,IDLE,IDLE,IDLE},
{IDLE,IDLE,IDLE,IDLE,IDLE},
{IDLE,IDLE,IDLE,IDLE,IDLE},
{IDLE,IDLE,IDLE,IDLE,IDLE},
{IDLE,IDLE,IDLE,IDLE,IDLE},
};
unsigned long lastTimeSent[ROWS][COLS] ={
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
};
bool keyIsHold[ROWS][COLS] = {
{false,false,false,false,false},
{false,false,false,false,false},
{false,false,false,false,false},
{false,false,false,false,false},
{false,false,false,false,false}
};
int repeatRate = 125; // in milliseconds
void doShortPress(char keyChar, int keyCode){
unsigned long currentMillis = millis();
if ((currentMillis - lastTimeSent[keyCode/ROWS][keyCode%ROWS]) > repeatRate) {
lastTimeSent[keyCode/ROWS][keyCode%ROWS] = currentMillis;
std::string command = get_command_short(currentScene, keyChar);
if (command != COMMAND_UNKNOWN) {
Serial.printf("key: key '%c', will use command '%s'\r\n", keyChar, command.c_str());
executeCommand(command);
} else {
Serial.printf("key: key '%c', but no command defined\r\n", keyChar);
}
}
}
void doLongPress(char keyChar, int keyCode){
std::string command = get_command_long(currentScene, keyChar);
if (command != COMMAND_UNKNOWN) {
Serial.printf("key: key '%c' (long press), will use command '%s'\r\n", keyChar, command.c_str());
executeCommand(command);
} else {
Serial.printf("key: key '%c' (long press), but no command defined\r\n", keyChar);
}
}
void keypad_loop(void) {
// we have to ignore the result, because in case of SINGLE_REPEATED we want to send the command again and again, but the keypad would give us only one single HOLD state, not repeatedly
customKeypad.getKeys();
for(int i=0; i < LIST_MAX; i++) {
if (!customKeypad.key[i].stateChanged) {
// we are not allowed to do this, because of the same reason as above
// continue;
} else {
resetStandbyTimer(); // Reset the sleep timer when a button is pressed
}
char keyChar = customKeypad.key[i].kchar;
int keyCode = customKeypad.key[i].kcode;
if (customKeypad.key[i].kstate == PRESSED) {
// Serial.println("pressed");
if ((get_key_repeatMode(currentScene, keyChar) == SHORT) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != PRESSED)) {
// Serial.printf("key: PRESSED of SHORT key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);
} else if ((get_key_repeatMode(currentScene, keyChar) == SHORT_REPEATED) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != PRESSED)) { // here do not repeat it too early, do the repeat only in HOLD
// Serial.printf("key: PRESSED of SHORT_REPEATED key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);
}
lastKeyState[keyCode/ROWS][keyCode%ROWS] = PRESSED;
} else if (customKeypad.key[i].kstate == HOLD) {
// Serial.println("hold");
if ((get_key_repeatMode(currentScene, keyChar) == SHORTorLONG) && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != HOLD)) {
// Serial.printf("key: HOLD of SHORTorLONG key %c (%d)\r\n", keyChar, keyCode);
// Serial.printf("will set keyIsHold to TRUE for keycode %d\r\n", keyCode);
keyIsHold[keyCode/ROWS][keyCode%ROWS] = true;
doLongPress(keyChar, keyCode);
} else if (get_key_repeatMode(currentScene, keyChar) == SHORT_REPEATED) { // this is the only case where we do not check the lastKeyState, because here it is intended to repeat the action
// Serial.printf("key: HOLD of SHORT_REPEATED key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);
}
lastKeyState[keyCode/ROWS][keyCode%ROWS] = HOLD;
} else if (customKeypad.key[i].kstate == RELEASED) {
// Serial.println("released");
if ((get_key_repeatMode(currentScene, keyChar) == SHORTorLONG) && !keyIsHold[keyCode/ROWS][keyCode%ROWS] && (lastKeyState[keyCode/ROWS][keyCode%ROWS] != RELEASED)) {
// Serial.printf("value of keyIsHold for keycode %d is %d\r\n", keyCode, keyIsHold[keyCode/ROWS][keyCode%ROWS]);
// Serial.printf("key: RELEASED of SHORTorLONG key %c (%d)\r\n", keyChar, keyCode);
doShortPress(keyChar, keyCode);
}
// Serial.printf("will set keyIsHold to FALSE for keycode %d\r\n", keyCode);
keyIsHold[keyCode/ROWS][keyCode%ROWS] = false;
// Serial.printf("key: press of key %c (%d)\r\n", keyChar, keyCode);
lastKeyState[keyCode/ROWS][keyCode%ROWS] = RELEASED;
} else if (customKeypad.key[i].kstate == IDLE) {
// Serial.println("idle");
// Serial.printf("key: idle of key %c (%d)\r\n", keyChar, keyCode);
lastKeyState[keyCode/ROWS][keyCode%ROWS] = IDLE;
}
}
}

View file

@ -0,0 +1,37 @@
#ifndef __KEYS_H__
#define __KEYS_H__
#include <Keypad.h> // modified for inverted logic
#define SW_1 32 // 1...5: Output
#define SW_2 26
#define SW_3 27
#define SW_4 14
#define SW_5 12
#define SW_A 37 // A...E: Input
#define SW_B 38
#define SW_C 39
#define SW_D 34
#define SW_E 35
#define BUTTON_PIN_BITMASK 0b1110110000000000000000000010000000000000 //IO34+IO35+IO37+IO38+IO39(+IO13)
enum repeatModes {
// only as fallback
REPEAT_MODE_UNKNOWN,
// if you short press or hold a key on the keypad, only one single command from keyCommands_short is sent
// -> best used if you do not want a command to be sent more than once, even if you press the key (too) long, e.g. when toggling power
SHORT,
// if you hold a key on the keypad, a command from keyCommands_short is sent repeatedly
// -> best used e.g. for holding the key for "volume up"
SHORT_REPEATED,
// if you short press a key, a command from keyCommands_short is sent once.
// if you hold a key on the keypad, a command from keyCommands_long is sent (no command from keyCommands_short before)
// -> best used if a long key press should send a different command than a short press
SHORTorLONG,
};
void init_keys(void);
void keypad_loop(void);
#endif /*__KEYS_H__*/

View file

@ -49,7 +49,7 @@
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/
#define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
#define LV_MEM_ADR 0 /*0: unused*/
@ -354,7 +354,7 @@
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
*https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_10 1
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1

View file

@ -0,0 +1,75 @@
#include <Arduino.h>
#include "hardware/battery.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/gui_settings.h"
void init_battery(void) {
// Currently the battery charge status cannot be recognized in a reliable way due to a design flaw in the PCB.
// See https://github.com/CoretechR/OMOTE/issues/55
// So charge status is deactivated for now.
//pinMode(CRG_STAT, INPUT_PULLUP);
pinMode(ADC_BAT, INPUT);
}
// Battery declares
int battery_analogRead = 0;
int battery_voltage = 0;
int battery_percentage = 100;
//bool battery_ischarging = false;
void update_battery_stats(void) {
battery_analogRead = analogRead(ADC_BAT);
// original values
// battery_voltage = battery_analogRead*2*3300/4095 + 350; // 350mV ADC offset
// adjusted values due to new measurements
battery_voltage = battery_analogRead*2*3350/4095 + 325;
battery_percentage = constrain(map(battery_voltage, 3700, 4200, 0, 100), 0, 100);
// Check if battery is charging, fully charged or disconnected
/*
"disconnected" cannot be recognized
https://electronics.stackexchange.com/questions/615215/level-shifting-of-a-3-state-pin
https://electrical.codidact.com/posts/286209
https://how2electronics.com/lithium-ion-battery-charger-circuit-using-mcp73831/
*/
//battery_ischarging = !digitalRead(CRG_STAT);
char buffer1[20];
sprintf(buffer1, "Voltage: %.2f V", (float)battery_voltage / 1000);
// GUI settings
lv_label_set_text_fmt(objBattSettingsVoltage, buffer1);
lv_label_set_text_fmt(objBattSettingsPercentage, "Percentage: %d%%", battery_percentage);
//lv_label_set_text_fmt(objBattSettingsIscharging, "Is charging: %s", battery_ischarging ? "yes" : "no");
// GUI status bar at the top
char buffer2[12];
// Voltage and percentage
// sprintf(buffer2, "%.1fV, %d%%", (float)battery_voltage / 1000, battery_percentage);
// only percentage
sprintf(buffer2, "%d%%", battery_percentage);
// sprintf(buffer, "%d%%", battery_percentage);
for (int i=0; i<strlen(buffer2); i++) {
if (buffer2[i] == '.') {
buffer2[i] = ',';
}
}
// if (battery_ischarging /*|| (!battery_ischarging && battery_voltage > 4350)*/){
// // lv_label_set_text(objBattPercentage, "");
// // lv_label_set_text_fmt(objBattPercentage, "%d%%", battery_percentage);
// // lv_label_set_text_fmt(objBattPercentage, "%.1f, %d%%", (float)battery_voltage / 1000, battery_percentage);
// lv_label_set_text(objBattPercentage, buffer2);
// lv_label_set_text(objBattIcon, LV_SYMBOL_USB);
// } else
{
// Update status bar battery indicator
// lv_label_set_text_fmt(objBattPercentage, "%.1f, %d%%", (float)battery_voltage / 1000, battery_percentage);
lv_label_set_text(objBattPercentage, buffer2);
if(battery_percentage > 95) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_FULL);
else if(battery_percentage > 75) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_3);
else if(battery_percentage > 50) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_2);
else if(battery_percentage > 25) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_1);
else lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_EMPTY);
}
}

View file

@ -0,0 +1,12 @@
#ifndef __BATTERY_H__
#define __BATTERY_H__
//#define CRG_STAT 21 // battery charger feedback, GPIO21, VSPIHD, EMAC_TX_EN
#define ADC_BAT 36 // Battery voltage sense input (1/2 divider), GPIO36, ADC1_CH0, RTC_GPIO0
extern int battery_percentage;
void init_battery(void);
void update_battery_stats(void);
#endif /*__BATTERY_H__*/

View file

@ -0,0 +1,197 @@
/*
Mainly based on IRrecvDumpV2 from IRremoteESP8266
Slightly changed to run with OMOTE hardware.
*/
/*
* IRremoteESP8266: IRrecvDumpV2 - dump details of IR codes with IRrecv
* An IR detector/demodulator must be connected to the input kRecvPin.
*
* Copyright 2009 Ken Shirriff, http://arcfn.com
* Copyright 2017-2019 David Conran
*
* Example circuit diagram:
* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving
*
* Changes:
* Version 1.2 October, 2020
* - Enable easy setting of the decoding tolerance value.
* Version 1.0 October, 2019
* - Internationalisation (i18n) support.
* - Stop displaying the legacy raw timing info.
* Version 0.5 June, 2019
* - Move A/C description to IRac.cpp.
* Version 0.4 July, 2018
* - Minor improvements and more A/C unit support.
* Version 0.3 November, 2017
* - Support for A/C decoding for some protocols.
* Version 0.2 April, 2017
* - Decode from a copy of the data so we can start capturing faster thus
* reduce the likelihood of miscaptures.
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
*/
//#include <Arduino.h>
#include <assert.h>
#include <IRrecv.h>
#include <IRremoteESP8266.h>
#include <IRac.h>
#include <IRtext.h>
#include <IRutils.h>
#include "hardware/infrared_receiver.h"
#include "gui_general_and_keys/gui_irReceiver.h"
bool irReceiverEnabled = false;
// The Serial connection baud rate.
// i.e. Status message will be sent to the PC at this baud rate.
// Try to avoid slow speeds like 9600, as you will miss messages and
// cause other problems. 115200 (or faster) is recommended.
// NOTE: Make sure you set your Serial Monitor to the same speed.
const uint32_t kBaudRate = 115200;
// As this program is a special purpose capture/decoder, let us use a larger
// than normal buffer so we can handle Air Conditioner remote codes.
const uint16_t kCaptureBufferSize = 1024;
// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a
// message ended.
// This parameter is an interesting trade-off. The longer the timeout, the more
// complex a message it can capture. e.g. Some device protocols will send
// multiple message packets in quick succession, like Air Conditioner remotes.
// Air Coniditioner protocols often have a considerable gap (20-40+ms) between
// packets.
// The downside of a large timeout value is a lot of less complex protocols
// send multiple messages when the remote's button is held down. The gap between
// them is often also around 20+ms. This can result in the raw data be 2-3+
// times larger than needed as it has captured 2-3+ messages in a single
// capture. Setting a low timeout value can resolve this.
// So, choosing the best kTimeout value for your use particular case is
// quite nuanced. Good luck and happy hunting.
// NOTE: Don't exceed kMaxTimeoutMs. Typically 130ms.
#if DECODE_AC
// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator
// A value this large may swallow repeats of some protocols
const uint8_t kTimeout = 50;
#else // DECODE_AC
// Suits most messages, while not swallowing many repeats.
const uint8_t kTimeout = 15;
#endif // DECODE_AC
// Alternatives:
// const uint8_t kTimeout = 90;
// Suits messages with big gaps like XMP-1 & some aircon units, but can
// accidentally swallow repeated messages in the rawData[] output.
//
// const uint8_t kTimeout = kMaxTimeoutMs;
// This will set it to our currently allowed maximum.
// Values this high are problematic because it is roughly the typical boundary
// where most messages repeat.
// e.g. It will stop decoding a message and start sending it to serial at
// precisely the time when the next message is likely to be transmitted,
// and may miss it.
// Set the smallest sized "UNKNOWN" message packets we actually care about.
// This value helps reduce the false-positive detection rate of IR background
// noise as real messages. The chances of background IR noise getting detected
// as a message increases with the length of the kTimeout value. (See above)
// The downside of setting this message too large is you can miss some valid
// short messages for protocols that this library doesn't yet decode.
//
// Set higher if you get lots of random short UNKNOWN messages when nothing
// should be sending a message.
// Set lower if you are sure your setup is working, but it doesn't see messages
// from your device. (e.g. Other IR remotes work.)
// NOTE: Set this value very high to effectively turn off UNKNOWN detection.
const uint16_t kMinUnknownSize = 12;
// How much percentage lee way do we give to incoming signals in order to match
// it?
// e.g. +/- 25% (default) to an expected value of 500 would mean matching a
// value between 375 & 625 inclusive.
// Note: Default is 25(%). Going to a value >= 50(%) will cause some protocols
// to no longer match correctly. In normal situations you probably do not
// need to adjust this value. Typically that's when the library detects
// your remote's message some of the time, but not all of the time.
const uint8_t kTolerancePercentage = kTolerance; // kTolerance is normally 25%
// Legacy (No longer supported!)
//
// Change to `true` if you miss/need the old "Raw Timing[]" display.
#define LEGACY_TIMING_INFO false
// ==================== end of TUNEABLE PARAMETERS ====================
// Use turn on the save buffer feature for more complete capture coverage.
IRrecv irrecv(IR_RX, kCaptureBufferSize, kTimeout, true);
decode_results results; // Somewhere to store the results
// This section of code runs only once at start-up.
void init_infraredReceiver() {
pinMode(IR_RX, INPUT);
pinMode(IR_VCC, OUTPUT);
digitalWrite(IR_VCC, HIGH); // Turn on IR receiver
// Perform a low level sanity checks that the compiler performs bit field
// packing as we expect and Endianness is as we expect.
assert(irutils::lowLevelSanityCheck() == 0);
Serial.printf("\n" D_STR_IRRECVDUMP_STARTUP "\n", IR_RX);
#if DECODE_HASH
// Ignore messages with less than minimum on or off pulses.
irrecv.setUnknownThreshold(kMinUnknownSize);
#endif // DECODE_HASH
irrecv.setTolerance(kTolerancePercentage); // Override the default tolerance.
irrecv.enableIRIn(); // Start the receiver
}
void shutdown_infraredReceiver() {
irrecv.disableIRIn();
digitalWrite(IR_VCC, LOW); // IR Receiver off
}
// The repeating section of the code
void infraredReceiver_loop() {
// Check if the IR code has been received.
if (irrecv.decode(&results)) {
// Display a crude timestamp.
uint32_t now = millis();
Serial.printf(D_STR_TIMESTAMP " : %06u.%03u\n", now / 1000, now % 1000);
// Check if we got an IR message that was to big for our capture buffer.
if (results.overflow)
Serial.printf(D_WARN_BUFFERFULL "\n", kCaptureBufferSize);
// Display the library version the message was captured with.
Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_STR "\n");
// Display the tolerance percentage if it has been change from the default.
if (kTolerancePercentage != kTolerance)
Serial.printf(D_STR_TOLERANCE " : %d%%\n", kTolerancePercentage);
// Display the basic output of what we found.
Serial.print(resultToHumanReadableBasic(&results));
// Display any extra A/C info if we have it.
String description = IRAcUtils::resultAcToString(&results);
if (description.length()) Serial.println(D_STR_MESGDESC ": " + description);
yield(); // Feed the WDT as the text output can take a while to print.
#if LEGACY_TIMING_INFO
// Output legacy RAW timing info of the result.
Serial.println(resultToTimingInfo(&results));
yield(); // Feed the WDT (again)
#endif // LEGACY_TIMING_INFO
// Output the results as source code
Serial.println(resultToSourceCode(&results));
Serial.println(); // Blank line between entries
String message = "";
message += typeToString((&results)->decode_type, (&results)->repeat);
message += " ";
message += resultToHexidecimal(&results);
showNewIRmessage(message);
yield(); // Feed the WDT (again)
// if message repeats infinitely, you can try one of these two workarounds. Don't know why this is needed.
// irrecv.resume();
// another workaround could be:
// irrecv.disableIRIn();
// irrecv.enableIRIn();
}
}

View file

@ -0,0 +1,13 @@
#ifndef __INFRARED_RECEIVER_H__
#define __INFRARED_RECEIVER_H__
#define IR_RX 15 // IR receiver input
#define IR_VCC 25 // IR receiver power
extern bool irReceiverEnabled;
void init_infraredReceiver(void);
void shutdown_infraredReceiver(void);
void infraredReceiver_loop(void);
#endif /*__INFRARED_RECEIVER_H__*/

View file

@ -0,0 +1,15 @@
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <IRrecv.h>
#include <IRutils.h>
#include "hardware/infrared_sender.h"
IRsend IrSender(IR_LED, true);
void init_infraredSender(void) {
// IR Pin Definition
pinMode(IR_LED, OUTPUT);
digitalWrite(IR_LED, HIGH); // HIGH off - LOW on
IrSender.begin();
}

View file

@ -0,0 +1,14 @@
#ifndef __INFRARED_H__
#define __INFRARED_H__
#include <IRsend.h>
#include <IRrecv.h>
#define IR_LED 33 // IR LED output
// IR declarations
extern IRsend IrSender;
void init_infraredSender(void);
#endif /*__INFRARED_H__*/

View file

@ -0,0 +1,88 @@
#include <PubSubClient.h>
#include <lvgl.h>
#include "hardware/mqtt.h"
#include "gui_general_and_keys/guiBase.h"
#include "secrets.h"
#include "commandHandler.h"
#ifdef ENABLE_WIFI_AND_MQTT
WiFiClient espClient;
PubSubClient mqttClient(espClient);
// WiFi status event
void WiFiEvent(WiFiEvent_t event){
//Serial.printf("[WiFi-event] event: %d\r\n", event);
if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP){
// connection to MQTT server will be done in checkMQTTconnection()
// mqttClient.setServer(MQTT_SERVER, 1883); // MQTT initialization
// mqttClient.connect("OMOTE"); // Connect using a client id
}
// Set status bar icon based on WiFi status
if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP || event == ARDUINO_EVENT_WIFI_STA_GOT_IP6) {
if (WifiLabel != NULL) {lv_label_set_text(WifiLabel, LV_SYMBOL_WIFI);}
} else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
if (WifiLabel != NULL) {lv_label_set_text(WifiLabel, "");}
// automatically try to reconnect
Serial.printf("WiFi got disconnected. Will try to reconnect.\r\n");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
} else {
// e.g. ARDUINO_EVENT_WIFI_STA_CONNECTED or many others
// connected is not enough, will wait for IP
if (WifiLabel != NULL) {lv_label_set_text(WifiLabel, "");}
}
}
void init_mqtt(void) {
// Setup WiFi
WiFi.setHostname("OMOTE"); //define hostname
WiFi.onEvent(WiFiEvent);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
WiFi.setSleep(true);
}
bool checkMQTTconnection() {
if (WiFi.isConnected()) {
if (mqttClient.connected()) {
return true;
} else {
// try to connect to mqtt server
mqttClient.setServer(MQTT_SERVER, MQTT_SERVER_PORT); // MQTT initialization
if (mqttClient.connect(MQTT_CLIENTNAME, MQTT_USER, MQTT_PASS)) {
Serial.printf(" Successfully connected to MQTT broker\r\n");
} else {
Serial.printf(" MQTT connection failed (but WiFi is available). Will try later ...\r\n");
}
return mqttClient.connected();
}
} else {
Serial.printf(" No connection to MQTT server, because WiFi ist not connected.\r\n");
return false;
}
}
bool publishMQTTMessage(const char *topic, const char *payload){
if (checkMQTTconnection()) {
// Serial.printf("Sending mqtt payload to topic \"%s\": %s\r\n", topic, payload);
if (mqttClient.publish(topic, payload)) {
// Serial.printf("Publish ok\r\n");
return true;
}
else {
Serial.printf("Publish failed\r\n");
}
} else {
Serial.printf(" Cannot publish mqtt message, because checkMQTTconnection failed (WiFi or mqtt is not connected)\r\n");
}
return false;
}
#endif

View file

@ -0,0 +1,15 @@
#ifndef __MQTT_H__
#define __MQTT_H__
#include "commandHandler.h"
#ifdef ENABLE_WIFI_AND_MQTT
#include "WiFi.h"
#include <PubSubClient.h>
void init_mqtt(void);
bool publishMQTTMessage(const char *topic, const char *payload);
#endif
#endif /*__MQTT_H__*/

View file

@ -0,0 +1,210 @@
#include <Arduino.h>
#include "SparkFunLIS3DH.h"
#include "hardware/sleep.h"
#include "hardware/tft.h"
#include "hardware/mqtt.h"
#include "hardware/infrared_sender.h"
#include "hardware/infrared_receiver.h"
#include "hardware/battery.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/keys.h"
#include "preferencesStorage.h"
#include "commandHandler.h"
#include "scenes/sceneHandler.h"
int motion = 0;
uint32_t actualSleepTimeout;
uint32_t standbyTimer;
bool wakeupByIMUEnabled = true;
LIS3DH IMU(I2C_MODE, 0x19); // Default constructor is I2C, addr 0x19.
byte wakeup_reason;
void resetStandbyTimer() {
standbyTimer = actualSleepTimeout;
}
void activityDetection(){
static int accXold;
static int accYold;
static int accZold;
int accX = IMU.readFloatAccelX()*1000;
int accY = IMU.readFloatAccelY()*1000;
int accZ = IMU.readFloatAccelZ()*1000;
// determine motion value as da/dt
motion = (abs(accXold - accX) + abs(accYold - accY) + abs(accZold - accZ));
// Calculate time to standby
standbyTimer -= 100;
if(standbyTimer < 0) standbyTimer = 0;
// If the motion exceeds the threshold, the standbyTimer is reset
if(motion > MOTION_THRESHOLD) resetStandbyTimer();
// Store the current acceleration and time
accXold = accX;
accYold = accY;
accZold = accZ;
}
void configIMUInterrupts()
{
uint8_t dataToWrite = 0;
//LIS3DH_INT1_CFG
//dataToWrite |= 0x80;//AOI, 0 = OR 1 = AND
//dataToWrite |= 0x40;//6D, 0 = interrupt source, 1 = 6 direction source
//Set these to enable individual axes of generation source (or direction)
// -- high and low are used generically
dataToWrite |= 0x20;//Z high
//dataToWrite |= 0x10;//Z low
dataToWrite |= 0x08;//Y high
//dataToWrite |= 0x04;//Y low
dataToWrite |= 0x02;//X high
//dataToWrite |= 0x01;//X low
if(wakeupByIMUEnabled) IMU.writeRegister(LIS3DH_INT1_CFG, 0b00101010);
else IMU.writeRegister(LIS3DH_INT1_CFG, 0b00000000);
//LIS3DH_INT1_THS
dataToWrite = 0;
//Provide 7 bit value, 0x7F always equals max range by accelRange setting
dataToWrite |= 0x45;
IMU.writeRegister(LIS3DH_INT1_THS, dataToWrite);
//LIS3DH_INT1_DURATION
dataToWrite = 0;
//minimum duration of the interrupt
//LSB equals 1/(sample rate)
dataToWrite |= 0x00; // 1 * 1/50 s = 20ms
IMU.writeRegister(LIS3DH_INT1_DURATION, dataToWrite);
//LIS3DH_CTRL_REG5
//Int1 latch interrupt and 4D on int1 (preserve fifo en)
IMU.readRegister(&dataToWrite, LIS3DH_CTRL_REG5);
dataToWrite &= 0xF3; //Clear bits of interest
dataToWrite |= 0x08; //Latch interrupt (Cleared by reading int1_src)
//dataToWrite |= 0x04; //Pipe 4D detection from 6D recognition to int1?
IMU.writeRegister(LIS3DH_CTRL_REG5, dataToWrite);
//LIS3DH_CTRL_REG3
//Choose source for pin 1
dataToWrite = 0;
//dataToWrite |= 0x80; //Click detect on pin 1
dataToWrite |= 0x40; //AOI1 event (Generator 1 interrupt on pin 1)
dataToWrite |= 0x20; //AOI2 event ()
//dataToWrite |= 0x10; //Data ready
//dataToWrite |= 0x04; //FIFO watermark
//dataToWrite |= 0x02; //FIFO overrun
IMU.writeRegister(LIS3DH_CTRL_REG3, dataToWrite);
}
// Enter Sleep Mode
void enterSleep(){
// Save settings to internal flash memory
save_preferences();
// Configure IMU
uint8_t intDataRead;
IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//clear interrupt
configIMUInterrupts();
IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//really clear interrupt
#ifdef ENABLE_WIFI_AND_MQTT
// Power down modem
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
#endif
#ifdef ENABLE_KEYBOARD_BLE
bleKeyboard.end();
#endif
// Prepare IO states
digitalWrite(LCD_DC, LOW); // LCD control signals off
digitalWrite(LCD_CS, LOW);
digitalWrite(LCD_MOSI, LOW);
digitalWrite(LCD_SCK, LOW);
digitalWrite(LCD_EN, HIGH); // LCD logic off
digitalWrite(LCD_BL, HIGH); // LCD backlight off
// pinMode(CRG_STAT, INPUT); // Disable Pull-Up
digitalWrite(IR_VCC, LOW); // IR Receiver off
// Configure button matrix for ext1 interrupt
pinMode(SW_1, OUTPUT);
pinMode(SW_2, OUTPUT);
pinMode(SW_3, OUTPUT);
pinMode(SW_4, OUTPUT);
pinMode(SW_5, OUTPUT);
digitalWrite(SW_1, HIGH);
digitalWrite(SW_2, HIGH);
digitalWrite(SW_3, HIGH);
digitalWrite(SW_4, HIGH);
digitalWrite(SW_5, HIGH);
gpio_hold_en((gpio_num_t)SW_1);
gpio_hold_en((gpio_num_t)SW_2);
gpio_hold_en((gpio_num_t)SW_3);
gpio_hold_en((gpio_num_t)SW_4);
gpio_hold_en((gpio_num_t)SW_5);
// Force display pins to high impedance
// Without this the display might not wake up from sleep
pinMode(LCD_BL, INPUT);
pinMode(LCD_EN, INPUT);
gpio_hold_en((gpio_num_t)LCD_BL);
gpio_hold_en((gpio_num_t)LCD_EN);
gpio_deep_sleep_hold_en();
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, ESP_EXT1_WAKEUP_ANY_HIGH);
delay(100);
// Sleep
esp_deep_sleep_start();
}
void init_sleep() {
if (actualSleepTimeout == 0){
actualSleepTimeout = DEFAULT_SLEEP_TIMEOUT;
}
// Find out wakeup cause
if(esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1){
if(log(esp_sleep_get_ext1_wakeup_status())/log(2) == 13) wakeup_reason = WAKEUP_BY_IMU;
else wakeup_reason = WAKEUP_BY_KEYPAD;
}
else {
wakeup_reason = WAKEUP_BY_RESET;
}
pinMode(ACC_INT, INPUT);
// Release GPIO hold in case we are coming out of standby
gpio_hold_dis((gpio_num_t)SW_1);
gpio_hold_dis((gpio_num_t)SW_2);
gpio_hold_dis((gpio_num_t)SW_3);
gpio_hold_dis((gpio_num_t)SW_4);
gpio_hold_dis((gpio_num_t)SW_5);
gpio_hold_dis((gpio_num_t)LCD_EN);
gpio_hold_dis((gpio_num_t)LCD_BL);
gpio_deep_sleep_hold_dis();
}
void setup_IMU(void) {
// Setup IMU
IMU.settings.accelSampleRate = 50; //Hz. Can be: 0,1,10,25,50,100,200,400,1600,5000 Hz
IMU.settings.accelRange = 2; //Max G force readable. Can be: 2, 4, 8, 16
IMU.settings.adcEnabled = 0;
IMU.settings.tempEnabled = 0;
IMU.settings.xAccelEnabled = 1;
IMU.settings.yAccelEnabled = 1;
IMU.settings.zAccelEnabled = 1;
IMU.begin();
uint8_t intDataRead;
IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//clear interrupt
}
void check_activity() {
activityDetection();
if(standbyTimer == 0){
Serial.println("Entering Sleep Mode. Goodbye.");
enterSleep();
}
}

View file

@ -0,0 +1,25 @@
#ifndef __SLEEP_H__
#define __SLEEP_H__
#include <Arduino.h>
#include "SparkFunLIS3DH.h"
#define ACC_INT 20
// IMU declarations
#define DEFAULT_SLEEP_TIMEOUT 20000 // time until device enters sleep mode in milliseconds
extern uint32_t actualSleepTimeout;
extern uint32_t standbyTimer;
extern bool wakeupByIMUEnabled;
#define MOTION_THRESHOLD 50 // motion above threshold keeps device awake
// Other declarations
extern byte wakeup_reason;
enum Wakeup_reasons{WAKEUP_BY_RESET, WAKEUP_BY_IMU, WAKEUP_BY_KEYPAD};
void init_sleep();
void setup_IMU();
void check_activity();
void resetStandbyTimer();
#endif /*__SLEEP_H__*/

View file

@ -0,0 +1,71 @@
#include <Arduino.h>
#include "driver/ledc.h"
#include "hardware/tft.h"
#include "hardware/sleep.h"
TFT_eSPI tft = TFT_eSPI();
Adafruit_FT6206 touch = Adafruit_FT6206();
TS_Point touchPoint;
TS_Point oldPoint;
byte backlight_brightness = 255;
void init_tft(void) {
// LCD Pin Definition
pinMode(LCD_EN, OUTPUT);
digitalWrite(LCD_EN, HIGH);
pinMode(LCD_BL, OUTPUT);
digitalWrite(LCD_BL, HIGH);
// Configure the backlight PWM
// Manual setup because ledcSetup() briefly turns on the backlight
ledc_channel_config_t ledc_channel_left;
ledc_channel_left.gpio_num = (gpio_num_t)LCD_BL;
ledc_channel_left.speed_mode = LEDC_HIGH_SPEED_MODE;
ledc_channel_left.channel = LEDC_CHANNEL_5;
ledc_channel_left.intr_type = LEDC_INTR_DISABLE;
ledc_channel_left.timer_sel = LEDC_TIMER_1;
ledc_channel_left.flags.output_invert = 1; // Can't do this with ledcSetup()
ledc_channel_left.duty = 0;
ledc_timer_config_t ledc_timer;
ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;
ledc_timer.duty_resolution = LEDC_TIMER_8_BIT;
ledc_timer.timer_num = LEDC_TIMER_1;
ledc_timer.freq_hz = 640;
ledc_channel_config(&ledc_channel_left);
ledc_timer_config(&ledc_timer);
// Setup TFT
// Slowly charge the VSW voltage to prevent a brownout
// Workaround for hardware rev 1!
for(int i = 0; i < 100; i++){
digitalWrite(LCD_EN, HIGH); // LCD Logic off
delayMicroseconds(1);
digitalWrite(LCD_EN, LOW); // LCD Logic on
}
delay(100); // Wait for the LCD driver to power on
tft.init();
tft.initDMA();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true);
// Setup touchscreen
Wire.begin(SDA, SCL, 400000); // Configure i2c pins and set frequency to 400kHz
touch.begin(128); // Initialize touchscreen and set sensitivity threshold
}
void update_backligthBrighness(void) {
static int fadeInTimer = millis(); // fadeInTimer = time after setup
if(millis() < fadeInTimer + backlight_brightness){ // Fade in the backlight brightness
ledcWrite(5, millis()-fadeInTimer);
}
else { // Dim Backlight before entering standby
if(standbyTimer < 2000) ledcWrite(5, 85); // Backlight dim
else ledcWrite(5, backlight_brightness); // Backlight on
}
}

View file

@ -0,0 +1,28 @@
#ifndef __TFT_H__
#define __TFT_H__
#include <TFT_eSPI.h> // Hardware-specific library
#include <Adafruit_FT6206.h>
#define LCD_DC 9 // defined in TFT_eSPI User_Setup.h
#define LCD_CS 5
#define LCD_MOSI 23
#define LCD_SCK 18
#define LCD_BL 4
#define LCD_EN 10
#define SCL 22
#define SDA 19
// LCD declarations
extern TFT_eSPI tft;
#define screenWidth 240
#define screenHeight 320
extern Adafruit_FT6206 touch;
extern TS_Point touchPoint;
extern byte backlight_brightness;
void init_tft(void);
void update_backligthBrighness(void);
#endif /*__TFT_H__*/

View file

@ -0,0 +1,10 @@
#include <Arduino.h>
#include "hardware/user_led.h"
void init_userled(void) {
pinMode(USER_LED, OUTPUT);
digitalWrite(USER_LED, LOW);
}
void update_userled(void) {
digitalWrite(USER_LED, millis() % 1000 > 500);
}

View file

@ -0,0 +1,9 @@
#ifndef __USER_LED_H__
#define __USER_LED_H__
#define USER_LED 2
void init_userled(void);
void update_userled(void);
#endif /*__USER_LED_H__*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
#include "preferencesStorage.h"
#include "hardware/sleep.h"
#include "hardware/tft.h"
#include "gui_general_and_keys/guiBase.h"
#include "commandHandler.h"
#include "scenes/sceneHandler.h"
Preferences preferences;
void init_preferences(void) {
// Restore settings from internal flash memory
preferences.begin("settings", false);
if(preferences.getBool("alreadySetUp")){
wakeupByIMUEnabled = preferences.getBool("wkpByIMU");
actualSleepTimeout = preferences.getUInt("slpTimeout");
backlight_brightness = preferences.getUChar("blBrightness");
currentScreen = preferences.getUChar("currentScreen");
currentScene = std::string(preferences.getString("currentScene").c_str());
// Serial.printf("Preferences restored: brightness %d, screen %d, scene %s\r\n", backlight_brightness, currentScreen, currentScene.c_str());
} else {
// Serial.printf("No preferences to restore\r\n");
}
preferences.end();
}
void save_preferences(void) {
preferences.begin("settings", false);
preferences.putBool("wkpByIMU", wakeupByIMUEnabled);
preferences.putUInt("slpTimeout", actualSleepTimeout);
preferences.putUChar("blBrightness", backlight_brightness);
preferences.putUChar("currentScreen", currentScreen);
preferences.putString("currentScene", currentScene.c_str());
if(!preferences.getBool("alreadySetUp")) preferences.putBool("alreadySetUp", true);
preferences.end();
}

View file

@ -0,0 +1,11 @@
#ifndef __PREFERENCESSTORAGE_H__
#define __PREFERENCESSTORAGE_H__
#include <Preferences.h>
extern Preferences preferences;
void init_preferences(void);
void save_preferences(void);
#endif /*__PREFERENCESSTORAGE_H__*/

View file

@ -0,0 +1,55 @@
#include <string>
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "scenes/sceneRegistry.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "commandHandler.h"
#include "gui_general_and_keys/guiBase.h"
std::string currentScene = "";
void handleScene(std::string command, commandData commandData, std::string additionalPayload = "") {
auto current = commandData.commandPayloads.begin();
std::string scene_name = *current;
// check if we know the new scene
if (!sceneExists(scene_name)) {
Serial.printf("scene: cannot start scene %s, because it is unknown\r\n", scene_name.c_str());
return;
} else {
Serial.printf("scene: will switch from old scene %s to new scene %s\r\n", currentScene.c_str(), scene_name.c_str());
}
lv_label_set_text(SceneLabel, "changing...");
gui_loop();
// end old scene
if (!sceneExists(currentScene) && (currentScene != "")) {
Serial.printf("scene: WARNING: cannot end scene %s, because it is unknown\r\n", currentScene.c_str());
} else {
if (currentScene != "") {
Serial.printf("scene: will call end sequence for scene %s\r\n", currentScene.c_str());
scene_end_sequence_from_registry(currentScene);
}
}
// start new scene
Serial.printf("scene: will call start sequence for scene %s\r\n", scene_name.c_str());
scene_start_sequence_from_registry(scene_name);
currentScene = scene_name;
lv_label_set_text(SceneLabel, currentScene.c_str());
Serial.printf("scene: scene handling finished, new scene %s is active\r\n", currentScene.c_str());
}
void setLabelCurrentScene() {
lv_label_set_text(SceneLabel, currentScene.c_str());
}

View file

@ -0,0 +1,12 @@
#ifndef __SCENEHANDLER_H__
#define __SCENEHANDLER_H__
#include <string>
#include "commandHandler.h"
extern std::string currentScene; // Current scene that is active
void handleScene(std::string command, commandData commandData, std::string additionalPayload = "");
void setLabelCurrentScene();
#endif /*__SCENEHANDLER_H__*/

View file

@ -0,0 +1,167 @@
#include <map>
#include <string>
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "scenes/sceneRegistry.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "commandHandler.h"
std::map<char, repeatModes> key_repeatModes_default {
{'o', SHORT },
{'=', SHORT }, {'<', SHORTorLONG }, {'p', SHORT }, {'>', SHORTorLONG },
{'c', SHORT }, {'i', SHORT },
{'u', SHORT },
{'l', SHORT }, {'k', SHORT }, {'r', SHORT },
{'d', SHORT },
{'b', SHORT }, {'s', SHORT },
{'+', SHORT_REPEATED}, {'m', SHORT }, {'^', SHORT },
{'-', SHORT_REPEATED}, {'e', SHORT }, {'v', SHORT },
{'1', SHORT }, {'2', SHORT }, {'3', SHORT }, {'4', SHORT },
};
std::map<char, std::string> key_commands_short_default {
{'o', SCENE_ALLOFF},
/*{'=', COMMAND_UNKNOWN},*/ /*{'<', COMMAND_UNKNOWN},*/ /*{'p', COMMAND_UNKNOWN},*/ /*{'>', COMMAND_UNKNOWN},*/
/*{'c', COMMAND_UNKNOWN}, */ /*{'i', COMMAND_UNKNOWN},*/
/*{'u', COMMAND_UNKNOWN},*/
/*{'l', COMMAND_UNKNOWN},*/ /*{'k', COMMAND_UNKNOWN},*/ /*{'r', COMMAND_UNKNOWN},*/
/*{'d', COMMAND_UNKNOWN},*/
/* {'b', COMMAND_UNKNOWN},*/ /*{'s', COMMAND_UNKNOWN},*/
{'+', YAMAHA_VOL_PLUS}, {'m', YAMAHA_MUTE_TOGGLE}, /*{'^', COMMAND_UNKNOWN},*/
{'-', YAMAHA_VOL_MINUS}, /*{'e', COMMAND_UNKNOWN},*/ /*{'v', COMMAND_UNKNOWN},*/
{'1', SCENE_TV}, {'2', SCENE_FIRETV}, {'3', SCENE_CHROMECAST}, {'4', YAMAHA_STANDARD},
};
std::map<char, std::string> key_commands_long_default {
};
// https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work
struct scene_definition {
scene_start_sequence this_scene_start_sequence;
scene_end_sequence this_scene_end_sequence;
key_repeatModes this_key_repeatModes;
key_commands_short this_key_commands_short;
key_commands_long this_key_commands_long;
};
std::map<std::string, scene_definition> registered_scenes;
void register_scene(
std::string a_scene_name,
scene_start_sequence a_scene_start_sequence,
scene_end_sequence a_scene_end_sequence,
key_repeatModes a_key_repeatModes,
key_commands_short a_key_commands_short,
key_commands_long a_key_commands_long) {
registered_scenes[a_scene_name] = scene_definition{
a_scene_start_sequence,
a_scene_end_sequence,
a_key_repeatModes,
a_key_commands_short,
a_key_commands_long
};
}
bool sceneExists(std::string sceneName) {
return (registered_scenes.count(sceneName) > 0);
}
void scene_start_sequence_from_registry(std::string sceneName) {
try {
registered_scenes.at(sceneName).this_scene_start_sequence();
}
catch (const std::out_of_range& oor) {
Serial.printf("scene_start_sequence_from_registry: internal error, sceneName not registered\r\n");
}
}
void scene_end_sequence_from_registry(std::string sceneName) {
try {
registered_scenes.at(sceneName).this_scene_end_sequence();
}
catch (const std::out_of_range& oor) {
Serial.printf("scene_end_sequence_from_registry: internal error, sceneName not registered\r\n");
}
}
repeatModes get_key_repeatMode(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_repeatModes->count(keyChar) > 0)) {
// Serial.printf("get_key_repeatMode: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_repeatModes->at(keyChar);
// look if there is a default definition
} else if (key_repeatModes_default.count(keyChar) > 0) {
// Serial.printf("get_key_repeatMode: will use default key\r\n");
return key_repeatModes_default.at(keyChar);
// no key definition found
} else {
// Serial.printf("get_key_repeatMode: WARNING no key definition found\r\n");
return REPEAT_MODE_UNKNOWN;
}
}
catch (const std::out_of_range& oor) {
Serial.printf("get_key_repeatMode: internal error, sceneName not registered\r\n");
return REPEAT_MODE_UNKNOWN;
}
}
std::string get_command_short(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_commands_short->count(keyChar) > 0)) {
Serial.printf("get_command_short: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_commands_short->at(keyChar);
// look if there is a default definition
} else if (key_commands_short_default.count(keyChar) > 0) {
Serial.printf("get_command_short: will use default key\r\n");
return key_commands_short_default.at(keyChar);
// no key definition found
} else {
Serial.printf("get_command_short: WARNING no key definition found\r\n");
return COMMAND_UNKNOWN;
}
}
catch (const std::out_of_range& oor) {
Serial.printf("get_command_short: internal error, sceneName not registered\r\n");
return COMMAND_UNKNOWN;
}
}
std::string get_command_long(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_commands_long->count(keyChar) > 0)) {
Serial.printf("get_command_long: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_commands_long->at(keyChar);
// look if there is a default definition
} else if (key_commands_long_default.count(keyChar) > 0) {
Serial.printf("get_command_long: will use default key\r\n");
return key_commands_long_default.at(keyChar);
// no key definition found
} else {
Serial.printf("get_command_long: WARNING no key definition found\r\n");
return COMMAND_UNKNOWN;
}
}
catch (const std::out_of_range& oor) {
Serial.printf("get_command_long: internal error, sceneName not registered\r\n");
return COMMAND_UNKNOWN;
}
}

View file

@ -0,0 +1,30 @@
#ifndef __SCENEREGISTRY_H__
#define __SCENEREGISTRY_H__
#include <map>
#include "gui_general_and_keys/keys.h"
typedef void (*scene_start_sequence)(void);
typedef void (*scene_end_sequence)(void);
typedef std::map<char, repeatModes> *key_repeatModes;
typedef std::map<char, std::string> *key_commands_short;
typedef std::map<char, std::string> *key_commands_long;
void register_scene(
std::string a_scene_name,
scene_start_sequence a_scene_start_sequence,
scene_end_sequence a_scene_end_sequence,
key_repeatModes a_key_repeatModes,
key_commands_short a_key_commands_short,
key_commands_long a_key_commands_long);
#define COMMAND_UNKNOWN "command_unknown"
bool sceneExists(std::string sceneName);
void scene_start_sequence_from_registry(std::string sceneName);
void scene_end_sequence_from_registry(std::string sceneName);
repeatModes get_key_repeatMode(std::string sceneName, char keyChar);
std::string get_command_short(std::string sceneName, char keyChar);
std::string get_command_long(std::string sceneName, char keyChar);
#endif /*__SCENEREGISTRY_H__*/

View file

@ -0,0 +1,70 @@
#include <map>
#include "gui_general_and_keys/keys.h"
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "scenes/sceneRegistry.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "commandHandler.h"
std::map<char, repeatModes> key_repeatModes_TV {
{'=', SHORT_REPEATED}, {'<', SHORT }, {'p', SHORT }, {'>', SHORT_REPEATED },
{'c', SHORT }, {'i', SHORT },
{'u', SHORT_REPEATED},
{'l', SHORT_REPEATED}, {'k', SHORT}, {'r', SHORT_REPEATED},
{'d', SHORT_REPEATED},
{'s', SHORT },
{'^', SHORT },
{'v', SHORT },
};
std::map<char, std::string> key_commands_short_TV {
{'=', SAMSUNG_REWIND}, {'<', SAMSUNG_PAUSE}, {'p', SAMSUNG_PLAY}, {'>', SAMSUNG_FASTFORWARD},
{'c', SAMSUNG_GUIDE}, {'i', SAMSUNG_MENU},
{'u', SAMSUNG_UP},
{'l', SAMSUNG_LEFT}, {'k', SAMSUNG_SELECT}, {'r', SAMSUNG_RIGHT},
{'d', SAMSUNG_DOWN},
{'s', SAMSUNG_EXIT},
{'^', SAMSUNG_CHANNEL_UP},
{'v', SAMSUNG_CHANNEL_DOWN},
};
std::map<char, std::string> key_commands_long_TV {
};
void scene_start_sequence_TV(void) {
executeCommand(SAMSUNG_POWER_ON);
delay(500);
executeCommand(YAMAHA_POWER_ON);
delay(1500);
executeCommand(YAMAHA_INPUT_DVD);
delay(3000);
executeCommand(SAMSUNG_INPUT_TV);
}
void scene_end_sequence_TV(void) {
}
std::string scene_name_TV = "TV";
void register_scene_TV(void){
register_scene(
scene_name_TV,
& scene_start_sequence_TV,
& scene_end_sequence_TV,
& key_repeatModes_TV,
& key_commands_short_TV,
& key_commands_long_TV);
commands[SCENE_TV] = makeCommandData(SCENE, {scene_name_TV});
}

View file

@ -0,0 +1,9 @@
#ifndef __SCENE_TV_H__
#define __SCENE_TV_H__
#define SCENE_TV "Scene_tv"
extern std::string scene_name_TV;
void register_scene_TV(void);
#endif /*__SCENE_TV_H__*/

View file

@ -0,0 +1,81 @@
#include <map>
#include "gui_general_and_keys/keys.h"
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "scenes/sceneRegistry.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "commandHandler.h"
std::map<char, repeatModes> key_repeatModes_allOff {
};
std::map<char, std::string> key_commands_short_allOff {
};
std::map<char, std::string> key_commands_long_allOff {
};
void scene_start_sequence_allOff(void) {
executeCommand(SAMSUNG_POWER_OFF);
delay(500);
executeCommand(YAMAHA_POWER_OFF);
delay(500);
// repeat IR to be sure
executeCommand(SAMSUNG_POWER_OFF);
delay(500);
executeCommand(YAMAHA_POWER_OFF);
delay(500);
// repeat IR to be sure
executeCommand(SAMSUNG_POWER_OFF);
delay(500);
executeCommand(YAMAHA_POWER_OFF);
delay(500);
// you cannot power off FireTV, but at least you can stop the currently running app
executeCommand(KEYBOARD_HOME);
delay(500);
executeCommand(KEYBOARD_HOME);
}
void scene_end_sequence_allOff(void) {
}
std::string scene_name_allOff = "Off";
void register_scene_allOff(void){
register_scene(
scene_name_allOff,
& scene_start_sequence_allOff,
& scene_end_sequence_allOff,
& key_repeatModes_allOff,
& key_commands_short_allOff,
& key_commands_long_allOff);
commands[SCENE_ALLOFF] = makeCommandData(SCENE, {scene_name_allOff});
}

View file

@ -0,0 +1,9 @@
#ifndef __SCENE_ALLOFF_H__
#define __SCENE_ALLOFF_H__
#define SCENE_ALLOFF "Scene_allOff"
extern std::string scene_name_allOff;
void register_scene_allOff(void);
#endif /*__SCENE_ALLOFF_H__*/

View file

@ -0,0 +1,70 @@
#include <map>
#include "gui_general_and_keys/keys.h"
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "scenes/sceneRegistry.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "commandHandler.h"
std::map<char, repeatModes> key_repeatModes_chromecast {
};
std::map<char, std::string> key_commands_short_chromecast {
};
std::map<char, std::string> key_commands_long_chromecast {
};
void scene_start_sequence_chromecast(void) {
executeCommand(SAMSUNG_POWER_ON);
delay(500);
executeCommand(YAMAHA_POWER_ON);
delay(1500);
executeCommand(YAMAHA_INPUT_DVD);
delay(3000);
executeCommand(SAMSUNG_INPUT_HDMI_1);
}
void scene_end_sequence_chromecast(void) {
}
std::string scene_name_chromecast = "Chromecast";
void register_scene_chromecast(void){
register_scene(
scene_name_chromecast,
& scene_start_sequence_chromecast,
& scene_end_sequence_chromecast,
& key_repeatModes_chromecast,
& key_commands_short_chromecast,
& key_commands_long_chromecast);
commands[SCENE_CHROMECAST] = makeCommandData(SCENE, {scene_name_chromecast});
}

View file

@ -0,0 +1,9 @@
#ifndef __SCENE_CHROMECAST_H__
#define __SCENE_CHROMECAST_H__
#define SCENE_CHROMECAST "Scene_chromecast"
extern std::string scene_name_chromecast;
void register_scene_chromecast(void);
#endif /*__SCENE_CHROMECAST_H__*/

View file

@ -0,0 +1,79 @@
#include <map>
#include "gui_general_and_keys/keys.h"
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "scenes/sceneRegistry.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "commandHandler.h"
std::map<char, repeatModes> key_repeatModes_fireTV {
{'<', SHORTorLONG }, {'p', SHORT }, {'>', SHORTorLONG },
{'c', SHORT }, {'i', SHORT },
{'u', SHORT },
{'l', SHORT }, {'k', SHORT }, {'r', SHORT },
{'d', SHORT },
{'s', SHORT },
};
std::map<char, std::string> key_commands_short_fireTV {
{'<', KEYBOARD_REWIND}, {'p', KEYBOARD_PLAYPAUSE}, {'>', KEYBOARD_FASTFORWARD},
{'c', KEYBOARD_HOME}, {'i', KEYBOARD_MENU},
{'u', KEYBOARD_UP},
{'l', KEYBOARD_LEFT}, {'k', KEYBOARD_SELECT}, {'r', KEYBOARD_RIGHT},
{'d', KEYBOARD_DOWN},
{'s', KEYBOARD_BACK},
};
std::map<char, std::string> key_commands_long_fireTV {
{'<', KEYBOARD_REWIND_LONG},
{'>', KEYBOARD_FASTFORWARD_LONG},
};
void scene_start_sequence_fireTV(void) {
executeCommand(SAMSUNG_POWER_ON);
delay(500);
executeCommand(YAMAHA_POWER_ON);
delay(1500);
executeCommand(YAMAHA_INPUT_DTV);
delay(3000);
executeCommand(SAMSUNG_INPUT_HDMI_2);
delay(100);
executeCommand(KEYBOARD_HOME);
delay(500);
executeCommand(KEYBOARD_HOME);
}
void scene_end_sequence_fireTV(void) {
// you cannot power off FireTV, but at least you can stop the currently running app
executeCommand(KEYBOARD_HOME);
delay(500);
executeCommand(KEYBOARD_HOME);
}
std::string scene_name_fireTV = "Fire TV";
void register_scene_fireTV(void){
register_scene(
scene_name_fireTV,
& scene_start_sequence_fireTV,
& scene_end_sequence_fireTV,
& key_repeatModes_fireTV,
& key_commands_short_fireTV,
& key_commands_long_fireTV);
commands[SCENE_FIRETV] = makeCommandData(SCENE, {scene_name_fireTV});
}

View file

@ -0,0 +1,9 @@
#ifndef __SCENE_FIRETV_H__
#define __SCENE_FIRETV_H__
#define SCENE_FIRETV "Scene_firetv"
extern std::string scene_name_fireTV;
void register_scene_fireTV(void);
#endif /*__SCENE_FIRETV_H__*/

19
Platformio/src/secrets.h Normal file
View file

@ -0,0 +1,19 @@
/*
Before changing anything in this file, consider to copy file "secrets_override_example.h" to file "secrets_override.h" and to do your changes there.
Doing so, you will
- keep your credentials secret
- most likely never have conflicts with new versions of this file
*/
#define WIFI_SSID "YourWifiSSID" // override it in file "secrets_override.h"
#define WIFI_PASSWORD "YourWifiPassword" // override it in file "secrets_override.h"
#define MQTT_SERVER "IPAddressOfYourBroker" // override it in file "secrets_override.h"
#define MQTT_SERVER_PORT 1883 // override it in file "secrets_override.h"
#define MQTT_USER "" // override it in file "secrets_override.h"
#define MQTT_PASS "" // override it in file "secrets_override.h"
#define MQTT_CLIENTNAME "OMOTE" // override it in file "secrets_override.h"
// --- include override settings from seperate file ---------------------------------------------------------------------------------------------------------------
#if __has_include("secrets_override.h")
#include "secrets_override.h"
#endif

View file

@ -0,0 +1,12 @@
/*
If you add additional overrides here, you have to
1. first add #undef
2. add new #define
*/
#undef WIFI_SSID
#undef WIFI_PASSWORD
#undef MQTT_SERVER
#define WIFI_SSID "YourWifiSSID" // override here
#define WIFI_PASSWORD "YourWifiPassword" // override here
#define MQTT_SERVER "IPAddressOfYourBroker" // override here

View file

@ -1,3 +1,6 @@
# Changes in this fork
For changes in this fork, please see https://github.com/CoretechR/OMOTE/discussions/58
# OMOTE - Open Universal Remote
![](P1030424_small.jpg)