diff --git a/watchos2/Kernel.cpp b/watchos2/Kernel.cpp index caa2ff6..76d6f8f 100644 --- a/watchos2/Kernel.cpp +++ b/watchos2/Kernel.cpp @@ -191,6 +191,39 @@ void Kernel::unregisterTask(Task* task) } } +void Kernel::initialize() +{ + for (int i = 0; i < KERNEL_MAX_TASKS; i++) + { + if (m_task[i] != nullptr) + { + m_task[i]->task->initialize(); + } + } +} + +void Kernel::start() +{ + for (int i = 0; i < KERNEL_MAX_TASKS; i++) + { + if (m_task[i] != nullptr) + { + m_task[i]->task->start(); + } + } +} + +void Kernel::suspend() +{ + for (int i = 0; i < KERNEL_MAX_TASKS; i++) + { + if (m_task[i] != nullptr) + { + m_task[i]->task->suspend(); + } + } +} + void Kernel::pushEvent(well_known_handle_t source, uint16_t event, byte param1, byte param2) { Event* e = new Event(); diff --git a/watchos2/Kernel.h b/watchos2/Kernel.h index a58a334..b6a89f2 100644 --- a/watchos2/Kernel.h +++ b/watchos2/Kernel.h @@ -27,6 +27,10 @@ private: public: Kernel(); + void initialize(); + void start(); + void suspend(); + /// /// Create a new kernel handle pointing to an object /// diff --git a/watchos2/Module_Input.cpp b/watchos2/Module_Input.cpp new file mode 100644 index 0000000..5113694 --- /dev/null +++ b/watchos2/Module_Input.cpp @@ -0,0 +1,79 @@ +#include "watchos.h" +#include "Module_Input.h" + +struct InputButtonState +{ + byte button; + byte pin; + uint64_t mask; + bool pressed = false; +}; + +uint64_t wakeup_bits; + +Module_Input::Module_Input() +{ + for (int i = 0; i < MODULE_INPUT_BUTTONS; i++) + { + m_button[i] = new InputButtonState(); + } + + m_button[0]->button = WATCHOS_BUTTON_BACK; + m_button[0]->pin = HW_BUTTON_TL; + m_button[0]->mask = HW_BUTTON_TL_MASK; + + m_button[1]->button = WATCHOS_BUTTON_OK; + m_button[1]->pin = HW_BUTTON_BL; + m_button[1]->mask = HW_BUTTON_BL_MASK; + + m_button[2]->button = WATCHOS_BUTTON_UP; + m_button[2]->pin = HW_BUTTON_TR; + m_button[2]->mask = HW_BUTTON_TR_MASK; + + m_button[3]->button = WATCHOS_BUTTON_DOWN; + m_button[3]->pin = HW_BUTTON_BR; + m_button[3]->mask = HW_BUTTON_BR_MASK; +} + +void Module_Input::start() +{ + if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1) + { + wakeup_bits = esp_sleep_get_ext1_wakeup_status(); + } + else + { + wakeup_bits = 0; + } +} + +void Module_Input::tick() +{ + byte pressed = 0; + + for (int i = 0; i < MODULE_INPUT_BUTTONS; i++) + { + if (digitalRead(m_button[i]->pin) == 1 || (wakeup_bits & m_button[i]->mask) != 0) + { + if (!m_button[i]->pressed) + { + watchos::debug("Button pressed: %d", i); + pressed |= m_button[i]->button; + m_button[i]->pressed = true; + } + } + else + { + if (m_button[i]->pressed) + watchos::debug("Button released!"); + m_button[i]->pressed = false; + } + } + + if (pressed != 0) + { + watchos::kernel()->pushEvent(WATCHOS_MODULE_INPUT, WATCHOS_EVENT_TYPE_INPUT_PRESSED, pressed, 0); + } + + wakeup_bits = 0; +} \ No newline at end of file diff --git a/watchos2/Module_Input.h b/watchos2/Module_Input.h new file mode 100644 index 0000000..446a256 --- /dev/null +++ b/watchos2/Module_Input.h @@ -0,0 +1,19 @@ +#ifndef _MODULE_INPUT_h +#define _MODULE_INPUT_h + +#define MODULE_INPUT_BUTTONS 4 + +struct InputButtonState; + +class Module_Input : public Task, public IRunnable +{ + InputButtonState* m_button[MODULE_INPUT_BUTTONS]; + +public: + Module_Input(); + void start(); + void tick(); +}; + + +#endif \ No newline at end of file diff --git a/watchos2/Module_Power.cpp b/watchos2/Module_Power.cpp new file mode 100644 index 0000000..bd5e99b --- /dev/null +++ b/watchos2/Module_Power.cpp @@ -0,0 +1,129 @@ +#include "watchos.h" +#include "Module_Power.h" + +void Module_Power::start() +{ + // TODO: Fetch input module to poll for button presses + + m_last_tick = 0; + m_light_sleep_accumulator = 0; + m_deep_sleep_accumulator = 0; + m_did_light_sleep = false; + + switch (esp_sleep_get_wakeup_cause()) + { + // Interactive + case ESP_SLEEP_WAKEUP_EXT1: // Button press + m_light_sleep_delay = MODULE_POWER_INTERACTIVE_LIGHT_SLEEP_DELAY_MS; + m_deep_sleep_delay = MODULE_POWER_INTERACTIVE_DEEP_SLEEP_DELAY_MS; + break; + // Non-interactive + case ESP_SLEEP_WAKEUP_EXT0: // External RTC + case ESP_SLEEP_WAKEUP_TIMER: // Internal RTC + default: // Reset or something else + m_light_sleep_delay = MODULE_POWER_NONINTERACTIVE_LIGHT_SLEEP_DELAY_MS; + m_deep_sleep_delay = MODULE_POWER_NONINTERACTIVE_DEEP_SLEEP_DELAY_MS; + break; + } + + watchos::subscribe(this, WATCHOS_MODULE_INPUT, WATCHOS_EVENT_TYPE_INPUT_PRESSED); +} + +void Module_Power::tick() +{ + if (m_last_tick == 0) + { + m_last_tick = millis(); + } + else + { + if (m_light_sleep_enabled) + { + m_light_sleep_accumulator += millis() - m_last_tick; + } + if (m_deep_sleep_enabled) + { + m_deep_sleep_accumulator += millis() - m_last_tick; + } + } + + m_last_tick = millis(); + + Event* e; + while (this->hasEvent()) + { + e = this->getEvent(); + + if (e->source == WATCHOS_MODULE_INPUT && e->event == WATCHOS_EVENT_TYPE_INPUT_PRESSED) + { + watchos::debug("Resetting accumulators"); + m_light_sleep_delay = MODULE_POWER_INTERACTIVE_LIGHT_SLEEP_DELAY_MS; + m_deep_sleep_delay = MODULE_POWER_INTERACTIVE_DEEP_SLEEP_DELAY_MS; + m_light_sleep_accumulator = 0; + m_deep_sleep_accumulator = 0; + } + + this->popEvent(); + } + + if (m_deep_sleep_accumulator > m_deep_sleep_delay) + { + watchos::debug("Going into deep sleep"); + //watchos::debug("Power: light %d/%d; heavy %d/%d", m_light_sleep_accumulator, m_light_sleep_delay, m_deep_sleep_accumulator, m_deep_sleep_delay); + + watchos::kernel()->suspend(); + + // TODO: Configure a... hourly? alarm to wake up and refresh other things. + // Interrogate tasks for next requested wakeup? + if (m_did_light_sleep) + { + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); + } + + esp_sleep_enable_ext0_wakeup(HW_RTC, 0); + esp_sleep_enable_ext1_wakeup(HW_BUTTON_MASK, ESP_EXT1_WAKEUP_ANY_HIGH); + // Turn off RTC -- needs to be on to handle ext0/ext1 interrupts properly I guess + //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); + // Turn off RTC fast mem + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF); + // Keep RTC slow ram on explicitly + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); + esp_deep_sleep_start(); + } + else if (m_light_sleep_accumulator > m_light_sleep_delay) + { + //watchos::debug("Going into light sleep"); + //watchos::debug("Power: light %d/%d; heavy %d/%d", m_light_sleep_accumulator, m_light_sleep_delay, m_deep_sleep_accumulator, m_deep_sleep_delay); + m_did_light_sleep = true; + // If nothing else happens, wake up when it's time to hibernate. + esp_sleep_enable_timer_wakeup((m_deep_sleep_delay - m_deep_sleep_accumulator) * 1000); + // Wake up if the RTC fires + esp_sleep_enable_ext0_wakeup(HW_RTC, 0); + // Wake up if any button is pressed + esp_sleep_enable_ext1_wakeup(HW_BUTTON_MASK, ESP_EXT1_WAKEUP_ANY_HIGH); + // Go into light sleep + esp_light_sleep_start(); + // And we're back, let everything continue! + m_light_sleep_accumulator = 0; + } +} + +void Module_Power::disableLightSleep() +{ + m_light_sleep_enabled = false; +} + +void Module_Power::enableLightSleep() +{ + m_light_sleep_enabled = true; +} + +void Module_Power::disableDeepSleep() +{ + m_deep_sleep_enabled = false; +} + +void Module_Power::enableDeepSleep() +{ + m_deep_sleep_enabled = true; +} \ No newline at end of file diff --git a/watchos2/Module_Power.h b/watchos2/Module_Power.h new file mode 100644 index 0000000..6b20004 --- /dev/null +++ b/watchos2/Module_Power.h @@ -0,0 +1,37 @@ +#ifndef _MODULE_POWER_h +#define _MODULE_POWER_h + +#include +#include "watchos_consts.h" +//#include "watchos_hw.h" +//#include "watchos_types.h" +#include "EventListener.h" + +#define MODULE_POWER_INTERACTIVE_LIGHT_SLEEP_DELAY_MS 200 +#define MODULE_POWER_INTERACTIVE_DEEP_SLEEP_DELAY_MS 3000 + +#define MODULE_POWER_NONINTERACTIVE_LIGHT_SLEEP_DELAY_MS 200 +#define MODULE_POWER_NONINTERACTIVE_DEEP_SLEEP_DELAY_MS 500 + +class Module_Power : public Task, public IRunnable, public EventListener +{ + int m_light_sleep_delay = 0, m_deep_sleep_delay = 0; + int m_light_sleep_accumulator = 0, m_deep_sleep_accumulator = 0; + + unsigned long m_last_tick; + bool m_did_light_sleep; + + bool m_light_sleep_enabled = true, m_deep_sleep_enabled = true; + +public: + void start(); + void tick(); + + void disableLightSleep(); + void enableLightSleep(); + void disableDeepSleep(); + void enableDeepSleep(); + +}; + +#endif \ No newline at end of file diff --git a/watchos2/Module_RTC.cpp b/watchos2/Module_RTC.cpp new file mode 100644 index 0000000..eb284a9 --- /dev/null +++ b/watchos2/Module_RTC.cpp @@ -0,0 +1,102 @@ +#include "watchos.h" +#include "Module_RTC.h" + +#include +// https://github.com/JChristensen/DS3232RTC + +DS3232RTC RTC; +tmElements_t rtc_time; + + +Module_RTC::Module_RTC() +{ + RTC.squareWave(SQWAVE_NONE); + RTC.setAlarm(ALM2_EVERY_MINUTE, 0, 0, 0, 0); + RTC.alarmInterrupt(ALARM_2, true); + RTC.alarmInterrupt(ALARM_1, false); + refresh(); +} + +void Module_RTC::initialize() +{ +} + +void Module_RTC::start() +{ +} + +void Module_RTC::suspend() +{ +} + +void Module_RTC::refresh() +{ + byte read_result = RTC.read(rtc_time); +} + +void Module_RTC::tick() +{ + if (RTC.alarm(ALARM_2)) + { + refresh(); + watchos::kernel()->pushEvent(WATCHOS_MODULE_RTC, WATCHOS_EVENT_TYPE_RTC_MINUTE, rtc_time.Hour, rtc_time.Minute); + watchos::debug("New time: %d:%d", rtc_time.Hour, rtc_time.Minute); + } + if (RTC.alarm(ALARM_1)) + { + watchos::debug("Alarm 1 fired?"); + } +} + +int Module_RTC::getHour() +{ + return rtc_time.Hour; +} + +int Module_RTC::getMinute() +{ + return rtc_time.Minute; +} + +int Module_RTC::getSecond() +{ + return rtc_time.Second; +} + +int Module_RTC::getYear() +{ + return rtc_time.Year; +} + +int Module_RTC::getMonth() +{ + return rtc_time.Month; +} + +int Module_RTC::getDay() +{ + return rtc_time.Day; +} + +char* Module_RTC::getDayOfWeek() +{ + switch (rtc_time.Wday) + { + case 1: + return "Sunday"; + case 2: + return "Monday"; + case 3: + return "Tuesday"; + case 4: + return "Wednesday"; + case 5: + return "Thursday"; + case 6: + return "Friday"; + case 7: + return "Saturday"; + default: + return "Unknown"; + } +} diff --git a/watchos2/Module_RTC.h b/watchos2/Module_RTC.h new file mode 100644 index 0000000..f73399b --- /dev/null +++ b/watchos2/Module_RTC.h @@ -0,0 +1,30 @@ +#ifndef _MODULE_RTC_h +#define _MODULE_RTC_h + +#include "watchos_consts.h" +#include "watchos_types.h" + +#include "Task.h" +#include "IRunnable.h" + +class Module_RTC : public Task, public IRunnable +{ + void refresh(); + +public: + Module_RTC(); + void initialize(); + void start(); + void suspend(); + void tick(); + + int getHour(); + int getMinute(); + int getSecond(); + int getYear(); + int getMonth(); + int getDay(); + char* getDayOfWeek(); +}; + +#endif diff --git a/watchos2/Module_Storage.cpp b/watchos2/Module_Storage.cpp new file mode 100644 index 0000000..66369d9 --- /dev/null +++ b/watchos2/Module_Storage.cpp @@ -0,0 +1,219 @@ +#include + +#include "watchos.h" +#include "Module_Storage.h" + +// TODO: Make tiered storage. Smaller allocations can go to RTC RAM. + +struct StorageEntry +{ + byte module; + uint16_t length; + byte options; +}; + +#define MODULE_STORAGE_SIZE 3524 +#define MODULE_STORAGE_HEADER_SIZE 4 +#define MODULE_STORAGE_ENTRY_SIZE 4 + +#define MODULE_STORAGE_MAGIC 0x6580 +#define MODULE_STORAGE_VERSION 0x00 + +RTC_NOINIT_ATTR byte RTC_MEM[MODULE_STORAGE_SIZE]; + +byte read_rtc(uint16_t offset); +void write_rtc(uint16_t offset, byte value); + +void Module_Storage::initialize() +{ + for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++) + { + m_entry[i] = nullptr; + } +} + +void Module_Storage::start() +{ + load(); +} + +void Module_Storage::suspend() +{ + commit(); +} + +bool Module_Storage::isValid() +{ + return ((read_rtc(0) << 8) | read_rtc(1)) == MODULE_STORAGE_MAGIC; +} + +void Module_Storage::load() +{ + // Read rtc ram to m_entry + m_magic = (read_rtc(0) << 8) | read_rtc(1); + m_version = read_rtc(2); + m_options = read_rtc(3); + + if (m_magic != MODULE_STORAGE_MAGIC) + watchos::panic("Invalid magic in persistent data"); + if (m_version != MODULE_STORAGE_VERSION) + watchos::panic("Invalid version of persistent data"); + + for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++) + { + int offset = MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * i); + byte module = read_rtc(offset + 0); + if (module == 0xFF) + continue; + + m_entry[i] = new StorageEntry(); + m_entry[i]->module = module; + m_entry[i]->length = (read_rtc(offset + 1) << 8) | read_rtc(offset + 2); + m_entry[i]->options = read_rtc(offset + 3); + } +} + +void Module_Storage::commit() +{ + // Sync allocations into RTC memory + write_rtc(0, (byte)(m_magic >> 8)); + write_rtc(1, (byte)(m_magic)); + write_rtc(2, m_version); + write_rtc(3, m_options); + + for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++) + { + int offset = MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * i); + if (m_entry[i] == nullptr) + { + write_rtc(offset + 0, 0xFF); + } + else + { + write_rtc(offset + 0, m_entry[i]->module); + write_rtc(offset + 1, (byte)(m_entry[i]->length >> 8)); + write_rtc(offset + 2, (byte)(m_entry[i]->length)); + write_rtc(offset + 3, m_entry[i]->options); + } + } +} + +void Module_Storage::reset() +{ + m_magic = MODULE_STORAGE_MAGIC; + m_version = MODULE_STORAGE_VERSION; + m_options = 0; + + // Write out blank allocations + write_rtc(0, (byte)(m_magic >> 8)); + write_rtc(1, (byte)(m_magic)); + write_rtc(2, m_version); + write_rtc(3, m_options); + + for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++) + { + if (m_entry[i] != nullptr) + { + free(m_entry[i]); + m_entry[i] = nullptr; + } + + write_rtc(MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * i), 0xFF); + } +} + +bool Module_Storage::allocate(byte module, uint16_t size, byte options) +{ + // Check if an allocation already exists + for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++) + { + if (m_entry[i] != nullptr && m_entry[i]->module == module) + { + if (m_entry[i]->length != size) + watchos::panic("Storage module does not support reallocating storage with a new size."); + return false; + } + } + + // None exists, allocate + for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++) + { + if (m_entry[i] == nullptr) + { + m_entry[i] = new StorageEntry(); + m_entry[i]->module = module; + m_entry[i]->length = size; + m_entry[i]->options = options; + return true; + } + } + + // None exists and we were unable to allocate one + watchos::panic("Exceeded MODULE_STORAGE_MAX_ENTRIES"); +} + +void Module_Storage::write(byte module, uint16_t offset, byte value) +{ + // TODO: Check offset doesn't put us past the end of the allocation + write_rtc(getOffset(module) + offset, value); +} + +byte Module_Storage::read(byte module, uint16_t offset) +{ + // TODO: Check offset doesn't put us past the end of the allocation + return read_rtc(getOffset(module) + offset); +} + +bool Module_Storage::allocate(well_known_handle_t module, uint16_t size, byte options) +{ + return allocate(wellKnownHandleToByte(module), size); +} + +void Module_Storage::write(well_known_handle_t module, uint16_t offset, byte value) +{ + return write(wellKnownHandleToByte(module), offset, value); +} + +byte Module_Storage::read(well_known_handle_t module, uint16_t offset) +{ + return read(wellKnownHandleToByte(module), offset); +} + +byte Module_Storage::wellKnownHandleToByte(well_known_handle_t handle) +{ + byte count = 0; + for (;;) + { + if (((handle >> count) & 1) == 1) + return count; + else + count++; + assert(count < 64); + } +} + +int Module_Storage::getOffset(byte module) +{ + int offset = MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * MODULE_STORAGE_MAX_ENTRIES); + for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++) + { + if (m_entry[i] != nullptr) + { + if (m_entry[i]->module == module) + return offset; + else + offset += m_entry[i]->length; + } + } + watchos::panic("Cannot get offset for module %d; not found", module); +} + +byte read_rtc(uint16_t offset) +{ + return RTC_MEM[offset]; +} + +void write_rtc(uint16_t offset, byte value) +{ + RTC_MEM[offset] = value; +} \ No newline at end of file diff --git a/watchos2/Module_Storage.h b/watchos2/Module_Storage.h new file mode 100644 index 0000000..6f5d934 --- /dev/null +++ b/watchos2/Module_Storage.h @@ -0,0 +1,47 @@ +#ifndef _MODULE_STORAGE_h +#define _MODULE_STORAGE_h + +#include "watchos_consts.h" +#include "watchos_types.h" + +#include "Task.h" + +// 4kB RTC +// 4kB EEPROM + +#define MODULE_STORAGE_MAX_ENTRIES 16 + +struct StorageEntry; + +class Module_Storage : public Task +{ + uint16_t m_magic; + byte m_version; + byte m_options; + StorageEntry* m_entry[MODULE_STORAGE_MAX_ENTRIES]; + + byte wellKnownHandleToByte(well_known_handle_t handle); + int getOffset(byte module); + +public: + void initialize(); + void start(); + void suspend(); + + bool isValid(); + + void load(); + void commit(); + void reset(); + + bool allocate(well_known_handle_t module, uint16_t size, byte options = 0); + void write(well_known_handle_t module, uint16_t offset, byte value); + byte read(well_known_handle_t module, uint16_t offset); + + bool allocate(byte module, uint16_t size, byte options = 0); + void write(byte module, uint16_t offset, byte value); + byte read(byte module, uint16_t offset); + +}; + +#endif diff --git a/watchos2/Module_UI.cpp b/watchos2/Module_UI.cpp index 9fc03c9..f7704dd 100644 --- a/watchos2/Module_UI.cpp +++ b/watchos2/Module_UI.cpp @@ -1,7 +1,7 @@ #include "watchos.h" #include "Module_UI.h" +#include "Module_Power.h" #include -#include GxEPD2_BW gfx = GxEPD2_154_D67(HW_DISPLAY_CS, HW_DISPLAY_DC, HW_DISPLAY_RESET, HW_DISPLAY_BUSY); @@ -14,6 +14,12 @@ struct UiWindow bool dirty; }; +struct UiFont +{ + byte id; + GFXfont* font; +}; + void destruct_window(kernel_handle_t handle, void* object) { } @@ -30,6 +36,20 @@ void draw_wrapper(void* parameter) #endif Module_UI::Module_UI() +{ + for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) + m_window[i] = nullptr; + for (int i = 0; i < MODULE_UI_MAX_FONTS; i++) + m_font[i] = nullptr; +} + +void Module_UI::initialize() +{ + m_dirty = true; + m_redraw = true; +} + +void Module_UI::start() { for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) { @@ -37,8 +57,8 @@ Module_UI::Module_UI() } m_root = createWindow(WATCHOS_HANDLE_NULL); - m_dirty = true; - m_redraw = true; + + module_power = static_cast(watchos::module(WATCHOS_MODULE_POWER)); } void Module_UI::tick() @@ -123,7 +143,8 @@ void Module_UI::draw() void Module_UI::do_draw() { watchos::debug("Draw!"); - // TODO: Disable light/deep sleep + module_power->disableLightSleep(); + module_power->disableDeepSleep(); // Initialize gfx if necessary if (!m_initialized) @@ -149,7 +170,8 @@ void Module_UI::do_draw() gfx.display(!m_redraw); m_redraw = false; - // TODO: Reenable sleep + module_power->enableDeepSleep(); + module_power->enableLightSleep(); } void Module_UI::draw_window(UiWindow* window, int x, int y, int width, int height) @@ -275,15 +297,21 @@ void Module_UI::print(int x, int y, char* str, uint16_t colour, void* font) local_to_screen(&x, &y); gfx.setCursor(x, y); gfx.setTextColor(colour); - if (font == nullptr) - { - gfx.setFont(&FreeMono12pt7b); - } - else + gfx.setFont(static_cast(font)); + gfx.print(str); +} + +void Module_UI::print(int x, int y, char* str, uint16_t colour, byte font_id) +{ + for (int i = 0; i < MODULE_UI_MAX_FONTS; i++) { - gfx.setFont(static_cast(font)); + if (m_font[i] != nullptr && m_font[i]->id == font_id) + { + print(x, y, str, colour, m_font[i]->font); + return; + } } - gfx.print(str); + watchos::panic("Unknown font id: %d", font_id); } void Module_UI::local_to_screen(int* x, int* y) @@ -298,4 +326,19 @@ void Module_UI::local_to_screen(int* x, int* y) *y = (m_draw_h - *y) + m_draw_y; else *y = m_draw_y + *y; +} + +void Module_UI::registerFont(byte id, const void* font) +{ + for (int i = 0; i < MODULE_UI_MAX_FONTS; i++) + { + if (m_font[i] == nullptr) + { + m_font[i] = new UiFont(); + m_font[i]->id = id; + m_font[i]->font = static_cast(const_cast(font)); + return; + } + } + watchos::panic("Exceeded MODULE_UI_MAX_FONTS"); } \ No newline at end of file diff --git a/watchos2/Module_UI.h b/watchos2/Module_UI.h index cd49801..1619231 100644 --- a/watchos2/Module_UI.h +++ b/watchos2/Module_UI.h @@ -7,22 +7,28 @@ #include "watchos_types.h" #include "Task.h" +#include "Module_Power.h" #include "IRunnable.h" #include "IDrawable.h" #define MODULE_UI_MAX_WINDOWS 32 #define MODULE_UI_MULTICORE_STACK_SIZE 10240 +#define MODULE_UI_MAX_FONTS 32 + #define MODULE_UI_LAYOUT_MODE_NONE 0 #define MODULE_UI_LAYOUT_MODE_SPLIT_VERTICAL 1 #define MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL 2 #define MODULE_UI_LAYOUT_MODE_ABSOLUTE 3 struct UiWindow; +struct UiFont; class Module_UI : public Task, public IRunnable { + Module_Power* module_power; UiWindow* m_window[MODULE_UI_MAX_WINDOWS]; + UiFont* m_font[MODULE_UI_MAX_FONTS]; bool m_initialized = false; bool m_dirty = false, m_redraw = false; @@ -39,6 +45,8 @@ public: kernel_handle_t createWindow(kernel_handle_t parent); kernel_handle_t getRoot(); + void initialize(); + void start(); void tick(); void draw(); @@ -51,7 +59,10 @@ public: int getHeight(); void fill(uint16_t colour = COLOUR_SECONDARY); void fill(int x, int y, int width, int height, uint16_t colour = COLOUR_SECONDARY); - void print(int x, int y, char* str, uint16_t colour = COLOUR_PRIMARY, void* font = nullptr); + void print(int x, int y, char* str, uint16_t colour, void* font); + void print(int x, int y, char* str, uint16_t colour = COLOUR_PRIMARY, byte font_id = 0); + + void registerFont(byte id, const void* font); }; #endif \ No newline at end of file diff --git a/watchos2/Task_Clock.cpp b/watchos2/Task_Clock.cpp new file mode 100644 index 0000000..fb231bd --- /dev/null +++ b/watchos2/Task_Clock.cpp @@ -0,0 +1,61 @@ +#include "watchos.h" +#include "Module_UI.h" +#include "Module_RTC.h" + +class Task_Clock : public IRunnable, public IDrawable, public Task, public EventListener +{ + Module_UI* ui = nullptr; + kernel_handle_t window_handle; + bool read_rtc = false; + byte hour, minute; + +public: + void start() + { + watchos::subscribe(this, WATCHOS_MODULE_RTC, WATCHOS_EVENT_TYPE_RTC_MINUTE); + + ui = static_cast(watchos::module(WATCHOS_MODULE_UI)); + ui->setLayoutMode(ui->getRoot(), MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL); + + window_handle = ui->createWindow(this, ui->getRoot()); + ui->createWindow(ui->getRoot()); + } + void tick() + { + Event* e; + while (this->hasEvent()) + { + e = this->getEvent(); + + if (e->source == WATCHOS_MODULE_RTC && e->event == WATCHOS_EVENT_TYPE_RTC_MINUTE) + { + if (!read_rtc || e->param1 != hour || e->param2 != minute) + { + hour = e->param1; + minute = e->param2; + read_rtc = true; + ui->setDirty(window_handle); + } + } + + this->popEvent(); + } + } + void draw(kernel_handle_t handle) + { + if (!read_rtc) + { + Module_RTC* rtc = static_cast(watchos::module(WATCHOS_MODULE_RTC)); + hour = rtc->getHour(); + minute = rtc->getMinute(); + read_rtc = true; + } + + char time[6]; + sprintf(time, "%02d:%02d", hour, minute); + + ui->fill(COLOUR_SECONDARY); + ui->print(0, ui->getHeight(), time, COLOUR_PRIMARY, 1); + } + +}; \ No newline at end of file diff --git a/watchos2/Task_Test.cpp b/watchos2/Task_Test.cpp index ef74b73..8229d48 100644 --- a/watchos2/Task_Test.cpp +++ b/watchos2/Task_Test.cpp @@ -1,18 +1,34 @@ #include "watchos.h" #include "Module_UI.h" +#include "Module_Storage.h" class Task_Test : public IRunnable, public IDrawable, public Task, public EventListener { Module_UI* ui = nullptr; + Module_Storage* storage = nullptr; kernel_handle_t window_handle; + int m_counter = 0; public: - Task_Test() + void start() { watchos::subscribe(this, 0xffffffffffffffffu, 0xffffu); + + storage = static_cast(watchos::module(WATCHOS_MODULE_STORAGE)); + if (storage->allocate((byte)64, 2)) + { + watchos::debug("Initialized counter"); + storage->write((byte)64, 0, 0); + storage->write((byte)64, 1, 0); + } + else + { + read(); + } + ui = static_cast(watchos::module(WATCHOS_MODULE_UI)); ui->setLayoutMode(ui->getRoot(), MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL); - + window_handle = ui->createWindow(this, ui->getRoot()); ui->createWindow(ui->getRoot()); } @@ -23,7 +39,13 @@ public: { e = this->getEvent(); - watchos::debug("Got event: src %#020llx evt %#06x param1 %#04x param2 %#04x", e->source, e->event, e->param1, e->param2); + if (e->source == WATCHOS_MODULE_INPUT && e->event == WATCHOS_EVENT_TYPE_INPUT_PRESSED) + { + watchos::debug("Got event: src %#020llx evt %#06x param1 %#04x param2 %#04x", e->source, e->event, e->param1, e->param2); + m_counter++; + write(); + ui->setDirty(window_handle); + } this->popEvent(); } @@ -31,6 +53,19 @@ public: void draw(kernel_handle_t handle) { ui->fill(COLOUR_SECONDARY); - ui->print(0, ui->getHeight(), "Hello, world!"); + char msg[8]; + sprintf(msg, "%d", m_counter); + ui->print(0, ui->getHeight(), msg); + } + + void read() + { + m_counter = (storage->read((byte)64, 0) << 8) | storage->read((byte)64, 1); + } + + void write() + { + storage->write((byte)64, 0, (byte)(m_counter >> 8)); + storage->write((byte)64, 1, (byte)(m_counter)); } }; \ No newline at end of file diff --git a/watchos2/__vm/Compile.vmps.xml b/watchos2/__vm/Compile.vmps.xml index 6ff0500..020ff79 100644 --- a/watchos2/__vm/Compile.vmps.xml +++ b/watchos2/__vm/Compile.vmps.xml @@ -2,7 +2,7 @@ - + diff --git a/watchos2/__vm/Upload.vmps.xml b/watchos2/__vm/Upload.vmps.xml index 6ff0500..020ff79 100644 --- a/watchos2/__vm/Upload.vmps.xml +++ b/watchos2/__vm/Upload.vmps.xml @@ -2,7 +2,7 @@ - + diff --git a/watchos2/watchos.cpp b/watchos2/watchos.cpp index c1df761..6556d01 100644 --- a/watchos2/watchos.cpp +++ b/watchos2/watchos.cpp @@ -2,11 +2,41 @@ #include "watchos.h" +#include + Kernel* watchos::s_kernel = nullptr; void watchos::initialize() { Serial.begin(115200); + Wire.begin(HW_I2C_SDA, HW_I2C_SCL); +} + +void watchos::setup() +{ + switch (esp_sleep_get_wakeup_cause()) + { + case ESP_SLEEP_WAKEUP_EXT0: // external rtc + watchos::debug("Wake due to external rtc"); + break; + case ESP_SLEEP_WAKEUP_EXT1: // button press + watchos::debug("Wake due to button press"); + break; + case ESP_SLEEP_WAKEUP_TIMER: // internal rtc + watchos::debug("Wake due to internal RTC"); + break; + default: // full reset + watchos::debug("Wake due to reset"); + kernel()->initialize(); + break; + } + + kernel()->start(); +} + +void watchos::tick() +{ + kernel()->tick(); } Kernel* watchos::kernel() diff --git a/watchos2/watchos.h b/watchos2/watchos.h index a057360..547d7f9 100644 --- a/watchos2/watchos.h +++ b/watchos2/watchos.h @@ -20,6 +20,9 @@ private: static Kernel* s_kernel; public: + static void setup(); + static void tick(); + static void initialize(); static Kernel* kernel(); static void panic(char* fmt...); diff --git a/watchos2/watchos2.ino b/watchos2/watchos2.ino index b756a62..bd810de 100644 --- a/watchos2/watchos2.ino +++ b/watchos2/watchos2.ino @@ -5,24 +5,49 @@ */ #include "watchos.h" +#include "Module_RTC.h" +#include "Module_Storage.h" +#include "Module_Power.h" +#include "Module_Input.h" #include "Task_Test.cpp" +#include "Task_Clock.cpp" #include +#include +#include +#include + // the setup function runs once when you press reset or power the board void setup() { - delay(500); + delay(100); watchos::initialize(); Kernel* kernel = watchos::kernel(); - kernel->registerTask(new Module_UI(), WATCHOS_MODULE_UI); + Module_Storage* storage; + Module_UI* ui; + kernel->registerTask(new Module_RTC(), WATCHOS_MODULE_RTC); + kernel->registerTask(storage = new Module_Storage(), WATCHOS_MODULE_STORAGE); + kernel->registerTask(new Module_Input(), WATCHOS_MODULE_INPUT); + kernel->registerTask(new Module_Power(), WATCHOS_MODULE_POWER); + kernel->registerTask(ui = new Module_UI(), WATCHOS_MODULE_UI); + watchos::run(new Task_Clock()); watchos::run(new Task_Test()); + if (!storage->isValid()) + { + watchos::debug("Resetting storage"); + storage->reset(); + } watchos::debug("Setup done!"); - kernel->pushEvent(0xDEADBEEFu, 0x01, 0x10, 0x20); + + ui->registerFont(0, &FreeMono12pt7b); + ui->registerFont(1, &DSEG7_Classic_Bold_53); + + watchos::setup(); } // the loop function runs over and over again until power down or reset void loop() { - watchos::kernel()->tick(); + watchos::tick(); } diff --git a/watchos2/watchos_consts.h b/watchos2/watchos_consts.h index 1320c90..cc15099 100644 --- a/watchos2/watchos_consts.h +++ b/watchos2/watchos_consts.h @@ -3,6 +3,8 @@ #include "watchos_config.h" +// If dark mode is enabled, use white as FG black as BG... otherwise +// do the opposite #ifdef WATCHOS_DARK_MODE #define COLOUR_PRIMARY 0xFFFF // white #define COLOUR_SECONDARY 0x0000 // black @@ -11,13 +13,36 @@ #define COLOUR_SECONDARY 0xFFFF // white #endif +// Event system + +// Masks to match all sources/events +#define WATCHOS_EVENT_SOURCE_ALL 0xffffffffffffffffu +#define WATCHOS_EVENT_TYPE_ALL 0xffff + +// Event system sources AND kernel well known handles #define WATCHOS_MODULE_UI 0x00000001 +#define WATCHOS_MODULE_POWER 0x00000002 +#define WATCHOS_MODULE_INPUT 0x00000004 +#define WATCHOS_MODULE_STORAGE 0x00000008 +#define WATCHOS_MODULE_RTC 0x00000010 +// Event system events +#define WATCHOS_EVENT_TYPE_INPUT_PRESSED 0x0001 +#define WATCHOS_EVENT_TYPE_RTC_MINUTE 0x0001 + +// Kernel handles #define WATCHOS_HANDLE_NULL 0x00000000 +// Kernel handle types #define WATCHOS_HANDLE_TYPE_TASK 0x01 #define WATCHOS_HANDLE_TYPE_EVENT 0x02 #define WATCHOS_HANDLE_TYPE_EVENT_SUBSCRIPTION 0x03 #define WATCHOS_HANDLE_TYPE_UI_WINDOW 0x04 +// Input module buttons +#define WATCHOS_BUTTON_BACK 0x01 +#define WATCHOS_BUTTON_OK 0x04 +#define WATCHOS_BUTTON_UP 0x08 +#define WATCHOS_BUTTON_DOWN 0x10 + #endif \ No newline at end of file