Browse Source

Eventing system

v1
Adam PIppin 3 years ago
parent
commit
30b446083d
  1. 69
      watchos/Events.cpp
  2. 30
      watchos/Events.h
  3. 20
      watchos/Kernel.cpp
  4. 57
      watchos/Module_EAT.cpp
  5. 3
      watchos/Module_RTC.cpp
  6. 49
      watchos/Module_UI.cpp
  7. 2
      watchos/Module_UI.h
  8. 67
      watchos/Task_Clock.cpp
  9. 54
      watchos/TestTask.cpp
  10. 2
      watchos/__vm/Compile.vmps.xml
  11. 2
      watchos/__vm/Upload.vmps.xml
  12. 14
      watchos/watchos.h
  13. 9
      watchos/watchos.ino
  14. 10
      watchos/watchos.vcxproj
  15. 16
      watchos/watchos.vcxproj.filters
  16. 29
      watchos/watchos_consts.h
  17. 0
      watchos/watchos_fonts.h
  18. 0
      watchos/watchos_hw.h
  19. 4
      watchos/watchos_types.h

69
watchos/Events.cpp

@ -0,0 +1,69 @@
#include "watchos_consts.h"
#include "watchos_types.h"
#include "Events.h"
#include "Kernel.h"
// temp
#include <Arduino.h>
EventQueue::EventQueue()
{
for (int i = 0; i < EVENT_RING_SIZE; i++)
{
m_events[i] = nullptr;
}
}
int EventQueue::next(int n)
{
if (n + 1 == EVENT_RING_SIZE)
return 0;
else
return n + 1;
}
void EventQueue::push(Event* event)
{
delay(500);
if (m_full)
{
// We're full
Kernel::panic("Cannot enqueue event: ring buffer full");
}
// Write at m_next
m_events[m_write] = event;
// Move the next write position up
m_write = next(m_write);
// If the next position to write has looped back around to the next position
// to read, then the ring is full.
if (m_read == m_write)
{
m_full = true;
}
}
Event* EventQueue::pop()
{
// If the next position to read and write match but the ring isn't full,
// then the ring is empty
if (m_read == m_write && !m_full)
{
Kernel::panic("Cannot dequeue event: ring buffer empty");
}
Event* nextEvent = m_events[m_read];
m_read = next(m_read);
// We can never be full if we just removed one
m_full = false;
return nextEvent;
}
bool EventQueue::hasEvent()
{
return m_read != m_write || m_full;
}

30
watchos/Events.h

@ -0,0 +1,30 @@
#ifndef EVENT_H
#define EVENT_H
#include "watchos_consts.h"
#include "watchos_types.h"
struct Event
{
byte source;
byte event;
byte param1;
byte param2;
};
class EventQueue
{
Event* m_events[EVENT_RING_SIZE];
int m_read = 0, m_write = 0;
bool m_full = false;
int next(int n);
public:
EventQueue();
void push(Event* event);
Event* pop();
bool hasEvent();
};
#endif

20
watchos/Kernel.cpp

@ -5,9 +5,9 @@
// I2C Library
#include <Wire.h>
// Hardware information
#include "hw.h"
#include "watchos_hw.h"
#include "Kernel.h"
#include "watchos.h"
#define SCOPE_SIZE 64
@ -148,4 +148,20 @@ Module* Kernel::getModule(int module_id)
}
}
Kernel::panic("Module not found");
}
void Kernel::event(byte module, byte event, byte param1, byte param2)
{
Event* e = new Event();
e->source = module;
e->event = event;
e->param1 = param1;
e->param2 = param2;
for (int i = 0; i < KERNEL_MAX_TASKS; i++)
{
if (m_task[i] != nullptr)
{
m_task[i]->pushEvent(e);
}
}
}

57
watchos/Module_EAT.cpp

