diff --git a/Platformio/.gitignore b/Platformio/.gitignore index 89cc49c..9a696bd 100644 --- a/Platformio/.gitignore +++ b/Platformio/.gitignore @@ -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 diff --git a/Platformio/.vscode/settings.json b/Platformio/.vscode/settings.json index a035777..8f6ef81 100644 --- a/Platformio/.vscode/settings.json +++ b/Platformio/.vscode/settings.json @@ -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" } \ No newline at end of file diff --git a/Platformio/lib/ESP32-BLE-Keyboard/.piopm b/Platformio/lib/ESP32-BLE-Keyboard/.piopm new file mode 100644 index 0000000..83d5c7f --- /dev/null +++ b/Platformio/lib/ESP32-BLE-Keyboard/.piopm @@ -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}} \ No newline at end of file diff --git a/Platformio/lib/ESP32-BLE-Keyboard/BleKeyboard.cpp b/Platformio/lib/ESP32-BLE-Keyboard/BleKeyboard.cpp new file mode 100644 index 0000000..a6216e3 --- /dev/null +++ b/Platformio/lib/ESP32-BLE-Keyboard/BleKeyboard.cpp @@ -0,0 +1,552 @@ +#include "BleKeyboard.h" + +#if defined(USE_NIMBLE) +#include +#include +#include +#include +#else +#include +#include +#include +#include "BLE2902.h" +#include "BLEHIDDevice.h" +#endif // USE_NIMBLE +#include "HIDTypes.h" +#include +#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) {} + } +} \ No newline at end of file diff --git a/Platformio/lib/ESP32-BLE-Keyboard/BleKeyboard.h b/Platformio/lib/ESP32-BLE-Keyboard/BleKeyboard.h new file mode 100644 index 0000000..f30dd40 --- /dev/null +++ b/Platformio/lib/ESP32-BLE-Keyboard/BleKeyboard.h @@ -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 diff --git a/Platformio/lib/ESP32-BLE-Keyboard/README.md b/Platformio/lib/ESP32-BLE-Keyboard/README.md new file mode 100644 index 0000000..eac0d4e --- /dev/null +++ b/Platformio/lib/ESP32-BLE-Keyboard/README.md @@ -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 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 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. diff --git a/Platformio/lib/ESP32-BLE-Keyboard/examples/SendKeyStrokes/SendKeyStrokes.ino b/Platformio/lib/ESP32-BLE-Keyboard/examples/SendKeyStrokes/SendKeyStrokes.ino new file mode 100644 index 0000000..03d1e70 --- /dev/null +++ b/Platformio/lib/ESP32-BLE-Keyboard/examples/SendKeyStrokes/SendKeyStrokes.ino @@ -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 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); +} diff --git a/Platformio/lib/ESP32-BLE-Keyboard/keywords.txt b/Platformio/lib/ESP32-BLE-Keyboard/keywords.txt new file mode 100644 index 0000000..0aa35b7 --- /dev/null +++ b/Platformio/lib/ESP32-BLE-Keyboard/keywords.txt @@ -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 +####################################### diff --git a/Platformio/lib/ESP32-BLE-Keyboard/library.properties b/Platformio/lib/ESP32-BLE-Keyboard/library.properties new file mode 100644 index 0000000..263a10f --- /dev/null +++ b/Platformio/lib/ESP32-BLE-Keyboard/library.properties @@ -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 diff --git a/Platformio/lib/Keypad/examples/loopCounter/loopCounter.ino b/Platformio/lib/Keypad/examples/loopCounter/loopCounter.ino index 695e489..b0470e7 100644 --- a/Platformio/lib/Keypad/examples/loopCounter/loopCounter.ino +++ b/Platformio/lib/Keypad/examples/loopCounter/loopCounter.ino @@ -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) diff --git a/Platformio/lib/Keypad/src/Keypad.cpp b/Platformio/lib/Keypad/src/Keypad.cpp index 96f9cbc..c4d8cfc 100644 --- a/Platformio/lib/Keypad/src/Keypad.cpp +++ b/Platformio/lib/Keypad/src/Keypad.cpp @@ -83,18 +83,31 @@ bool Keypad::getKeys() { void Keypad::scanKeys() { // Re-intialize the row pins. Allows sharing these pins with other hardware. for (byte r=0; r +#include +#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 commands; + +commandData makeCommandData(commandHandlers a, std::list 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"); + } +} diff --git a/Platformio/src/commandHandler.h b/Platformio/src/commandHandler.h new file mode 100644 index 0000000..1a03e90 --- /dev/null +++ b/Platformio/src/commandHandler.h @@ -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 +#include +#include +#include + +#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 commandPayloads; +}; + +commandData makeCommandData(commandHandlers a, std::list b); + +extern std::map commands; + +void register_specialCommands(); +void executeCommand(std::string command, std::string additionalPayload = ""); + +#endif /*__COMMANDHANDLER_H__*/ diff --git a/Platformio/src/device_appleTV/device_appleTV.cpp b/Platformio/src/device_appleTV/device_appleTV.cpp new file mode 100644 index 0000000..e1ee7ee --- /dev/null +++ b/Platformio/src/device_appleTV/device_appleTV.cpp @@ -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 +} diff --git a/Platformio/src/device_appleTV/device_appleTV.h b/Platformio/src/device_appleTV/device_appleTV.h new file mode 100644 index 0000000..3cf6935 --- /dev/null +++ b/Platformio/src/device_appleTV/device_appleTV.h @@ -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__*/ diff --git a/Platformio/src/device_appleTV/gui_appleTV.cpp b/Platformio/src/device_appleTV/gui_appleTV.cpp new file mode 100644 index 0000000..c167194 --- /dev/null +++ b/Platformio/src/device_appleTV/gui_appleTV.cpp @@ -0,0 +1,73 @@ +#include +#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); +} diff --git a/Platformio/src/device_appleTV/gui_appleTV.h b/Platformio/src/device_appleTV/gui_appleTV.h new file mode 100644 index 0000000..b7dd127 --- /dev/null +++ b/Platformio/src/device_appleTV/gui_appleTV.h @@ -0,0 +1,8 @@ +#ifndef __GUI_APPLETV_H__ +#define __GUI_APPLETV_H__ + +#include + +void register_gui_appleTV(void); + +#endif /*__GUI_APPLETV_H__*/ diff --git a/Platformio/src/assets.c b/Platformio/src/device_appleTV/gui_appleTV_assets.c similarity index 87% rename from Platformio/src/assets.c rename to Platformio/src/device_appleTV/gui_appleTV_assets.c index c782111..82b42ee 100644 --- a/Platformio/src/assets.c +++ b/Platformio/src/device_appleTV/gui_appleTV_assets.c @@ -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, -}; \ No newline at end of file diff --git a/Platformio/src/device_keyboard_ble/device_keyboard_ble.cpp b/Platformio/src/device_keyboard_ble/device_keyboard_ble.cpp new file mode 100644 index 0000000..2cedda5 --- /dev/null +++ b/Platformio/src/device_keyboard_ble/device_keyboard_ble.cpp @@ -0,0 +1,230 @@ +#include +#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}; +*/ diff --git a/Platformio/src/device_keyboard_ble/device_keyboard_ble.h b/Platformio/src/device_keyboard_ble/device_keyboard_ble.h new file mode 100644 index 0000000..b39506c --- /dev/null +++ b/Platformio/src/device_keyboard_ble/device_keyboard_ble.h @@ -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 + +#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__*/ diff --git a/Platformio/src/device_keyboard_mqtt/device_keyboard_mqtt.cpp b/Platformio/src/device_keyboard_mqtt/device_keyboard_mqtt.cpp new file mode 100644 index 0000000..ffafb9c --- /dev/null +++ b/Platformio/src/device_keyboard_mqtt/device_keyboard_mqtt.cpp @@ -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 \ No newline at end of file diff --git a/Platformio/src/device_keyboard_mqtt/device_keyboard_mqtt.h b/Platformio/src/device_keyboard_mqtt/device_keyboard_mqtt.h new file mode 100644 index 0000000..f41f360 --- /dev/null +++ b/Platformio/src/device_keyboard_mqtt/device_keyboard_mqtt.h @@ -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__*/ diff --git a/Platformio/src/device_samsungTV/device_samsungTV.cpp b/Platformio/src/device_samsungTV/device_samsungTV.cpp new file mode 100644 index 0000000..f6936e6 --- /dev/null +++ b/Platformio/src/device_samsungTV/device_samsungTV.cpp @@ -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"}); +} diff --git a/Platformio/src/device_samsungTV/device_samsungTV.h b/Platformio/src/device_samsungTV/device_samsungTV.h new file mode 100644 index 0000000..460f1cd --- /dev/null +++ b/Platformio/src/device_samsungTV/device_samsungTV.h @@ -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__*/ diff --git a/Platformio/src/device_smarthome/device_smarthome.cpp b/Platformio/src/device_smarthome/device_smarthome.cpp new file mode 100644 index 0000000..1c9c999 --- /dev/null +++ b/Platformio/src/device_smarthome/device_smarthome.cpp @@ -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 +} diff --git a/Platformio/src/device_smarthome/device_smarthome.h b/Platformio/src/device_smarthome/device_smarthome.h new file mode 100644 index 0000000..22ee525 --- /dev/null +++ b/Platformio/src/device_smarthome/device_smarthome.h @@ -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__*/ diff --git a/Platformio/src/device_smarthome/gui_smarthome.cpp b/Platformio/src/device_smarthome/gui_smarthome.cpp new file mode 100644 index 0000000..8fcdfc3 --- /dev/null +++ b/Platformio/src/device_smarthome/gui_smarthome.cpp @@ -0,0 +1,146 @@ +#include +#include +#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); +} diff --git a/Platformio/src/device_smarthome/gui_smarthome.h b/Platformio/src/device_smarthome/gui_smarthome.h new file mode 100644 index 0000000..c283431 --- /dev/null +++ b/Platformio/src/device_smarthome/gui_smarthome.h @@ -0,0 +1,8 @@ +#ifndef __GUI_SMARTHOME_H__ +#define __GUI_SMARTHOME_H__ + +#include + +void register_gui_smarthome(void); + +#endif /*__GUI_SMARTHOME_H__*/ diff --git a/Platformio/src/device_smarthome/gui_smarthome_assets.c b/Platformio/src/device_smarthome/gui_smarthome_assets.c new file mode 100644 index 0000000..171fa29 --- /dev/null +++ b/Platformio/src/device_smarthome/gui_smarthome_assets.c @@ -0,0 +1,43 @@ +#include + + +#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, +}; diff --git a/Platformio/src/device_yamahaAmp/device_yamahaAmp.cpp b/Platformio/src/device_yamahaAmp/device_yamahaAmp.cpp new file mode 100644 index 0000000..cd8c37f --- /dev/null +++ b/Platformio/src/device_yamahaAmp/device_yamahaAmp.cpp @@ -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"}); +} diff --git a/Platformio/src/device_yamahaAmp/device_yamahaAmp.h b/Platformio/src/device_yamahaAmp/device_yamahaAmp.h new file mode 100644 index 0000000..f8202c0 --- /dev/null +++ b/Platformio/src/device_yamahaAmp/device_yamahaAmp.h @@ -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__*/ diff --git a/Platformio/src/gui_general_and_keys/guiBase.cpp b/Platformio/src/gui_general_and_keys/guiBase.cpp new file mode 100644 index 0000000..d41618e --- /dev/null +++ b/Platformio/src/gui_general_and_keys/guiBase.cpp @@ -0,0 +1,219 @@ +#include +#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(); +} diff --git a/Platformio/src/gui_general_and_keys/guiBase.h b/Platformio/src/gui_general_and_keys/guiBase.h new file mode 100644 index 0000000..fa8de52 --- /dev/null +++ b/Platformio/src/gui_general_and_keys/guiBase.h @@ -0,0 +1,23 @@ +#ifndef __GUIBASE_H__ +#define __GUIBASE_H__ + +#include +#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__*/ diff --git a/Platformio/src/gui_general_and_keys/guiBase_assets.c b/Platformio/src/gui_general_and_keys/guiBase_assets.c new file mode 100644 index 0000000..06f105c --- /dev/null +++ b/Platformio/src/gui_general_and_keys/guiBase_assets.c @@ -0,0 +1,113 @@ +#include + + +#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, +}; diff --git a/Platformio/src/gui_general_and_keys/guiRegistry.cpp b/Platformio/src/gui_general_and_keys/guiRegistry.cpp new file mode 100644 index 0000000..e1a6011 --- /dev/null +++ b/Platformio/src/gui_general_and_keys/guiRegistry.cpp @@ -0,0 +1,31 @@ +#include +#include +#include +#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 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::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::iterator it; + for (it = registered_guis.begin(); it != registered_guis.end(); ++it) { + it->this_init_gui_pageIndicator(panel); + } +} diff --git a/Platformio/src/gui_general_and_keys/guiRegistry.h b/Platformio/src/gui_general_and_keys/guiRegistry.h new file mode 100644 index 0000000..f2f22b1 --- /dev/null +++ b/Platformio/src/gui_general_and_keys/guiRegistry.h @@ -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_();" 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 +#include + +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__*/ diff --git a/Platformio/src/gui_general_and_keys/gui_irReceiver.cpp b/Platformio/src/gui_general_and_keys/gui_irReceiver.cpp new file mode 100644 index 0000000..c2fae2f --- /dev/null +++ b/Platformio/src/gui_general_and_keys/gui_irReceiver.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#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 +#include + +void register_gui_irReceiver(void); +void showNewIRmessage(String); + +#endif /*__GUI_IRRECEIVER_H__*/ diff --git a/Platformio/src/gui_general_and_keys/gui_numpad.cpp b/Platformio/src/gui_general_and_keys/gui_numpad.cpp new file mode 100644 index 0000000..6fbedc3 --- /dev/null +++ b/Platformio/src/gui_general_and_keys/gui_numpad.cpp @@ -0,0 +1,101 @@ +#include +#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); +} diff --git a/Platformio/src/gui_general_and_keys/gui_numpad.h b/Platformio/src/gui_general_and_keys/gui_numpad.h new file mode 100644 index 0000000..7cdb1af --- /dev/null +++ b/Platformio/src/gui_general_and_keys/gui_numpad.h @@ -0,0 +1,8 @@ +#ifndef __GUI_NUMPAD_H__ +#define __GUI_NUMPAD_H__ + +#include + +void register_gui_numpad(void); + +#endif /*__GUI_NUMPAD_H__*/ diff --git a/Platformio/src/gui_general_and_keys/gui_settings.cpp b/Platformio/src/gui_general_and_keys/gui_settings.cpp new file mode 100644 index 0000000..004e18c --- /dev/null +++ b/Platformio/src/gui_general_and_keys/gui_settings.cpp @@ -0,0 +1,178 @@ +#include +#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); +} diff --git a/Platformio/src/gui_general_and_keys/gui_settings.h b/Platformio/src/gui_general_and_keys/gui_settings.h new file mode 100644 index 0000000..94918bf --- /dev/null +++ b/Platformio/src/gui_general_and_keys/gui_settings.h @@ -0,0 +1,12 @@ +#ifndef __GUI_SETTINGS_H__ +#define __GUI_SETTINGS_H__ + +#include + +extern lv_obj_t* objBattSettingsVoltage; +extern lv_obj_t* objBattSettingsPercentage; +//extern lv_obj_t* objBattSettingsIscharging; + +void register_gui_settings(void); + +#endif /*__GUI_SETTINGS_H__*/ diff --git a/Platformio/src/gui_general_and_keys/keys.cpp b/Platformio/src/gui_general_and_keys/keys.cpp new file mode 100644 index 0000000..611b14b --- /dev/null +++ b/Platformio/src/gui_general_and_keys/keys.cpp @@ -0,0 +1,176 @@ +#include +#include +#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; + + } + } +} diff --git a/Platformio/src/gui_general_and_keys/keys.h b/Platformio/src/gui_general_and_keys/keys.h new file mode 100644 index 0000000..5d4cfdd --- /dev/null +++ b/Platformio/src/gui_general_and_keys/keys.h @@ -0,0 +1,37 @@ +#ifndef __KEYS_H__ +#define __KEYS_H__ + +#include // 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__*/ diff --git a/Platformio/include/lv_conf.h b/Platformio/src/gui_general_and_keys/lv_conf.h similarity index 99% rename from Platformio/include/lv_conf.h rename to Platformio/src/gui_general_and_keys/lv_conf.h index bc4f44f..60127fd 100644 --- a/Platformio/include/lv_conf.h +++ b/Platformio/src/gui_general_and_keys/lv_conf.h @@ -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 diff --git a/Platformio/src/hardware/battery.cpp b/Platformio/src/hardware/battery.cpp new file mode 100644 index 0000000..283f0e2 --- /dev/null +++ b/Platformio/src/hardware/battery.cpp @@ -0,0 +1,75 @@ +#include +#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 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); + } +} + diff --git a/Platformio/src/hardware/battery.h b/Platformio/src/hardware/battery.h new file mode 100644 index 0000000..446b1af --- /dev/null +++ b/Platformio/src/hardware/battery.h @@ -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__*/ diff --git a/Platformio/src/hardware/infrared_receiver.cpp b/Platformio/src/hardware/infrared_receiver.cpp new file mode 100644 index 0000000..a840694 --- /dev/null +++ b/Platformio/src/hardware/infrared_receiver.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#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(); + + } +} diff --git a/Platformio/src/hardware/infrared_receiver.h b/Platformio/src/hardware/infrared_receiver.h new file mode 100644 index 0000000..6ce471e --- /dev/null +++ b/Platformio/src/hardware/infrared_receiver.h @@ -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__*/ diff --git a/Platformio/src/hardware/infrared_sender.cpp b/Platformio/src/hardware/infrared_sender.cpp new file mode 100644 index 0000000..6efe03a --- /dev/null +++ b/Platformio/src/hardware/infrared_sender.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#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(); +} diff --git a/Platformio/src/hardware/infrared_sender.h b/Platformio/src/hardware/infrared_sender.h new file mode 100644 index 0000000..a75ed08 --- /dev/null +++ b/Platformio/src/hardware/infrared_sender.h @@ -0,0 +1,14 @@ +#ifndef __INFRARED_H__ +#define __INFRARED_H__ + +#include +#include + +#define IR_LED 33 // IR LED output + +// IR declarations +extern IRsend IrSender; + +void init_infraredSender(void); + +#endif /*__INFRARED_H__*/ diff --git a/Platformio/src/hardware/mqtt.cpp b/Platformio/src/hardware/mqtt.cpp new file mode 100644 index 0000000..3c80c19 --- /dev/null +++ b/Platformio/src/hardware/mqtt.cpp @@ -0,0 +1,88 @@ +#include +#include +#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 diff --git a/Platformio/src/hardware/mqtt.h b/Platformio/src/hardware/mqtt.h new file mode 100644 index 0000000..875c0e4 --- /dev/null +++ b/Platformio/src/hardware/mqtt.h @@ -0,0 +1,15 @@ +#ifndef __MQTT_H__ +#define __MQTT_H__ + +#include "commandHandler.h" + +#ifdef ENABLE_WIFI_AND_MQTT + +#include "WiFi.h" +#include + +void init_mqtt(void); +bool publishMQTTMessage(const char *topic, const char *payload); +#endif + +#endif /*__MQTT_H__*/ diff --git a/Platformio/src/hardware/sleep.cpp b/Platformio/src/hardware/sleep.cpp new file mode 100644 index 0000000..0d3bd41 --- /dev/null +++ b/Platformio/src/hardware/sleep.cpp @@ -0,0 +1,210 @@ +#include +#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(); + } +} + diff --git a/Platformio/src/hardware/sleep.h b/Platformio/src/hardware/sleep.h new file mode 100644 index 0000000..8f75735 --- /dev/null +++ b/Platformio/src/hardware/sleep.h @@ -0,0 +1,25 @@ +#ifndef __SLEEP_H__ +#define __SLEEP_H__ + +#include +#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__*/ diff --git a/Platformio/src/hardware/tft.cpp b/Platformio/src/hardware/tft.cpp new file mode 100644 index 0000000..1bac427 --- /dev/null +++ b/Platformio/src/hardware/tft.cpp @@ -0,0 +1,71 @@ +#include +#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 + } +} diff --git a/Platformio/src/hardware/tft.h b/Platformio/src/hardware/tft.h new file mode 100644 index 0000000..c62dc20 --- /dev/null +++ b/Platformio/src/hardware/tft.h @@ -0,0 +1,28 @@ +#ifndef __TFT_H__ +#define __TFT_H__ + +#include // Hardware-specific library +#include + +#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__*/ diff --git a/Platformio/src/hardware/user_led.cpp b/Platformio/src/hardware/user_led.cpp new file mode 100644 index 0000000..6c1a2b0 --- /dev/null +++ b/Platformio/src/hardware/user_led.cpp @@ -0,0 +1,10 @@ +#include +#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); +} diff --git a/Platformio/src/hardware/user_led.h b/Platformio/src/hardware/user_led.h new file mode 100644 index 0000000..45a3800 --- /dev/null +++ b/Platformio/src/hardware/user_led.h @@ -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__*/ diff --git a/Platformio/src/main.cpp b/Platformio/src/main.cpp index fa3a055..00c885e 100644 --- a/Platformio/src/main.cpp +++ b/Platformio/src/main.cpp @@ -1,964 +1,103 @@ +#include + // OMOTE firmware for ESP32 // 2023 Maximilian Kern -#include // Hardware-specific library -#include // modified for inverted logic -#include -#include "SparkFunLIS3DH.h" -#include "Wire.h" -#include -#include -#include -#include -#include -#include "WiFi.h" -#include -#include "driver/ledc.h" -#include - -#define ENABLE_WIFI // Comment out to diable connected features - -// Pin assignment ----------------------------------------------------------------------------------------------------------------------- - -#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 USER_LED 2 - -#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 IR_RX 15 // IR receiver input -#define ADC_BAT 36 // Battery voltage sense input (1/2 divider) -#define IR_VCC 25 // IR receiver power -#define IR_LED 33 // IR LED output - -#define SCL 22 -#define SDA 19 -#define ACC_INT 20 - -#define CRG_STAT 21 // battery charger feedback - -// Variables and Object declarations ------------------------------------------------------------------------------------------------------ - -// Battery declares -int battery_voltage = 0; -int battery_percentage = 100; -bool battery_ischarging = false; - -// IMU declarations -int motion = 0; -#define SLEEP_TIMEOUT 20000 // time until device enters sleep mode in milliseconds -#define MOTION_THRESHOLD 50 // motion above threshold keeps device awake -int standbyTimer = SLEEP_TIMEOUT; -bool wakeupByIMUEnabled = true; -LIS3DH IMU(I2C_MODE, 0x19); // Default constructor is I2C, addr 0x19. - -// LCD declarations -TFT_eSPI tft = TFT_eSPI(); -#define screenWidth 240 -#define screenHeight 320 -Adafruit_FT6206 touch = Adafruit_FT6206(); -TS_Point touchPoint; -TS_Point oldPoint; -int backlight_brightness = 255; - -// LVGL declarations -static lv_disp_draw_buf_t draw_buf; -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_obj_t* objBattPercentage; -lv_obj_t* objBattIcon; -LV_IMG_DECLARE(gradientLeft); -LV_IMG_DECLARE(gradientRight); -LV_IMG_DECLARE(appleTvIcon); -LV_IMG_DECLARE(appleDisplayIcon); -LV_IMG_DECLARE(appleBackIcon); -LV_IMG_DECLARE(high_brightness); -LV_IMG_DECLARE(low_brightness); -LV_IMG_DECLARE(lightbulb); -lv_obj_t* panel; -lv_color_t color_primary = lv_color_hex(0x303030); // gray - -// Keypad declarations -const byte ROWS = 5; //four rows -const byte COLS = 5; //four columns -//define the symbols on the buttons of the keypads -char hexaKeys[ROWS][COLS] = { - {'s','^','-','m','r'}, // 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 -}; -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); -#define BUTTON_PIN_BITMASK 0b1110110000000000000000000010000000000000 //IO34+IO35+IO37+IO38+IO39(+IO13) -byte keyMapTechnisat[ROWS][COLS] = { - {0x69,0x20,0x11,0x0D,0x56}, - {0x4F,0x37,0x10,0x57,0x51}, - {0x6E,0x21,0x6B,0x6D,0x6C}, - {0x34,0x0C,0x22,0x50,0x55}, - {'?' ,0x35,0x2F,0x32,0x36} -}; -byte virtualKeyMapTechnisat[10] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0}; -byte currentDevice = 1; // Current Device to control (allows switching mappings between devices) - -// IR declarations -IRsend IrSender(IR_LED, true); -IRrecv IrReceiver(IR_RX); - -// Other declarations -byte wakeup_reason; -enum Wakeup_reasons{WAKEUP_BY_RESET, WAKEUP_BY_IMU, WAKEUP_BY_KEYPAD}; -Preferences preferences; - -#define WIFI_SSID "YOUR_WIFI_SSID" -#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" -#define MQTT_SERVER "YOUR_MQTT_SERVER_IP" -lv_obj_t* WifiLabel; -WiFiClient espClient; -PubSubClient client(espClient); - -// 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 device when the tabview page is changes -static void tabview_device_event_cb(lv_event_t* e){ - currentDevice = lv_tabview_get_tab_act(lv_event_get_target(e)); -} - -// 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); -} - -// 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 - Serial.println(virtualKeyMapTechnisat[(int)target->user_data]); - // Send IR command based on the button user data - IrSender.sendRC5(IrSender.encodeRC5X(0x00, virtualKeyMapTechnisat[(int)target->user_data])); -} - -// Apple Key Event handler -static void appleKey_event_cb(lv_event_t* e) { - // Send IR command based on the event user data - IrSender.sendSony(50 + (int)e->user_data, 15); - Serial.println(50 + (int)e->user_data); -} - -// 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); -} - -// Smart Home Toggle Event handler -static void smartHomeToggle_event_cb(lv_event_t * e){ - char payload[8]; - if(lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED)) strcpy(payload,"true"); - else strcpy(payload,"false"); - // Publish an MQTT message based on the event user data - if((int)e->user_data == 1) client.publish("bulb1_set", payload); - if((int)e->user_data == 2) client.publish("bulb2_set", payload); -} - -// Smart Home Toggle 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); - // Publish an MQTT message based on the event user data - if((int)e->user_data == 1) client.publish("bulb1_setbrightness", payload); - if((int)e->user_data == 2) client.publish("bulb2_setbrightness", payload); -} - -// 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; - standbyTimer = SLEEP_TIMEOUT; - } - - 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); - } -} - -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) standbyTimer = SLEEP_TIMEOUT; - - // 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 - preferences.putBool("wkpByIMU", wakeupByIMUEnabled); - preferences.putUChar("blBrightness", backlight_brightness); - preferences.putUChar("currentDevice", currentDevice); - if(!preferences.getBool("alreadySetUp")) preferences.putBool("alreadySetUp", true); - preferences.end(); - - // 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 - // Power down modem - WiFi.disconnect(); - WiFi.mode(WIFI_OFF); - #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(); -} - -#ifdef ENABLE_WIFI -// WiFi status event -void WiFiEvent(WiFiEvent_t event){ - //Serial.printf("[WiFi-event] event: %d\n", event); - if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP){ - client.setServer(MQTT_SERVER, 1883); // MQTT initialization - client.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){ - lv_label_set_text(WifiLabel, LV_SYMBOL_WIFI); - } - else{ - lv_label_set_text(WifiLabel, ""); - } -} -#endif - -// Setup ---------------------------------------------------------------------------------------------------------------------------------- +// hardware +#include "hardware/battery.h" +#include "hardware/sleep.h" +#include "hardware/user_led.h" +#include "hardware/tft.h" +#include "hardware/mqtt.h" +#include "hardware/infrared_sender.h" +#include "hardware/infrared_receiver.h" +// devices +#include "device_samsungTV/device_samsungTV.h" +#include "device_yamahaAmp/device_yamahaAmp.h" +#include "device_smarthome/device_smarthome.h" +#include "device_appleTV/device_appleTV.h" +#include "device_keyboard_mqtt/device_keyboard_mqtt.h" +#include "device_keyboard_ble/device_keyboard_ble.h" +// gui and keys +#include "gui_general_and_keys/guiBase.h" +#include "gui_general_and_keys/gui_irReceiver.h" +#include "gui_general_and_keys/gui_settings.h" +#include "gui_general_and_keys/gui_numpad.h" +#include "device_appleTV/gui_appleTV.h" +#include "device_smarthome/gui_smarthome.h" +#include "gui_general_and_keys/keys.h" +// scenes +#include "scenes/scene_allOff.h" +#include "scenes/scene_TV.h" +#include "scenes/scene_fireTV.h" +#include "scenes/scene_chromecast.h" +#include "scenes/sceneHandler.h" +// misc +#include "preferencesStorage.h" +#include "commandHandler.h" void setup() { - setCpuFrequencyMhz(240); // Make sure ESP32 is running at full speed - // 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; - } - - // --- IO Initialization --- - - // 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); - - // Power Pin Definition - pinMode(CRG_STAT, INPUT_PULLUP); - pinMode(ADC_BAT, INPUT); - - // IR Pin Definition - pinMode(IR_RX, INPUT); - pinMode(IR_LED, OUTPUT); - pinMode(IR_VCC, OUTPUT); - digitalWrite(IR_LED, HIGH); // HIGH off - LOW on - digitalWrite(IR_VCC, LOW); // HIGH on - LOW off - - // LCD Pin Definition - pinMode(LCD_EN, OUTPUT); - digitalWrite(LCD_EN, HIGH); - pinMode(LCD_BL, OUTPUT); - digitalWrite(LCD_BL, HIGH); - - // Other Pin Definition - pinMode(ACC_INT, INPUT); - pinMode(USER_LED, OUTPUT); - digitalWrite(USER_LED, LOW); - - // 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(); - - // 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); - // --- Startup --- - Serial.begin(115200); // Restore settings from internal flash memory - preferences.begin("settings", false); - if(preferences.getBool("alreadySetUp")){ - wakeupByIMUEnabled = preferences.getBool("wkpByIMU"); - backlight_brightness = preferences.getUChar("blBrightness"); - currentDevice = preferences.getUChar("currentDevice"); - } + init_preferences(); + // Button Pin definition + init_keys(); + // Power Pin definition + init_battery(); + // get wakeup reason + init_sleep(); + // Pin definition + init_userled(); + // init TFT + init_tft(); + // setup the Inertial Measurement Unit (IMU) for motion detection + // needs to be after init_tft()) because of I2C + setup_IMU(); + // setup IR sender + init_infraredSender(); - // Setup TFT + // register commands for the devices + register_device_samsung(); + register_device_yamaha(); + register_device_smarthome(); + register_device_appleTV(); + #ifdef ENABLE_KEYBOARD_MQTT + register_device_keyboard_mqtt(); + #endif + #ifdef ENABLE_KEYBOARD_BLE + register_device_keyboard_ble(); + #endif + register_specialCommands(); - // 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 - } + // register the GUIs. They will be displayed in the order they are registered. + register_gui_irReceiver(); + register_gui_settings(); + register_gui_numpad(); + register_gui_appleTV(); + register_gui_smarthome(); + // init GUI + init_gui(); + gui_loop(); // Run the LVGL UI once before the loop takes over - 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 - - // Setup LVGL - lv_init(); - lv_disp_draw_buf_init( &draw_buf, bufA, bufB, screenWidth * screenHeight / 10 ); + // register the scenes + register_scene_allOff(); + register_scene_TV(); + register_scene_fireTV(); + register_scene_chromecast(); + setLabelCurrentScene(); - // 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 4 tabs (names are irrelevant since the labels are hidden) - lv_obj_t* tab1 = lv_tabview_add_tab(tabview, "Settings"); - lv_obj_t* tab2 = lv_tabview_add_tab(tabview, "Technisat"); - lv_obj_t* tab3 = lv_tabview_add_tab(tabview, "Apple TV"); - lv_obj_t* tab4 = lv_tabview_add_tab(tabview, "Smart Home"); - - // 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 tab2 - lv_obj_set_style_pad_all(tab2, 0, LV_PART_MAIN); - lv_obj_t* cont = lv_obj_create(tab2); - 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); - - - // Add content to the Apple TV tab (3) - // Add a nice apple tv logo - lv_obj_t* appleImg = lv_img_create(tab3); - 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(tab3); - 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(tab3); - 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); - - - - - // Add content to the settings tab - // With a flex layout, setting groups/boxes will position themselves automatically - lv_obj_set_layout(tab1, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(tab1, LV_FLEX_FLOW_COLUMN); - lv_obj_set_scrollbar_mode(tab1, LV_SCROLLBAR_MODE_ACTIVE); - - // Add a label, then a box for the display settings - lv_obj_t* menuLabel = lv_label_create(tab1); - lv_label_set_text(menuLabel, "Display"); - - lv_obj_t* menuBox = lv_obj_create(tab1); - 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" - "30s\n" - "1m\n" - "3m"); - 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); - - // Add another label, then a settings box for WiFi - menuLabel = lv_label_create(tab1); - lv_label_set_text(menuLabel, "Wi-Fi"); - menuBox = lv_obj_create(tab1); - 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(tab1); - lv_label_set_text(menuLabel, "Battery"); - menuBox = lv_obj_create(tab1); - lv_obj_set_size(menuBox, lv_pct(100), 125); - lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); - - - - - - // Add content to the smart home tab (4) - lv_obj_set_layout(tab4, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(tab4, LV_FLEX_FLOW_COLUMN); - lv_obj_set_scrollbar_mode(tab4, LV_SCROLLBAR_MODE_ACTIVE); - - // Add a label, then a box for the light controls - menuLabel = lv_label_create(tab4); - lv_label_set_text(menuLabel, "Living Room"); - - menuBox = lv_obj_create(tab4); - 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); - - 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(tab4); - 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(tab4); - lv_label_set_text(menuLabel, "Kitchen"); - - menuBox = lv_obj_create(tab4); - 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); - - - // Set current page according to the current Device - lv_tabview_set_act(tabview, currentDevice, LV_ANIM_OFF); - - - // Create a page indicator - 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 - 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); - - btn = lv_btn_create(panel); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(btn, 150, lv_pct(100)); - label = lv_label_create(btn); - lv_label_set_text_fmt(label, "Technisat"); - 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); - - btn = lv_btn_create(panel); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(btn, 150, lv_pct(100)); - 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); - - btn = lv_btn_create(panel); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(btn, 150, lv_pct(100)); - 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); - // 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 - 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); - - 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); - - - - - - objBattPercentage = lv_label_create(statusbar); - lv_label_set_text(objBattPercentage, ""); - lv_obj_align(objBattPercentage, LV_ALIGN_RIGHT_MID, -16, 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 --- - - - #ifdef ENABLE_WIFI - // Setup WiFi - WiFi.setHostname("OMOTE"); //define hostname - WiFi.onEvent(WiFiEvent); - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - WiFi.setSleep(true); + // init WiFi - needs to be after gui because WifiLabel must be available + #ifdef ENABLE_WIFI_AND_MQTT + init_mqtt(); #endif - // 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 - - // Setup IR - IrSender.begin(); - digitalWrite(IR_VCC, HIGH); // Turn on IR receiver - IrReceiver.enableIRIn(); // Start the receiver - - - lv_timer_handler(); // Run the LVGL UI once before the loop takes over - - - Serial.print("Setup finised in "); + Serial.print("Setup finished in "); Serial.print(millis()); Serial.println("ms."); - } // Loop ------------------------------------------------------------------------------------------------------------------------------------ @@ -966,78 +105,43 @@ void setup() { void loop() { // Update Backlight brightness - 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 - } + update_backligthBrighness(); // Update LVGL UI - lv_timer_handler(); + gui_loop(); // Blink debug LED at 1 Hz - digitalWrite(USER_LED, millis() % 1000 > 500); + update_userled(); // Refresh IMU data at 10Hz static unsigned long IMUTaskTimer = millis(); if(millis() - IMUTaskTimer >= 100){ - activityDetection(); - if(standbyTimer == 0){ - Serial.println("Entering Sleep Mode. Goodbye."); - enterSleep(); - } + check_activity(); IMUTaskTimer = millis(); } - // Update battery stats at 1Hz - static unsigned long batteryTaskTimer = millis() + 1000; // add 1s to start immediately - if(millis() - batteryTaskTimer >= 1000){ - battery_voltage = analogRead(ADC_BAT)*2*3300/4095 + 350; // 350mV ADC offset - battery_percentage = constrain(map(battery_voltage, 3700, 4200, 0, 100), 0, 100); - batteryTaskTimer = millis(); - battery_ischarging = !digitalRead(CRG_STAT); - // Check if battery is charging, fully charged or disconnected - if(battery_ischarging || (!battery_ischarging && battery_voltage > 4350)){ - lv_label_set_text(objBattPercentage, ""); - lv_label_set_text(objBattIcon, LV_SYMBOL_USB); - } - else{ - // Update status bar battery indicator - //lv_label_set_text_fmt(objBattPercentage, "%d%%", battery_percentage); - 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); - } + // Update battery and BLE stats at 1Hz + static unsigned long updateStatusTimer = millis(); + if(millis() - updateStatusTimer >= 1000){ + update_battery_stats(); + updateStatusTimer = millis(); + + #ifdef ENABLE_BLUETOOTH + // adjust this if you implement other bluetooth devices than the BLE keyboard + #ifdef ENABLE_KEYBOARD_BLE + update_keyboard_ble_status(); + #endif + #endif + + // Serial.printf("heapSize: %lu, heapFree: %lu, heapMin: %lu, heapMax: %lu\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); + } // Keypad Handling - customKeypad.getKey(); // Populate key list - for(int i=0; i + +extern Preferences preferences; + +void init_preferences(void); +void save_preferences(void); + +#endif /*__PREFERENCESSTORAGE_H__*/ diff --git a/Platformio/src/scenes/sceneHandler.cpp b/Platformio/src/scenes/sceneHandler.cpp new file mode 100644 index 0000000..366815f --- /dev/null +++ b/Platformio/src/scenes/sceneHandler.cpp @@ -0,0 +1,55 @@ +#include +#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()); +} diff --git a/Platformio/src/scenes/sceneHandler.h b/Platformio/src/scenes/sceneHandler.h new file mode 100644 index 0000000..431904f --- /dev/null +++ b/Platformio/src/scenes/sceneHandler.h @@ -0,0 +1,12 @@ +#ifndef __SCENEHANDLER_H__ +#define __SCENEHANDLER_H__ + +#include +#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__*/ diff --git a/Platformio/src/scenes/sceneRegistry.cpp b/Platformio/src/scenes/sceneRegistry.cpp new file mode 100644 index 0000000..c9d0586 --- /dev/null +++ b/Platformio/src/scenes/sceneRegistry.cpp @@ -0,0 +1,167 @@ +#include +#include +#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 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 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 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 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; + } + +} + diff --git a/Platformio/src/scenes/sceneRegistry.h b/Platformio/src/scenes/sceneRegistry.h new file mode 100644 index 0000000..1a8a8b6 --- /dev/null +++ b/Platformio/src/scenes/sceneRegistry.h @@ -0,0 +1,30 @@ +#ifndef __SCENEREGISTRY_H__ +#define __SCENEREGISTRY_H__ + +#include +#include "gui_general_and_keys/keys.h" + +typedef void (*scene_start_sequence)(void); +typedef void (*scene_end_sequence)(void); +typedef std::map *key_repeatModes; +typedef std::map *key_commands_short; +typedef std::map *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__*/ diff --git a/Platformio/src/scenes/scene_TV.cpp b/Platformio/src/scenes/scene_TV.cpp new file mode 100644 index 0000000..98db302 --- /dev/null +++ b/Platformio/src/scenes/scene_TV.cpp @@ -0,0 +1,70 @@ +#include +#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 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 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 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}); +} diff --git a/Platformio/src/scenes/scene_TV.h b/Platformio/src/scenes/scene_TV.h new file mode 100644 index 0000000..415f290 --- /dev/null +++ b/Platformio/src/scenes/scene_TV.h @@ -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__*/ diff --git a/Platformio/src/scenes/scene_allOff.cpp b/Platformio/src/scenes/scene_allOff.cpp new file mode 100644 index 0000000..ca21024 --- /dev/null +++ b/Platformio/src/scenes/scene_allOff.cpp @@ -0,0 +1,81 @@ +#include +#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 key_repeatModes_allOff { + + + + + + + + + + +}; + +std::map key_commands_short_allOff { + + + + + + + + + + +}; + +std::map 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}); +} diff --git a/Platformio/src/scenes/scene_allOff.h b/Platformio/src/scenes/scene_allOff.h new file mode 100644 index 0000000..91b639c --- /dev/null +++ b/Platformio/src/scenes/scene_allOff.h @@ -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__*/ diff --git a/Platformio/src/scenes/scene_chromecast.cpp b/Platformio/src/scenes/scene_chromecast.cpp new file mode 100644 index 0000000..51c9d11 --- /dev/null +++ b/Platformio/src/scenes/scene_chromecast.cpp @@ -0,0 +1,70 @@ +#include +#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 key_repeatModes_chromecast { + + + + + + + + + + +}; + +std::map key_commands_short_chromecast { + + + + + + + + + + +}; + +std::map 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}); +} diff --git a/Platformio/src/scenes/scene_chromecast.h b/Platformio/src/scenes/scene_chromecast.h new file mode 100644 index 0000000..87a8865 --- /dev/null +++ b/Platformio/src/scenes/scene_chromecast.h @@ -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__*/ diff --git a/Platformio/src/scenes/scene_fireTV.cpp b/Platformio/src/scenes/scene_fireTV.cpp new file mode 100644 index 0000000..07350c7 --- /dev/null +++ b/Platformio/src/scenes/scene_fireTV.cpp @@ -0,0 +1,79 @@ +#include +#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 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 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 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}); +} diff --git a/Platformio/src/scenes/scene_fireTV.h b/Platformio/src/scenes/scene_fireTV.h new file mode 100644 index 0000000..ec3c4d3 --- /dev/null +++ b/Platformio/src/scenes/scene_fireTV.h @@ -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__*/ diff --git a/Platformio/src/secrets.h b/Platformio/src/secrets.h new file mode 100644 index 0000000..6aeac28 --- /dev/null +++ b/Platformio/src/secrets.h @@ -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 diff --git a/Platformio/src/secrets_override_example.h b/Platformio/src/secrets_override_example.h new file mode 100644 index 0000000..857b643 --- /dev/null +++ b/Platformio/src/secrets_override_example.h @@ -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 diff --git a/README.md b/README.md index 09ee397..0e5fcd4 100644 --- a/README.md +++ b/README.md @@ -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) @@ -103,4 +106,4 @@ Maximilian Kern - [kernm.de](https://kernm.de) Project Page on Hackaday.io: [https://hackaday.io/project/191752-omote-diy-universal-remote](https://hackaday.io/project/191752-omote-diy-universal-remote) -[link1]: https://discord.gg/5PnYFAsKsG \ No newline at end of file +[link1]: https://discord.gg/5PnYFAsKsG