aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/Clock.cpp217
-rwxr-xr-xsrc/Clock.h133
-rwxr-xr-xsrc/Configuration.h65
-rwxr-xr-xsrc/Machine.cpp148
-rwxr-xr-xsrc/Machine.h72
-rwxr-xr-xsrc/Monitor.cpp312
-rwxr-xr-xsrc/Monitor.h226
-rwxr-xr-xsrc/SafetyThird.cpp47
-rwxr-xr-xsrc/SafetyThird.h198
-rwxr-xr-xsrc/TaskScheduler.h824
-rwxr-xr-xsrc/ems-light.ino3
-rwxr-xr-xsrc/keywords.h122
12 files changed, 2367 insertions, 0 deletions
diff --git a/src/Clock.cpp b/src/Clock.cpp
new file mode 100755
index 0000000..4f6431d
--- /dev/null
+++ b/src/Clock.cpp
@@ -0,0 +1,217 @@
+/*-------------------------------------------------------------------Clock.cpp
+ * Authors: Joseph Wayne Dumoulin, Donald Delmar Davis, Suspect Devices
+ *
+ * Liscence: "Simplified BSD License"
+ *
+ * Copyright (c) 2016, Donald Delmar Davis, Suspect Devices
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in thedocumentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *-----------------------------------------------------------------------------
+ * The clock module is about time.
+ * This includes
+ * An RTC backed (if avaliable) time module for timestamps
+ * A shcedular for most tasks.
+ */
+
+#include "Monitor.h"
+#include "Clock.h"
+
+
+/*
+ * notes on sync weirdness.
+ *
+ * This class grew out of a system which used external RTC that had to share
+ * a buss with all sorts of sensors.
+ *
+ * Rather than hog the buss a running seconds count was maintained (unixtime)
+ * and the time variables were updated and the rtc referenced once a minute.
+ *
+ * With an internal rtc this gets a little krufty.
+ *
+ * On the other hand a less than perfect clock can be implimented on systems
+ * w/o access to RTCs by counting seconds using whatever millis is using.
+ *
+ * this is not currently implimented but we will leave the cruft so it can be.
+ *
+ */
+
+/* Create an rtc object mebby should be private to the class JOE?? */
+
+// add preprocessor code here for non M0 systems.
+#ifdef ARDUINO_SAMD_ZERO
+RTCZero rtc;
+#endif
+/* calculate build date and time (this should be referenceable from monitor for SWV ... */
+const byte build_seconds = ((__TIME__[6] - '0') * 10 + __TIME__[7] - '0');
+const byte build_minutes = ((__TIME__[3] - '0') * 10 + __TIME__[4] - '0');
+const byte build_hours = ((__TIME__[0] - '0') * 10 + __TIME__[1] - '0');
+
+
+const byte build_day = ((__DATE__[4] >= '0') ? (__DATE__[4] - '0') * 10 : 0) + \
+ (__DATE__[5] - '0') ;
+const byte build_month = ( \
+ (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n') ? 1 : \
+ (__DATE__[0] == 'F') ? 2 : \
+ (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') ? 3 : \
+ (__DATE__[0] == 'A' && __DATE__[1] == 'p') ? 4 : \
+ (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') ? 5 : \
+ (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') ? 6 : \
+ (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') ? 7 : \
+ (__DATE__[0] == 'A' && __DATE__[1] == 'u') ? 8 : \
+ (__DATE__[0] == 'S') ? 9 : \
+ (__DATE__[0] == 'O') ? 10 : \
+ (__DATE__[0] == 'N') ? 11 : \
+ (__DATE__[0] == 'D') ? 12 : \
+ /* error default */ 99 \
+ );
+const byte build_year = ( \
+ (__DATE__[ 9] - '0') * 10 + \
+ (__DATE__[10] - '0') \
+ );
+
+
+Clock clock;
+
+
+void Clock::init()
+
+{
+
+ rtc.begin(); // initialize RTC
+
+ // if the rtc is set in the early 00s Set the and date to build time initially
+ // otherwise assume that the battery was keeping it running appropriately.
+ if (!rtc.getYear()){
+ rtc.setTime(build_hours,build_minutes,build_seconds);
+ rtc.setDate(build_day, build_month, build_year);
+ }
+
+ monitor.debug("Clock module initialized");
+ monitor.registerAction(_TIM_, &TIM);
+ monitor.registerAction(_NOW_, &NOW);
+}
+
+// bracket with pre-processor defs ....
+bool Clock::RTCIsRunning(void) {
+ return (RTC->MODE2.CTRL.reg & RTC_MODE2_CTRL_ENABLE);
+}
+
+const uint8_t daysInMonth [] { 31,28,31,30,31,30,31,31,30,31,30,31 };
+
+// number of days since 2000/01/01, valid for 2001..2099
+static uint16_t date2days(uint16_t yy, uint8_t mm, uint8_t dd) {
+ if (yy >= 2000)
+ yy -= 2000;
+ uint16_t days = dd;
+ for (uint8_t i = 1; i < mm; ++i)
+ days += pgm_read_byte(daysInMonth + i - 1);
+ if (mm > 2 && yy % 4 == 0)
+ ++days;
+ return days + 365 * yy + (yy + 3) / 4 - 1;
+}
+
+static long time2long(uint16_t days, uint8_t hh, uint8_t mm, uint8_t ss) {
+ return ((days * 24L + hh) * 60 + mm) * 60 + ss;
+}
+
+static uint8_t conv2d(const char* p) {
+ uint8_t v = 0;
+ if ('0' <= *p && *p <= '9')
+ v = *p - '0';
+ return 10 * v + *++p - '0';
+}
+
+void Clock::calcUnixTime(void) {
+ uint32_t t;
+ uint16_t days = date2days(yOff, m, d);
+ t = time2long(days, hh, mm, ss) + SECONDS_FROM_1970_TO_2000 ;
+ unixtime = t;
+}
+
+static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
+static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }
+
+void Clock::set(time_t t) {
+ rtc.setEpoch((uint32_t)t);
+}
+
+void Clock::set(const char * dateString) {
+ //TIM:%02d/%02d/%04d %02d:%02d:%02d"
+ //TIM:mm/dd/yyyy hh:mm:ss
+ //0123456789012345678901234
+ uint8_t seconds,minutes,hours,days,leap,month;
+ long int yearoff;
+ char dateStringBuffer[24];
+ strncpy(dateStringBuffer,dateString,23);
+ dateStringBuffer[2]=dateStringBuffer[5]=dateStringBuffer[10]
+ =dateStringBuffer[13]=dateStringBuffer[16]=dateStringBuffer[19]='\0';
+ seconds = atoi(dateStringBuffer+17);
+ minutes = atoi(dateStringBuffer+14);
+ hours = atoi(dateStringBuffer+11);;
+ days = atoi(dateStringBuffer+3);
+ month = atoi(dateStringBuffer);
+ yearoff = atoi(dateStringBuffer+6) - (2000);
+ monitor.debug("?:%02d/%02d/%04d %02d:%02d:%02d",month,days,yearoff+(2000),hours,minutes,seconds);
+ rtc.setTime(hours,minutes,seconds);
+ rtc.setDate(days, month, yearoff);
+}
+
+uint8_t Clock::dayofweek() /* 0 = Sunday */
+{ int y=year();
+ int m=month();
+ int d=day();
+ static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
+ y -= m < 3;
+ return (uint8_t) ((y + y/4 - y/100 + y/400 + t[m-1] + d) % 7);
+}
+
+// populate running values from RTC
+void Clock::sync() {
+ ss = rtc.getSeconds();
+ rtcisrunning = 1; //FIXME!!!
+ mm = rtc.getMinutes();
+ hh = rtc.getHours();
+ d = rtc.getDay();
+ m = rtc.getMonth();
+ yOff = rtc.getYear();
+#if DEBUG_SYNC
+ monitor.debug("RTC: SYNC mm=%d,ss=%d",mm,ss);
+#endif
+
+}
+
+void Clock::run() {
+
+}
+
+
+void timeStamp ( char *buffer ) {
+ sprintf(buffer,"%02d/%02d/%04d %02d:%02d:%02d",
+ clock.month(), clock.day(), clock.year(),
+ clock.hour(), clock.minute(), clock.second());
+}
+
+
+
+
diff --git a/src/Clock.h b/src/Clock.h
new file mode 100755
index 0000000..690d72a
--- /dev/null
+++ b/src/Clock.h
@@ -0,0 +1,133 @@
+/*---------------------------------------------------------------------Clock.h
+ * Authors: Joseph Wayne Dumoulin, Donald Delmar Davis, Suspect Devices
+ *
+ * Liscence: "Simplified BSD License"
+ *
+ * Copyright (c) 2016, Donald Delmar Davis, Suspect Devices
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in thedocumentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *-----------------------------------------------------------------------------
+ *
+ *
+ *
+ */
+
+#ifndef Clock_h
+#define Clock_h
+
+#include "Configuration.h"
+#include "Monitor.h"
+#include "TaskScheduler.h"
+
+#define SECONDS_PER_DAY 86400L
+#define SECONDS_FROM_1970_TO_2000 946684800
+#define DEBUG_SYNC 0
+#define CHARS_IN_TIMESTAMP 20
+
+// add preprocessor code here for non M0 systems.
+#ifdef ARDUINO_SAMD_ZERO
+#include <RTCZero.h>
+extern RTCZero rtc;
+#endif
+
+// forward
+class Clock;
+extern Clock clock;
+
+void timeStamp(char *);
+
+class Clock
+{
+private:
+
+
+public:
+
+
+ /*----------------------------------------------------------------------
+ * Monitor Callbacks
+ *--------------------------------------------------------------------*/
+
+ static void TIM (uint8_t kwIndex, uint8_t verb,char *args) {
+ static char thetimestamp[CHARS_IN_TIMESTAMP];
+ if (verb == '!') {
+ clock.set(args);
+ }
+ timeStamp(thetimestamp);
+ monitor.update("TIM","%s",thetimestamp);
+ }
+
+ static void NOW (uint8_t kwIndex, uint8_t verb,char *args) {
+ static char intbuff[25];
+ if (verb == '!') {
+ clock.set(atol(args));
+ }
+ ultoa(clock.time(),intbuff,10);
+ monitor.update("NOW","%s",intbuff);
+
+ }
+
+
+ Clock() {} ;
+ void init();
+ bool RTCIsRunning();// const { return (rtcisrunning==1); };
+ void set(time_t);
+ time_t get();
+ void set(const char *);
+
+// need to generalize this at some point.
+#if 0
+ uint16_t year() const { return 2000 + yOff; }
+ uint8_t month() const { return m; }
+ uint8_t day() const { return d; }
+ uint8_t hour() const { return hh % 24; }
+ uint8_t minute() const { return mm % 60; }
+ uint8_t second() const { return ss % 60; }
+ time_t time() const {return unixtime;}
+#else
+ uint16_t year() const { return 2000 + rtc.getYear(); }
+ uint8_t month() const { return rtc.getMonth(); }
+ uint8_t day() const { return rtc.getDay(); }
+ uint8_t hour() const { return rtc.getHours(); }
+ uint8_t minute() const { return rtc.getMinutes(); }
+ uint8_t second() const { return rtc.getSeconds(); }
+ time_t time() const {return rtc.getEpoch();}
+#endif
+ uint8_t dayofweek();
+ void tick(void);
+ void run(); // this is wrong.
+ //bool rolledOver(); //??
+ //void addToCalendar();
+ //void removeFromCalendar();
+ void sync();
+ void calcUnixTime();
+
+protected:
+ uint8_t yOff, m, d, hh, mm, ss, rtcisrunning;
+ time_t unixtime;
+
+};
+
+
+#endif
diff --git a/src/Configuration.h b/src/Configuration.h
new file mode 100755
index 0000000..822846d
--- /dev/null
+++ b/src/Configuration.h
@@ -0,0 +1,65 @@
+/*------------------------------------------------------------------
+ * Common defines for EMS hardware
+ */
+
+#ifndef EMS_COMMON_h
+#define EMS_COMMON_h
+
+#include <Arduino.h>
+
+#define GIT_VERSION "xxxxxxxxxxxxxxxxx"
+#define MAX_PSN_LENGTH 33
+//
+// reccommend major and minor rev plus build date.
+//
+#ifndef FIRMWAREREV
+#define FIRMWAREREV "EMS1"
+#endif
+
+#define SAFETY_LAMP_PIN 13
+//
+// Define E_STOP_PIN to enable estop switch
+//
+//#define E_STOP_PIN x
+#ifndef E_STOP_PULLED
+#define E_STOP_PULLED 1
+#endif
+
+//
+// Define LIGHT_CURTAIN pins to enable light curtain
+//
+//#define LIGHT_CURTAIN_OSDD_PIN 5
+//#define LIGHT_CURTAIN_START_PIN 6
+#define LIGHT_CURTAIN_START LOW
+#define LIGHT_CURTAIN_STOP HIGH
+#define LIGHT_CURTAIN_OK LOW
+#define LIGHT_CURTAIN_NOT_CLEAR HIGH
+
+//
+// Define K1_PIN if konactor is connected to machine
+//
+//#define K1_PIN x
+
+//
+#define POST_TIMESTAMP_DELIMETER ','
+
+// analog write resolution (new for m0s)
+#define ANALOG_WRITE_RESOLUTION 8
+
+// tzoffset from localtime (PDT) to GMT (7 hrs) in seconds
+#define LOCAL_TZ_OFFSET ( 7L * 3600L )
+
+// non volitile memory addresses go here......
+#define CLEAN_SHUTDOWN_EEPROM_ADDR ((uint8_t *)0)
+#define CLEAN_SHUTDOWN_CODE 0xAA //unlikely to randomly be in there
+#define LOG_LEVEL_EEPROM_ADDR ((uint8_t*) 2)
+#define DEBUG_LEVEL_EEPROM_ADDR ((uint8_t*) 3)
+
+
+
+#define MONITOR_UART Serial
+#define MONITOR_BAUD 9600
+
+#define FAKE_A_FLOAT(_f) int((_f)), int(abs((_f) - int(_f))*100)
+
+#endif
diff --git a/src/Machine.cpp b/src/Machine.cpp
new file mode 100755
index 0000000..1f9e8a5
--- /dev/null
+++ b/src/Machine.cpp
@@ -0,0 +1,148 @@
+#include "Machine.h"
+#include "Monitor.h"
+#include "Clock.h"
+#include "SafetyThird.h"
+
+const char statenames[][STATENAMELEN]={STATENAMES};
+Machine machine;
+
+
+
+void setup() {
+ machine.init();
+}
+
+// POST
+void Machine::init(){
+ monitor.init();
+ inSafeStateToRun=false;
+ setState(STATE_SELF_TEST_INIT);
+ safety.init();
+ if (state() == STATE_SELF_TEST_INIT) {
+ inSafeStateToRun=true;
+ setState(STATE_WARM_UP);
+ }
+ monitorTask.set(100L, TASK_FOREVER, &Monitor::run);
+ machine.todolist.addTask(monitorTask);
+ monitorTask.enable();
+ machineTask.set(500L, TASK_FOREVER, &systemInCharge);
+ machine.todolist.addTask(machineTask);
+ machineTask.enable();
+ monitor.registerAction(_TMR_, &TMR);
+ monitor.registerAction(_TMS_, &TMS);
+ monitor.registerAction(_TRM_, &TRM);
+
+}
+
+const char * Machine::stateName(int s) { return statenames[s]; };
+void loop() {
+ machine.todolist.execute();
+}
+
+void Machine::setState(int newState) {
+ previousState=currentState;
+ currentState=newState;
+ monitor.update("STC","%.04d %s ==> %s",
+ newState,
+ stateName(previousState),
+ stateName(newState));
+}
+#define ENTERING (machine.previousState!=machine.currentState)
+// this is the state machine.
+
+static void systemInCharge(){
+ static time_t lastUpdate;
+
+ switch (machine.state()){
+
+ case STATE_OFF:
+ case STATE_SELF_TEST_INIT:break;
+
+ case STATE_WARM_UP:
+ if (ENTERING) {
+ safety.redLightVal=RED_FULL_ON;//fix number foo
+ } else {
+ machine.setState(STATE_RUN_MODE);
+ }
+ break;
+ case STATE_RUN_MODE:
+ if (ENTERING) {
+ lastUpdate=clock.time();
+ }
+ time_t delta;
+ delta=lastUpdate=(clock.time()-lastUpdate);
+ if ((delta>0)
+ && (machine.timerIsRunning)
+ ){
+ machine.timeRemaining -= (int) delta;
+ if (machine.timeRemaining<0) {
+ machine.timeRemaining=0;
+ }
+ monitor.update("TRM","%d",machine.timeRemaining);
+ }
+ if ((machine.timeRemaining==0)
+ && machine.timerIsRunning
+ ){
+ //pump.Release();
+ machine.timerIsRunning=false;
+ monitor.log("-- Job finished --");
+ machine.timeRemaining=machine.timerSetting;
+ monitor.update("TRM","%d",machine.timeRemaining);
+ monitor.update("TMR","%d",machine.timerIsRunning);
+ }
+ break;
+
+
+ case STATE_SHUTDOWN:
+ case STATE_POST_FAILURE:
+ case STATE_E_SHUTDOWN:
+ if (ENTERING) {
+ safety.redLightVal=255;//fix number foo
+ } else {
+ machine.setState(STATE_RUN_MODE);
+ }
+ break;
+ case STATE_MANUAL_MODE:
+ case STATE_DIAGNOSTICS_MODE:break;
+ case STATE_UNKNOWN_STATE:break;
+ }
+ machine.previousState=machine.currentState;
+ lastUpdate=clock.time();
+}
+/*----------------------------------------------------------------------
+ * Monitor Callbacks
+ *--------------------------------------------------------------------*/
+
+static void TMR (uint8_t kwIndex, uint8_t verb,char *args) {
+ if (verb == '!') {
+ bool wasRunning=machine.timerIsRunning;
+ bool start=machine.timerIsRunning=atoi(args);
+ if (start) {
+ //
+ monitor.log("-- Job (Re)Started --"); //if was notrunning?
+ } else {
+ if (wasRunning) {
+ monitor.log("-- Job Paused --");
+ }
+ }
+ }
+ monitor.update("TMR","%d",machine.timerIsRunning);
+}
+
+static void TMS (uint8_t kwIndex, uint8_t verb,char *args) {
+
+ if (verb == '!') {
+ machine.timeRemaining=machine.timerSetting=atoi(args);
+ monitor.update("TRM","%d",machine.timeRemaining);
+ }
+ monitor.update("TMS","%d",machine.timerSetting);
+}
+
+static void TRM (uint8_t kwIndex, uint8_t verb,char *args) {
+
+ if (verb == '!') {
+ machine.timeRemaining=atoi(args);
+ monitor.update("TRM","%d",machine.timeRemaining);
+ }
+ monitor.update("TRM","%d",machine.timeRemaining);
+}
diff --git a/src/Machine.h b/src/Machine.h
new file mode 100755
index 0000000..db7c9fd
--- /dev/null
+++ b/src/Machine.h
@@ -0,0 +1,72 @@
+#ifndef Machine_h
+#define Machine_h
+#include "Configuration.h"
+#include "Monitor.h"
+#include "TaskScheduler.h"
+// This stuff probably belongs in Machine.h
+
+#define STATENAMELEN 25
+
+enum machine_states {
+ STATE_OFF,
+ STATE_SELF_TEST_INIT,
+ STATE_WARM_UP,
+ STATE_RUN_MODE,
+ STATE_SHUTDOWN,
+ STATE_POST_FAILURE,
+ STATE_E_SHUTDOWN,
+ STATE_MANUAL_MODE,
+ STATE_DIAGNOSTICS_MODE,
+ STATE_UNKNOWN_STATE,
+};
+
+#define NSTATES (STATE_UNKNOWN_STATE)
+
+#define STATENAMES \
+"OFF\000 ",\
+"SELF TEST INIT\000 ",\
+"WARMUP\000 ",\
+"NORMAL OPERATION\000 ",\
+"SHUT DOWN\000 ",\
+"POST FAILURE\000 ",\
+"EMERGENCY STOP\000 ",\
+"MANUAL MODE ",\
+"DIAGNOSTICS MODE\000 ",\
+"UNKNOWN\000 "
+
+class Machine;
+extern Machine machine;
+
+static void STC (uint8_t kwIndex, uint8_t verb,char *args);
+static void TMR (uint8_t kwIndex, uint8_t verb,char *args);
+static void TMS (uint8_t kwIndex, uint8_t verb,char *args);
+static void TRM (uint8_t kwIndex, uint8_t verb,char *args);
+static void systemInCharge();
+
+class Machine
+{
+private:
+ Task monitorTask;
+ Task machineTask;
+
+public:
+ int previousState;
+ int currentState;
+ int timerSetting;
+ bool timerIsRunning;
+ int timeRemaining;
+ bool inSafeStateToRun;
+
+ Machine() : currentState(STATE_OFF) {}
+ Scheduler todolist;
+ void init();
+ void setPreviousState(int newState); // {currentState=newState;};
+ void setState(int newState); // {currentState=newState;};
+ int state() { return currentState; };
+ const char * stateName(int) ;
+
+protected:
+
+};
+
+#endif
diff --git a/src/Monitor.cpp b/src/Monitor.cpp
new file mode 100755
index 0000000..fd77089
--- /dev/null
+++ b/src/Monitor.cpp
@@ -0,0 +1,312 @@
+/*---------------------------------------------------------------------Monitor.h
+ * Author: Joseph Wayne Dumoulin, Donald Delmar Davis, Suspect Devices
+ *
+ * Liscence: "Simplified BSD License"
+ *
+ * Copyright (c) 2016, Donald Delmar Davis, Suspect Devices
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in thedocumentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *-----------------------------------------------------------------------------
+ * The idea here is to create an automation freindly monitor protocol and debug
+ * interface. Because this needs to be efficient (and flexible) I have chosen a
+ * very simple interface which will access and may manipulate a devices public
+ * variables.
+ *
+ * Basic Syntax for the monitor is
+ * XXX[?!:] <variable stuff> <CR>
+ * where:
+ * XXX is a 3 letter command or variable.
+ * ! execute command or store value
+ * ? get value
+ * :<20 char timestamp>, value returned.
+ *
+ * keywords are described in keywords.h
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "Monitor.h"
+#include "Clock.h"
+
+/*
+ * +1 is for the trailing null.
+ */
+const char keywords[((NKEYWORDS)*3)+1] = KEYWORDS;
+#define KWHELPLEN 26
+
+char kwhelp[((NKEYWORDS+1) * KWHELPLEN)+1] = HELPTEXT;
+char updateBuffer[MAX_RETURN_VALUE]; //allocate this once use it often.
+actionptr actions[NKEYWORDS];
+
+
+void Monitor::update(const char *data, const char *format, ...) {
+ va_list blarg;
+ updateBuffer[0]=data[0];updateBuffer[1]=data[1];updateBuffer[2]=data[2];updateBuffer[3]=':';
+ timeStamp(updateBuffer+4);
+ updateBuffer[23]=POST_TIMESTAMP_DELIMETER;
+ va_start(blarg, format);
+ vsnprintf(updateBuffer+24, MAX_RETURN_VALUE-24, format, blarg);
+ va_end(blarg);
+ if (!commandMode()) {
+ MONITOR_UART.println(updateBuffer);
+ MONITOR_UART.flush();
+ }
+};
+
+Monitor monitor;
+
+void Monitor::fatal(int newstate, const char *format, ...) {
+ va_list blarg;
+ update("FTL", format, blarg);
+ va_end(blarg);
+ //update("FTL","Fatal Error, New State = %s", soh, stateName(soh)); //maybe move this to setstate.
+ //sendActionOut();
+ //stateChange(soh);
+};
+
+void Monitor::debug(const char *format, ...) {
+ va_list blarg;
+// if (_debugLevel >= SEV_DEBUG)
+ update("DBG:", format, blarg);
+ va_end(blarg);
+};
+
+
+
+
+char * samd21m0psn(char * buffer) {
+
+ // https://gist.github.com/mgk/c9ec87436d2d679e5d08
+ volatile uint32_t val1, val2, val3, val4;
+ volatile uint32_t *ptr1 = (volatile uint32_t *)0x0080A00C;
+ val1 = *ptr1;
+ volatile uint32_t *ptr = (volatile uint32_t *)0x0080A040;
+ val2 = *ptr;
+ ptr++;
+ val3 = *ptr;
+ ptr++;
+ val4 = *ptr;
+ snprintf(buffer, MONITOR_SERIAL_NUMBER_CHARS ,"%8x%8x%8x%8x", val1, val2, val3, val4);
+
+}
+
+Monitor::Monitor() {
+
+ _debugLevel=EEPROM_NOT_SET;
+ samd21m0psn(this->_psn);
+ /*
+ * insure that all jump table pointers go somewhere
+ */
+ int k;
+
+ for (k=0;k<NKEYWORDS;k++) {
+ if (actions[k]==NULL) {
+ actions[k]=&NOP;
+ }
+ }
+}
+void Monitor::init(){
+
+
+ MONITOR_UART.begin(MONITOR_BAUD);
+ while (!MONITOR_UART)
+ delay(1); // wait for USB serial to initialize (BAD IDEA)
+// console=&MONITOR_UART;
+ MONITOR_UART.flush();
+
+
+
+ if (debugLevel()==EEPROM_NOT_SET) {
+ setDebugLevel(SEV_WARN); // default level?
+ }
+
+ monitor.setCommandMode(0);
+
+ clock.init(); // needed to timestamp debug statement.
+
+ registerAction(_SYN_, &ACK);
+ registerAction(_DVL_, &DVL);
+ registerAction(_GIT_, &GIT);
+ registerAction(_HLP_, &HLP);
+ registerAction(_CMD_, &CMD); //
+ registerAction(_SSN_, &SSN);
+ registerAction(_SWV_, &SWV);
+ registerAction(_HWV_, &HWV);
+ registerAction(_RST_, &RST);
+ registerAction(_BLD_, &BLD);
+
+ //machine.todolist.addTask(__commands);
+ //__commands.enable();
+// monitor.update("monitor init");
+
+ monitor.debug("Monitor Init");
+
+}
+
+int commandBufferIndex=0;
+bool gotLine=false;
+char commandBuffer[84]={}; // rename and use constants.
+
+void Monitor::registerAction(uint8_t ndx, actionptr action){
+ if (ndx >=0 && ndx<NKEYWORDS) {
+ actions[ndx]=action;
+ }
+}
+
+void Monitor::run() {
+ int index;
+ char key[3];
+ char action;
+ char *arguments;
+ char ch;
+ int linlen;
+// if( (monitor.console!=NULL) && monitor.console->available() ){
+// while( monitor.console->available() && commandBufferIndex < MAX_MONITOR_LINE_LENGTH && ((ch=monitor.console->read()) != '\n') && ch !='\r' ) {
+// commandBuffer[commandBufferIndex++] = isprint(ch)?ch:'*';
+// monitor.console->flush();
+// } monitor.debug("monitor run");
+
+ if( (&MONITOR_UART!=NULL) && MONITOR_UART.available() ){
+ while(MONITOR_UART.available() && commandBufferIndex < MAX_MONITOR_LINE_LENGTH && ((ch=MONITOR_UART.read()) != '\n') && ch !='\r' ) {
+ commandBuffer[commandBufferIndex++] = isprint(ch)?ch:'*';
+ MONITOR_UART.flush();
+ }
+
+ if (commandBufferIndex && ((ch=='\n')||(ch=='\r'))) {
+ while (commandBufferIndex<4) { commandBuffer[commandBufferIndex++]='*';};
+ // things get questionable if there are less than 4 characters.
+ commandBuffer[commandBufferIndex]='\0';
+ commandBufferIndex=0;
+ gotLine=true;
+ }
+ }
+
+ if (gotLine) {
+ key[0]=toupper(commandBuffer[0]);
+ key[1]=toupper(commandBuffer[1]);
+ key[2]=toupper(commandBuffer[2]);
+ action=commandBuffer[3];
+ arguments=commandBuffer+4;
+ index=monitor.lookupIndex(key);
+ if (index>=0&&index<NKEYWORDS) {
+ actions[index](index,action,arguments);
+ } else {
+ monitor.update("NAK","%c%c%c[%c] is not understood HLP? for a list of keywords",
+ key[0],key[1],key[2],action); // better than no response...
+ }
+ gotLine=false;
+ }
+}
+
+int Monitor::lookupIndex(char *key) {
+ int ndx=0;
+ int ptr=0;
+ while (ndx<NKEYWORDS) {
+ if ( ( keywords[ptr]==toupper(key[0]) )
+ && ( keywords[ptr+1] == toupper(key[1]) )
+ && ( keywords[ptr+2] == toupper(key[2]) )
+ ) { return ndx;
+ } else {
+ ndx++;
+ ptr+=3;
+ }
+ }
+ return -1;
+}
+
+
+char keyBuff[4]; //fix or move this ???.
+
+char * Monitor::lookupKey(int ndx) {
+ int ptr=0;
+ keyBuff[0]=keyBuff[1]=keyBuff[2]='?';
+ if (ndx >=0 && ndx<NKEYWORDS) {
+ ptr=ndx*3;
+ keyBuff[0]=(keywords[ptr]);
+ keyBuff[1]=(keywords[ptr+1]);
+ keyBuff[2]=(keywords[ptr+2]);
+ keyBuff[3]='\0';
+ }
+
+ return keyBuff;
+}
+
+
+
+extern "C" char *sbrk(int i);
+
+int getFreeMemory()
+{
+
+ // avr specific
+ // extern int __heap_start, *__brkval;
+ // int v;
+ // return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
+ // return 0;
+ //https://learn.adafruit.com/adafruit-feather-m0-basic-proto/adapting-sketches-to-m0
+ char stack_dummy = 0;
+ return &stack_dummy - sbrk(0);
+
+}
+
+#ifdef ARDUINO_SAMD_ZERO
+#include "Adafruit_SleepyDog.h"
+#include "Reset.h"
+
+void jump2bootloader(){
+ initiateReset(10);
+}
+void reboot(){
+ Watchdog.enable(150);
+ for(;;);
+}
+#elif defined(ARDUINO_AVR_MICRO) || defined(ARDUINO_AVR_LEONARDO)
+#include <avr/wdt.h>
+#ifndef MAGIC_KEY
+#define MAGIC_KEY 0x7777
+#endif
+#ifndef MAGIC_KEY_POS
+#define MAGIC_KEY_POS 0x0800
+#endif
+void jump2bootloader(){
+ int16_t magic_key_pos = MAGIC_KEY_POS;
+ *(uint16_t *)magic_key_pos = MAGIC_KEY;
+ wdt_enable(WDTO_120MS);
+ for(;;);
+}
+void reboot(){
+ int16_t magic_key_pos = MAGIC_KEY_POS;
+ *(uint16_t *)magic_key_pos = 0;
+ wdt_enable(WDTO_120MS);
+ for(;;);
+
+}
+#endif
+
+
+
+
+
+
diff --git a/src/Monitor.h b/src/Monitor.h
new file mode 100755
index 0000000..2c01c65
--- /dev/null
+++ b/src/Monitor.h
@@ -0,0 +1,226 @@
+/*---------------------------------------------------------------------Monitor.h
+ Author: Joseph Wayne Dumoulin, Donald Delmar Davis, Suspect Devices
+
+ Liscence: "Simplified BSD License"
+
+ Copyright (c) 2016, Donald Delmar Davis, Suspect Devices
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in thedocumentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ -----------------------------------------------------------------------------
+ The idea here is to create an automation freindly monitor protocol and debug
+ interface. Because this needs to be efficient (and flexible) I have chosen a
+ very simple interface which will access and may manipulate a devices public
+ variables.
+
+ Basic Syntax for the monitor is
+ XXX[?!:] <variable stuff> <CR>
+ where:
+ XXX is a 3 letter command or variable.
+ ! execute command or store value
+ ? get value
+ :<20 char timestamp>, value returned.
+
+
+ keywords are described in keywords.h
+*/
+
+#ifndef Monitor_h
+#define Monitor_h
+
+#include "Configuration.h"
+class Monitor;
+extern Monitor monitor;
+#include "Machine.h"
+
+void jump2bootloader();
+void reboot();
+
+
+#include "TaskScheduler.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+// fix this for processor the stubbed here is for the AVR
+#define EEPROM_NOT_SET 0xff
+#ifndef eeprom_read_byte
+#define eeprom_read_byte(x) (0xff)
+#endif
+#ifndef eeprom_write_byte
+# define eeprom_write_byte(x,v) (v)
+#endif
+
+#include "keywords.h"
+
+#define MONITOR_SERIAL_NUMBER_CHARS 20
+
+enum severityIndex {
+ SEV_NONE, SEV_FATAL, SEV_ALERT, SEV_WARN, SEV_INFO, SEV_DEBUG, SEV_LOG
+};
+
+#define MAX_RETURN_VALUE 104
+#define MAX_MONITOR_LINE_LENGTH (MAX_RETURN_VALUE-6)
+
+typedef void(*actionptr)(uint8_t, uint8_t, char *);
+
+extern char actionBuffer[MAX_RETURN_VALUE];
+extern actionptr actions[];
+
+
+#define MAX_PSN_LENGTH 33
+
+//forwards...
+//class Monitor;
+//extern Monitor monitor;
+class Machine;
+extern Machine machine;
+extern int getFreeMemory();
+extern char commandBuffer[];
+
+
+class Monitor
+{
+ private:
+ uint8_t _debugLevel;
+ uint8_t _commandMode;
+ uint8_t _logLevel;
+ char _psn[MAX_PSN_LENGTH]; // bracket this ... samd processor serial number
+ int get_free_memory();
+
+ int lookupIndex(char *key);
+
+
+ public:
+ char * lookupKey(int ndx);
+
+ static char *keyword(int ndx);
+
+ /*--------------------------------------------------------------------------------
+ * built in callback routines.
+ *-------------------------------------------------------------------------------*/
+
+ static void ACK (uint8_t kwIndex, uint8_t verb, char *args) {
+ monitor.update("ACK","");
+ }
+
+ static void NOP(uint8_t kwIndex, uint8_t verb, char* args) {
+ monitor.update("NAK", "%s is not implemented!", monitor.lookupKey(kwIndex));
+ }
+
+ static void DVL(uint8_t kwIndex, uint8_t verb, char* args) {
+ if (verb == '!') {
+ monitor.setDebugLevel(atoi(args));
+ }
+ monitor.update("DVL", "%d", monitor.debugLevel());
+ }
+
+ static void MEM (uint8_t kwIndex, uint8_t verb,char *args) {
+ monitor.update("MEM","%d",getFreeMemory());
+ }
+ static void HWV (uint8_t kwIndex, uint8_t verb,char *args) {
+ monitor.update("HWV","%d",monitor.unitHardwareVersion());
+ }
+
+ static void CMD(uint8_t kwIndex, uint8_t verb, char *args) {
+ if (verb=='!') {
+ bool setIt = (atoi(args));
+ monitor.setCommandMode(setIt);
+ }
+ monitor.update("CMD","%s",monitor.commandMode()?"ON":"OFF");
+ }
+
+ static void SWV(uint8_t kwIndex, uint8_t verb, char *args) {
+ monitor.update("SWV","%s",FIRMWAREREV);
+ }
+
+ static void SSN(uint8_t kwIndex, uint8_t verb, char *args) {
+ monitor.update("SSN","%s",monitor.unitSerialNumber());
+ }
+
+ static void GIT(uint8_t kwIndex, uint8_t verb, char *args) {
+ monitor.update("GIT","%s",GIT_VERSION);
+ }
+
+ static void RST(uint8_t kwIndex, uint8_t verb, char *args) {
+ if (verb=='!') {
+ monitor.warn("Rebooting!");
+ MONITOR_UART.end();
+ reboot();
+ } else {
+ monitor.update("NAK","RST Requires verb!");
+ }
+ }
+
+ static void BLD(uint8_t kwIndex, uint8_t verb, char *args) {
+ if (verb=='!') {
+ monitor.warn("Jumping to Bootloader!");
+ jump2bootloader();
+ } else {
+ monitor.update("NAK","BLD Requires verb!");
+ }
+ }
+
+ // stubb
+ static void HLP(uint8_t kwIndex, uint8_t verb, char *args) {
+ monitor.update("NAK","HLP requires multi line data (MLD)");
+ }
+
+ Monitor();
+
+ static void run();
+
+ char *unitSerialNumber() {
+ return _psn;
+ };
+
+ int unitHardwareVersion() {
+ return 0;
+ };
+
+
+ void update(const char *, const char *format, ...);
+ void fatal(int, const char *format, ...);
+ void alert(const char *format, ...) { va_list blarg; update("ALT", format, blarg); va_end(blarg);};
+ void warn(const char *format, ...) { va_list blarg; update("WAR", format, blarg); va_end(blarg);};
+ void log(const char *format, ...) { va_list blarg; update("LOG", format, blarg); va_end(blarg);};
+ // void info(int);
+ void debug(const char *format, ...);
+ void setDebugLevel(uint8_t lvl) {
+ //eeprom_write_byte(DEBUG_LEVEL_EEPROM_ADDR, (_debugLevel = lvl));
+ _debugLevel = lvl;
+
+ }; //FIX
+ void setCommandMode(uint8_t mode) {
+ _commandMode = mode;
+ }
+ uint8_t commandMode() {
+ return _commandMode;
+ }
+ uint8_t debugLevel() {
+ return _debugLevel;// = eeprom_read_byte(DEBUG_LEVEL_EEPROM_ADDR);
+ }; //FIX
+ void init();
+ void registerAction(uint8_t, actionptr);
+};
+#endif
+
diff --git a/src/SafetyThird.cpp b/src/SafetyThird.cpp
new file mode 100755
index 0000000..180edd2
--- /dev/null
+++ b/src/SafetyThird.cpp
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------SafetyThird.cpp
+ *
+ * *********************** Don't put safety third !!!! *************************
+ *
+ * This is a reminder not to put safety third like your workplace does.
+ * (after corporate profits and management whim)
+ *
+ * Author: Donald Delmar Davis, Suspect Devices
+ * Liscence: "Simplified BSD License"
+ *
+ * Copyright (c) 2016, Donald Delmar Davis, Suspect Devices
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in thedocumentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *-----------------------------------------------------------------------------*/
+/*
+ * The safteyThird module is a place for items relating to the safety of machines.
+ * Examples include:
+ * Watchdog timer (a mechanism for keeping code from running away from its job)
+ * K1, K2 ( in large machinery a pair of Kontactors designed to drop power )
+ * E-stop ( A big red button or a lanyard for emergencies)
+ * Light Curtain (a device to keep fingers from getting into dangerous areas)
+ * red. (lights or other indicators that there may be problems.
+ */
+#include "SafetyThird.h"
+
+SafetyThird safety;
diff --git a/src/SafetyThird.h b/src/SafetyThird.h
new file mode 100755
index 0000000..3b318a5
--- /dev/null
+++ b/src/SafetyThird.h
@@ -0,0 +1,198 @@
+/*-----------------------------------------------------------------SafetyThird.h
+ *
+ * *********************** Don't put safety third !!!! *************************
+ *
+ * This is a reminder not to put safety third like your workplace does.
+ * (after corporate profits and management whim)
+ *
+ * Author: Donald Delmar Davis, Suspect Devices
+ * Liscence: "Simplified BSD License"
+ *
+ * Copyright (c) 2016, Donald Delmar Davis, Suspect Devices
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in thedocumentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *-----------------------------------------------------------------------------*/
+/*
+ * The safteyThird module is a place for items relating to the safety of machines.
+ * Examples include:
+ * Watchdog timer (a mechanism for keeping code from running away from its job)
+ * K1, K2 ( in large machinery a pair of Kontactors designed to drop power )
+ * E-stop ( A big red button or a lanyard for emergencies)
+ * Light Curtain (a device to keep fingers from getting into dangerous areas)
+ * red. (lights or other indicators that there may be problems.
+ */
+
+#ifndef SafetyThird_h
+#define SafetyThird_h
+
+
+#include "Configuration.h"
+#include "Monitor.h"
+
+class SafetyThird;
+extern SafetyThird safety;
+#define RED_FULL_ON (255 * (1 << (ANALOG_WRITE_RESOLUTION - 8)))
+#define RED_OFF 0
+
+// preprocessor logic for core specific WDT implimentation here....
+#include <Adafruit_SleepyDog.h>
+
+static void RED (uint8_t kwIndex, uint8_t verb,char *args) ;
+
+class SafetyThird {
+
+private:
+ bool _eStop;
+ bool _prevEStop;
+ bool _prevLightCurtain;
+ // put the contactor variables back here
+ // mebby look at the light curtain as well
+ Task _doDoRunRun;
+
+public:
+
+ bool _lightCurtain;
+ bool _lightCurtainReset;
+
+#ifdef LIGHT_CURTAIN_START_PIN
+ static void LCS(uint8_t kwIndex, uint8_t verb, char *args) {
+ //
+ // set logic here
+ //
+ monitor.update("LCS","%s",safety._lightCurtain?"ON":"OFF");
+ safety.first();
+ }
+
+ static void LCR(uint8_t kwIndex, uint8_t verb, char *args) {
+ if (verb=='!') {
+ safety._lightCurtainReset = (atoi(args));
+ digitalWrite(LIGHT_CURTAIN_START_PIN,safety._lightCurtainReset);
+ }
+ safety.first();
+ monitor.update("LCR","%s",safety._lightCurtainReset?"ON":"OFF");
+ }
+#endif
+
+ static void run() {safety.first();}
+
+ void first () { // could also be called paranoia.
+
+#ifdef E_STOP_PIN
+ _eStop = (digitalRead(E_STOP_PIN)==E_STOP_PULLED );
+#else
+ _eStop = false;
+#endif
+
+#ifdef LIGHT_CURTAIN_OSDD_PIN
+ _lightCurtain = (digitalRead(LIGHT_CURTAIN_OSDD_PIN) == LIGHT_CURTAIN_NOT_CLEAR);
+#else
+ _lightCurtain = false;
+#endif
+ if ((machine.inSafeStateToRun)
+ && (_eStop)
+ )
+ {
+ monitor.update("STP","1 ESTOP Pushed: Initiating Emergency Stop");
+ machine.setState(STATE_E_SHUTDOWN);
+ }
+ if (_eStop!=_prevEStop) {
+ _eStop = _prevEStop;
+ if (_eStop){
+ // probably should warn or alert!
+ monitor.update("STP","1 ESTOP Pushed: Contactors are OFF");
+#ifdef K1_PIN
+ digitalWrite(K1_PIN,KONTACTOR_OFF);
+#endif
+ } else {
+ monitor.update("STP","0 E-STOP Released: Contactors are ON");
+#ifdef K1_PIN
+ digitalWrite(K1_PIN,KONTACTOR_ON);
+#endif
+ }
+ }
+ //
+ // this is rough... Fix when there is hardware to test with
+ //
+ if(_lightCurtain!=_prevLightCurtain) {
+ if (_lightCurtain) {
+#ifdef K1_PIN
+ digitalWrite(K1_PIN,KONTACTOR_OFF);
+#endif
+ } else {
+#ifdef K1_PIN
+ digitalWrite(K1_PIN,KONTACTOR_OFF);
+#endif≄
+ }
+ monitor.update("LCS","%s",_lightCurtain?"ON":"OFF");
+ }
+ _prevLightCurtain=_lightCurtain;
+ _prevEStop=_eStop;
+ }
+ uint redLightVal; // red led value prolly bad style
+
+ void redLight(int newVal) {
+ if(newVal!=redLightVal) {
+ redLightVal=newVal;
+ monitor.update("RED","%d",redLightVal);
+ }
+#ifdef SAFETY_LAMP_PIN
+ analogWrite(SAFETY_LAMP_PIN, safety.redLightVal * (1 << (ANALOG_WRITE_RESOLUTION - 8) ) );
+#endif
+ }
+
+ void init() {
+#ifdef E_STOP_PIN
+ pinMode (E_STOP_PIN, INPUT_PULLUP);
+#endif
+
+ // Might move this to machine or other more generic place.
+ // http://www.arduino.org/learning/reference/analogwriteresolution
+ analogWriteResolution(ANALOG_WRITE_RESOLUTION);
+#ifdef LIGHT_CURTAIN_RESET_PIN
+ _lightCurtainReset = true;
+ digitalWrite(LIGHT_CURTAIN_START_PIN,LIGHT_CURTAIN_START);
+#else
+ _lightCurtainReset = false;
+#endif
+ _prevLightCurtain=false;
+ _prevEStop=false;
+
+ redLight(redLightVal=RED_OFF);
+ _doDoRunRun.set(77L, TASK_FOREVER, &run);
+ machine.todolist.addTask(_doDoRunRun);
+ _doDoRunRun.enable();
+
+ monitor.registerAction(_RED_, &RED);
+ }
+};
+
+static void RED (uint8_t kwIndex, uint8_t verb,char *args) {
+ // monitor update?
+ safety.redLight(atol(args));
+
+}
+
+
+#endif
+
diff --git a/src/TaskScheduler.h b/src/TaskScheduler.h
new file mode 100755
index 0000000..89ae8a2
--- /dev/null
+++ b/src/TaskScheduler.h
@@ -0,0 +1,824 @@
+// Cooperative multitasking library for Arduino version 2.0.2sd
+// Copyright (c) 2015 Anatoli Arkhipenko
+//
+// Changelog:
+// v1.0.0:
+// 2015-02-24 - Initial release
+// 2015-02-28 - added delay() and disableOnLastIteration() methods
+// 2015-03-25 - changed scheduler execute() method for a more precise delay calculation:
+// 1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
+// 2. Delay is invoked only if none of the tasks ran
+// 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself.
+// 2015-05-11 - added restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations
+// 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead
+//
+// v1.4.1:
+// 2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE)
+// sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined
+//
+// v1.5.0:
+// 2015-09-20 - access to currently executing task (for callback methods)
+// 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
+// 2015-09-20 - option to create a task already enabled
+//
+// v1.5.1:
+// 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations().
+// Thanks to Hannes Morgenstern for catching this one
+//
+// v1.6.0:
+// 2015-09-22 - revert back to having all tasks disable on last iteration.
+// 2015-09-22 - deprecated disableOnLastIteration method as a result
+// 2015-09-22 - created a separate branch 'disable-on-last-iteration' for this
+// 2015-10-01 - made version numbers semver compliant (documentation only)
+//
+// v1.7.0:
+// 2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number.
+// 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled.
+// 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
+// 2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled
+// 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left
+//
+// v1.8.0:
+// 2015-10-13 - support for status request objects allowing tasks waiting on requests
+// 2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
+//
+// v1.8.1:
+// 2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
+//
+// v1.8.2:
+// 2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
+// 2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
+// 2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
+// 2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
+//
+// v1.8.3:
+// 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations (0 and 1 are still default values)
+// 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion delayed for one current interval
+// 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility
+// 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.)
+// 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option
+//
+// v1.8.4:
+// 2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before. Especially since OnEnable method can change the interval
+// 2015-11-16 - further optimizations of the task scheduler execute loop
+//
+// v1.8.5:
+// 2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
+// 2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
+//
+// v1.9.0:
+// 2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance
+//
+// v1.9.2:
+// 2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
+// 2015-12-16 - bug fixes: automatic millis rollover support for delay methods
+// 2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay()
+//
+// v2.0.0:
+// 2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
+//
+// v2.0.1:
+// 2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
+//
+// v2.0.2:
+// 2016-01-05 - bug fix: time constants wrapped inside compile option
+// 2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option
+//
+// v2.1.0:
+// 2016-02-01 - support for microsecond resolution
+// 2016-02-02 - added Scheduler baseline start time reset method: startNow()
+
+/* ============================================
+Cooperative multitasking library code is placed under the MIT license
+Copyright (c) 2015 Anatoli Arkhipenko
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+===============================================
+*/
+
+
+#include <Arduino.h>
+
+#ifndef _TASKSCHEDULER_H_
+#define _TASKSCHEDULER_H_
+
+/** ----------------------------------------
+ * The following "defines" control library functionality at compile time,
+ * and should be used in the main sketch depending on the functionality required
+ *
+ * #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
+ * #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
+ * #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
+ * #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
+ * #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
+ * #define _TASK_PRIORITY // Support for layered scheduling priority
+ * #define _TASK_MICRO_RES // Support for microsecond resolution
+ */
+
+
+ #ifdef _TASK_MICRO_RES
+
+ #undef _TASK_SLEEP_ON_IDLE_RUN // SLEEP_ON_IDLE has only millisecond resolution
+ #define _TASK_TIME_FUNCTION() micros()
+
+ #else
+
+ #define _TASK_TIME_FUNCTION() millis()
+
+ #endif // _TASK_MICRO_RES
+
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+
+#ifdef ARDUINO_ARCH_AVR
+#include <avr/sleep.h>
+#include <avr/power.h>
+#endif // ARDUINO_ARCH_AVR
+
+#ifdef ARDUINO_ARCH_ESP8266
+extern "C" {
+#include "user_interface.h"
+}
+#define _TASK_ESP8266_DLY_THRESHOLD 200L
+#endif // ARDUINO_ARCH_ESP8266
+
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+
+#define TASK_IMMEDIATE 0
+#define TASK_FOREVER (-1)
+#define TASK_ONCE 1
+
+
+#ifndef _TASK_MICRO_RES
+
+#define TASK_SECOND 1000L
+#define TASK_MINUTE 60000L
+#define TASK_HOUR 3600000L
+
+#else
+
+#define TASK_SECOND 1000000L
+#define TASK_MINUTE 60000000L
+#define TASK_HOUR 3600000000L
+
+#endif // _TASK_MICRO_RES
+
+
+#ifdef _TASK_STATUS_REQUEST
+
+#define _TASK_SR_NODELAY 1
+#define _TASK_SR_DELAY 2
+
+class StatusRequest {
+ public:
+ StatusRequest() {iCount = 0; iStatus = 0; }
+ inline void setWaiting(unsigned int aCount = 1) { iCount = aCount; iStatus = 0; }
+ bool signal(int aStatus = 0);
+ void signalComplete(int aStatus = 0);
+ inline bool pending() { return (iCount != 0); }
+ inline bool completed() { return (iCount == 0); }
+ inline int getStatus() { return iStatus; }
+
+ private:
+ unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
+ int iStatus; // status of the last completed request. negative = error; zero = OK; >positive = OK with a specific status
+};
+#endif // _TASK_STATUS_REQUEST
+
+
+typedef struct {
+ bool enabled : 1; // indicates that task is enabled or not.
+ bool inonenable : 1; // indicates that task execution is inside OnEnable method (preventing infinite loops)
+#ifdef _TASK_STATUS_REQUEST
+ byte waiting : 2; // indication if task is waiting on the status request
+#endif
+} __task_status;
+
+class Scheduler;
+
+
+#ifdef _TASK_WDT_IDS
+ static unsigned int __task_id_counter = 0; // global task ID counter for assiging task IDs automatically.
+#endif // _TASK_WDT_IDS
+
+class Task {
+ friend class Scheduler;
+ public:
+ Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+#ifdef _TASK_STATUS_REQUEST
+ Task(void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+#endif // _TASK_STATUS_REQUEST
+
+ void enable();
+ bool enableIfNot();
+ void enableDelayed(unsigned long aDelay=0);
+ void delay(unsigned long aDelay=0);
+ void forceNextIteration();
+ void restart();
+ void restartDelayed(unsigned long aDelay=0);
+ bool disable();
+ inline bool isEnabled() { return iStatus.enabled; }
+ void set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
+ void setInterval(unsigned long aInterval);
+ inline unsigned long getInterval() { return iInterval; }
+ void setIterations(long aIterations);
+ inline long getIterations() { return iIterations; }
+ inline unsigned long getRunCounter() { return iRunCounter; }
+ inline void setCallback(void (*aCallback)()) { iCallback = aCallback; }
+ inline void setOnEnable(bool (*aCallback)()) { iOnEnable = aCallback; }
+ inline void setOnDisable(void (*aCallback)()) { iOnDisable = aCallback; }
+#ifdef _TASK_TIMECRITICAL
+ inline long getOverrun() { return iOverrun; }
+ inline long getStartDelay() { return iStartDelay; }
+#endif // _TASK_TIMECRITICAL
+ inline bool isFirstIteration() { return (iRunCounter <= 1); }
+ inline bool isLastIteration() { return (iIterations == 0); }
+#ifdef _TASK_STATUS_REQUEST
+ void waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
+ void waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
+ inline StatusRequest* getStatusRequest() {return iStatusRequest; }
+#endif // _TASK_STATUS_REQUEST
+#ifdef _TASK_WDT_IDS
+ inline void setId(unsigned int aID) { iTaskID = aID; }
+ inline unsigned int getId() { return iTaskID; }
+ inline void setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
+ inline unsigned int getControlPoint() { return iControlPoint; }
+#endif // _TASK_WDT_IDS
+#ifdef _TASK_LTS_POINTER
+ inline void setLtsPointer(void *aPtr) { iLTS = aPtr; }
+ inline void* getLtsPointer() { return iLTS; }
+#endif // _TASK_LTS_POINTER
+
+ private:
+ void reset();
+
+ volatile __task_status iStatus;
+ volatile unsigned long iInterval; // execution interval in milliseconds (or microseconds). 0 - immediate
+ volatile unsigned long iDelay; // actual delay until next execution (usually equal iInterval)
+ volatile unsigned long iPreviousMillis; // previous invocation time (millis). Next invocation = iPreviousMillis + iInterval. Delayed tasks will "catch up"
+#ifdef _TASK_TIMECRITICAL
+ volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past)
+ volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis
+#endif // _TASK_TIMECRITICAL
+ volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations
+ long iSetIterations; // number of iterations originally requested (for restarts)
+ unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable.
+ void (*iCallback)(); // pointer to the void callback method
+ bool (*iOnEnable)(); // pointer to the bolol OnEnable callback method
+ void (*iOnDisable)(); // pointer to the void OnDisable method
+ Task *iPrev, *iNext; // pointers to the previous and next tasks in the chain
+ Scheduler *iScheduler; // pointer to the current scheduler
+#ifdef _TASK_STATUS_REQUEST
+ StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on
+#endif // _TASK_STATUS_REQUEST
+#ifdef _TASK_WDT_IDS
+ unsigned int iTaskID; // task ID (for debugging and watchdog identification)
+ unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass
+#endif // _TASK_WDT_IDS
+#ifdef _TASK_LTS_POINTER
+ void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct).
+#endif // _TASK_LTS_POINTER
+};
+
+
+#ifdef _TASK_PRIORITY
+ static Scheduler* iCurrentScheduler;
+#endif // _TASK_PRIORITY
+
+class Scheduler {
+ friend class Task;
+ public:
+ Scheduler();
+ void init();
+ void addTask(Task& aTask);
+ void deleteTask(Task& aTask);
+ void disableAll(bool aRecursive = true);
+ void enableAll(bool aRecursive = true);
+ bool execute(); // Returns true if at none of the tasks' callback methods was invoked (true if idle run)
+ void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW.
+ inline Task& currentTask() {return *iCurrent; }
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ void allowSleep(bool aState = true);
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+#ifdef _TASK_LTS_POINTER
+ inline void* currentLts() {return iCurrent->iLTS; }
+#endif // _TASK_LTS_POINTER
+#ifdef _TASK_TIMECRITICAL
+ inline bool isOverrun() { return (iCurrent->iOverrun < 0); }
+#endif // _TASK_TIMECRITICAL
+#ifdef _TASK_PRIORITY
+ void setHighPriorityScheduler(Scheduler* aScheduler);
+ static Scheduler& currentScheduler() { return *(iCurrentScheduler); };
+#endif // _TASK_PRIORITY
+
+ private:
+ Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ bool iAllowSleep; // indication if putting avr to IDLE_SLEEP mode is allowed by the program at this time.
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+#ifdef _TASK_PRIORITY
+ Scheduler *iHighPriority; // Pointer to a higher priority scheduler
+#endif // _TASK_PRIORITY
+};
+
+
+// ------------------ TaskScheduler implementation --------------------
+
+/** Constructor, uses default values for the parameters
+ * so could be called with no parameters.
+ */
+inline Task::Task( unsigned long aInterval, long aIterations, void (*aCallback)(), Scheduler* aScheduler, bool aEnable, bool (*aOnEnable)(), void (*aOnDisable)() ) {
+ reset();
+ set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable);
+ if (aScheduler) aScheduler->addTask(*this);
+#ifdef _TASK_STATUS_REQUEST
+ iStatusRequest = NULL;
+#endif // _TASK_STATUS_REQUEST
+#ifdef _TASK_WDT_IDS
+ iTaskID = ++__task_id_counter;
+#endif // _TASK_WDT_IDS
+ if (aEnable) enable();
+}
+
+
+#ifdef _TASK_STATUS_REQUEST
+
+/** Constructor with reduced parameter list for tasks created for
+ * StatusRequest only triggering (always immediate and only 1 iteration)
+ */
+inline Task::Task( void (*aCallback)(), Scheduler* aScheduler, bool (*aOnEnable)(), void (*aOnDisable)() ) {
+ reset();
+ set(TASK_IMMEDIATE, TASK_ONCE, aCallback, aOnEnable, aOnDisable);
+ if (aScheduler) aScheduler->addTask(*this);
+ iStatusRequest = NULL;
+#ifdef _TASK_WDT_IDS
+ iTaskID = ++__task_id_counter;
+#endif // _TASK_WDT_IDS
+}
+
+/** Signals completion of the StatusRequest by one of the participating events
+ * @param: aStatus - if provided, sets the return code of the StatusRequest: negative = error, 0 (default) = OK, positive = OK with a specific status code
+ * Negative status will complete Status Request fully (since an error occured).
+ * @return: true, if StatusRequest is complete, false otherwise (still waiting for other events)
+ */
+inline bool StatusRequest::signal(int aStatus) {
+ if ( iCount) { // do not update the status request if it was already completed
+ if (iCount > 0) --iCount;
+ if ( (iStatus = aStatus) < 0 ) iCount = 0; // if an error is reported, the status is requested to be completed immediately
+ }
+ return (iCount == 0);
+}
+
+inline void StatusRequest::signalComplete(int aStatus) {
+ if (iCount) { // do not update the status request if it was already completed
+ iCount = 0;
+ iStatus = aStatus;
+ }
+}
+
+/** Sets a Task to wait until a particular event completes
+ * @param: aStatusRequest - a pointer for the StatusRequest to wait for.
+ * If aStatusRequest is NULL, request for waiting is ignored, and the waiting task is not enabled.
+ */
+inline void Task::waitFor(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
+ if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL
+ setIterations(aIterations);
+ setInterval(aInterval);
+ iStatus.waiting = _TASK_SR_NODELAY; // no delay
+ enable();
+ }
+}
+
+inline void Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
+ if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL
+ setIterations(aIterations);
+ if ( aInterval ) setInterval(aInterval); // For the dealyed version only set the interval if it was not a zero
+ iStatus.waiting = _TASK_SR_DELAY; // with delay equal to the current interval
+ enable();
+ }
+}
+#endif // _TASK_STATUS_REQUEST
+
+/** Resets (initializes) the task/
+ * Task is not enabled and is taken out
+ * out of the execution chain as a result
+ */
+inline void Task::reset() {
+ iStatus.enabled = false;
+ iStatus.inonenable = false;
+ iPreviousMillis = 0;
+ iInterval = iDelay = 0;
+ iPrev = NULL;
+ iNext = NULL;
+ iScheduler = NULL;
+ iRunCounter = 0;
+#ifdef _TASK_TIMECRITICAL
+ iOverrun = 0;
+ iStartDelay = 0;
+#endif // _TASK_TIMECRITICAL
+#ifdef _TASK_WDT_IDS
+ iControlPoint = 0;
+#endif // _TASK_WDT_IDS
+#ifdef _TASK_LTS_POINTER
+ iLTS = NULL;
+#endif // _TASK_LTS_POINTER
+#ifdef _TASK_STATUS_REQUEST
+ iStatus.waiting = 0;
+#endif // _TASK_STATUS_REQUEST
+}
+
+/** Explicitly set Task execution parameters
+ * @param aInterval - execution interval in ms
+ * @param aIterations - number of iterations, use -1 for no limit
+ * @param aCallback - pointer to the callback method which executes the task actions
+ * @param aOnEnable - pointer to the callback method which is called on enable()
+ * @param aOnDisable - pointer to the callback method which is called on disable()
+ */
+inline void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)(), void (*aOnDisable)()) {
+ setInterval(aInterval);
+ iSetIterations = iIterations = aIterations;
+ iCallback = aCallback;
+ iOnEnable = aOnEnable;
+ iOnDisable = aOnDisable;
+}
+
+/** Sets number of iterations for the task
+ * if task is enabled, schedule for immediate execution
+ * @param aIterations - number of iterations, use -1 for no limit
+ */
+inline void Task::setIterations(long aIterations) {
+ iSetIterations = iIterations = aIterations;
+}
+
+/** Enables the task
+ * schedules it for execution as soon as possible,
+ * and resets the RunCounter back to zero
+ */
+inline void Task::enable() {
+ if (iScheduler) { // activation without active scheduler does not make sense
+ iRunCounter = 0;
+ if ( iOnEnable && !iStatus.inonenable ) {
+ Task *current = iScheduler->iCurrent;
+ iScheduler->iCurrent = this;
+ iStatus.inonenable = true; // Protection against potential infinite loop
+ iStatus.enabled = (*iOnEnable)();
+ iStatus.inonenable = false; // Protection against potential infinite loop
+ iScheduler->iCurrent = current;
+ }
+ else {
+ iStatus.enabled = true;
+ }
+ iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
+ }
+}
+
+/** Enables the task only if it was not enabled already
+ * Returns previous state (true if was already enabled, false if was not)
+ */
+inline bool Task::enableIfNot() {
+ bool previousEnabled = iStatus.enabled;
+ if ( !previousEnabled ) enable();
+ return (previousEnabled);
+}
+
+/** Enables the task
+ * and schedules it for execution after a delay = aInterval
+ */
+inline void Task::enableDelayed(unsigned long aDelay) {
+ enable();
+ delay(aDelay);
+}
+
+/** Delays Task for execution after a delay = aInterval (if task is enabled).
+ * leaves task enabled or disabled
+ * if aDelay is zero, delays for the original scheduling interval from now
+ */
+inline void Task::delay(unsigned long aDelay) {
+// if (!aDelay) aDelay = iInterval;
+ iDelay = aDelay ? aDelay : iInterval;
+ iPreviousMillis = _TASK_TIME_FUNCTION(); // - iInterval + aDelay;
+}
+
+/** Schedules next iteration of Task for execution immediately (if enabled)
+ * leaves task enabled or disabled
+ * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time
+ */
+inline void Task::forceNextIteration() {
+ iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
+}
+
+/** Sets the execution interval.
+ * Task execution is delayed for aInterval
+ * Use enable() to schedule execution ASAP
+ * @param aInterval - new execution interval
+ */
+inline void Task::setInterval (unsigned long aInterval) {
+ iInterval = aInterval;
+ delay(); // iDelay will be updated by the delay() function
+}
+
+/** Disables task
+ * Task will no longer be executed by the scheduler
+ * Returns status of the task before disable was called (i.e., if the task was already disabled)
+ */
+inline bool Task::disable() {
+ bool previousEnabled = iStatus.enabled;
+ iStatus.enabled = false;
+ iStatus.inonenable = false;
+ if (previousEnabled && iOnDisable) {
+ Task *current = iScheduler->iCurrent;
+ iScheduler->iCurrent = this;
+ (*iOnDisable)();
+ iScheduler->iCurrent = current;
+ }
+ return (previousEnabled);
+}
+
+/** Restarts task
+ * Task will run number of iterations again
+ */
+inline void Task::restart() {
+ iIterations = iSetIterations;
+ enable();
+}
+
+/** Restarts task delayed
+ * Task will run number of iterations again
+ */
+inline void Task::restartDelayed(unsigned long aDelay) {
+ iIterations = iSetIterations;
+ enableDelayed(aDelay);
+}
+
+// ------------------ Scheduler implementation --------------------
+
+/** Default constructor.
+ * Creates a scheduler with an empty execution chain.
+ */
+inline Scheduler::Scheduler() {
+ init();
+}
+
+/** Initializes all internal varaibles
+ */
+inline void Scheduler::init() {
+ iFirst = NULL;
+ iLast = NULL;
+ iCurrent = NULL;
+#ifdef _TASK_PRIORITY
+ iHighPriority = NULL;
+#endif // _TASK_PRIORITY
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ allowSleep(true);
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+}
+
+/** Appends task aTask to the tail of the execution chain.
+ * @param &aTask - reference to the Task to be appended.
+ * @note Task can only be part of the chain once.
+ */
+inline void Scheduler::addTask(Task& aTask) {
+
+ aTask.iScheduler = this;
+// First task situation:
+ if (iFirst == NULL) {
+ iFirst = &aTask;
+ aTask.iPrev = NULL;
+ }
+ else {
+// This task gets linked back to the previous last one
+ aTask.iPrev = iLast;
+ iLast->iNext = &aTask;
+ }
+// "Previous" last task gets linked to this one - as this one becomes the last one
+ aTask.iNext = NULL;
+ iLast = &aTask;
+}
+
+/** Deletes specific Task from the execution chain
+ * @param &aTask - reference to the task to be deleted from the chain
+ */
+inline void Scheduler::deleteTask(Task& aTask) {
+ if (aTask.iPrev == NULL) {
+ if (aTask.iNext == NULL) {
+ iFirst = NULL;
+ iLast = NULL;
+ return;
+ }
+ else {
+ aTask.iNext->iPrev = NULL;
+ iFirst = aTask.iNext;
+ aTask.iNext = NULL;
+ return;
+ }
+ }
+
+ if (aTask.iNext == NULL) {
+ aTask.iPrev->iNext = NULL;
+ iLast = aTask.iPrev;
+ aTask.iPrev = NULL;
+ return;
+ }
+
+ aTask.iPrev->iNext = aTask.iNext;
+ aTask.iNext->iPrev = aTask.iPrev;
+ aTask.iPrev = NULL;
+ aTask.iNext = NULL;
+}
+
+/** Disables all tasks in the execution chain
+ * Convenient for error situations, when the only
+ * task remaining active is an error processing task
+ * @param aRecursive - if true, tasks of the higher priority chains are disabled as well recursively
+ */
+inline void Scheduler::disableAll(bool aRecursive) {
+ Task *current = iFirst;
+ while (current) {
+ current->disable();
+ current = current->iNext;
+ }
+#ifdef _TASK_PRIORITY
+ if (aRecursive && iHighPriority) iHighPriority->disableAll(true);
+#endif // _TASK_PRIORITY
+}
+
+
+/** Enables all the tasks in the execution chain
+ * @param aRecursive - if true, tasks of the higher priority chains are enabled as well recursively
+ */
+inline void Scheduler::enableAll(bool aRecursive) {
+ Task *current = iFirst;
+ while (current) {
+ current->enable();
+ current = current->iNext;
+ }
+#ifdef _TASK_PRIORITY
+ if (aRecursive && iHighPriority) iHighPriority->enableAll(true);
+#endif // _TASK_PRIORITY
+}
+
+/** Sets scheduler for the higher priority tasks (support for layered task priority)
+ * @param aScheduler - pointer to a scheduler for the higher priority tasks
+ */
+#ifdef _TASK_PRIORITY
+inline void Scheduler::setHighPriorityScheduler(Scheduler* aScheduler) {
+ if (aScheduler != this) iHighPriority = aScheduler; // Setting yourself as a higher priority one will create infinite recursive call
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ if (iHighPriority) {
+ iHighPriority->allowSleep(false); // Higher priority schedulers should not do power management
+ }
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+};
+#endif // _TASK_PRIORITY
+
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+inline void Scheduler::allowSleep(bool aState) {
+ iAllowSleep = aState;
+
+#ifdef ARDUINO_ARCH_ESP8266
+ wifi_set_sleep_type( iAllowSleep ? LIGHT_SLEEP_T : NONE_SLEEP_T );
+#endif // ARDUINO_ARCH_ESP8266
+
+}
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+
+
+inline void Scheduler::startNow( bool aRecursive ) {
+ unsigned long t = _TASK_TIME_FUNCTION();
+
+ iCurrent = iFirst;
+ while (iCurrent) {
+ if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay;
+ iCurrent = iCurrent->iNext;
+ }
+
+#ifdef _TASK_PRIORITY
+ if (aRecursive && iHighPriority) iHighPriority->startNow( true );
+#endif // _TASK_PRIORITY
+}
+
+/** Makes one pass through the execution chain.
+ * Tasks are executed in the order they were added to the chain
+ * There is no concept of priority
+ * Different pseudo "priority" could be achieved
+ * by running task more frequently
+ */
+inline bool Scheduler::execute() {
+ bool idleRun = true;
+ register unsigned long m, i; // millis, interval;
+
+#ifdef ARDUINO_ARCH_ESP8266
+ unsigned long t1 = micros();
+ unsigned long t2 = 0;
+#endif // ARDUINO_ARCH_ESP8266
+
+ iCurrent = iFirst;
+
+ while (iCurrent) {
+
+#ifdef _TASK_PRIORITY
+ // If scheduler for higher priority tasks is set, it's entire chain is executed on every pass of the base scheduler
+ if (iHighPriority) idleRun = iHighPriority->execute() && idleRun;
+ iCurrentScheduler = this;
+#endif // _TASK_PRIORITY
+
+ do {
+ if ( iCurrent->iStatus.enabled ) {
+
+#ifdef _TASK_WDT_IDS
+ // For each task the control points are initialized to avoid confusion because of carry-over:
+ iCurrent->iControlPoint = 0;
+#endif // _TASK_WDT_IDS
+
+ // Disable task on last iteration:
+ if (iCurrent->iIterations == 0) {
+ iCurrent->disable();
+ break;
+ }
+ m = _TASK_TIME_FUNCTION();
+ i = iCurrent->iInterval;
+
+#ifdef _TASK_STATUS_REQUEST
+ // If StatusRequest object was provided, and still pending, and task is waiting, this task should not run
+ // Otherwise, continue with execution as usual. Tasks waiting to StatusRequest need to be rescheduled according to
+ // how they were placed into waiting state (waitFor or waitForDelayed)
+ if ( iCurrent->iStatus.waiting ) {
+ if ( (iCurrent->iStatusRequest)->pending() ) break;
+ if (iCurrent->iStatus.waiting == _TASK_SR_NODELAY) {
+ iCurrent->iPreviousMillis = m - (iCurrent->iDelay = i);
+ }
+ else {
+ iCurrent->iPreviousMillis = m;
+ }
+ iCurrent->iStatus.waiting = 0;
+ }
+#endif // _TASK_STATUS_REQUEST
+
+ if ( m - iCurrent->iPreviousMillis < iCurrent->iDelay ) break;
+
+ if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--; // do not decrement (-1) being a signal of never-ending task
+ iCurrent->iRunCounter++;
+ iCurrent->iPreviousMillis += iCurrent->iDelay;
+
+#ifdef _TASK_TIMECRITICAL
+ // Updated_previous+current interval should put us into the future, so iOverrun should be positive or zero.
+ // If negative - the task is behind (next execution time is already in the past)
+ unsigned long p = iCurrent->iPreviousMillis;
+ iCurrent->iOverrun = (long) ( p + i - m );
+ iCurrent->iStartDelay = (long) ( m - p );
+#endif // _TASK_TIMECRITICAL
+
+ iCurrent->iDelay = i;
+ if ( iCurrent->iCallback ) {
+ ( *(iCurrent->iCallback) )();
+ idleRun = false;
+ }
+ }
+ } while (0); //guaranteed single run - allows use of "break" to exit
+ iCurrent = iCurrent->iNext;
+ }
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ if (idleRun && iAllowSleep) {
+
+#ifdef ARDUINO_ARCH_AVR // Could be used only for AVR-based boards.
+ set_sleep_mode(SLEEP_MODE_IDLE);
+ sleep_enable();
+ /* Now enter sleep mode. */
+ sleep_mode();
+
+ /* The program will continue from here after the timer timeout ~1 ms */
+ sleep_disable(); /* First thing to do is disable sleep. */
+#endif // ARDUINO_ARCH_AVR
+
+#ifdef ARDUINO_ARCH_ESP8266
+// to do: find suitable sleep function for esp8266
+ t2 = micros() - t1;
+ if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1); // ESP8266 implementation of delay() uses timers and yield
+#endif // ARDUINO_ARCH_ESP8266
+ }
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+
+ return (idleRun);
+}
+
+
+
+#endif /* _TASKSCHEDULER_H_ */
diff --git a/src/ems-light.ino b/src/ems-light.ino
new file mode 100755
index 0000000..26bc988
--- /dev/null
+++ b/src/ems-light.ino
@@ -0,0 +1,3 @@
+/*
+ * empty placeholder to keep the arduino preprocessor away from any real code
+ */
diff --git a/src/keywords.h b/src/keywords.h
new file mode 100755
index 0000000..1cf39e1
--- /dev/null
+++ b/src/keywords.h
@@ -0,0 +1,122 @@
+
+/*---------------------------------------------------------------------kewords.h
+ * Copyright 2012-2018 Donald Delmar Davis, Suspect Devices. All Rights Reserved
+ *
+ * !!!!!!!!!!!! ..... This may or may not be free software ..... !!!!!!!!!!!!!!!
+ *
+ * This file is autogenerated and while created by and needed for several open
+ * source modules the vocabulary is specific to the project and the routines
+ * referenced will be liscenced on a per project basis
+ *----------------------------------------------------------------------------*/
+
+/*----------------------------- Monitor keywords--------------------------------
+* The idea here is to create an automation freindly monitor protocol and debug
+* interfacse. Because this needs to be efficient (and flexible) I have chosen a
+* very simple interface which will access and may manipulate the devices public
+* variables.
+*
+* Basic Syntax for the monitor is
+* XXX[?!:] <variable stuff> <CR>
+* where:
+* XXX is a 3 letter command or variable.
+* ! execute command or store value
+* ? request a value
+* : value sent as a response or an update.
+* simple example
+* (reboot device)
+* RST!
+* (set red led value)
+* RED!255
+* RED:255
+* (request pressure sensor, recieve sensor value
+* note: unsolicited temperature sensor update )
+* PS1?
+* PS1:1240.2
+* TS1:150.5
+*
+*/
+#ifndef keywords_h
+#define keywords_h
+
+
+#define HELPTEXT \
+"SYN[! ]sync (hello) "\
+"ACK[ : ]acknowlege(yes) "\
+"NAK[ : ]negative (no) "\
+"SWV[ :?]Software Vers. "\
+"HWV[ :?]Hardware Vers. "\
+"GIT[ :?]GIT Repo Vers. "\
+"MEM[ :?]Avaliable Mem. "\
+"SSN[ :?]Unit Serial No. "\
+"HLP[ :?]help "\
+"FTL[!: ]Fatal Error "\
+"ALT[!: ]Alert "\
+"WAR[!: ]Warning "\
+"INF[!: ]Info "\
+"DBG[!: ]Debugging Info "\
+"LOG[!: ]Log "\
+"STC[!:?]State Change "\
+"DVL[!:?]Display Level "\
+"LVL[!:?]Log Level "\
+"RST[! ]Reboot "\
+"BLD[! ]Reboot to loader"\
+"STP[!:?]Sim/Chk E-Stop "\
+"CMD[!:?]Command Mode "\
+"NOW[!:?]Time as an int "\
+"TIM[!:?]Time for humans "\
+"MLD[!:?]Enable Multiline"\
+"SOD[!: ]Start Multiline "\
+"EOD[!: ]End Multiline "\
+"RED[!: ]Led (0-255) "\
+"AMT[ :?]Ambient Temp "\
+"AMH[ :?]Ambient Humidity"\
+"PX1[ :?]Proximity Sense "\
+"AD1[ :?]Analog Input 1 "\
+"AD2[ :?]Analog Input 2 "\
+"AD3[ :?]Analog Input 3 "\
+"AD4[ :?]Analog Input 4 "\
+"AD5[ :?]Analog Input 5 "\
+"AD6[ :?]Analog Input 6 "\
+"AD7[ :?]Analog Input 7 "\
+"AD8[ :?]Analog Input 8 "\
+"DI1[ :?]Digital Input 1 "\
+"DI2[ :?]Digital Input 2 "\
+"DI3[ :?]Digital Input 3 "\
+"DI4[ :?]Digital Input 4 "\
+"DI5[ :?]Digital Input 5 "\
+"DI6[ :?]Digital Input 6 "\
+"DI7[ :?]Digital Input 7 "\
+"DI8[ :?]Digital Input 8 "\
+"DO1[!:?]Digital Output 1"\
+"DO2[!:?]Digital Output 2"\
+"DO3[!:?]Digital Output 3"\
+"DO4[!:?]Digital Output 4"\
+"DO5[!:?]Digital Output 5"\
+"DO6[!:?]Digital Output 6"\
+"DO7[!:?]Digital Output 7"\
+"DO8[!:?]Digital Output 8"\
+"TMS[!:?]Timer Set "\
+"TMR[!:?]Timer Run(ing) "\
+"TRM[!:?]Time Remaining "\
+"NOP[!: ]Not implemented "
+
+
+#define KEYWORDS \
+"SYN" "ACK" "NAK" "SWV" "HWV" "GIT" "MEM" "SSN" "HLP" "FTL" "ALT" "WAR" "INF" \
+"DBG" "LOG" "STC" "DVL" "LVL" "RST" "BLD" "STP" "CMD" "NOW" "TIM" "MLD" "SOD" \
+"EOD" "RED" "AMT" "AMH" "PX1" "AD1" "AD2" "AD3" "AD4" "AD5" "AD6" "AD7" "AD8" \
+"DI1" "DI2" "DI3" "DI4" "DI5" "DI6" "DI7" "DI8" "DO1" "DO2" "DO3" "DO4" "DO5" \
+"DO6" "DO7" "DO8" "TMS" "TMR" "TRM" "NOP"
+
+
+enum keywordIndex {
+_SYN_,_ACK_,_NAK_,_SWV_,_HWV_,_GIT_,_MEM_,_SSN_,_HLP_,_FTL_,_ALT_,_WAR_,_INF_,
+_DBG_,_LOG_,_STC_,_DVL_,_LVL_,_RST_,_BLD_,_STP_,_CMD_,_NOW_,_TIM_,_MLD_,_SOD_,
+_EOD_,_RED_,_AMT_,_AMH_,_PX1_,_AD1_,_AD2_,_AD3_,_AD4_,_AD5_,_AD6_,_AD7_,_AD8_,
+_DI1_,_DI2_,_DI3_,_DI4_,_DI5_,_DI6_,_DI7_,_DI8_,_DO1_,_DO2_,_DO3_,_DO4_,_DO5_,
+_DO6_,_DO7_,_DO8_,_TMS_,_TMR_,_TRM_,_NOP_};
+
+// use enum to determine the size of the keyword arrays.
+#define NKEYWORDS _NOP_ + 1
+
+#endif