@ -1,5 +1,7 @@
#include <EEPROM.h>
#include "consts.h"
#include "Kernel.h"
#include "Module_EAT.h"
#include <EEPROM.h>
struct EAT_Entry
{
@ -9,7 +11,7 @@ struct EAT_Entry
void Module_EAT::initialize()
{
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++)
for (int i = 0; i < MODULE_EAT_MAX_ENTRIES; i++)
{
m_entry[i] = nullptr;
}
@ -30,10 +32,17 @@ int Module_EAT::getId()
return MODULE_EAT;
}
bool Module_EAT::hasValidHeader()
{
EEPROM.begin(EEPROM_SIZE);
uint16_t magic = (EEPROM.read(0) << 8) | EEPROM.read(1);
return magic == MODULE_EAT_MAGIC;
}
void Module_EAT::load()
{
char msg[255];
EEPROM.begin(EEPROM_SIZE);
byte magic_h = EEPROM.read(0);
byte magic_l = EEPROM.read(1);
@ -52,10 +61,10 @@ void Module_EAT::load()
Kernel::panic(msg);
}
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++)
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);
byte module = EEPROM.read((i * 2) + MODULE_EAT_HEADER_LENGTH);
byte length = EEPROM.read((i * 2) + MODULE_EAT_HEADER_LENGTH + 1);
m_entry[i] = new EAT_Entry();
m_entry[i]->module = module;
@ -71,10 +80,10 @@ void Module_EAT::commit()
EEPROM.write(2, m_version);
EEPROM.write(3, m_options);
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++)
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.write((i * 2) + MODULE_EAT_HEADER_LENGTH, m_entry[i]->module);
EEPROM.write((i * 2) + MODULE_EAT_HEADER_LENGTH + 1, m_entry[i]->length);
}
EEPROM.commit();
@ -86,7 +95,7 @@ void Module_EAT::reset()
m_version = MODULE_EAT_VERSION;
m_options = 0;
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++)
for (int i = 0; i < MODULE_EAT_MAX_ENTRIES; i++)
{
m_entry[i] = new EAT_Entry();
m_entry[i]->module = 0;
@ -98,7 +107,7 @@ void Module_EAT::reset()
bool Module_EAT::allocate(byte module, byte length)
{
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++)
for (int i = 0; i < MODULE_EAT_MAX_ENTRIES; i++)
{
if (m_entry[i]->module == module)
{
@ -119,9 +128,13 @@ bool Module_EAT::allocate(byte module, byte length)
int Module_EAT::getLength(byte module)
{
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++)
for (int i = 0; i < MODULE_EAT_MAX_ENTRIES; i++)
{
if (m_entry[i]->module == module)
if (m_entry[i] == nullptr)
{
continue;
}
else if (m_entry[i]->module == module)
{
return m_entry[i]->length;
}
@ -132,10 +145,14 @@ int Module_EAT::getLength(byte module)
int Module_EAT::getOffset(byte module)
{
int offset = MODULE_EAT_STATIC_OFFSET;
for (int i=0; i<MODULE_EAT_MAX_ENTRIES; i++)
int offset = MODULE_EAT_HEADER_LENGTH + MODULE_EAT_TABLE_LENGTH;
for (int i = 0; i < MODULE_EAT_MAX_ENTRIES; i++)
{
if (m_entry[i]->module == module)
if (m_entry[i] == nullptr)
{
continue;
}
else if (m_entry[i]->module == module)
{
return offset;
}
@ -161,9 +178,6 @@ byte Module_EAT::read(byte module, byte offset)
}
#endif
offset += entry_offset;
char msg[255];
sprintf(msg, "Reading from %d", offset);
Kernel::debug(msg);
return EEPROM.read(offset);
}
@ -181,9 +195,6 @@ void Module_EAT::write(byte module, byte offset, byte value)
}
#endif
offset += entry_offset;
char msg[255];
sprintf(msg, "Writing %d to %d", value, offset);
Kernel::debug(msg);
EEPROM.write(offset, value);
EEPROM.commit();
EEPROM.commit();
}

3
watchos/Module_RTC.cpp

@ -1,3 +1,4 @@
#include "watchos.h"
#include "Module_RTC.h"
#include <DS3232RTC.h>
@ -21,7 +22,7 @@ void Module_RTC::tick()
{
// Refresh every time interrupt fires (every minute)
refresh();
// TODO: Signal kernel/processes/whatever?
Kernel::get()->event(MODULE_RTC, MODULE_RTC_EVENT_MINUTE, lastTime.Hour, lastTime.Minute);
}
}

49
watchos/Module_UI.cpp

@ -1,6 +1,7 @@
#include "watchos.h"
#include "hw.h"
#include "watchos_hw.h"
#include "Module_UI.h"
#include "Task.h"
#include <GxEPD2_BW.h>
@ -13,6 +14,7 @@ struct UI_Window
byte layout_mode = 0;
void (*callback)(int, int, int, int, int); // hwnd, x, y, width, height
bool dirty = false;
Task* task = nullptr;
byte reserved1;
byte reserved2;
};
@ -77,6 +79,31 @@ int Module_UI::createWindow(void (*callback)(int, int, int, int, int), int paren
Kernel::panic("Exceeded MODULE_UI_MAX_WINDOWS!");
}
int Module_UI::createWindow(Task* task, 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]->task = task;
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++)
@ -116,6 +143,10 @@ void Module_UI::setLayoutMode(int hwnd, byte layout_mode)
void Module_UI::setDirty(int hwnd)
{
// This sort of doesn't work beyond the simplest of cases.
// If we mark all parent windows as dirty but have them redraw their entire window, then they'll overwrite
// sibling windows, so currently we just end up losing half the screen. Or we redraw siblings and their children
// and... we're basically back in the same spot.
UI_Window* window = nullptr;
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++)
{
@ -156,9 +187,16 @@ void Module_UI::draw(int hwnd, int x, int y, int width, int height)
}
// Draw window if applicable
if (window->callback != nullptr && window->dirty)
if (window->dirty)
{
(window->callback)(hwnd, x, y, width, height);
if (window->callback != nullptr)
{
(window->callback)(hwnd, x, y, width, height);
}
else if (window->task != nullptr)
{
window->task->draw(hwnd, x, y, width, height);
}
}
// Count children
@ -178,6 +216,8 @@ void Module_UI::draw(int hwnd, int x, int y, int width, int height)
}
// Grab all children
// TODO: This is really unnecessary I guess. We could just loop the whole array again.
// We really only need child_count to figure out layouts.
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++)
@ -212,6 +252,7 @@ void Module_UI::draw(int hwnd, int x, int y, int width, int height)
draw(children[i]->hwnd, x + (i * child_width), y, child_width, height);
}
break;
}
free(children);
}

