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