reserve LED1 for use by firmware

reserve LED1 for use by firmware

--- TClab/TClab_Firmware/TClab_Firmware.ino
+++ TClab/TClab_Firmware/TClab_Firmware.ino
@@ -4,7 +4,6 @@
   This firmware is initially loaded into the Temperature Control Laboratory Arduino.
   The firmware scans the serial port looking for case-insensitive commands:

-  LED1 int  set LED1, range 0 to 255, limit to 50
   Q int     set Heater 1, range 0 to 255,  limit to 150 to prevent overheating
             (note: Q turns off Heater 2 as safety precaution)
   Q1 int    set Heater 1, range 0 to 255,  limit to 150 to prevent overheating
@@ -13,7 +12,7 @@
   T1        get Temperature T1, returns deg C as string
   T2        get Temperature T2, returns dec C as string
   VERSION   get firmware version string
-  X         shutdown, set LED1, Q1, Q2 to zero
+  X         shutdown, set Q1, Q2 to zero

   LED1 is on during normal operation.

@@ -24,7 +23,9 @@
 */

 // global variables
-char Buffer[64];         // buffer for parsing serial input
+char Buffer[64];               // buffer for parsing serial input
+String cmd;                    // command
+int pv;                        // command pin value

 // constants
 const String vers = "0.1";     // version of this firmware
@@ -46,8 +47,9 @@
 const int limT1   = 310;       // T1 high alarm (approx. 50 deg C)
 const int limT2   = 310;       // T2 high alarm (approx. 50 deg C)

-// parse and process serial input
-void SerialParser(void) {
+
+// parse serial input
+void SerialParse(void) {
   // read serial input
   int ByteCount = Serial.readBytesUntil(nl,Buffer,sizeof(Buffer));
   String read_ = String(Buffer);
@@ -55,20 +57,21 @@

   // separate command from associated data
   int idx = read_.indexOf(sp);
-  String cmd = read_.substring(0,idx);
+  cmd = read_.substring(0,idx);
   cmd.trim();
   cmd.toUpperCase();

   // extract data. toInt() returns 0 on error
   String data = read_.substring(idx+1);
   data.trim();
-  int pv = data.toInt();
-
+  pv = data.toInt();
+}
+
+
+// dispatch commands
+void CommandDispatch(void) {
   // process commands
-  if (cmd == "LED1") {
-    analogWrite(pinLED1, max(0,min(limLED1,pv)));
-  }
-  else if (cmd == "Q") {
+  if (cmd == "Q") {
     analogWrite(pinQ1, max(0,min(limQ1,pv)));
     analogWrite(pinQ2, 0);
   }
@@ -98,7 +101,6 @@
   }
   // shutdown
   else if (cmd == "X") {
-    analogWrite(pinLED1, 0);
     analogWrite(pinQ1, 0);
     analogWrite(pinQ2, 0);
   }
@@ -116,7 +118,9 @@
   }
 }

-void CheckAlarms(void) {
+
+// check alarms and update status on LED1
+void AlarmsCheck(void) {
   if ((analogRead(pinT1) > limT1) or (analogRead(pinT2) > limT2)) {
     analogWrite(pinLED1,limLED1);
   } else {
@@ -124,6 +128,8 @@
   }
 }

+
+// setup on reboot
 void setup() {
   analogReference(EXTERNAL);
   Serial.begin(baud);
@@ -135,7 +141,10 @@
   analogWrite(pinQ2,0);
 }

+
+// main event loop
 void loop() {
-  SerialParser();
-  CheckAlarms();
+  SerialParse();
+  CommandDispatch();
+  AlarmsCheck();
 }


Support a 1-wire temperature sensor

Support a 1-wire temperature sensor

--- KomaRoof.ino
+++ KomaRoof.ino
@@ -21,32 +21,41 @@
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */

- #include <TaskScheduler.h>
+#include <DallasTemperature.h>
+#include <OneWire.h>
+#include <TaskScheduler.h>

- #include "DualVNH5019MotorShield.h"
- #include "NMEASerial.h"
- #include "MessageHandler.h"
- #include "PowerConsumptionLog.h"
- #include "Roof.h"
- #include "Version.h"
+#include "DualVNH5019MotorShield.h"
+#include "NMEASerial.h"
+#include "MessageHandler.h"
+#include "PowerConsumptionLog.h"
+#include "Roof.h"
+#include "Version.h"

 #define BOARD_NAME "KOMAROOF"
+#define ONE_WIRE_BUS 2
 #define MOTOR_POLARITY 1    // Change to -1 to invert movement direction
 #define FULL_SPEED 400
 #define RAMP_LENGTH 20      // two seconds

 void motorTick();
+void temperatureTick();

+static OneWire oneWire(ONE_WIRE_BUS);
+static DallasTemperature dallasTemperature(&oneWire);
 static MessageHandler handler;
 static NMEASerial serial(&handler);
 static DualVNH5019MotorShield motorShield;
 static PowerConsumptionLog powerConsumptionLog;
 static Task motorTask(100, TASK_FOREVER, &motorTick);
+static Task temperatureTask(1000, TASK_FOREVER, &temperatureTick);
 static Scheduler taskScheduler;
 static RoofState roofState = STOPPED;
 static Phase phase = IDLE;
 static int countSincePhase;
 static int direction;
+static float temperature = DEVICE_DISCONNECTED_C;
+static bool temperatureRequested = false;

 void setup() {
     Serial.begin(57600);
@@ -58,6 +67,14 @@

     taskScheduler.init();
     taskScheduler.addTask(motorTask);
+
+    dallasTemperature.begin();
+    if (dallasTemperature.getDeviceCount() > 0) {
+        dallasTemperature.setWaitForConversion(false);
+        dallasTemperature.setResolution(12);
+        taskScheduler.addTask(temperatureTask);
+    }
+
     motorTask.enable();
     test("");
 }
@@ -101,6 +118,19 @@
             }
             break;
         }
+    }
+}
+
+void temperatureTick() {
+    // called @ 1hz
+
+    // alternate between sending requests and reading temperature
+    if (!temperatureRequested) {
+        dallasTemperature.requestTemperaturesByIndex(0);
+        temperatureRequested = true;
+    } else {
+        temperature = dallasTemperature.getTempCByIndex(0);
+        temperatureRequested = false;
     }
 }

@@ -149,5 +179,11 @@
     message += roofStateNames[roofState];
     message += ",PHASE=";
     message += phaseNames[phase];
+    if (temperature != (float)DEVICE_DISCONNECTED_C) {
+        message += ",TEMP1=";
+        message += (int)(temperature);
+        message += ".";
+        message += (int)(roundf(temperature*10)) % 10;
+    }
     serial.print(message);
 }


cancel out up+down and left+right on busted controllers, can mess with hat direction calculation

--- pokken/pokken.ino
+++ pokken/pokken.ino
@@ -35,6 +35,12 @@
   if (++usbTimer >= 8) {
     usbTimer = 0;

+    // filter out up+down and left+right
+    if ((debounced_btns & (BTN_UP | BTN_DOWN)) == (BTN_UP | BTN_DOWN))
+      debounced_btns &= ~(BTN_UP | BTN_DOWN);
+    if ((debounced_btns & (BTN_LEFT | BTN_RIGHT)) == (BTN_LEFT | BTN_RIGHT))
+      debounced_btns &= ~(BTN_LEFT | BTN_RIGHT);
+
     uint16_t payload_buttons = 0;
     payload_buttons |= (!!(debounced_btns & BTN_Y) << 0);
     payload_buttons |= (!!(debounced_btns & BTN_B) << 1);


Added in proper throttle control.

Added in proper throttle control.

Got my linear pots in the mail, coded up the actual routine for getting throttle and replaced the dummy call with a real one.

--- KSPboard.ino
+++ KSPboard.ino
@@ -21,6 +21,7 @@
 // state pins
 #define PIN_LED 13 // status
 #define PIN_ENABLE 2 // this should be high to run
+
 // helm pins
 #define PIN_PITCH_U 12
 #define PIN_PITCH_D 11
@@ -28,6 +29,8 @@
 #define PIN_ROLL_R 9
 #define PIN_YAW_L 8
 #define PIN_YAW_R 7
+#define PIN_THROTTLE A0 // analog read
+
 // ops pins
 #define PIN_OP_LAUNCH 6
 #define PIN_OP_STAGE 5
@@ -41,6 +44,9 @@

 // misc
 #define OPS (sizeof(opPin) / sizeof(byte))
+
+#define ANALOG_MAX (float)1023 // these two are for converting 0-1023 to 0-100
+#define THOTTLE_MAX (float)100

 // helm control variables
@@ -74,9 +80,14 @@
 //// helper functions
 //

-byte readDirection(byte pinH, byte pinL)
+byte readDirection(byte pinH, byte pinL) // get control input
 {
   return (digitalRead(pinH) ? FULL : (digitalRead(pinL) ? ZERO : HALF));
+}
+
+byte readThrottle(byte pin) // get throttle input
+{
+  return ((byte) (((float)analogRead(pin) * THOTTLE_MAX) / ANALOG_MAX));
 }

@@ -148,6 +159,8 @@
   Serial.println(yaw);
   Serial.println(roll);

+  // read throttle
+  throttle = readThrottle(PIN_THROTTLE);
   Serial.println(throttle);
 }

@@ -196,7 +209,6 @@
   setupSerial();
   setupState();
   setupHelm();
-  setupOps();

   delay(DELAY_START);
 }
@@ -214,4 +226,3 @@
   Serial.flush();
   delay(DELAY_LOOP);
 }
-


Cleaned up file

Cleaned up file

--- SevenSegment_UltrasonicRangeFinder_IR.ino
+++ SevenSegment_UltrasonicRangeFinder_IR.ino
@@ -7,108 +7,77 @@
 Sindormir7segs mi7S;

 //ir receiver setup
-int IrReceiverPin = 11;
-IRrecv irrecv(IrReceiverPin);
-decode_results results;
+int IrReceiverPin = 11;
+IRrecv irrecv(IrReceiverPin);
+decode_results results;

 //ultrasonic range finder setup
-int triggerPin =  10;
-int echoPin = 9;
-int maxDistance = 375;
-Ultrasonic sonar(triggerPin, echoPin);
+int triggerPin =  10;
+int echoPin = 9;
+int maxDistance = 375;
+Ultrasonic sonar(triggerPin, echoPin);

-void setup()
+void setup()
 {
   Serial.begin(9600);
-
+
   irrecv.enableIRIn(); // Start the IrReceiverPin
-
+
   mi7S.commonType(CATODO);
   //2-8 are the pins this is hooked into on arduino
   mi7S.attach(2,3,4,5,6,7,8,0);//didnt setup the . so put it to 0
 }