2
watchos/Module_UI.h

@ -2,6 +2,7 @@
#define MODULE_UI_H
#include "watchos.h"
#include "Task.h"
#include <GxEPD2_GFX.h>
struct UI_Window;
@ -19,6 +20,7 @@ public:
void tick();
int createWindow(void (*callback)(int, int, int, int, int), int parent = 0, int zorder = 0);
int createWindow(Task* task, int parent = 0, int zorder = 0);
int createWindow(int parent = 0, int zorder = 0);
void draw();
GxEPD2_GFX* getGfx();

67
watchos/Task_Clock.cpp

@ -0,0 +1,67 @@
#include "watchos.h"
#include "Module_UI.h"
#include "watchos_fonts.h"
#include "Events.h"
class Task_Clock : public Task
{
private:
int hwnd;
Module_UI* ui;
byte hour, minute;
public:
void initialize()
{
ui = (Module_UI*)Kernel::get()->getModule(MODULE_UI);
hwnd = ui->createWindow((Task*)this, 0, 0);
}
void tick(unsigned int signal)
{
bool dirty = false;
char msg[255];
while (this->hasEvent())
{
Event* e = this->popEvent();
sprintf(msg, "event source %d id %d", e->source, e->event);
Kernel::debug(msg);
if (e->source == MODULE_RTC && e->event == MODULE_RTC_EVENT_MINUTE)
{
sprintf(msg, "old %d:%d new %d:%d", hour, minute, e->param1, e->param2);
Kernel::debug(msg);
if (e->param1 != hour || e->param2 != minute)
{
Kernel::debug("Processed RTC event");
hour = e->param1;
minute = e->param2;
dirty = true;
}
}
}
if (dirty)
{
Module_UI* ui = (Module_UI*)Kernel::get()->getModule(MODULE_UI);
ui->setDirty(hwnd);
}
}
void suspend()
{
}
void draw(int hwnd, int x, int y, int width, int height)
{
char time[6];
sprintf(time, "%02d:%02d", hour, minute);
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);
}
};

