Adam PIppin
3 years ago
commit
3c3547057c
25 changed files with 1374 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||
/.vs/ |
|||
*.swp |
|||
/watchos/__vm/.watchos.vsarduino.h |
|||
/watchos/watchos.vcxproj.user |
|||
/watchos/Debug/ |
|||
/watchos/Release/ |
@ -0,0 +1,25 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio Version 16 |
|||
VisualStudioVersion = 16.0.31105.61 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "watchos", "watchos\watchos.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|x86 = Debug|x86 |
|||
Release|x86 = Release|x86 |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32 |
|||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32 |
|||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32 |
|||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32 |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(ExtensibilityGlobals) = postSolution |
|||
SolutionGuid = {0B0DCFBF-F360-4A90-8778-58D84486EAFB} |
|||
EndGlobalSection |
|||
EndGlobal |
@ -0,0 +1,151 @@ |
|||
// Arduino System
|
|||
#include <Arduino.h> |
|||
// RTC
|
|||
#include <DS3232RTC.h> |
|||
// I2C Library
|
|||
#include <Wire.h> |
|||
// Hardware information
|
|||
#include "hw.h" |
|||
|
|||
#include "Kernel.h" |
|||
|
|||
#define SCOPE_SIZE 64 |
|||
|
|||
Kernel* Kernel::s_kernel = nullptr; |
|||
char scope[SCOPE_SIZE]; |
|||
|
|||
|
|||
/**
|
|||
* Create a singleton instance of the kernel |
|||
*/ |
|||
void Kernel::initialize() |
|||
{ |
|||
Serial.begin(115200); |
|||
s_kernel = new Kernel(); |
|||
sprintf(scope, "[SYSTEM]"); |
|||
|
|||
Wire.begin(HW_I2C_SDA, HW_I2C_SCL); |
|||
|
|||
// Initialize empty tasks
|
|||
for (int i = 0; i < KERNEL_MAX_TASKS; i++) |
|||
{ |
|||
s_kernel->m_task[i] = nullptr; |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Fetch the singleton instance of the kernel |
|||
*/ |
|||
Kernel* Kernel::get() |
|||
{ |
|||
return s_kernel; |
|||
} |
|||
|
|||
/**
|
|||
* Unrecoverable error -- shut everything down |
|||
*/ |
|||
void Kernel::panic(char* message) |
|||
{ |
|||
// TODO: Would be good to print to screen once display module exists
|
|||
// TODO: Track active pid/module/whatever and include in output
|
|||
Serial.printf("Kernel panic!\n"); |
|||
Serial.printf("Scope: %s\n", scope); |
|||
Serial.printf("%s\n", message); |
|||
while (true) {} |
|||
} |
|||
|
|||
/**
|
|||
* Write out debug messaging |
|||
*/ |
|||
void Kernel::debug(char* message) |
|||
{ |
|||
// TODO: Print active PID
|
|||
Serial.printf("%s %s\n", scope, message); |
|||
} |
|||
|
|||
/**
|
|||
* Tick -- run all processes/modules as appropriate |
|||
*/ |
|||
void Kernel::tick() |
|||
{ |
|||
for (int i = 0; i < KERNEL_MAX_MODULES; i++) |
|||
{ |
|||
if (m_module[i] != nullptr) |
|||
{ |
|||
sprintf(scope, "[MODULE:%d]", m_module[i]->getId()); |
|||
m_module[i]->tick(); |
|||
} |
|||
} |
|||
|
|||
for (int i = 0; i < KERNEL_MAX_TASKS; i++) |
|||
{ |
|||
if (m_task[i] != nullptr) |
|||
{ |
|||
sprintf(scope, "[TASK:%d]", m_task[i]->getId()); |
|||
m_task[i]->tick(0); |
|||
} |
|||
} |
|||
|
|||
sprintf(scope, "[SYSTEM]"); |
|||
} |
|||
|
|||
/**
|
|||
* Tell all processes/modules to suspend |
|||
*/ |
|||
void Kernel::suspend() |
|||
{ |
|||
for (int i = 0; i < KERNEL_MAX_TASKS; i++) |
|||
{ |
|||
if (m_task[i] != nullptr) |
|||
{ |
|||
m_task[i]->suspend(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Register a new task in the kernel |
|||
*/ |
|||
void Kernel::registerTask(Task* task) |
|||
{ |
|||
for (int i = 0; i < KERNEL_MAX_TASKS; i++) |
|||
{ |
|||
if (m_task[i] == nullptr) |
|||
{ |
|||
m_task[i] = task; |
|||
task->setId(i); // TODO: real pid
|
|||
task->initialize(); |
|||
return; |
|||
} |
|||
} |
|||
Kernel::panic("Exceeded KERNEL_MAX_TASKS!"); |
|||
} |
|||
|
|||
/**
|
|||
* Register a new module in the kernel |
|||
*/ |
|||
void Kernel::registerModule(Module* module) |
|||
{ |
|||
for (int i = 0; i < KERNEL_MAX_MODULES; i++) |
|||
{ |
|||
if (m_module[i] == nullptr) |
|||
{ |
|||
m_module[i] = module; |
|||
module->initialize(); |
|||
return; |
|||
} |
|||
} |
|||
Kernel::panic("Exceeded KERNEL_MAX_MODULES!"); |
|||
} |
|||
|
|||
Module* Kernel::getModule(int module_id) |
|||
{ |
|||
for (int i = 0; i < KERNEL_MAX_MODULES; i++) |
|||
{ |
|||
if (m_module[i] != nullptr && m_module[i]->getId() == module_id) |
|||
{ |
|||
return m_module[i]; |
|||
} |
|||
} |
|||
Kernel::panic("Module not found"); |
|||
} |
@ -0,0 +1,31 @@ |
|||
#ifndef KERNEL_H |
|||
#define KERNEL_H |
|||
|
|||
#include "watchos.h" |
|||
#include "Task.h" |
|||
#include "Module.h" |
|||
|
|||
class Kernel |
|||
{ |
|||
static Kernel* s_kernel; |
|||
|
|||
Task* m_task[KERNEL_MAX_TASKS]; |
|||
Module* m_module[KERNEL_MAX_MODULES]; |
|||
|
|||
public: |
|||
static void initialize(); |
|||
static Kernel* get(); |
|||
static void panic(char* message); |
|||
static void debug(char* message); |
|||
|
|||
void tick(); |
|||
void suspend(); |
|||
|
|||
// Tasks
|
|||
void registerTask(Task* task); |
|||
|
|||
// Modules
|
|||
void registerModule(Module* module); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,13 @@ |
|||
#ifndef MODULE_H |
|||
#define MODULE_H |
|||
|
|||
class Module |
|||
{ |
|||
public: |
|||
virtual void initialize() = 0; |
|||
virtual void suspend() = 0; |
|||
virtual int getId() = 0; |
|||
virtual void tick() = 0; |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,189 @@ |
|||
#include <EEPROM.h> |
|||
#include "Module_EAT.h" |
|||
|
|||
struct EAT_Entry |
|||
{ |
|||
byte module; |
|||
byte length; |
|||
}; |
|||
|
|||
void Module_EAT::initialize() |
|||
{ |
|||
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++) |
|||
{ |
|||
m_entry[i] = nullptr; |
|||
} |
|||
load(); |
|||
} |
|||
|
|||
void Module_EAT::tick() |
|||
{ |
|||
} |
|||
|
|||
void Module_EAT::suspend() |
|||
{ |
|||
commit(); |
|||
} |
|||
|
|||
int Module_EAT::getId() |
|||
{ |
|||
return MODULE_EAT; |
|||
} |
|||
|
|||
void Module_EAT::load() |
|||
{ |
|||
char msg[255]; |
|||
|
|||
EEPROM.begin(EEPROM_SIZE); |
|||
byte magic_h = EEPROM.read(0); |
|||
byte magic_l = EEPROM.read(1); |
|||
m_magic = (magic_h << 8) | magic_l; |
|||
m_version = EEPROM.read(2); |
|||
m_options = EEPROM.read(3); |
|||
|
|||
if (m_magic != MODULE_EAT_MAGIC) |
|||
{ |
|||
Kernel::panic("Invalid magic in EEPROM, corrupted or not initialize"); |
|||
} |
|||
|
|||
if (m_version != MODULE_EAT_VERSION) |
|||
{ |
|||
sprintf(msg, "Unsupported version (%d)--please reinitialize EEPROM", m_version); |
|||
Kernel::panic(msg); |
|||
} |
|||
|
|||
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++) |
|||
{ |
|||
byte module = EEPROM.read((i * 2) + MODULE_EAT_STATIC_OFFSET); |
|||
byte length = EEPROM.read((i * 2) + MODULE_EAT_STATIC_OFFSET + 1); |
|||
|
|||
m_entry[i] = new EAT_Entry(); |
|||
m_entry[i]->module = module; |
|||
m_entry[i]->length = length; |
|||
} |
|||
} |
|||
|
|||
void Module_EAT::commit() |
|||
{ |
|||
EEPROM.begin(EEPROM_SIZE); |
|||
EEPROM.write(0, (byte)(m_magic >> 8)); |
|||
EEPROM.write(1, (byte)(m_magic)); |
|||
EEPROM.write(2, m_version); |
|||
EEPROM.write(3, m_options); |
|||
|
|||
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++) |
|||
{ |
|||
EEPROM.write((i * 2) + MODULE_EAT_STATIC_OFFSET, m_entry[i]->module); |
|||
EEPROM.write((i * 2) + MODULE_EAT_STATIC_OFFSET + 1, m_entry[i]->length); |
|||
} |
|||
|
|||
EEPROM.commit(); |
|||
} |
|||
|
|||
void Module_EAT::reset() |
|||
{ |
|||
m_magic = MODULE_EAT_MAGIC; |
|||
m_version = MODULE_EAT_VERSION; |
|||
m_options = 0; |
|||
|
|||
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++) |
|||
{ |
|||
m_entry[i] = new EAT_Entry(); |
|||
m_entry[i]->module = 0; |
|||
m_entry[i]->length = 0; |
|||
} |
|||
|
|||
commit(); |
|||
} |
|||
|
|||
bool Module_EAT::allocate(byte module, byte length) |
|||
{ |
|||
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++) |
|||
{ |
|||
if (m_entry[i]->module == module) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (m_entry[i]->module == 0) |
|||
{ |
|||
m_entry[i]->module = module; |
|||
m_entry[i]->length = length; |
|||
commit(); |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
Kernel::panic("Cannot allocate storage in EAT--out of entries"); |
|||
} |
|||
|
|||
int Module_EAT::getLength(byte module) |
|||
{ |
|||
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++) |
|||
{ |
|||
if (m_entry[i]->module == module) |
|||
{ |
|||
return m_entry[i]->length; |
|||
} |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
int Module_EAT::getOffset(byte module) |
|||
{ |
|||
int offset = MODULE_EAT_STATIC_OFFSET; |
|||
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++) |
|||
{ |
|||
if (m_entry[i]->module == module) |
|||
{ |
|||
return offset; |
|||
} |
|||
else |
|||
{ |
|||
offset += m_entry[i]->length; |
|||
} |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
byte Module_EAT::read(byte module, byte offset) |
|||
{ |
|||
int entry_offset = getOffset(module); |
|||
#ifdef WATCHOS_DEBUG |
|||
if (entry_offset == -1) |
|||
{ |
|||
Kernel::panic("Attempt to read from an uninitialized EAT module"); |
|||
} |
|||
if (offset > getLength(module)) |
|||
{ |
|||
Kernel::panic("Attempt to read past the end of EAT allocation"); |
|||
} |
|||
#endif |
|||
offset += entry_offset; |
|||
char msg[255]; |
|||
sprintf(msg, "Reading from %d", offset); |
|||
Kernel::debug(msg); |
|||
return EEPROM.read(offset); |
|||
} |
|||
|
|||
void Module_EAT::write(byte module, byte offset, byte value) |
|||
{ |
|||
int entry_offset = getOffset(module); |
|||
#ifdef WATCHOS_DEBUG |
|||
if (entry_offset == -1) |
|||
{ |
|||
Kernel::panic("Attempt to read from an uninitialized EAT module"); |
|||
} |
|||
if (offset > getLength(module)) |
|||
{ |
|||
Kernel::panic("Attempt to read past the end of EAT allocation"); |
|||
} |
|||
#endif |
|||
offset += entry_offset; |
|||
char msg[255]; |
|||
sprintf(msg, "Writing %d to %d", value, offset); |
|||
Kernel::debug(msg); |
|||
EEPROM.write(offset, value); |
|||
EEPROM.commit(); |
|||
} |
@ -0,0 +1,33 @@ |
|||
#ifndef MODULE_EAT_H |
|||
#define MODULE_EAT_H |
|||
|
|||
#include "watchos.h" |
|||
|
|||
struct EAT_Entry; |
|||
|
|||
class Module_EAT : public Module |
|||
{ |
|||
uint16_t m_magic; |
|||
byte m_version; |
|||
byte m_options; |
|||
EAT_Entry* m_entry[MODULE_EAT_MAX_ENTRIES]; |
|||
|
|||
int getOffset(byte module); |
|||
|
|||
public: |
|||
void initialize(); |
|||
void suspend(); |
|||
int getId(); |
|||
void tick(); |
|||
|
|||
void load(); |
|||
void commit(); |
|||
void reset(); |
|||
|
|||
bool allocate(byte module, byte length); |
|||
int getLength(byte module); |
|||
void write(byte module, byte offset, byte value); |
|||
byte read(byte module, byte offset); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,93 @@ |
|||
#include "Module_RTC.h" |
|||
#include <DS3232RTC.h> |
|||
|
|||
DS3232RTC RTC; |
|||
// https://github.com/JChristensen/DS3232RTC
|
|||
|
|||
tmElements_t lastTime; |
|||
|
|||
void Module_RTC::initialize() |
|||
{ |
|||
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::tick() |
|||
{ |
|||
if (RTC.alarm(ALARM_2)) |
|||
{ |
|||
// Refresh every time interrupt fires (every minute)
|
|||
refresh(); |
|||
// TODO: Signal kernel/processes/whatever?
|
|||
} |
|||
} |
|||
|
|||
void Module_RTC::suspend() |
|||
{ |
|||
} |
|||
|
|||
int Module_RTC::getId() |
|||
{ |
|||
return MODULE_RTC; |
|||
} |
|||
|
|||
void Module_RTC::refresh() |
|||
{ |
|||
RTC.read(lastTime); |
|||
} |
|||
|
|||
int Module_RTC::getHour() |
|||
{ |
|||
return lastTime.Hour; |
|||
} |
|||
|
|||
int Module_RTC::getMinute() |
|||
{ |
|||
return lastTime.Minute; |
|||
} |
|||
|
|||
int Module_RTC::getSecond() |
|||
{ |
|||
return lastTime.Second; |
|||
} |
|||
|
|||
int Module_RTC::getYear() |
|||
{ |
|||
return lastTime.Year + 1970; |
|||
} |
|||
|
|||
int Module_RTC::getMonth() |
|||
{ |
|||
return lastTime.Month; |
|||
} |
|||
|
|||
int Module_RTC::getDay() |
|||
{ |
|||
return lastTime.Day; |
|||
} |
|||
|
|||
char* Module_RTC::getDayOfWeek() |
|||
{ |
|||
switch (lastTime.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"; |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
#ifndef MODULE_RTC_H |
|||
#define MODULE_RTC_H |
|||
|
|||
#include "watchos.h" |
|||
|
|||
class Module_RTC : public Module |
|||
{ |
|||
public: |
|||
void initialize(); |
|||
void suspend(); |
|||
int getId(); |
|||
void tick(); |
|||
void refresh(); |
|||
int getHour(); |
|||
int getMinute(); |
|||
int getSecond(); |
|||
int getYear(); |
|||
int getMonth(); |
|||
int getDay(); |
|||
char* getDayOfWeek(); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,217 @@ |
|||
#include "watchos.h" |
|||
#include "hw.h" |
|||
#include "Module_UI.h" |
|||
|
|||
#include <GxEPD2_BW.h> |
|||
|
|||
GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> gfx = GxEPD2_154_D67(HW_DISPLAY_CS, HW_DISPLAY_DC, HW_DISPLAY_RESET, HW_DISPLAY_BUSY); |
|||
|
|||
struct UI_Window |
|||
{ |
|||
int hwnd = 0; |
|||
UI_Window* parent; |
|||
byte layout_mode = 0; |
|||
void (*callback)(int, int, int, int, int); // hwnd, x, y, width, height
|
|||
bool dirty = false; |
|||
byte reserved1; |
|||
byte reserved2; |
|||
}; |
|||
|
|||
void Module_UI::initialize() |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
m_window[i] = nullptr; |
|||
} |
|||
|
|||
// Create root window
|
|||
createWindow(-1, 0); |
|||
|
|||
gfx.init(0, false); // init(bitrate, initial)?
|
|||
gfx.setFullWindow(); |
|||
gfx.fillScreen(GxEPD_WHITE); |
|||
gfx.display(true); |
|||
} |
|||
|
|||
void Module_UI::tick() |
|||
{ |
|||
} |
|||
|
|||
void Module_UI::suspend() |
|||
{ |
|||
gfx.hibernate(); |
|||
} |
|||
|
|||
int Module_UI::getId() |
|||
{ |
|||
return MODULE_UI; |
|||
} |
|||
|
|||
GxEPD2_GFX* Module_UI::getGfx() |
|||
{ |
|||
return &gfx; |
|||
} |
|||
|
|||
int Module_UI::createWindow(void (*callback)(int, int, int, int, int), int parent, int zorder) |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] == nullptr) |
|||
{ |
|||
m_window[i] = new UI_Window(); |
|||
m_window[i]->hwnd = m_nextHwnd++; |
|||
m_window[i]->parent = nullptr; |
|||
for (int j = 0; j < MODULE_UI_MAX_WINDOWS; j++) |
|||
{ |
|||
if (m_window[j] != nullptr && m_window[j]->hwnd == parent) |
|||
{ |
|||
m_window[i]->parent = m_window[j]; |
|||
} |
|||
} |
|||
m_window[i]->layout_mode = MODULE_UI_LAYOUT_MODE_NONE; |
|||
m_window[i]->callback = callback; |
|||
m_window[i]->dirty = true; |
|||
return m_window[i]->hwnd; |
|||
} |
|||
} |
|||
Kernel::panic("Exceeded MODULE_UI_MAX_WINDOWS!"); |
|||
} |
|||
|
|||
int Module_UI::createWindow(int parent, int zorder) |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] == nullptr) |
|||
{ |
|||
m_window[i] = new UI_Window(); |
|||
m_window[i]->hwnd = m_nextHwnd++; |
|||
m_window[i]->parent = nullptr; |
|||
for (int j = 0; j < MODULE_UI_MAX_WINDOWS; j++) |
|||
{ |
|||
if (m_window[j] != nullptr && m_window[j]->hwnd == parent) |
|||
{ |
|||
m_window[i]->parent = m_window[j]; |
|||
} |
|||
} |
|||
m_window[i]->layout_mode = MODULE_UI_LAYOUT_MODE_NONE; |
|||
m_window[i]->callback = nullptr; |
|||
return m_window[i]->hwnd; |
|||
} |
|||
} |
|||
Kernel::panic("Exceeded MODULE_UI_MAX_WINDOWS!"); |
|||
} |
|||
|
|||
void Module_UI::setLayoutMode(int hwnd, byte layout_mode) |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->hwnd == hwnd) |
|||
{ |
|||
m_window[i]->layout_mode = layout_mode; |
|||
return; |
|||
} |
|||
} |
|||
Kernel::panic("Window hwnd not found!"); |
|||
} |
|||
|
|||
void Module_UI::setDirty(int hwnd) |
|||
{ |
|||
UI_Window* window = nullptr; |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i]->hwnd == hwnd) |
|||
{ |
|||
window = m_window[i]; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
while (window != nullptr) |
|||
{ |
|||
window->dirty = true; |
|||
window = window->parent; |
|||
} |
|||
} |
|||
|
|||
void Module_UI::draw() |
|||
{ |
|||
draw(0, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); |
|||
getGfx()->display(true); |
|||
} |
|||
|
|||
void Module_UI::draw(int hwnd, int x, int y, int width, int height) |
|||
{ |
|||
UI_Window* window = nullptr; |
|||
// Find the window and draw it if applicable
|
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->hwnd == hwnd) |
|||
{ |
|||
window = m_window[i]; |
|||
} |
|||
} |
|||
if (window == nullptr) |
|||
{ |
|||
Kernel::panic("Trying to draw non-existent window!"); |
|||
} |
|||
|
|||
// Draw window if applicable
|
|||
if (window->callback != nullptr && window->dirty) |
|||
{ |
|||
(window->callback)(hwnd, x, y, width, height); |
|||
} |
|||
|
|||
// Count children
|
|||
int child_count = 0; |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->parent == window) |
|||
{ |
|||
child_count++; |
|||
} |
|||
} |
|||
|
|||
if (child_count == 0) |
|||
{ |
|||
// No children to draw -- we're out of here!
|
|||
return; |
|||
} |
|||
|
|||
// Grab all children
|
|||
UI_Window** children = (UI_Window**)calloc(child_count, sizeof(UI_Window*)); |
|||
int next_child = 0; |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->parent == window) |
|||
{ |
|||
children[next_child++] = m_window[i]; |
|||
} |
|||
} |
|||
|
|||
int layout_mode = window->layout_mode; |
|||
int child_height, child_width; |
|||
switch (layout_mode) |
|||
{ |
|||
case MODULE_UI_LAYOUT_MODE_NONE: |
|||
for (int i = 0; i < child_count; i++) |
|||
{ |
|||
draw(children[i]->hwnd, x, y, width, height); |
|||
} |
|||
break; |
|||
case MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL: |
|||
child_height = height / child_count; |
|||
for (int i = 0; i < child_count; i++) |
|||
{ |
|||
draw(children[i]->hwnd, x, y + (i * child_height), width, child_height); |
|||
} |
|||
break; |
|||
case MODULE_UI_LAYOUT_MODE_SPLIT_VERTICAL: |
|||
child_width = width / child_count; |
|||
for (int i = 0; i < child_count; i++) |
|||
{ |
|||
draw(children[i]->hwnd, x + (i * child_width), y, child_width, height); |
|||
} |
|||
break; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
#ifndef MODULE_UI_H |
|||
#define MODULE_UI_H |
|||
|
|||
#include "watchos.h" |
|||
#include <GxEPD2_GFX.h> |
|||
|
|||
struct UI_Window; |
|||
|
|||
class Module_UI : public Module |
|||
{ |
|||
UI_Window* m_window[MODULE_UI_MAX_WINDOWS]; |
|||
int m_nextHwnd = 0; |
|||
void draw(int hwnd, int x, int y, int width, int height); |
|||
|
|||
public: |
|||
void initialize(); |
|||
void suspend(); |
|||
int getId(); |
|||
void tick(); |
|||
|
|||
int createWindow(void (*callback)(int, int, int, int, int), int parent = 0, int zorder = 0); |
|||
int createWindow(int parent = 0, int zorder = 0); |
|||
void draw(); |
|||
GxEPD2_GFX* getGfx(); |
|||
void setLayoutMode(int hwnd, byte layout_mode); |
|||
void setDirty(int hwnd); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,11 @@ |
|||
#include "Task.h" |
|||
|
|||
void Task::setId(int pid) |
|||
{ |
|||
pid = pid; |
|||
} |
|||
|
|||
int Task::getId() |
|||
{ |
|||
return pid; |
|||
} |
@ -0,0 +1,16 @@ |
|||
#ifndef TASK_H |
|||
#define TASK_H |
|||
|
|||
class Task |
|||
{ |
|||
private: |
|||
int pid; |
|||
public: |
|||
virtual void initialize() = 0; |
|||
virtual void tick(unsigned int signal) = 0; |
|||
virtual void suspend() = 0; |
|||
int getId(); |
|||
void setId(int pid); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,54 @@ |
|||
#include "watchos.h" |
|||
#include "Module_UI.h" |
|||
#include "Module_RTC.h" |
|||
#include "fonts.h" |
|||
|
|||
class TestTask : public Task |
|||
{ |
|||
private: |
|||
int hwnd; |
|||
Module_UI* ui; |
|||
int lastMinute = 0; |
|||
public: |
|||
void initialize() |
|||
{ |
|||
ui = (Module_UI*)Kernel::get()->getModule(MODULE_UI); |
|||
hwnd = ui->createWindow(&TestTask::draw, 0, 0); |
|||
} |
|||
void tick(unsigned int signal) |
|||
{ |
|||
// TODO: Replace once there's a signal for RTC events
|
|||
Module_RTC* rtc = (Module_RTC*)Kernel::get()->getModule(MODULE_RTC); |
|||
Module_UI* ui = (Module_UI*)Kernel::get()->getModule(MODULE_UI); |
|||
if (rtc->getMinute() != lastMinute) |
|||
{ |
|||
lastMinute = rtc->getMinute(); |
|||
ui->setDirty(hwnd); |
|||
} |
|||
} |
|||
|
|||
void suspend() |
|||
{ |
|||
} |
|||
|
|||
static void draw(int hwnd, int x, int y, int width, int height) |
|||
{ |
|||
Module_RTC* rtc = (Module_RTC*)Kernel::get()->getModule(MODULE_RTC); |
|||
char time[5]; |
|||
sprintf(time, "%02d:%02d", rtc->getHour(), rtc->getMinute()); |
|||
|
|||
Module_UI* ui = (Module_UI*)Kernel::get()->getModule(MODULE_UI); |
|||
ui->getGfx()->fillRect(x, y, width, height, GxEPD_WHITE); |
|||
ui->getGfx()->setFont(&DSEG7_Classic_Bold_53); |
|||
ui->getGfx()->setTextColor(GxEPD_BLACK); |
|||
ui->getGfx()->setCursor(0, y+height); |
|||
ui->getGfx()->print(time); |
|||
/*
|
|||
ui->getGfx()->drawRect(x, y, width, height, GxEPD_BLACK); |
|||
ui->getGfx()->setFont(&FreeMono12pt7b); |
|||
ui->getGfx()->setTextColor(GxEPD_BLACK); |
|||
ui->getGfx()->setCursor(x, y + height); |
|||
ui->getGfx()->print("Hello!"); |
|||
*/ |
|||
} |
|||
}; |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,7 @@ |
|||
#ifndef FONTS_H |
|||
#define FONTS_H |
|||
|
|||
#include <DSEG7_Classic_Bold_53.h> |
|||
#include <Fonts/FreeMono12pt7b.h> |
|||
|
|||
#endif |
@ -0,0 +1,58 @@ |
|||
#ifndef HW_H |
|||
#define HW_H |
|||
|
|||
//////////
|
|||
// I2C
|
|||
#define HW_I2C_SDA 21 |
|||
#define HW_I2C_SCL 22 |
|||
|
|||
|
|||
//////////
|
|||
// RTC
|
|||
|
|||
// Pin
|
|||
// #define HW_RTC 27
|
|||
#define HW_RTC GPIO_NUM_27 |
|||
|
|||
//////////
|
|||
// Display
|
|||
|
|||
// Controller pins
|
|||
// Chip select?
|
|||
#define HW_DISPLAY_CS 5 |
|||
// Data/Command selector?
|
|||
#define HW_DISPLAY_DC 10 |
|||
#define HW_DISPLAY_RESET 9 |
|||
#define HW_DISPLAY_BUSY 19 |
|||
|
|||
#define DISPLAY_WIDTH 200 |
|||
#define DISPLAY_HEIGHT 200 |
|||
|
|||
// Enable GFX library in display driver
|
|||
#define ENABLE_GxEPD2_GFX 1 |
|||
|
|||
|
|||
//////////
|
|||
// Vibration Motor
|
|||
|
|||
// Pin
|
|||
#define HW_VIBRATION_MOTOR 13 |
|||
|
|||
//////////
|
|||
// Buttons
|
|||
|
|||
// Pin assignments for buttons
|
|||
#define HW_BUTTON_TL 25 |
|||
#define HW_BUTTON_TR 32 |
|||
#define HW_BUTTON_BL 26 |
|||
#define HW_BUTTON_BR 4 |
|||
|
|||
// Masks for wakeups
|
|||
#define HW_BUTTON_TL_MASK GPIO_SEL_25 |
|||
#define HW_BUTTON_TR_MASK GPIO_SEL_32 |
|||
#define HW_BUTTON_BL_MASK GPIO_SEL_26 |
|||
#define HW_BUTTON_BR_MASK GPIO_SEL_4 |
|||
// Combined mask for any button
|
|||
#define HW_BUTTON_MASK HW_BUTTON_TL_MASK|HW_BUTTON_TR_MASK|HW_BUTTON_BL_MASK|HW_BUTTON_BR_MASK |
|||
|
|||
#endif |
@ -0,0 +1,14 @@ |
|||
##################################################### |
|||
Arduino How To: Use sub folders for source code |
|||
http://www.visualmicro.com/ |
|||
##################################################### |
|||
|
|||
* .INO code can exist in the project folder and '\src' folder. |
|||
* .Cpp/.c/.S/.h etc. can exist in the project folder, the '\src' folder and in any folder(s) below the '\src' folder. |
|||
* .Cpp/.c/.S/.h sources in shared code projects should follow the same folder rules (because they are merged with the project sources into a temporary build folder prior to compile). |
|||
|
|||
* Use the 'Show All Files' icon above the 'Solution Explorer' to switch between 'included project files' and 'physical files/folders'. |
|||
* Source code in the project folder will always be compiled regardless of inclusion in the project. This functionality can be disabled in Global Options. |
|||
|
|||
|
|||
|
@ -0,0 +1,13 @@ |
|||
#include "Kernel.h" |
|||
|
|||
#define WATCHOS_DEBUG |
|||
|
|||
#define KERNEL_MAX_MODULES 8 |
|||
#define KERNEL_MAX_TASKS 16 |
|||
|
|||
#define MODULE_EAT 0x0001 |
|||
#define MODULE_EAT_MAGIC 0x4150 |
|||
#define MODULE_EAT_VERSION 1 |
|||
#define MODULE_EAT_MAX_ENTRIES 16 |
|||
#define MODULE_EAT_STATIC_OFFSET 4 + (MODULE_EAT_MAX_ENTRIES * 2) |
|||
#define EEPROM_SIZE 4096 |
@ -0,0 +1,125 @@ |
|||
#include "watchos.h" |
|||
#include "hw.h" |
|||
|
|||
#include "Module_EAT.h" |
|||
#include "Module_UI.h" |
|||
#include "Module_RTC.h" |
|||
|
|||
#include "TestTask.cpp" |
|||
|
|||
#include <GxEPD2_BW.h> |
|||
|
|||
Kernel* k; |
|||
int count = 0; |
|||
|
|||
void wakeup(); |
|||
|
|||
void test_draw(int hwnd, int x, int y, int width, int height) |
|||
{ |
|||
Kernel::debug("Test draw!"); |
|||
/*
|
|||
Module_UI* ui = (Module_UI*)k->getModule(MODULE_UI); |
|||
ui->getGfx()->drawRect(x, y, width, height, GxEPD_BLACK); |
|||
ui->getGfx()->setFont(&FreeMono12pt7b); |
|||
ui->getGfx()->setTextColor(GxEPD_BLACK); |
|||
ui->getGfx()->setCursor(x, y + height); |
|||
ui->getGfx()->print("Hello!"); |
|||
*/ |
|||
} |
|||
|
|||
void setup() { |
|||
Kernel::initialize(); |
|||
k = Kernel::get(); |
|||
|
|||
delay(1000); |
|||
|
|||
Serial.printf("Started!\n"); |
|||
|
|||
// Initialize eeprom module
|
|||
Module_EAT* module_eat = new Module_EAT(); |
|||
// If the right magic bytes aren't there then create header + allocation table
|
|||
if (!module_eat->hasValidHeader()) |
|||
{ |
|||
module_eat->reset(); |
|||
} |
|||
// Register module with the kernel
|
|||
k->registerModule(module_eat); |
|||
|
|||
// Initialize UI module
|
|||
Module_UI* module_ui = new Module_UI(); |
|||
k->registerModule(module_ui); |
|||
module_ui->setLayoutMode(0, MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL); |
|||
/*module_ui->createWindow(&test_draw, 0);
|
|||
int shwnd = module_ui->createWindow(); |
|||
module_ui->setLayoutMode(shwnd, MODULE_UI_LAYOUT_MODE_SPLIT_VERTICAL); |
|||
module_ui->createWindow(&test_draw, shwnd); |
|||
module_ui->createWindow(&test_draw, shwnd);*/ |
|||
module_ui->draw(); |
|||
|
|||
Module_RTC* module_rtc = new Module_RTC(); |
|||
k->registerModule(module_rtc); |
|||
|
|||
|
|||
k->registerTask(new TestTask()); |
|||
k->registerTask(new TestTask()); |
|||
|
|||
// TODO: Figure out why we woke up
|
|||
wakeup(); |
|||
|
|||
} |
|||
|
|||
int iters = 0; |
|||
void loop() { |
|||
k->tick(); |
|||
if (iters++ == 5) |
|||
{ |
|||
Module_UI* ui = (Module_UI*)k->getModule(MODULE_UI); |
|||
ui->draw(); |
|||
Kernel::debug("Going to sleep!"); |
|||
iters = 0; |
|||
esp_sleep_enable_timer_wakeup(100000000); // 1 second = 1000000 microseconds
|
|||
esp_sleep_enable_ext0_wakeup(HW_RTC, 0); |
|||
esp_sleep_enable_ext1_wakeup(HW_BUTTON_MASK, ESP_EXT1_WAKEUP_ANY_HIGH); |
|||
esp_light_sleep_start(); |
|||
wakeup(); |
|||
} |
|||
} |
|||
|
|||
void wakeup() |
|||
{ |
|||
Kernel::debug("Wakeup!"); |
|||
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); |
|||
uint64_t wakeup_bits; |
|||
switch (wakeup_reason) |
|||
{ |
|||
case ESP_SLEEP_WAKEUP_TIMER: |
|||
Kernel::debug("Wakeup: Internal RTC"); |
|||
break; |
|||
case ESP_SLEEP_WAKEUP_EXT0: |
|||
Kernel::debug("Wakeup: External RTC"); |
|||
break; |
|||
case ESP_SLEEP_WAKEUP_EXT1: |
|||
Kernel::debug("Wakeup: Button Press"); |
|||
wakeup_bits = esp_sleep_get_ext1_wakeup_status(); |
|||
if (digitalRead(HW_BUTTON_TL) == 1 || (wakeup_bits & HW_BUTTON_TL_MASK)) |
|||
{ |
|||
Kernel::debug("Pressed TL Button"); |
|||
} |
|||
if (digitalRead(HW_BUTTON_TR) == 1 || (wakeup_bits & HW_BUTTON_TR_MASK)) |
|||
{ |
|||
Kernel::debug("Pressed TR Button"); |
|||
} |
|||
if (digitalRead(HW_BUTTON_BL) == 1 || (wakeup_bits & HW_BUTTON_BL_MASK)) |
|||
{ |
|||
Kernel::debug("Pressed BL Button"); |
|||
} |
|||
if (digitalRead(HW_BUTTON_BR) == 1 || (wakeup_bits & HW_BUTTON_BR_MASK)) |
|||
{ |
|||
Kernel::debug("Pressed BR Button"); |
|||
} |
|||
break; |
|||
default: |
|||
Kernel::debug("Wakeup: Reset / First Boot"); |
|||
break; |
|||
} |
|||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,75 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<Filter Include="Source Files"> |
|||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> |
|||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> |
|||
</Filter> |
|||
<Filter Include="Header Files"> |
|||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> |
|||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> |
|||
</Filter> |
|||
<Filter Include="Misc Files"> |
|||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> |
|||
<Extensions>xml;json;txt;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> |
|||
</Filter> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<None Include="watchos.ino" /> |
|||
<None Include="src\arduino folders read me.txt"> |
|||
<Filter>Misc Files</Filter> |
|||
</None> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ClInclude Include="__vm\.watchos.vsarduino.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="..\..\..\..\..\Arduino\watchos\Kernel.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="..\..\..\..\..\Arduino\watchos\Module.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="..\..\..\..\..\Arduino\watchos\Module_EAT.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="..\..\..\..\..\Arduino\watchos\Task.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="..\..\..\..\..\Arduino\watchos\watchos.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="hw.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="Module_UI.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="Module_RTC.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
<ClInclude Include="fonts.h"> |
|||
<Filter>Header Files</Filter> |
|||
</ClInclude> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ClCompile Include="Kernel.cpp"> |
|||
<Filter>Source Files</Filter> |
|||
</ClCompile> |
|||
<ClCompile Include="..\..\..\..\..\Arduino\watchos\Module_EAT.cpp"> |
|||
<Filter>Source Files</Filter> |
|||
</ClCompile> |
|||
<ClCompile Include="..\..\..\..\..\Arduino\watchos\Task.cpp"> |
|||
<Filter>Source Files</Filter> |
|||
</ClCompile> |
|||
<ClCompile Include="Module_UI.cpp"> |
|||
<Filter>Source Files</Filter> |
|||
</ClCompile> |
|||
<ClCompile Include="Module_RTC.cpp"> |
|||
<Filter>Source Files</Filter> |
|||
</ClCompile> |
|||
<ClCompile Include="TestTask.cpp"> |
|||
<Filter>Source Files</Filter> |
|||
</ClCompile> |
|||
</ItemGroup> |
|||
</Project> |
Loading…
Reference in new issue