-void loop()
-{
+void loop()
+{
     int irVal = getIrValue();
     int sonarVal = sonar.Ranging(CM);
-    if(irVal != -1) {
+    if(irVal != -1)
+    {
       mi7S.print(irVal);
       Serial.println(irVal + 1);
       Serial.println(sonarVal);
     }
-
+
     delay(100);//10 times a second seems like enough
 }

 //get the value from the ir sensor.
-int getIrValue() {
+int getIrValue()
+{
   int irVal = -1;
   if (irrecv.decode(&results)) // have we received an IR signal?
   {
     irVal = translateIR();
     irrecv.resume(); // receive the next value
-  }
+  }
   return irVal;
 }

 // takes in the code from the remote and gives back a number
-int translateIR(){
-
+int translateIR()
+{
   switch(results.value)
-
   {
-  case 0xFFE01F:
-    return 10;
-
-  case 0xFFA857:
-    return 11;
-
-  case 0xFF906F:
-    return 12;
-
-  case 0xFF6897:
-    return 0;
-
-  case 0xFF9867:
-    return 13;
-
-  case 0xFFB04F:
-    return 14;
-
-  case 0xFF30CF:
-    return 1;
-
-  case 0xFF18E7:
-    return 2;
-
-  case 0xFF7A85:
-    return 3;
-
-  case 0xFF10EF:
-    return 4;
-
-  case 0xFF38C7:
-    return 5;
-
-  case 0xFF5AA5:
-    return 6;
-
-  case 0xFF42BD:
-    return 7;
-
-  case 0xFF4AB5:
-    return 8;
-
-  case 0xFF52AD:
-    return 9;
-
-  default:
-    //Serial.println(" other button   ");
-    return -1;
+    case 0xFF6897: return 0;
+    case 0xFF30CF: return 1;
+    case 0xFF18E7: return 2;
+    case 0xFF7A85: return 3;
+    case 0xFF10EF: return 4;
+    case 0xFF38C7: return 5;
+    case 0xFF5AA5: return 6;
+    case 0xFF42BD: return 7;
+    case 0xFF4AB5: return 8;
+    case 0xFF52AD: return 9;
+    case 0xFFE01F: return 10;
+    case 0xFFA857: return 11;
+    case 0xFF906F: return 12;
+    case 0xFF9867: return 13;
+    case 0xFFB04F: return 14;
+  default:
+    return -1; //Serial.println(" other button   ");
   }
-
-  //reset the device
+  //reset the device
   delay(25);
 }


Delete DualSonar-slave.ino

Delete DualSonar-slave.ino

--- extras/DualSonar-slave.ino
+++ extras/DualSonar-slave.ino
@@ -1,167 +0,0 @@
-#include <avr/interrupt.h>
-#include <TinyWireS.h>
-#define REG_STATUS 0x01 //I2C register 1: holds status (1 byte)
-#define REG_DATA 0x02   //I2C register 2: holds data (4 bytes:
-                        // distance 1 - high, d1- low, d2-high, d2-low
-#define I2C_SLAVE_ADDRESS 0x11 //I just like number 17
-#define ECHO1_PIN 3 //if you change these values, also change the enabled interrupts in setup()
-#define ECHO2_PIN 4
-#define TRIG_PIN 1
-#define TIMEOUT 20000 //timeout waiting for return echo, in microseconds. 20000 is about 3.5 meters
-#define INTERVAL 50   //interval between conclusion of one ping and beginning of next, in ms
-uint32_t ping_emit_time = 0,                  //in microseconds
-         last_ping_time = 0;                 //time of competion of last ping, in milliseconds
-volatile uint32_t echo1_start_time = 0, echo2_start_time = 0; //in microseconds
-volatile uint16_t echo1_duration = 0, echo2_duration = 0; //in microseconds
-volatile  bool  echo1_pin_state, echo2_pin_state; //last known state of pins - for detecting changes
-
-uint16_t  distance1 = 0, distance2 = 0;       //measured distance, in mm. Running average of last measurements,
-//using simple low-pass filter
-
-volatile bool status=false; // is the sensor active?
-volatile uint8_t requestedRegister = 0; //requested i2c register
-bool  pinging=false;                               //are we actively pinging now?
-
-void start_ping()
-{
-  digitalWrite(TRIG_PIN, LOW);
-  delayMicroseconds(2);
-  digitalWrite(TRIG_PIN, HIGH);
-  delayMicroseconds(10);
-  digitalWrite(TRIG_PIN, LOW);
-  ping_emit_time = micros();
-  pinging = true;
-  echo1_pin_state = LOW; echo2_pin_state = LOW; //they must be low, we do not even need to check
-}
-
-void end_ping()
-{
-  //update distances
-  // rule: distance =0.7* distance+0.3* new_distance;
-  // new distance = echo_duration/5.8 (in mm)
-  if (echo1_duration > 100) //shorter durations are most likely result of error in readings
-    distance1 = 0.7 * distance1 + 3 * echo1_duration / 58;
-  if (echo2_duration > 100)
-    distance2 = 0.7 * distance2 + 3 * echo2_duration / 58;
-  //reset variables
-  pinging = false;
-  last_ping_time = millis();
-  echo1_duration = 0; echo2_duration = 0;
-  //digitalWrite(LED_PIN,LOW);
-}
-
-// Interrupt vector for external interrupt on pin PCINT7..0
-// This will be called when any of the pins D0 - D4 on the trinket change
-// or pins D8 - D13 on an Arduino Uno change.
-
-// the echo pin will flip HIGH at the point when the pulse has completed
-// and timing should begin, it will then flip LOW once the sound wave is received
-// so we need to detect both of these states
-
-ISR(PCINT0_vect)
-{
-  if (pinging)
-  {
-    //detect pin state changes for echo1
-    if ((echo1_pin_state == LOW) && (digitalRead(ECHO1_PIN))) //transition low->high
-    {
-      echo1_start_time = micros();
-      echo1_duration = 0;
-      echo1_pin_state = HIGH;
-    } else if ((echo1_pin_state == HIGH ) && (digitalRead(ECHO1_PIN) == LOW) ) //high->low
-    {
-      echo1_duration = micros() - echo1_start_time;
-      echo1_pin_state = LOW;
-    }
-    //detect pin state changes for echo2
-    if ((echo2_pin_state == LOW) && (digitalRead(ECHO2_PIN))) //transition low->high
-    {
-      echo2_start_time = micros();
-      echo2_duration = 0;
-      echo2_pin_state = HIGH;
-    } else if ((echo2_pin_state == HIGH ) && (digitalRead(ECHO2_PIN) == LOW) ) //high->low
-    {
-      echo2_duration = micros() - echo2_start_time;
-      echo2_pin_state = LOW;
-    }
-
-  }//end of 'if pinging'
-}
-
-
-void setup()
-{
-  pinMode(ECHO1_PIN, INPUT);
-  pinMode(ECHO2_PIN, INPUT);
-  pinMode(TRIG_PIN, OUTPUT);
-
-  //enable interrupts - see https://thewanderingengineer.com/2014/08/11/pin-change-interrupts-on-attiny85/
-  GIMSK |= 0b00100000;    // turns on pin change interrupts
-  PCMSK |= 0b00011000;    // turn on interrupts on pin PB3, PB4
-  sei();
-  //start I2C
-  TinyWireS.begin(I2C_SLAVE_ADDRESS); // join i2c
-  TinyWireS.onReceive(receiveEvent);
-  TinyWireS.onRequest(requestEvent);
-}
-
-void loop()
-{
-  TinyWireS_stop_check(); //check i2c bus
-  if (pinging)
-  {
-    //check timeouts for echos
-    if ( micros() - ping_emit_time > TIMEOUT )
-    {
-      if (!echo1_duration)
-        echo1_duration = TIMEOUT;
-      if (!echo2_duration)
-        echo2_duration = TIMEOUT;
-    }
-    // check if we are done
-    if ( echo1_duration && echo2_duration )
-      end_ping();
-
-  }
-  else //not pinging
-  {
-    if  ( status && (millis() - last_ping_time > INTERVAL))
-      start_ping();
-  }
-}
-
-void receiveEvent(uint8_t howMany) //number of bytes received
-{
-  if (howMany == 0) return; //no data sent
-  //first byte must be register address
-  requestedRegister = TinyWireS.receive();
-  if ((howMany == 2) && (requestedRegister == REG_STATUS))
-  {
-    //this is a write request to REG_STATUS
-    status = (bool)TinyWireS.receive();
-  }
-  //in all other cases, ignore subsequent bytes (if any)
-
-}
-
-void requestEvent() //send previously requested register
-{
-
-  switch (requestedRegister)
-  {
-    case 1: //requested status
-      TinyWireS.send(status);
-      break;
-    case 2: //requested data
-      //send four bytes
-      TinyWireS.send(highByte(distance1));
-      TinyWireS.send(lowByte(distance1));
-      TinyWireS.send(highByte(distance2));
-      TinyWireS.send(lowByte(distance2));
-      break;
-    default:
-      break;
-      //send nothing
-  }
-}
-


arduino 2.0

arduino 2.0


Many changes

Many changes

--- Arduino/coopController/coopController.ino
+++ Arduino/coopController/coopController.ino
@@ -54,6 +54,9 @@
 // MQTT Host Settings
 #define mHost "domain.com"
 #define mPort 1883
+#define mClientID "coop-duino"
+#define mUsername "chicken_coop"
+#define mPassword "secret"

 // MQTT Subscription Channels
 #define sTime "time/beacon"
@@ -67,9 +70,9 @@
 // Misc Settings
 const unsigned long millisPerDay    = 86400000; // Milliseconds per day
 const unsigned long debounceWait    =       25; // Debounce timer (25 ms)
-const unsigned long ledBlinkRate    =      500; // Delay between LED blinks (1/2 sec)
+const unsigned long ledBlinkRate    =      250; // Delay between LED blinks (1/4 sec)
 const unsigned long remoteOverride  =   600000; // Length of time to lockout readings. (10 min)
-const unsigned long readingInterval =   300000; // How often to take sensor readings. (5 min)
+const unsigned long readingInterval =    60000; // How often to take sensor readings. (1 min)
 const int           fanThreshold    =       78; // When temperature (Farenheit) exceeds this threshold, turn on fan
 const int           heaterThreshold =       36; // When temperature (Farenheit) is below this threshold, turn on water heater

@@ -80,31 +83,32 @@
 const int           nightLockEnd   =        4; // Hour (in 24hr time) to end night time lockout (4am)

 // Runtime variables
-unsigned long lastReading     = 0;
-unsigned long lastDebounce    = 0;
-unsigned long lastRTCSync     = 0;
-unsigned long lastLEDBlink    = 0;
-String        doorState       = "closed"; // Values will be one of: closed, closing, open, opening
-boolean       heaterState     = false;
-boolean       lampState       = false;
-boolean       fanState        = false;
-float         tempC           = 0;
-float         tempF           = 0;
-int           brightness      = 0;
-int           doorTopVal      = 0;
-int           doorTopVal2     = 0;
-int           doorTopState    = 0;
-int           doorTopPrev     = 0;
-int           doorBottomVal   = 0;
-int           doorBottomVal2  = 0;
-int           doorBottomState = 0;
-int           doorBottomPrev  = 0;
-
-RTC_DS1307 rtc;
+unsigned long lastReading        = 0;
+unsigned long lastDebounce       = 0;
+unsigned long lastRTCSync        = 0;
+unsigned long lastLEDBlink       = 0;
+unsigned long remoteLockStart    = 0;
+String        doorState          = "open"; // Values will be one of: closed, closing, open, opening
+boolean       heaterState        = false;
+boolean       lampState          = false;
+boolean       fanState           = false;
+float         tempC              = 0;
+float         tempF              = 0;
+int           brightness         = 0;
+int           doorTopVal         = 0;
+int           doorTopVal2        = 0;
+int           doorTopState       = 0;
+int           doorTopPrev        = 0;
+int           doorBottomVal      = 0;
+int           doorBottomVal2     = 0;
+int           doorBottomState    = 0;
+int           doorBottomPrev     = 0;
+
+RTC_DS1307 RTC;
 OneWire ds(tempSense);
 LiquidCrystal lcd(lcd_4, lcd_5, lcd_6, lcd_11, lcd_12, lcd_13, lcd_14);

-ESP esp(&espPort, chpdPin);
+ESP esp(&espPort, &debugger, chpdPin);
 MQTT mqtt(&esp);
 boolean wifiConnected = false;

@@ -142,10 +146,10 @@
   }

   // Subscribe to time beacon channel to keep RTC up to date.
-  mqtt.subscribe(sTime);
+  mqtt.subscribe(sTime, 0);

   // Subscribe to remote trigger channel to allow remote control of chicken coop
-  mqtt.subscribe(sRemote);
+  mqtt.subscribe(sRemote, 2);
 }

 /**
@@ -168,10 +172,12 @@
   String topic = res.popString();
   String data  = res.popString();
   if (Debugging) {
-    debugger.print("MQTT Data published to channel '");
-    debugger.print(topic);
-    debugger.print("': ");
-    debugger.println(data);
+    if (topic != "time/beacon") {
+      debugger.print("MQTT Data published to channel '");
+      debugger.print(topic);
+      debugger.print("': ");
+      debugger.println(data);
+    }
   }
 }

@@ -191,45 +197,59 @@
     debugger.println(data);
   }

-  if (topic == "coop/remotetrigger") {
+  if (topic == sRemote) {
     // If door movement is triggered, toggle door state to
     // opening or closing based on current state.
     // If door is currently moving, the trigger is ignored.
     if (data == "door") {
       if (doorState == "open") {
         doorState = "closing";
-        lastReading = millis() + remoteOverride;
+        remoteLockStart = millis();
       }
       else if (doorState == "closed") {
         doorState = "opening";
-        lastReading = millis() + remoteOverride;
+        remoteLockStart = millis();
       }
     }

     // Toggle interior light
     if (data == "light") {
       toggleLamp();
-      lastReading = millis() + remoteOverride;
+      remoteLockStart = millis();
     }

     // Toggle water heater
     if (data == "water heater") {
       toggleHeater();
-      lastReading = millis() + remoteOverride;
+      remoteLockStart = millis();
     }

     // Toggle fan
     if (data == "fan") {
       toggleFan();
-      lastReading = millis() + remoteOverride;
+      remoteLockStart = millis();
     }
   }

   // Sync RTC to time beacon once/day
   if (topic == "time/beacon") {
-    if ((unsigned long)(millis() - millisPerDay) >= lastRTCSync) {
-      rtc.adjust(atoi(data.c_str()));
+    if (lastRTCSync == 0 || ((unsigned long)(millis() - lastRTCSync) > millisPerDay)) {
+      RTC.adjust(strtoul(data.c_str(), NULL, 0));
       lastRTCSync = millis();
+      if (Debugging) {
+        DateTime now = RTC.now();
+        debugger.println("RTC Updated:");
+        debugger.print(now.month(), DEC);
+        debugger.print("/");
+        debugger.print(now.day(), DEC);
+        debugger.print("/");
+        debugger.println(now.year(), DEC);
+        debugger.print(now.hour(), DEC);
+        debugger.print(":");
+        debugger.print(now.minute(), DEC);
+        debugger.print(":");
+        debugger.println(now.second(), DEC);
+      }
     }
   }
 }
@@ -243,14 +263,18 @@
     if (Debugging) {
       debugger.println("Interior light off.");
     }
-    mqtt.publish(pStatus, "light|off");
+    if (wifiConnected) {
+      mqtt.publish(pStatus, "light|off", 2, 0);
+    }
   }
   else {
     digitalWrite(lampRelay, HIGH);
     if (Debugging) {
       debugger.println("Interior light on.");
     }
-    mqtt.publish(pStatus, "light|on");
+    if (wifiConnected) {
+      mqtt.publish(pStatus, "light|on", 2, 0);
+    }
   }
   lampState = !lampState;
 }
@@ -264,14 +288,18 @@
     if (Debugging) {
       debugger.println("Water heater off.");
     }
-    mqtt.publish(pStatus, "water heater|off");
+    if (wifiConnected) {
+      mqtt.publish(pStatus, "water heater|off", 2, 0);
+    }
   }
   else {
     digitalWrite(heaterRelay, HIGH);
     if (Debugging) {
       debugger.println("Water heater on.");
     }
-    mqtt.publish(pStatus, "water heater|on");
+    if (wifiConnected) {
+      mqtt.publish(pStatus, "water heater|on", 2, 0);
+    }
   }
   heaterState = !heaterState;
 }
@@ -286,7 +314,9 @@
     if (Debugging) {
       debugger.println("Fan off.");
     }
-    mqtt.publish(pStatus, "fan|off");
+    if (wifiConnected) {
+      mqtt.publish(pStatus, "fan|off", 2, 0);
+    }
   }
   else {
     digitalWrite(fan, HIGH);
@@ -294,7 +324,9 @@
     if (Debugging) {
       debugger.println("Fan on.");
     }
-    mqtt.publish(pStatus, "fan|on");
+    if (wifiConnected) {
+      mqtt.publish(pStatus, "fan|on", 2, 0);
+    }
   }
   fanState = !fanState;
 }
@@ -397,10 +429,10 @@
   }
   // If door is opening/closing (or stuck?) blink LEDs back and forth
   else {
-    if ((unsigned long)(millis() - lastLEDBlink) >= ledBlinkRate) {
-      lastLEDBlink = millis();
+    if ((unsigned long)(millis() - lastLEDBlink) > ledBlinkRate) {
       digitalWrite(doorClosedLED, !digitalRead(doorClosedLED));
       digitalWrite(doorOpenLED, !digitalRead(doorOpenLED));
+      lastLEDBlink = millis();
     }
   }
 }
@@ -448,15 +480,18 @@
   tempC = getTemp();
   tempF = ((tempC * 9.0) / 5.0) + 32;
   char tempStr[5];
-  mqtt.publish(pTemp, dtostrf(tempF, 5, 1, tempStr));
+  if (wifiConnected) {
+    mqtt.publish(pTemp, dtostrf(tempF, 5, 1, tempStr), 2, 0);
+  }

   // Read light sensor and convert to brightness percentage
-  brightness = analogRead(tempSense);
-  brightness = map(brightness, 746, 13, 0, 100);  // Remap value to a 0-100 scale
+  brightness = analogRead(lightSense);
+  brightness = map(brightness, 300, 150, 0, 100);  // Remap value to a 0-100 scale
   brightness = constrain(brightness, 0, 100);     // constrain value to 0-100 scale
   char briStr[3];
-  mqtt.publish(pLight, dtostrf(brightness, 3, 0, briStr));
-
+  if (wifiConnected) {
+    mqtt.publish(pLight, dtostrf(brightness, 3, 0, briStr), 2, 0);
+  }
   lastReading = millis();
 }

@@ -509,7 +544,7 @@
   // ---------------------

   // Fetch current time from RTC
-  DateTime now = rtc.now();
+  DateTime now = RTC.now();
   // If nightlock is enabled, and we are within the designated time period, simply
   // ensure interior light is off and door is closed.
   if (nightLock && (now.hour() > nightLockStart || now.hour() < nightLockEnd)) {
@@ -526,7 +561,9 @@
         debugger.println("NIGHTLOCK ENABLED: Closing door.");
       }
       doorState = "closing";
-      mqtt.publish(pStatus, "door|closing");
+      if (wifiConnected) {
+        mqtt.publish(pStatus, "door|closing", 2, 0);
+      }
     }
   }
   // Otherwise, handle brightness level based reactions
@@ -556,7 +593,9 @@
           debugger.println("Opening door.");
         }
         doorState = "opening";
-        mqtt.publish(pStatus, "door|opening");
+        if (wifiConnected) {
+          mqtt.publish(pStatus, "door|opening", 2, 0);
+        }
       }
     }
     // Otherwise, close door when light level falls to 15% or below.
@@ -566,7 +605,9 @@
           debugger.println("Closing door.");
         }
         doorState = "closing";
-        mqtt.publish(pStatus, "door|closing");
+        if (wifiConnected) {
+          mqtt.publish(pStatus, "door|closing", 2, 0);
+        }
       }
     }
   }
@@ -593,7 +634,9 @@
             if (Debugging) {
               debugger.println("Door open.");
             }
-            mqtt.publish(pStatus, "door|open");
+            if (wifiConnected) {
+              mqtt.publish(pStatus, "door|open", 2, 0);
+            }
           }
         }
       }
@@ -607,7 +650,9 @@
             if (Debugging) {
               debugger.println("Door closed.");
             }
-            mqtt.publish(pStatus, "door|closed");
+            if (wifiConnected) {
+              mqtt.publish(pStatus, "door|closed", 2, 0);
+            }
           }
         }
       }
@@ -620,35 +665,37 @@
  * Initialization on startup.
  */
 void setup() {
+  debugger.begin(19200);
   if (Debugging) {
-    debugger.begin(19200);
     debugger.println("Initialising...");
   }
   // Init LCD
   pinMode(lcd_bl, OUTPUT);
   digitalWrite(lcd_bl, HIGH);
+  RTC.begin();
   lcd.begin(16,2);
   lcdWrite("Initializing...");

   espPort.begin(19200);
-  lcdWrite("Initializing...", "WIFI Connecting");
+  lcdWrite("Initializing...", "WIFI Config");
   esp.enable();
   delay(500);
   esp.reset();
   delay(500);
   while(!esp.ready());
-
+
   if (Debugging) {
     debugger.println("ARDUINO: Setup MQTT client");
   }
-  lcdWrite("Initializing...", "MQTT Connecting");
-  if (!mqtt.begin("coop_duino", "", "", 20, 1)) { // client_id, username, password, keepalive time, cleansession boolean
+  lcdWrite("Initializing...", "MQTT Config");
+  if (!mqtt.begin(mClientID, mUsername, mPassword, 20, 1)) { // client_id, username, password, keepalive time, cleansession boolean
     if (Debugging) {
       debugger.println("ARDUINO: Failed to setup MQTT");
     }
     lcdWrite("MQTT Connect", "Failure");
     while(1);
   }
+  mqtt.lwt("/lwt", "Offline");

   mqtt.connectedCb.attach(&mqttConnected);
   mqtt.disconnectedCb.attach(&mqttDisconnected);
@@ -661,7 +708,7 @@
   esp.wifiCb.attach(&wifiCb);
   esp.wifiConnect(wHost, wPass);

-  lcdWrite("System Ready");
+  lcdWrite("Initializing...", "WIFI Connecting");

   /**
    * Setup pin modes
@@ -690,6 +737,8 @@
   digitalWrite(doorBottom, HIGH); // Enable resistor
   digitalWrite(fan, LOW);
   digitalWrite(fanSpeed, LOW);
+
+  lcdWrite("System Ready");
 }

 /**
@@ -698,21 +747,24 @@
 void loop() {
   esp.process();

-  // Only fetch new sensor data if it's been long enough since the last reading.
-  if ((unsigned long)(millis() - readingInterval) >= lastReading) {
-    // Read new data from sensors
-    readSensors();
-
-    // Respond ot sensor data
-    handleSensorReadings();
-  }
-
-  // Move the door as needed
-  doorMove();
-
-  // Update door LEDs as needed
-  doorLEDs();
-
-  // Update the LCD display
-  updateLCD();
-}
+    // Only fetch new sensor data if it's been long enough since the last reading.
+    if (lastReading == 0 ||
+        ((unsigned long)(millis() - remoteLockStart) > remoteOverride &&
+        (unsigned long)(millis() - lastReading) > readingInterval)) {
+      // Read new data from sensors
+      readSensors();
+
+      // Respond ot sensor data
+      handleSensorReadings();
+    }
+
+    // Move the door as needed
+    doorMove();
+
+    // Update door LEDs as needed
+    doorLEDs();
+
+    // Update the LCD display
+    updateLCD();
+
+}


Allow timers to be rescheduled into the future

--- FlixCapacitor.ino
+++ FlixCapacitor.ino
@@ -23,13 +23,13 @@
 #define SD_CS   10
 ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);

-periodicCall ImageLoad      = {   6000, autoPlayPhotos,         true};
-periodicCall TrackSwitch    = { 120000, autoPlayMusic,          false};
-periodicCall VolumeAdjust   = {     50, adjustVolume,           true };
-periodicCall RamCheck       = {  10000, reportAvailableRAM,     true };
-periodicCall JoystickReport = {    200, readJoystick,           true };
-
-periodicCall timers[] = { ImageLoad, VolumeAdjust, RamCheck, TrackSwitch, JoystickReport };
+periodicCall ImageTimer    = {   6000, autoPlayPhotos,         true};
+periodicCall TrackTimer    = { 120000, autoPlayMusic,          false};
+periodicCall VolumeTimer   = {     50, adjustVolume,           true };
+periodicCall RamCheckTimer = {  10000, reportAvailableRAM,     true };
+periodicCall JoystickTimer = {    200, readJoystick,           true };
+
+periodicCall *timers[] = { &ImageTimer, &VolumeTimer, &RamCheckTimer, &TrackTimer, &JoystickTimer };
 periodicCall *executingTimer;

 FileList *photos, *tracks;
@@ -82,9 +82,11 @@
             playTrack(PREVIOUS);
         } else if (joystick.readX() > 0.95) {
             lastJoystickAction = millis();
+            ImageTimer.lastCallMs = millis() + 3 * ImageTimer.frequencyMs;
             playImage(NEXT);
         } else if (joystick.readX() < 0.05) {
             lastJoystickAction = millis();
+            ImageTimer.lastCallMs = millis() + 3 * ImageTimer.frequencyMs;
             playImage(PREVIOUS);
         }
     }
@@ -199,13 +201,13 @@
 }

 void loop() {
-    for (uint8_t i = 0; i < sizeof(timers) / sizeof(periodicCall); i++ ) {
-        periodicCall *t = &timers[i];
+    for (uint8_t i = 0; i < sizeof(timers) / sizeof(periodicCall *); i++ ) {
+        periodicCall *t = timers[i];

         if (!t->active) continue;

         uint32_t now = millis();
-        if (now - t->lastCallMs > t->frequencyMs) {
+        if (now > t->lastCallMs && (now - t->lastCallMs) > t->frequencyMs) {
             executingTimer = t;
             t->callback();
             executingTimer = NULL;


Updated basic blink example

Updated basic blink example

--- examples-ltc/7.LTCVolume1/1_BasicBlink/BasicBlink.ino
+++ examples-ltc/7.LTCVolume1/1_BasicBlink/BasicBlink.ino
@@ -1,37 +1,13 @@
 //Love to Code
-//Volume 1: Six Pin Blink
+//Volume 1: Basic Blink

 void setup() {
   outputMode(0);
-  outputMode(1);
-  outputMode(2);
-  outputMode(3);
-  outputMode(4);
-  outputMode(5);
 }

 void loop() {
   on(0);
   pause(1000);
   off(0);
-
-  on(1);
   pause(1000);
-  off(1);
-
-  on(2);
-  pause(1000);
-  off(2);
-
-  on(3);
-  pause(1000);
-  off(3);
-
-  on(4);
-  pause(1000);
-  off(4);
-
-  on(5);
-  pause(1000);
-  off(5);
 }


- Sensor de Temperatura da agua tenta fazer diversas medidas e caso todas retornem erro, nao envia nenhum valor para a nuvem

--- rede-firmware.ino
+++ rede-firmware.ino
@@ -68,14 +68,14 @@

 /******* ID CONFIG *******/
 // Id of the device
-char id[] = "+5511941924127";
+char id[] = "+5511997646041";

 /******* TIMER CONFIG *******/
 // Time elapsed since last measurement (in seconds)
-int time_elapsed = 5000;
+int time_elapsed = 60;

 // Interval between consecutive data sends (in seconds)
-#define SEND_INTERVAL 10//3600
+#define SEND_INTERVAL 1 //3600
 #define NUM_RETRIES 5

 /*********************************************************************************/
@@ -121,6 +121,8 @@
 // Message buffer
 char buffer[161];
 char buffer2[201];
+
+static byte addr[8];

 // Destination number of the message
 char destination[] = "5511947459448";
@@ -218,7 +220,7 @@
     // <YYYY-MM-DDTHH:MM:SS-03:00>;<type1>:<value1>:{unit1};...;<typeN>:<valueN>:{unitN}
     // Manually add 20 to the beginning of the buffer.
     buffer[0] = '2';
-    buffer[1] = '0';
+    //buffer[1] = '0';
     int len = strlen(buffer);
     // Convert the date received to ISO format.
     for (i = 0; i < len; i++) {
@@ -314,14 +316,17 @@

 #if WATER_TEMPERATURE_SENSOR
     // Add water temperature
-    len = strlen(buffer);
-    buffer[len] = ';';
-    len++;
-    sprintf(&buffer[len], "Tw:");
-    len = strlen(buffer);
-    sprintf(&buffer[len], "C=");
-    len = strlen(buffer);
-    dtostrf(wtemp, 1, 1, &buffer[len]);
+	if (wtemp != -100)
+	{
+		len = strlen(buffer);
+		buffer[len] = ';';
+		len++;
+		sprintf(&buffer[len], "Tw:");
+		len = strlen(buffer);
+		sprintf(&buffer[len], "C=");
+		len = strlen(buffer);
+		dtostrf(wtemp, 1, 1, &buffer[len]);
+	}
 #endif // WATER_TEMPERATURE_SENSOR

     // Created message
@@ -359,7 +364,8 @@
     }
     if (i >= NUM_RETRIES) {
       Serial.println(F("HTTP POST Failed"));
-    } else {
+    }
+    else {
       Serial.println(F("HTTP POST Sent!"));
     }
 #endif // SEND_HTTP_POST
@@ -477,7 +483,8 @@
 }

 // Get the values for each sensor
-void calc_sensors() {
+void calc_sensors()
+{
   Wire.begin();
   delay(100);
   float val, minVal,maxVal;
@@ -611,11 +618,11 @@
   for(i = 0; i < 3; i++)
   {
     wtemp = get_water_temperature();
-    if(wtemp == -100)
+    if(water_offline())
     {
       Serial.println("Error with water temperature reading, trying again");
       wtemp = get_water_temperature();
-      if (wtemp == -100)
+      if (water_offline())
       {
         Serial.println("Couldn't get water temperature");
         wtemp = 0;
@@ -624,28 +631,30 @@
     }
     val += wtemp;
   }
+
   if (j == 0)
   {
-    wtemp = -100;
-  } else {
+    wtemp = -100;
+  }
+  else
+  {
     wtemp = val/j;
   }
 #endif // WATER_TEMPERATURE_SENSOR
+}
+
+int water_offline()
+{
+	waterTemperature.reset_search();
+    return (!waterTemperature.search(addr));
 }

 // Get the water temperature for a DS18B20 sensor
 float get_water_temperature() {
-  static byte addr[8];
   static byte data[12];
   static int16_t raw;
   static byte i;
   static byte cfg;
-
-  waterTemperature.reset_search();
-  if (!waterTemperature.search(addr)) {
-    // No sensors found.
-    return -100;
-  }

   waterTemperature.reset();
   waterTemperature.select(addr);


Some code changes

Some code changes

Cleaned up codes, removed redundant functions and variables, removed security features, removed all print and write statements, now the controller is dumb and will only do what the computer tells it to do, and it will not respond back once it did what the computer told it to do. This is great for simpler stuff that don't complex features.

--- ps2_controller_v2/ps2_controller_v2.ino
+++ ps2_controller_v2/ps2_controller_v2.ino
@@ -15,7 +15,6 @@
 #define latchPin 2 // HCF4094/74HC595 Latch/Strobe Input
 #define dataPin 3 // HCF4094/74HC595 Data Input
 #define clockPin 4 // HCF4094/74HC595 Clock Input
-#define outputEnable 5 // HCF4094/74HC595 Output Enable Input, the HCF4094 allows output when the output enable input level is HIGH, the 74HC595 allows output when the output enable input level is LOW

 #define buttonSelect 0 // Left 10
 #define buttonL3 1 // Right 9
@@ -59,7 +58,7 @@
 // Right 11 = Right Motor In = A1
 // Right 12 = Analog LED = A2
 bool defaultStatus[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
-bool inputStatus[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
+bool inputStatus[] =   {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
 unsigned int commandArray[] = {buttonSelect, buttonL3, buttonR3, buttonStart, buttonDUp, buttonDRight, buttonDDown, buttonDLeft, buttonL2, buttonR2, buttonL1, buttonR1, buttonTriangle, buttonCircle, buttonCross, buttonSquare, axisRxHalf, axisRxFull, axisRyHalf, axisRyFull, axisLxHalf, axisLxFull, axisLyHalf, axisLyHalf, buttonAnalog, 25, 26, 27, 28, 29, 30, 31};
 unsigned int motorArray[] = {leftMotor, rightMotor, analogLed};

@@ -70,18 +69,9 @@
 unsigned long previousInputDelay = 0;
 unsigned long currentMillis = 0;

-unsigned long sendInputOnce = 0; // 0 = Send only once, 1 = Send for every iteration of Loop
-
-boolean sentInputOnce = false;
-boolean sendInputOnlyOnce = false;
-
 unsigned long baudRate = 2000000;

 byte serial_rx_buffer[12];
-byte serial_rx_buffer_inverted[12];
-byte serial_rx_buffer_controller[12];
-byte serial_rx_buffer_inverted_controller[12];
-unsigned long serial_rx_buffer_counter = 0;
 unsigned long controller = 0;

 void setup()
@@ -95,9 +85,6 @@
   pinMode(latchPin, OUTPUT);
   pinMode(dataPin, OUTPUT);
   pinMode(clockPin, OUTPUT);
-  pinMode(outputEnable, OUTPUT);
-
-  digitalWrite(outputEnable, HIGH);

   digitalWrite(latchPin, LOW);
   for (int i = 31; i >= 0; i--)
@@ -108,80 +95,30 @@
   }
   digitalWrite(latchPin, HIGH);

-  delay(2500);
-
-  //  Send these on startup so the computer knows the Arduino is ready!
-  serial_rx_buffer_controller[1] = 0x00;
-  serial_rx_buffer_controller[2] = 0x00;
-  serial_rx_buffer_controller[3] = 0x7F;
-  serial_rx_buffer_controller[4] = 0x7F;
-  serial_rx_buffer_controller[5] = 0x7F;
-  serial_rx_buffer_controller[6] = 0x7F;
-  serial_rx_buffer_controller[7] = 0x00;
-  serial_rx_buffer_controller[8] = 0x00;
-  //serial_rx_buffer_controller[9] = 0x00;
-  //serial_rx_buffer_controller[10] = 0x00;
-
-  for (serial_rx_buffer_counter = 0; serial_rx_buffer_counter < sizeof(serial_rx_buffer_controller); serial_rx_buffer_counter++) {
-    Serial.write(serial_rx_buffer_controller[serial_rx_buffer_counter]); //  This line writes the serial data back to the computer as a way to check if the Arduino isn't interpreting wrong values
-    //  Invert 0 to 255 and vice versa to make it easier to determine which commands to enable or not, and their values
-    serial_rx_buffer_inverted_controller[serial_rx_buffer_counter] = (serial_rx_buffer_controller[serial_rx_buffer_counter]);
-    //Serial.println("TEST 2");
-  }
-
-  //  Reset delay to 0
-  serial_rx_buffer_controller[1] = 0x00;
-  serial_rx_buffer_controller[2] = 0x00;
-  serial_rx_buffer_controller[3] = 0x7F;
-  serial_rx_buffer_controller[4] = 0x7F;
-  serial_rx_buffer_controller[5] = 0x7F;
-  serial_rx_buffer_controller[6] = 0x7F;
-  serial_rx_buffer_controller[7] = 0x00;
-  serial_rx_buffer_controller[8] = 0x00;
-  serial_rx_buffer_controller[9] = 0x00;
-  serial_rx_buffer_controller[10] = 0x00;
-
-  for (serial_rx_buffer_counter = 0; serial_rx_buffer_counter < sizeof(serial_rx_buffer_controller); serial_rx_buffer_counter++) {
-    Serial.write(serial_rx_buffer_controller[serial_rx_buffer_counter]); //  This line writes the serial data back to the computer as a way to check if the Arduino isn't interpreting wrong values
-    //  Invert 0 to 255 and vice versa to make it easier to determine which commands to enable or not, and their values
-    serial_rx_buffer_inverted_controller[serial_rx_buffer_counter] = (serial_rx_buffer_controller[serial_rx_buffer_counter]);
-    //Serial.println("TEST 3");
-  }
+  //  Reset everything
+  serial_rx_buffer[1] = 0x00;
+  serial_rx_buffer[2] = 0x00;
+  serial_rx_buffer[3] = 0x7F;
+  serial_rx_buffer[4] = 0x7F;
+  serial_rx_buffer[5] = 0x7F;
+  serial_rx_buffer[6] = 0x7F;
+  serial_rx_buffer[7] = 0x00;
+  serial_rx_buffer[8] = 0x00;
+  serial_rx_buffer[9] = 0x00;
+  serial_rx_buffer[10] = 0x00;
+
 }
 void loop()
 {
   currentMillis = millis();
   if (Serial.available() > 0) {
-
     controller = Serial.readBytes(serial_rx_buffer, sizeof(serial_rx_buffer)) && 0xFF;

     //  Set Start Byte (Preamble Byte) and End Byte (Postamble Byte)
     //  1 == 0x01
-    if ((serial_rx_buffer[0] == 0x01) && (serial_rx_buffer[11] == 0x01)) {
-
-      //  Count bytes and parse ASCII values to their respective commands, such as buttons and axis
-      for (serial_rx_buffer_counter = 0; serial_rx_buffer_counter < sizeof(serial_rx_buffer); serial_rx_buffer_counter++) {
-        Serial.write(serial_rx_buffer[serial_rx_buffer_counter]); //  This line writes the serial data back to the computer as a way to check if the Arduino isn't interpreting wrong values
-        //  Invert 0 to 255 and vice versa to make it easier to determine which commands to enable or not, and their values
-        //Serial.println("TEST 0");
-        serial_rx_buffer_inverted[serial_rx_buffer_counter] = ( serial_rx_buffer[serial_rx_buffer_counter]);
-        serial_rx_buffer_controller[serial_rx_buffer_counter] = serial_rx_buffer[serial_rx_buffer_counter];
-        serial_rx_buffer_inverted_controller[serial_rx_buffer_counter] = ( serial_rx_buffer_controller[serial_rx_buffer_counter]);
-      }
-
-      if (sendInputOnce == 0)
-      {
-        sendInputOnlyOnce = true;
-        sentInputOnce = false;
-      }
-      if (sendInputOnce != 0)
-      {
-        sendInputOnlyOnce = false;
-        sentInputOnce = true;
-      }
-
-      //Serial.print('\n');
-      Serial.flush();
+    if ((serial_rx_buffer[0] == 0x01) && (serial_rx_buffer[11] == 0x01))
+    {
+
       // Make the button presses actually work
       isInputting = true;
       previousInputDelay = currentMillis;
@@ -198,80 +135,49 @@
   }
   if (isInputting == true)
   {
-    if (sendInputOnlyOnce == true)
-    {
-      if (sentInputOnce == false)
-      {
-        for (serial_rx_buffer_counter = 0; serial_rx_buffer_counter < sizeof(serial_rx_buffer_controller); serial_rx_buffer_counter++) {
-          Serial.write(serial_rx_buffer_controller[serial_rx_buffer_counter]); //  This line writes the serial data back to the computer as a way to check if the Arduino isn't interpreting wrong values
-          //  Invert 0 to 255 and vice versa to make it easier to determine which commands to enable or not, and their values
-          serial_rx_buffer_inverted_controller[serial_rx_buffer_counter] = (serial_rx_buffer_controller[serial_rx_buffer_counter]);
-          sentInputOnce = true;
-          //Serial.println("TEST 1A");
-        }
-        //Serial.println("TEST 1C");
-      }
-      //Serial.println("TEST 1D");
-    }
-
-    if (sendInputOnlyOnce == false)
-    {
-      if (sentInputOnce == true)
-      {
-        for (serial_rx_buffer_counter = 0; serial_rx_buffer_counter < sizeof(serial_rx_buffer_controller); serial_rx_buffer_counter++) {
-          Serial.write(serial_rx_buffer_controller[serial_rx_buffer_counter]); //  This line writes the serial data back to the computer as a way to check if the Arduino isn't interpreting wrong values
-          //  Invert 0 to 255 and vice versa to make it easier to determine which commands to enable or not, and their values
-          serial_rx_buffer_inverted_controller[serial_rx_buffer_counter] = (serial_rx_buffer_controller[serial_rx_buffer_counter]);
-          //sentInputOnce = true;
-          //Serial.println("TEST 1B");
-        }
-      }
-    }
-    //Serial.print('\n');
-    Serial.flush();
     //  Press Button

     //  First 8 buttons, Buffer Array Element 1
     //  SELECT, L3, R3, START, DUP, DRIGHT, DDOWN, DLEFT
-    inputStatus[0] = (serial_rx_buffer_inverted_controller[1] & B00000001);
-    inputStatus[1] = (serial_rx_buffer_inverted_controller[1] & B00000010);
-    inputStatus[2] = (serial_rx_buffer_inverted_controller[1] & B00000100);
-    inputStatus[3] = (serial_rx_buffer_inverted_controller[1] & B00001000);
-    inputStatus[4] = (serial_rx_buffer_inverted_controller[1] & B00010000);
-    inputStatus[5] = (serial_rx_buffer_inverted_controller[1] & B00100000);
-    inputStatus[6] = (serial_rx_buffer_inverted_controller[1] & B01000000);
-    inputStatus[7] = (serial_rx_buffer_inverted_controller[1] & B10000000);
+    inputStatus[0] = (serial_rx_buffer[1] & B00000001);
+    inputStatus[1] = (serial_rx_buffer[1] & B00000010);
+    inputStatus[2] = (serial_rx_buffer[1] & B00000100);
+    inputStatus[3] = (serial_rx_buffer[1] & B00001000);
+    inputStatus[4] = (serial_rx_buffer[1] & B00010000);
+    inputStatus[5] = (serial_rx_buffer[1] & B00100000);
+    inputStatus[6] = (serial_rx_buffer[1] & B01000000);
+    inputStatus[7] = (serial_rx_buffer[1] & B10000000);

     //  Second 8 buttons, Buffer Array Element 2
     //  L2, R2, L1, R1, Triangle/Delta, Circle/O, Cross/X, Square
-    inputStatus[8] = (serial_rx_buffer_inverted_controller[2] & B00000001);
-    inputStatus[9] = (serial_rx_buffer_inverted_controller[2] & B00000010);
-    inputStatus[10] = (serial_rx_buffer_inverted_controller[2] & B00000100);
-    inputStatus[11] = (serial_rx_buffer_inverted_controller[2] & B00001000);
-    inputStatus[12] = (serial_rx_buffer_inverted_controller[2] & B00010000);
-    inputStatus[13] = (serial_rx_buffer_inverted_controller[2] & B00100000);
-    inputStatus[14] = (serial_rx_buffer_inverted_controller[2] & B01000000);
-    inputStatus[15] = (serial_rx_buffer_inverted_controller[2] & B10000000);
+    inputStatus[8] = (serial_rx_buffer[2] & B00000001);
+    inputStatus[9] = (serial_rx_buffer[2] & B00000010);
+    inputStatus[10] = (serial_rx_buffer[2] & B00000100);
+    inputStatus[11] = (serial_rx_buffer[2] & B00001000);
+    inputStatus[12] = (serial_rx_buffer[2] & B00010000);
+    inputStatus[13] = (serial_rx_buffer[2] & B00100000);
+    inputStatus[14] = (serial_rx_buffer[2] & B01000000);
+    inputStatus[15] = (serial_rx_buffer[2] & B10000000);

     //  4 Axis, Buffer Array Elements 3, 4, 5, 6
     //  RX, RY, LX, LY

     //RX STICK
-    if (serial_rx_buffer_inverted_controller[3] > 127)
+    if (serial_rx_buffer[3] > 127)
     {
       // Push RX Stick to Right
       inputStatus[16] = HIGH;
       inputStatus[17] = HIGH;
     }

-    if (serial_rx_buffer_inverted_controller[3] < 127)
+    if (serial_rx_buffer[3] < 127)
     {
       // Push RX Stick to Left
       inputStatus[16] = LOW;
       inputStatus[17] = LOW;
     }

-    if (serial_rx_buffer_inverted_controller[3] == 127)
+    if (serial_rx_buffer[3] == 127)
     {
       // Keep RX Stick Centered
       inputStatus[16] = HIGH;
@@ -279,21 +185,21 @@
     }

     //RY STICK
-    if (serial_rx_buffer_inverted_controller[4] > 127)
+    if (serial_rx_buffer[4] > 127)
     {
       // Push RY Stick to Up
       inputStatus[18] = HIGH;
       inputStatus[19] = HIGH;
     }

-    if (serial_rx_buffer_inverted_controller[4] < 127)
+    if (serial_rx_buffer[4] < 127)
     {
       // Push RY Stick to Down
       inputStatus[18] = LOW;
       inputStatus[19] = LOW;
     }

-    if (serial_rx_buffer_inverted_controller[4] == 127)
+    if (serial_rx_buffer[4] == 127)
     {
       // Keep RY Stick Centered
       inputStatus[18] = HIGH;
@@ -301,21 +207,21 @@
     }

     //LX STICK
-    if (serial_rx_buffer_inverted_controller[5] > 127)
+    if (serial_rx_buffer[5] > 127)
     {
       // Push LX Stick to Right
       inputStatus[20] = HIGH;
       inputStatus[21] = HIGH;
     }

-    if (serial_rx_buffer_inverted_controller[5] < 127)
+    if (serial_rx_buffer[5] < 127)
     {
       // Push LX Stick to Left
       inputStatus[20] = LOW;
       inputStatus[21] = LOW;
     }

-    if (serial_rx_buffer_inverted_controller[5] == 127)
+    if (serial_rx_buffer[5] == 127)
     {
       // Keep LX Stick Centered
       inputStatus[20] = HIGH;
@@ -323,21 +229,21 @@
     }

     //LY STICK
-    if (serial_rx_buffer_inverted_controller[6] > 127)
+    if (serial_rx_buffer[6] > 127)
     {
       // Push LY Stick to Down
       inputStatus[22] = HIGH;
       inputStatus[23] = HIGH;
     }

-    if (serial_rx_buffer_inverted_controller[6] < 127)
+    if (serial_rx_buffer[6] < 127)
     {
       // Push LY Stick to Up
       inputStatus[22] = LOW;
       inputStatus[23] = LOW;
     }

-    if (serial_rx_buffer_inverted_controller[6] == 127)
+    if (serial_rx_buffer[6] == 127)
     {
       // Keep LY Stick Centered
       inputStatus[22] = HIGH;
@@ -346,7 +252,7 @@

     //  Analog button (Used to change between Digital and Analog modes in older games), Buffer Array Element 7
     //  All other bits in this Buffer Array Element are unused
-    inputStatus[24] = (serial_rx_buffer_inverted_controller[7] & B00000001);
+    inputStatus[24] = (serial_rx_buffer[7] & B00000001);

     digitalWrite(latchPin, LOW);
     for (int i = 31; i >= 0; i--)
@@ -370,85 +276,59 @@
         if (currentMillis - previousInputDelay >= inputDelay) {
           //  Now we need to stop the Soft Delay

-          //  Depress Buttons (Only if Buffer Array Element 9 and 10 != 0x00)
-          serial_rx_buffer_controller[1] = 0x00;
-          serial_rx_buffer_controller[2] = 0x00;
-          serial_rx_buffer_controller[3] = 0x7F;
-          serial_rx_buffer_controller[4] = 0x7F;
-          serial_rx_buffer_controller[5] = 0x7F;
-          serial_rx_buffer_controller[6] = 0x7F;
-          serial_rx_buffer_controller[7] = 0x00;
-          serial_rx_buffer_controller[8] = 0x00;
-          //serial_rx_buffer_controller[9] = 0x00;
-          //serial_rx_buffer_controller[10] = 0x00;
-
-          for (serial_rx_buffer_counter = 0; serial_rx_buffer_counter < sizeof(serial_rx_buffer_controller); serial_rx_buffer_counter++) {
-            Serial.write(serial_rx_buffer_controller[serial_rx_buffer_counter]); //  This line writes the serial data back to the computer as a way to check if the Arduino isn't interpreting wrong values
-            //  Invert 0 to 255 and vice versa to make it easier to determine which commands to enable or not, and their values
-            serial_rx_buffer_inverted_controller[serial_rx_buffer_counter] = (serial_rx_buffer_controller[serial_rx_buffer_counter]);
-            //Serial.println("TEST 2");
-          }
-
-          //  Reset delay to 0
-          serial_rx_buffer_controller[1] = 0x00;
-          serial_rx_buffer_controller[2] = 0x00;
-          serial_rx_buffer_controller[3] = 0x7F;
-          serial_rx_buffer_controller[4] = 0x7F;
-          serial_rx_buffer_controller[5] = 0x7F;
-          serial_rx_buffer_controller[6] = 0x7F;
-          serial_rx_buffer_controller[7] = 0x00;
-          serial_rx_buffer_controller[8] = 0x00;
-          serial_rx_buffer_controller[9] = 0x00;
-          serial_rx_buffer_controller[10] = 0x00;
-
-          for (serial_rx_buffer_counter = 0; serial_rx_buffer_counter < sizeof(serial_rx_buffer_controller); serial_rx_buffer_counter++) {
-            Serial.write(serial_rx_buffer_controller[serial_rx_buffer_counter]); //  This line writes the serial data back to the computer as a way to check if the Arduino isn't interpreting wrong values
-            //  Invert 0 to 255 and vice versa to make it easier to determine which commands to enable or not, and their values
-            serial_rx_buffer_inverted_controller[serial_rx_buffer_counter] = (serial_rx_buffer_controller[serial_rx_buffer_counter]);
-            //Serial.println("TEST 3");
-          }
+          //  Reset everything
+          serial_rx_buffer[1] = 0x00;
+          serial_rx_buffer[2] = 0x00;
+          serial_rx_buffer[3] = 0x7F;
+          serial_rx_buffer[4] = 0x7F;
+          serial_rx_buffer[5] = 0x7F;
+          serial_rx_buffer[6] = 0x7F;
+          serial_rx_buffer[7] = 0x00;
+          serial_rx_buffer[8] = 0x00;
+          serial_rx_buffer[9] = 0x00;
+          serial_rx_buffer[10] = 0x00;

           //  First 8 buttons, Buffer Array Element 1
           //  SELECT, L3, R3, START, DUP, DRIGHT, DDOWN, DLEFT
-          inputStatus[0] = (serial_rx_buffer_inverted_controller[1] & B00000001);
-          inputStatus[1] = (serial_rx_buffer_inverted_controller[1] & B00000010);
-          inputStatus[2] = (serial_rx_buffer_inverted_controller[1] & B00000100);
-          inputStatus[3] = (serial_rx_buffer_inverted_controller[1] & B00001000);
-          inputStatus[4] = (serial_rx_buffer_inverted_controller[1] & B00010000);
-          inputStatus[5] = (serial_rx_buffer_inverted_controller[1] & B00100000);
-          inputStatus[6] = (serial_rx_buffer_inverted_controller[1] & B01000000);
-          inputStatus[7] = (serial_rx_buffer_inverted_controller[1] & B10000000);
+          inputStatus[0] = (serial_rx_buffer[1] & B00000001);
+          inputStatus[1] = (serial_rx_buffer[1] & B00000010);
+          inputStatus[2] = (serial_rx_buffer[1] & B00000100);
+          inputStatus[3] = (serial_rx_buffer[1] & B00001000);
+          inputStatus[4] = (serial_rx_buffer[1] & B00010000);
+          inputStatus[5] = (serial_rx_buffer[1] & B00100000);
+          inputStatus[6] = (serial_rx_buffer[1] & B01000000);
+          inputStatus[7] = (serial_rx_buffer[1] & B10000000);

           //  Second 8 buttons, Buffer Array Element 2
           //  L2, R2, L1, R1, Triangle/Delta, Circle/O, Cross/X, Square
-          inputStatus[8] = (serial_rx_buffer_inverted_controller[2] & B00000001);
-          inputStatus[9] = (serial_rx_buffer_inverted_controller[2] & B00000010);
-          inputStatus[10] = (serial_rx_buffer_inverted_controller[2] & B00000100);
-          inputStatus[11] = (serial_rx_buffer_inverted_controller[2] & B00001000);
-          inputStatus[12] = (serial_rx_buffer_inverted_controller[2] & B00010000);
-          inputStatus[13] = (serial_rx_buffer_inverted_controller[2] & B00100000);
-          inputStatus[14] = (serial_rx_buffer_inverted_controller[2] & B01000000);
-          inputStatus[15] = (serial_rx_buffer_inverted_controller[2] & B10000000);
+          inputStatus[8] = (serial_rx_buffer[2] & B00000001);
+          inputStatus[9] = (serial_rx_buffer[2] & B00000010);
+          inputStatus[10] = (serial_rx_buffer[2] & B00000100);
+          inputStatus[11] = (serial_rx_buffer[2] & B00001000);
+          inputStatus[12] = (serial_rx_buffer[2] & B00010000);
+          inputStatus[13] = (serial_rx_buffer[2] & B00100000);
+          inputStatus[14] = (serial_rx_buffer[2] & B01000000);
+          inputStatus[15] = (serial_rx_buffer[2] & B10000000);

           //  4 Axis, Buffer Array Elements 3, 4, 5, 6
           //  RX, RY, LX, LY

           //RX STICK
-          if (serial_rx_buffer_inverted_controller[3] > 127)
+          if (serial_rx_buffer[3] > 127)
           {
             // Push RX Stick to Right
             inputStatus[16] = HIGH;
             inputStatus[17] = HIGH;
           }

-          if (serial_rx_buffer_inverted_controller[3] < 127)
+          if (serial_rx_buffer[3] < 127)
           {
             // Push RX Stick to Left
             inputStatus[16] = LOW;
             inputStatus[17] = LOW;
           }

-          if (serial_rx_buffer_inverted_controller[3] == 127)
+          if (serial_rx_buffer[3] == 127)
           {
             // Keep RX Stick Centered
             inputStatus[16] = HIGH;
@@ -456,21 +336,21 @@
           }

           //RY STICK
-          if (serial_rx_buffer_inverted_controller[4] > 127)
+          if (serial_rx_buffer[4] > 127)
           {
             // Push RY Stick to Up
             inputStatus[18] = HIGH;
             inputStatus[19] = HIGH;
           }

-          if (serial_rx_buffer_inverted_controller[4] < 127)
+          if (serial_rx_buffer[4] < 127)
           {
             // Push RY Stick to Down
             inputStatus[18] = LOW;
             inputStatus[19] = LOW;
           }

-          if (serial_rx_buffer_inverted_controller[4] == 127)
+          if (serial_rx_buffer[4] == 127)
           {
             // Keep RY Stick Centered
             inputStatus[18] = HIGH;
@@ -478,21 +358,21 @@
           }

           //LX STICK
-          if (serial_rx_buffer_inverted_controller[5] > 127)
+          if (serial_rx_buffer[5] > 127)
           {
             // Push LX Stick to Right
             inputStatus[20] = HIGH;
             inputStatus[21] = HIGH;
           }

-          if (serial_rx_buffer_inverted_controller[5] < 127)
+          if (serial_rx_buffer[5] < 127)
           {
             // Push LX Stick to Left
             inputStatus[20] = LOW;
             inputStatus[21] = LOW;
           }

-          if (serial_rx_buffer_inverted_controller[5] == 127)
+          if (serial_rx_buffer[5] == 127)
           {
             // Keep LX Stick Centered
             inputStatus[20] = HIGH;
@@ -500,21 +380,21 @@
           }

           //LY STICK
-          if (serial_rx_buffer_inverted_controller[6] > 127)
+          if (serial_rx_buffer[6] > 127)
           {
             // Push LY Stick to Down
             inputStatus[22] = HIGH;
             inputStatus[23] = HIGH;
           }

-          if (serial_rx_buffer_inverted_controller[6] < 127)
+          if (serial_rx_buffer[6] < 127)
           {
             // Push LY Stick to Up
             inputStatus[22] = LOW;
             inputStatus[23] = LOW;
           }

-          if (serial_rx_buffer_inverted_controller[6] == 127)
+          if (serial_rx_buffer[6] == 127)
           {
             // Keep LY Stick Centered
             inputStatus[22] = HIGH;
@@ -523,7 +403,7 @@

           //  Analog button (Used to change between Digital and Analog modes in older games), Buffer Array Element 7
           //  All other bits in this Buffer Array Element are unused
-          inputStatus[24] = (serial_rx_buffer_inverted_controller[7] & B00000001);
+          inputStatus[24] = (serial_rx_buffer[7] & B00000001);

           digitalWrite(latchPin, LOW);
           for (int i = 31; i >= 0; i--)
@@ -535,12 +415,8 @@
           digitalWrite(latchPin, HIGH);

           //  Buffer Array Element 8 is unused in this code, but is existing in case changes are needed
-          //Serial.print('\n');
-          Serial.flush();
-
           isInputtingDelayed = false;
           isInputting = false;
-          //sentInputOnce = true;
           previousInputDelay += inputDelay;
           inputDelay = 0;
         }


Preparing PS2 controller to do frame perfect input

--- ps2_controller_v3/ps2_controller_v3.ino
+++ ps2_controller_v3/ps2_controller_v3.ino
@@ -55,6 +55,8 @@
 #define leftMotor A0   // Left 9
 #define rightMotor A1  // Right 11
 #define analogLed A2   // Right 12
+
+#define attentionPin A3

 // Left 9 = Left Motor In = A0
 // Right 11 = Right Motor In = A1
@@ -107,6 +109,8 @@
   pinMode(leftMotor, INPUT);
   pinMode(rightMotor, INPUT);
   pinMode(analogLed, INPUT);
+
+  pinMode(attentionPin, INPUT);

   pinMode(latchPin, OUTPUT);
   pinMode(dataPin, OUTPUT);


src: switch pin for garage door control

src: switch pin for garage door control

--- DoorAndTempSensor/DoorAndTempSensor.ino
+++ DoorAndTempSensor/DoorAndTempSensor.ino
@@ -33,7 +33,7 @@
 DallasTemperature tempSensors(&ds);

 #define BUTTON_PUSH_TIME 2000
-#define GARAGE_REMOTE_BUTTON D6
+#define GARAGE_REMOTE_BUTTON D7
 #define GARAGE_DOOR_TOPIC "house/garage"

 WiFiClient wclient;


Update ConsoleSerialTemp.ino

Update ConsoleSerialTemp.ino

--- examples/ConsoleSerialTemp/ConsoleSerialTemp.ino
+++ examples/ConsoleSerialTemp/ConsoleSerialTemp.ino
@@ -4,7 +4,8 @@

  This exmaple writes the current temperarure
  in celsius and fahrenheit to the console.
- */
+
+*/

 #include <TMP36.h>
 #include <Bridge.h>


Implement point widgets.

Implement point widgets.

--- examples/vseg/vseg.ino
+++ examples/vseg/vseg.ino
@@ -71,7 +71,58 @@

 void loop_scroll_callback(int delta);
 void repeat_scroll_callback(int delta);
-// TODO: declare points callbacks
+void points_scroll_callback(uint8_t selected, int delta);
+void points_invert_callback(uint8_t p);
+
+class DU_VSEG_PointWidget : public DUINO_Widget
+{
+public:
+  DU_VSEG_PointWidget(uint8_t p, uint8_t parameter)
+    : p_(p)
+    , x_(parameter ? 89 : 64)
+    , w_(parameter ? 29 : 11)
+    , inverted_(false) { }
+
+  virtual void invert(bool update_display = true)
+  {
+    // invert underline
+    Display.draw_hline(x_, 8, w_, DUINO_SH1106::Inverse);
+
+    // blank area
+    Display.fill_rect(45, 0, 73, 7, DUINO_SH1106::Black);
+
+    if (!inverted_)
+    {
+      // display point number
+      Display.draw_char(45, 0, '0' + p_, DUINO_SH1106::White);
+
+      if(invert_callback_)
+      {
+        invert_callback_(p_);
+      }
+    }
+
+    inverted_ = !inverted_;
+
+    if (update_display)
+    {
+      Display.display(45, 117, 0, 1);
+    }
+  }
+
+  virtual bool inverted() const { return inverted_; }
+
+  void attach_invert_callback(void (*callback)())
+  {
+    invert_callback_ = callback;
+  }
+
+protected:
+  void (*invert_callback_)(uint8_t);
+
+  const uint8_t p_, x_, w_;
+  bool inverted_;
+};

 class DU_VSEG_Function : public DUINO_Function
 {
@@ -89,13 +140,17 @@
     widget_loop_->attach_scroll_callback(loop_scroll_callback);
     widget_repeat_ = new DUINO_DisplayWidget(115, 11, 13, 9, DUINO_Widget::Full);
     widget_repeat_->attach_scroll_callback(repeat_scroll_callback);
-    // TODO: widgets points
-    // TODO: attach points callbacks
     container_loop_repeat_ = new DUINO_WidgetContainer<2>(DUINO_Widget::Click);
     container_loop_repeat_->attach_child(widget_loop_, 0);
     container_loop_repeat_->attach_child(widget_repeat_, 1);
     container_points_ = new DUINO_WidgetContainer<8>(DUINO_Widget::Click);
-    // TODO: attach widgets points
+    for(uint8_t i  = 0; i < 8; ++i)
+    {
+      widgets_points_[i] = new DU_VSEG_PointWidget((i + 1) / 2, i & 1);
+      widgets_points_[i]->attach_invert_callback(points_invert_callback);
+      container_points_->attach_child(widgets_points_[i], i);
+      container_points_->attach_scroll_callback_array(points_scroll_callback);
+    }
     container_outer_ = new DUINO_WidgetContainer<3>(DUINO_Widget::DoubleClick, 1);
     container_outer_->attach_child(widget_save_, 0);
     container_outer_->attach_child(container_loop_repeat_, 1);
@@ -114,6 +169,208 @@

     // draw save box
     Display.fill_rect(widget_save_->x() + 1, widget_save_->y() + 1, 5, 5, DUINO_SH1106::White);
+
+    // draw fixed elements
+    Display.draw_char(widget_repeat_->x() + 1, widget_repeat_->y() + 1, 'x', DUINO_SH1106::White);
+
+    // draw parameters
+    display_loop(false);
+    display_repeat(false);
+    display_envelope(false);
+
+    // initialize widgets
+    widget_setup(container_outer_);
+
+    // output full display
+    Display.display();
+
+    // attach gate interrupt
+    gt_attach_interrupt(GT3, gate_isr, CHANGE);
+  }
+
+  virtual void loop()
+  {
+    // TODO: envelope function
+
+    widget_loop();
+  }
+
+  void gate_callback()
+  {
+    gate_ = gt_read_debounce(GT3);
+    if (gate_)
+    {
+      retrigger_ = true;
+    }
+  }
+
+  void widget_loop_scroll_callback(int delta)
+  {
+    const int8_t loop_last = widget_save_->params.vals.loop;
+
+    widget_save_->params.vals.loop += delta;
+    sanitize_loop();
+    if (widget_save_->params.vals.loop != loop_last)
+    {
+      widget_save_->mark_changed();
+      widget_save_->display();
+      display_loop();
+    }
+  }
+
+  void widget_repeat_scroll_callback(int delta)
+  {
+    const int8_t repeat_last = widget_save_->params.vals.repeat;
+
+    widget_save_->params.vals.repeat += delta;
+    sanitize_repeat();
+    if (widget_save_->params.vals.repeat != repeat_last)
+    {
+      widget_save_->mark_changed();
+      widget_save_->display();
+      display_repeat();
+    }
+  }
+
+  void widgets_points_scroll_callback(uint8_t selected, int delta)
+  {
+    const uint8_t p = (selected + 1) / 2;
+    const uint8_t parameter = selected & 1;
+
+    if (parameter)
+    {
+      // adjust rate
+      const int8_t rate_last = widget_save_->params.vals.rate[p];
+      widget_save_->params.vals.rate[p - 1] += delta;
+      sanitize_rate();
+      if(widget_save_->params.vals.rate[p - 1] != rate_last)
+      {
+        widget_save_->mark_changed();
+        widget_save_->display();
+        display_plr(p);
+        display_envelope();
+      }
+    }
+    else
+    {
+      // adjust level
+      const int8_t level_last = widget_save_->params.vals.level[p];
+      widget_save_->params.vals.level[p] += delta;
+      sanitize_level();
+      if(widget_save_->params.vals.level[p] != level_last)
+      {
+        widget_save_->mark_changed();
+        widget_save_->display();
+        display_plr(p);
+        display_envelope();
+      }
+    }
+  }
+
+  void widgets_points_invert_callback(uint8_t p)
+  {
+    display_plr(p);
+  }
+
+private:
+  void sanitize_level()
+  {
+    for (uint8_t p = 0; p < 4; ++p)
+    {
+      if (widget_save_->params.vals.level[p] < 0)
+      {
+        widget_save_->params.vals.level[p] = 0;
+      }
+      else if (widget_save_->params.vals.level[p] > 99)
+      {
+        widget_save_->params.vals.level[p] = 99;
+      }
+    }
+  }
+
+  void sanitize_rate()
+  {
+    for (uint8_t p = 0; p < 4; ++p)
+    {
+      if (widget_save_->params.vals.rate[p] < 0)
+      {
+        widget_save_->params.vals.rate[p] = 0;
+      }
+      else if (widget_save_->params.vals.rate[p] > 96)
+      {
+        widget_save_->params.vals.rate[p] = 96;
+      }
+    }
+  }
+
+  void sanitize_loop()
+  {
+    if (widget_save_->params.vals.loop < -1)
+    {
+      widget_save_->params.vals.loop = -1;
+    }
+    else if (widget_save_->params.vals.loop > 5)
+    {
+      widget_save_->params.vals.loop = 5;
+    }
+  }
+
+  void sanitize_repeat()
+  {
+    if (widget_save_->params.vals.repeat < 0)
+    {
+      widget_save_->params.vals.repeat = 0;
+    }
+    else if (widget_save_->params.vals.repeat > 7)
+    {
+      widget_save_->params.vals.repeat = 7;
+    }
+  }
+
+  float level_to_cv(uint8_t p)
+  {
+    if (p == 4)
+    {
+      return 0.0;
+    }
+
+    return (float)widget_save_->params.vals.level[p] / 9.9;
+  }
+
+  uint16_t rate_to_ms(uint8_t p)
+  {
+    if (p == 0)
+    {
+      return 0;
+    }
+
+    return (uint16_t)pgm_read_word(&rate_lut[widget_save_->params.vals.rate[p - 1]]);
+  }
+
+  uint8_t level_to_y(uint8_t p)
+  {
+    if (p == 4)
+    {
+      return 57;
+    }
+
+    return 24 + (99 - widget_save_->params.vals.level[p]) / 3;
+  }
+
+  uint8_t rate_to_x(uint8_t p)
+  {
+    if (p == 0)
+    {
+      return 0;
+    }
+
+    return rate_to_x(p - 1) + 6 + (widget_save_->params.vals.rate[p - 1] / 4) + (p == 4 ? 2 : 0);
+  }
+
+  void display_plr(uint8_t p, bool update = true)
+  {
+    // blank area
+    Display.fill_rect(64, 0, 54, 7, DUINO_SH1106::Black);

     // draw fixed elements
     Display.draw_char(54, 0, 'L', DUINO_SH1106::White);
@@ -122,196 +379,41 @@
     Display.draw_char(79, 0, 'R', DUINO_SH1106::White);
     Display.draw_pixel(86, 1, DUINO_SH1106::White);
     Display.draw_pixel(86, 5, DUINO_SH1106::White);
-    Display.draw_char(widget_repeat_->x() + 1, widget_repeat_->y() + 1, 'x', DUINO_SH1106::White);
-
-    // draw parameters
-    display_loop(false);
-    display_repeat(false);
-    display_envelope(false);
-
-    // initialize widgets
-    widget_setup(container_outer_);
-
-    // output full display
-    Display.display();
-
-    // attach gate interrupt
-    gt_attach_interrupt(GT3, gate_isr, CHANGE);
-  }
-
-  virtual void loop()
-  {
-    // TODO: envelope function
-
-    widget_loop();
-  }
-
-  void gate_callback()
-  {
-    gate_ = gt_read_debounce(GT3);
-    if (gate_)
-    {
-      retrigger_ = true;
-    }
-  }
-
-  void widget_loop_scroll_callback(int delta)
-  {
-    const int8_t loop_last = widget_save_->params.vals.loop;
-
-    widget_save_->params.vals.loop += delta;
-    sanitize_loop();
-    if (widget_save_->params.vals.loop != loop_last)
-    {
-      widget_save_->mark_changed();
-      widget_save_->display();
-    }
-
-    display_loop();
-  }
-
-  void widget_repeat_scroll_callback(int delta)
-  {
-    const int8_t repeat_last = widget_save_->params.vals.repeat;
-
-    widget_save_->params.vals.repeat += delta;
-    sanitize_repeat();
-    if (widget_save_->params.vals.repeat != repeat_last)
-    {
-      widget_save_->mark_changed();
-      widget_save_->display();
-    }
-
-    display_repeat();
-  }
-
-  // TODO: points callbacks
-
-private:
-  void sanitize_level()
-  {
-    for (uint8_t p = 0; p < 4; ++p)
-    {
-      if (widget_save_->params.vals.level[p] < 0)
-      {
-        widget_save_->params.vals.level[p] = 0;
-      }
-      else if (widget_save_->params.vals.level[p] > 99)
-      {
-        widget_save_->params.vals.level[p] = 99;
-      }
-    }
-  }
-
-  void sanitize_rate()
-  {
-    for (uint8_t p = 0; p < 4; ++p)
-    {
-      if (widget_save_->params.vals.rate[p] < 0)
-      {
-        widget_save_->params.vals.rate[p] = 0;
-      }
-      else if (widget_save_->params.vals.rate[p] > 96)
-      {
-        widget_save_->params.vals.rate[p] = 96;
-      }
-    }
-  }
-
-  void sanitize_loop()
-  {
-    if (widget_save_->params.vals.loop < -1)
-    {
-      widget_save_->params.vals.loop = -1;
-    }
-    else if (widget_save_->params.vals.loop > 5)
-    {
-      widget_save_->params.vals.loop = 5;
-    }
-  }
-
-  void sanitize_repeat()
-  {
-    if (widget_save_->params.vals.repeat < 0)
-    {
-      widget_save_->params.vals.repeat = 0;
-    }
-    else if (widget_save_->params.vals.repeat > 7)
-    {
-      widget_save_->params.vals.repeat = 7;
-    }
-  }
-
-  float level_to_cv(uint8_t p)
-  {
-    if (p == 4)
-    {
-      return 0.0;
-    }
-
-    return (float)widget_save_->params.vals.level[p] / 9.9;
-  }
-
-  uint16_t rate_to_ms(uint8_t p)
-  {
-    if (p == 0)
-    {
-      return 0;
-    }
-
-    return (uint16_t)pgm_read_word(&rate_lut[widget_save_->params.vals.rate[p - 1]]);
-  }
-
-  uint8_t level_to_y(uint8_t p)
-  {
-    if (p == 4)
-    {
-      return 57;
-    }
-
-    return 24 + (99 - widget_save_->params.vals.level[p]) / 3;
-  }
-
-  uint8_t rate_to_x(uint8_t p)
-  {
-    if (p == 0)
-    {
-      return 0;
-    }
-
-    return rate_to_x(p - 1) + 6 + (widget_save_->params.vals.rate[p - 1] / 4) + (p == 4 ? 2 : 0);
-  }
-
-  void display_plr(uint8_t p, bool update = true)
-  {
-    // blank areas
-    Display.fill_rect(45, 0, 5, 7, DUINO_SH1106::Black);
-    Display.fill_rect(64, 0, 11, 7, DUINO_SH1106::Black);
-    Display.fill_rect(89, 0, 29, 7, DUINO_SH1106::Black);
-
-    // display point number
-    Display.draw_char(45, 0, '0' + p, DUINO_SH1106::White);

     uint8_t i;

     // display level
     int8_t level = widget_save_->params.vals.level[p];
-    i = 0;
-    while (level)
-    {
-      Display.draw_char(70 - 6 * i, 0, '0' + level % 10, DUINO_SH1106::White);
-      level /= 10;
-      i++;
+    if (level)
+    {
+      i = 0;
+      while (level)
+      {
+        Display.draw_char(70 - 6 * i, 0, '0' + level % 10, DUINO_SH1106::White);
+        level /= 10;
+        i++;
+      }
+    }
+    else
+    {
+      Display.draw_char(70, 0, '0', DUINO_SH1106::White);
     }

     // display rate
     uint16_t rate = rate_to_ms(p);
-    i = 0;
-    while (rate)
-    {
-      Display.draw_char(113 - 6 * i, 0, '0' + rate % 10, DUINO_SH1106::White);
-      rate /= 10;
-      i++;
+    if (rate)
+    {
+      i = 0;
+      while (rate)
+      {
+        Display.draw_char(113 - 6 * i, 0, '0' + rate % 10, DUINO_SH1106::White);
+        rate /= 10;
+        i++;
+      }
+    }
+    else
+    {
+      Display.draw_char(113, 0, '0', DUINO_SH1106::White);
     }

     if (update)
@@ -410,7 +512,7 @@
   DUINO_SaveWidget<ParameterValues> * widget_save_;
   DUINO_DisplayWidget * widget_loop_;
   DUINO_DisplayWidget * widget_repeat_;
-  DUINO_Widget * widgets_points_[8];
+  DU_VSEG_PointWidget * widgets_points_[8];

   volatile bool gate_, retrigger_;
 };
@@ -421,7 +523,8 @@

 void loop_scroll_callback(int delta) { function->widget_loop_scroll_callback(delta); }
 void repeat_scroll_callback(int delta) { function->widget_repeat_scroll_callback(delta); }
-// TODO: define points callbacks
+void points_scroll_callback(uint8_t selected, int delta) { function->widgets_points_scroll_callback(selected, delta); }
+void points_invert_callback(uint8_t p) { function->widgets_points_invert_callback(p); }

 void setup()
 {