54
watchos/TestTask.cpp

@ -1,54 +0,0 @@
#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!");
*/
}
};

2
watchos/__vm/Compile.vmps.xml

File diff suppressed because one or more lines are too long

2
watchos/__vm/Upload.vmps.xml

File diff suppressed because one or more lines are too long

14
watchos/watchos.h

@ -1,13 +1,5 @@
#include "consts.h"
// #include <Arduino.h>
#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
typedef unsigned char byte;

9
watchos/watchos.ino

@ -1,11 +1,11 @@
#include "watchos.h"
#include "hw.h"
#include "watchos_hw.h"
#include "Module_EAT.h"
#include "Module_UI.h"
#include "Module_RTC.h"
#include "TestTask.cpp"
#include "Task_Clock.cpp"
#include <GxEPD2_BW.h>
@ -60,8 +60,8 @@ void setup() {
k->registerModule(module_rtc);
k->registerTask(new TestTask());
k->registerTask(new TestTask());
k->registerTask(new Task_Clock());
k->registerTask(new Task_Clock());
// TODO: Figure out why we woke up
wakeup();
@ -76,6 +76,7 @@ void loop() {
Module_UI* ui = (Module_UI*)k->getModule(MODULE_UI);
ui->draw();
Kernel::debug("Going to sleep!");
delay(500);
iters = 0;
esp_sleep_enable_timer_wakeup(100000000); // 1 second = 1000000 microseconds
esp_sleep_enable_ext0_wakeup(HW_RTC, 0);

10
watchos/watchos.vcxproj

File diff suppressed because one or more lines are too long

16
watchos/watchos.vcxproj.filters

@ -39,7 +39,7 @@
<ClInclude Include="..\..\..\..\..\Arduino\watchos\watchos.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hw.h">
<ClInclude Include="watchos_hw.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Module_UI.h">
@ -48,7 +48,16 @@
<ClInclude Include="Module_RTC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fonts.h">
<ClInclude Include="watchos_fonts.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Events.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="watchos_consts.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="watchos_types.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -71,5 +80,8 @@
<ClCompile Include="TestTask.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Events.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

29
watchos/watchos_consts.h

@ -0,0 +1,29 @@
#ifndef CONSTS_H
#define CONSTS_H
#define WATCHOS_DEBUG
#define KERNEL_MAX_MODULES 8
#define KERNEL_MAX_TASKS 16
#define EVENT_RING_SIZE 8
#define MODULE_EAT 0x0001
#define MODULE_UI 0x0002
#define MODULE_RTC 0x0004
#define MODULE_RTC_EVENT_MINUTE 0x0001
#define MODULE_EAT_MAGIC 0x4150
#define MODULE_EAT_VERSION 1
#define MODULE_EAT_MAX_ENTRIES 16
#define MODULE_EAT_HEADER_LENGTH 4
#define MODULE_EAT_TABLE_LENGTH (MODULE_EAT_MAX_ENTRIES * 2)
#define EEPROM_SIZE 4096
#define MODULE_UI_MAX_WINDOWS 32
#define MODULE_UI_LAYOUT_MODE_NONE 0
#define MODULE_UI_LAYOUT_MODE_SPLIT_VERTICAL 1
#define MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL 2
#endif

0
watchos/fonts.h → watchos/watchos_fonts.h

0
watchos/hw.h → watchos/watchos_hw.h

4
watchos/watchos_types.h

@ -0,0 +1,4 @@
#ifndef TYPES_H
#define TYPES_H
typedef unsigned char byte;
#endif
Loading…
Cancel
Save