Initial FlyByWire commit

Initial FlyByWire commit

Commit the current draft of the FlyByWire code that Soren wrote.

error: pathspec 'FlyByWire/FlyByWire.ino' did not match any file(s) known to git

--- FlyByWire/FlyByWire.ino
+++ FlyByWire/FlyByWire.ino
@@ -0,0 +1,947 @@
+// Carpet CANTroller II  Source Code  - For Arduino Mega 2560 with Adafruit 2.8inch Captouch TFT shield.
+
+// Libraries to include.  Note all these have example code when installed into arduino ide
+#include <Servo.h>   // Makes PWM output to control motors (for our gas and steering)
+#include <FastPID.h>  // Fixed-point math based PID loop (for our brakes and maybe cruise control)
+#include <SPI.h>   // SPI serial bus needed to talk to the LCD and the SD card
+#include <Wire.h>      // Contains I2C serial bus, needed to talk to touchscreen chip
+#include <Adafruit_ILI9341.h>   // For interfacing with the TFT LCD controller chip
+#include <Adafruit_FT6206.h>   // For interfacing with the cap touchscreen controller chip
+#include <SdFat.h>        // SD card & FAT filesystem library
+#include <Adafruit_GFX.h>    // For drawing pictures & text on the screen
+//#include <Adafruit_ImageReader.h>  // We don't really need thihs but can use it to test that SD flash is working
+//#include "TouchScreen.h"  // Touchscreen library (I don't think we need it? Maybe for resistive TP we're not using)
+
+/*
+# Here are the different runmodes documented
+#
+# ** Shutdown Mode **
+# - Required: Ignition Off
+# - Priority: 1 (Highest)
+# This mode is active whenever the ignition is off.  In other words, whenever the
+# little red pushbutton switch by the joystick is unclicked.  This happens before the
+# ignition is pressed before driving, but it also may happen if the driver needs to
+# panic and E-stop due to loss of control or any other reason.  The ignition will get cut
+# independent of the controller, but we can help stop the car faster by applying the
+# brakes. Once car is stopped, we release all actuators and go to low power mode.
+# - Actions: 1. Release throttle. If car is moving AND BasicMode Off, apply brakes to stop car
+# - Actions: 2: Deactivate all actuators including steering
+#
+# ** Basic Mode **
+# - Required: BasicMode switch On & Ignition On
+# - Priority: 2
+# The gas and brake don't do anything in Basic Mode. Just the steering works, so use the pedals.
+# This mode is enabled with a toggle switch in the controller box.  When in Basic Mode, the only
+# other valid mode is Shutdown Mode. Shutdown Mode may override Basic Mode.
+# - Actions: Release and deactivate brake and gas actuators.  Steering PID keep active
+#
+# ** Stall Mode **
+# - Required: Engine stopped & BasicMode switch Off & Ignition On
+# - Priority: 3
+# This mode is active when the engine is not running.  If car is moving, then it presumably may
+# coast to a stop.  The actuators are all enabled and work normally.  Starting the engine will
+# bring you into Hold Mode.  Shutdown Mode and Basic Mode both override Stall Mode. Note: This
+# mode allows for driver to steer while being towed or pushed, or working on the car.
+# - Actions: Enable all actuators
+#
+# ** Hold Mode **
+# - Required: Engine running & JoyVert<=Center & BasicMode switch Off & Ignition On
+# - Priority: 4
+# This mode is entered from Stall Mode once engine is started, and also, whenever the car comes
+# to a stop while driving around in Fly Mode.  This mode releases the throttle and will
+# continuously increase the brakes until the car is stopped, if it finds the car is moving.
+# Pushing up on the joystick from Hold mode releases the brakes & begins Fly Mode.
+# Shutdown, Basic & Stall Modes override Hold Mode.
+# # Actions: Close throttle, and Apply brake to stop car, continue to ensure it stays stopped.
+#
+# ** Fly Mode **
+# - Required: (Car Moving OR JoyVert>Center) & In gear & Engine running & BasicMode Off & Ign On
+# - Priority: 5
+# This mode is for driving under manual control. In Fly Mode, vertical joystick positions
+# result in a proportional level of gas or brake (AKA "Manual" control).  Fly Mode is
+# only active when the car is moving - Once stopped or taken out of gear, we go back to Hold Mode.
+# If the driver performs a special secret "cruise gesture" on the joystick, then go to Cruise Mode.
+# Special cruise gesture might be: Pair of sudden full-throttle motions in rapid succession
+# - Actions: Enable all actuators, Watch for gesture
+#
+# ** Cruise Mode **
+# - Required: Car Moving & In gear & Engine running & BasicMode switch Off & Ignition On
+# - Priority: 6 (Lowest)
+# This mode is entered from Fly Mode by doing a special joystick gesture. In Cruise Mode,
+# the brake is disabled, and the joystick vertical is different: If joyv at center, the
+# throttle will actively maintain current car speed.  Up or down momentary joystick presses
+# serve to adjust that target speed. A sharp, full-downward gesture will drop us back to
+# Fly Mode, promptly resulting in braking (if kept held down).
+# - Actions: Release brake, Maintain car speed, Handle joyvert differently, Watch for gesture
+*/
+
+// Some human readable integers for array indexing
+#define STR 0   // indexer for steering values
+#define BRK 1   // indexer for brake values
+#define GAS 2   // indexer for throttle values
+#define H 0   // indexer for horizontal values
+#define V 1   // indexer for vertical values
+#define BOOT -1
+#define SHUTDOWN 0
+#define BASIC 1
+#define STALL 2
+#define HOLD 3
+#define FLY 4
+#define CRUISE 5
+
+#define arraysize(x)  (sizeof(x) / sizeof((x)[0]))  // To determine the length of string arrays
+
+
+// LCD is 2.8in diagonal, 240x320 pixels
+// LCD supports 18-bit color, but GFX library uses 16-bit color, organized (MSB) 5b-red, 6b-green, 5b-blue (LSB)
+// Since the RGB don't line up with the nibble boundaries, it's tricky to quantify a color, here are some colors:
+#define BLACK    0x0000
+#define BLUE     0x001F
+#define RED      0xF800
+#define GREEN    0x07E0
+#define CYAN     0x07FF
+#define MAGENTA  0xF81F
+#define YELLOW   0xFFE0
+#define WHITE    0xFFFF
+
+// void drawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bg, uint8_t size);
+//void setCursor(uint16_t x0, uint16_t y0);
+//void setTextColor(uint16_t color);
+//void setTextColor(uint16_t color, uint16_t backgroundcolor);
+//void setTextSize(uint8_t size);
+//void setTextWrap(boolean w);
+
+// Defines for all the GPIO pins we're using
+#define enc_b_pin 2  // Int input, Encoder is user input knob for the UI.  This is its B quadrature output, active low (needs pullup)
+#define enc_a_pin 3  // Int input, Encoder is user input knob for the UI.  This is its A quadrature output, active low (needs pullup)
+#define usd_cs_pin 4  // Output, active low, Chip select allows SD card controller chip use of the SPI bus
+#define tft_ledk_pin 5   // Output, Optional PWM signal to control brightness of LCD backlight (needs modification to shield board to work)
+#define brake_pwm_pin 6  // Output, PWM signal duty cycle sets speed of brake actuator from full speed extend to full speed retract, (50% is stopped)
+#define tp_irq_pin 7  // Optional int input so touchpanel can interrupt us (need to modify shield board for this to work)
+#define steer_pwm_pin 8  // Output, PWM signal duty cycle sets speed of steering motor from full speed left, to full speed right, (50% is stopped)
+#define tft_dc_pin 9  // Output, Assert when sending data to display chip to indicate commands vs. screen data
+#define tft_cs_pin 10  // Output, active low, Chip select allows ILI9341 display chip use of the SPI bus
+#define gas_pwm_pin 13 // Output, PWM signal duty cycle controls throttle target
+#define ignition_pin 15  // Input tells us if ignition signal is on or off, active high (no pullup)
+#define neutral_pin 16  // Input, active low, is asserted when car is in neutral, i.e. out of gear.  (needs pullup)
+#define basicmodesw_pin 17  // Input, active low, asserted to tell us to run in basic mode.   (needs pullup)
+#define tach_pulse_pin 18  // Int Input, active high, asserted when magnet is in range of sensor. 4 pulses per engine rotation. (no pullup)
+#define speedo_pulse_pin 19  // Int Input, active high, asserted when magnet is in range of sensor. 8 pulses per driven pulley rotation. (no pullup)
+#define enc_sw_pin 20  // Int input, Encoder is user input knob for the UI.  This is its pushbutton output, active low (needs pullup)
+#define joy_horz_pin A0  // Analog input, tells us up-down position of joystick
+#define joy_vert_pin A1  // Analog input, tells us left-right position of joystick
+#define brake_pres_pin A2  // Analog input, tells us brake fluid pressure (5V = 1000psi)
+#define brake_pos_pin A6  // Analog input, tells us linear position of brake actuator
+#define mule_volt_pin A7  // Analog input, mule battery voltage level, full scale is 5V * 3.1276 = 15.638V
+
+// Readily available possibilities we could wire up if we want
+//
+// * Status LEDs (digital out)
+// * UI Potentiometer (analog in)
+// * Control of steering motor coast vs. brake
+// * Control of brake motor coast vs. brake
+// * CAN bus as a superior interface to brake and steering Jaguars (only on Due I think?)
+// * Steering limit switches left and right, handle here instead of in Jaguar (digital in)
+// * Engine temperature module overheat panic input (digital in)
+// * Remote E-Stop panic inputs (digital in)
+// * Serial interface to the lighting controller (if we can think of a reason)
+// * Mule starter (digital out)
+// * E-brake handle position (digital in)
+
+#define disp_lineheight 15
+#define disp_linewidth 320
+#define disp_screenheight 240
+
+#define adcrange 1024    // = 1024
+#define adcmidscale 512    // = 1024
+//int adcrange = 2^10;    // = 1024
+//int adcmidscale = adcrange/2;    // = 1024
+
+// Constants
+//
+char telemetry[16][14] = {
+    "   Run Mode: ",
+    "  Air Speed: ",
+    "   Joy Horz: ",
+    "   Joy Vert: ",
+    "SteerTarget: ",
+    "  Steer PWM: ",
+    "BrakeTarget: ",
+    "  Brake PWM: ",
+    "BrakePressr: ",
+    "  GasTarget: ",
+    "    Gas PWM: ",
+    " Engine RPM: ",
+    "    Touch X: ",
+    "    Touch Y: ",
+    "JoyHorz Raw: ",
+    "         NA: "
+};
+char modecard[6][9] = {
+    "Shutdown",
+    "Basic   ",
+    "Stall   ",
+    "Hold    ",
+    "Fly     ",
+    "Cruise  "
+};
+/*
+char actioncard[6][9] = {
+    "Unknown ",
+    "Stopping",
+    "New Mode",
+    "Flying  ",
+    "Cruising",
+    "Releasng",
+    "Holding "
+};
+*/
+
+// Settable calibration values and control parameters
+//
+float steer_kp = 0.0;  // PID proportional coefficient (steering)
+float steer_ki = 0.0;  // PID integral coefficient (steering)
+float steer_kd = 0.0;  // PID derivative coefficient (steering)
+int steer_pid_freq = 10;  // How fast to loop in Hz
+int steer_stop = 90;  // Servo target corresponding to no steering (zero steering speed)
+int steer_max_speed = 90;  // Fastest steering we want.  (in change of PWM duty from center)
+float brake_kp = 0.1;  // PID proportional coefficient (brake)
+float brake_ki = 0.5;  // PID integral coefficient (brake)
+float brake_kd = 0.0;  // PID derivative coefficient (brake)
+int brake_pid_freq = 10;  // How fast to loop in Hz
+int brake_min_release = 0;  // Brake pressure when brakes are effectively off
+int brake_max_pressure = 0;  // Highest possible pressure achievable by the actuator
+int brake_hold_initial = 0;  // Pressure initially applied when brakes are hit to auto-stop the car
+int brake_hold_increment = 0;  // Incremental pressure per second added until car stops (when auto stopping)
+int brake_pos_full_retract = 0;  // Brake position value corresponding to retract limit of actuator
+int brake_pos_full_extend = 0;  // Brake position value corresponding to extend limit of actuator
+int brake_increment_interval = 1000;  // How often to apply increment during auto-stopping
+float gas_kp = 0.0;  // PID proportional coefficient (gas)
+float gas_ki = 0.0;  // PID integral coefficient (gas)
+float gas_kd = 0.0;  // PID derivative coefficient (gas)
+int gas_pid_freq = 10;  // How fast to loop in Hz
+int throttle_min_closed = 45;  // servo target angle setting (in degrees) corresponding to fully closed throttle
+int throttle_max_open = 120;  // servo target angle setting (in degrees) corresponding to full open throttle
+int engine_rpm_min_idle = 0;  // Min value for engine_rpm, corresponding to low idle
+int engine_rpm_max_redline = 0;  // Max value for engine_rpm, pedal to the metal
+int engine_rpm_timeout = 400;  // ms since last magnet pulse when we can assume the engine is stopped
+float cruise_kp = 0.0;  // PID proportional coefficient (cruise)
+float cruise_ki = 0.0;  // PID integral coefficient (cruise)
+float cruise_kd = 0.0;  // PID derivative coefficient (cruise)
+int cruise_pid_freq = 10;  // How fast to loop in Hz
+int cruise_max_change = 0;  // What's the max car speed change from a single joystick motion in cruise mode?
+int cruise_max_carspeed = 0;  // How fast can we go?
+int joy_min_v = 0;  // ADC count of furthest joy position in down direction
+int joy_max_v = 0;  // ADC count of furthest joy position in up direction
+int joy_min_h = 0;  // ADC count of furthest joy position in left direction
+int joy_max_h = 0;  // ADC count of furthest joy position in right direction
+int joy_deadband_v = 140;  // ADC count width of inert readings around center which we should treat as center (vertical)
+int joy_deadband_h = 140;  // ADC count width of inert readings around center which we should treat as center (horizontal)
+int gesture_adj_timeout = 1000;  // How many ms allowed to complete a speed adjustment gesture
+int gesture_fly_timeout = 500;  // How many ms allowed to push joy down to go to Fly mode from Cruise mode
+int car_speed_timeout = 400;  // ms since last magnet pulse when we can assume the car is stopped
+
+// Non-settable control variables
+//
+int steer_max_left = steer_stop+steer_max_speed;   // PWM duty cycle from 0-255
+int steer_max_right = steer_stop-steer_max_speed;    // PWM duty cycle from 0-255
+int steer_fullrange = abs(steer_max_left-steer_max_right);  // Possible range for target values
+int steer_target = 0;  // Stores new setpoint to give to the pid loop (steering)
+int steer_pwm = 0;  // pid loop output to send to the actuator (steering)
+int brake_fullrange = brake_max_pressure-brake_min_release;  // Possible range for target values
+int brake_target = 0;  // Stores new setpoint to give to the pid loop (brake)
+int brake_pwm = 0;  // pid loop output to send to the actuator (brake)
+int gas_target = 0;  // Stores new setpoint to give to the pid loop (gas)
+int gas_pwm = 0;  // pid loop output to send to the actuator (gas)
+int throttle_fullrange = throttle_max_open-throttle_min_closed;  // Possible range for target values
+int engine_rpm_fullrange = engine_rpm_max_redline-engine_rpm_min_idle;  // Possible range for target values
+int joy_deadband_bot_v = int((adcrange-joy_deadband_v)/2);  // Lower threshold of vertical joystick deadband
+int joy_deadband_top_v = int((adcrange+joy_deadband_v)/2);  // Upper threshold of vertical joystick deadband
+int joy_deadband_bot_h = int((adcrange-joy_deadband_h)/2);  // Lower threshold of horizontal joystick deadband
+int joy_deadband_top_h = int((adcrange+joy_deadband_h)/2);  // Upper threshold of horizontal joystick deadband
+int car_speed = -1;  // This is in units of rotations per minute of the driven torque converter pulley
+int engine_rpm = -1;  // This is in units of rotations per minute of the engine crankshaft
+const int gesture_complete = 0;  // How many steps are there in the Cruise Mode gesture
+int gesture_extent = 0;  // Keeps track of max joystick movement during cruise mode speed adjustments
+
+int brake_timer = 0;  // Timer used to control braking increments
+int cruise_target = 0;  // Stores new setpoint to give to the pid loop (cruise)
+int gesture_timer = 0;  // Used to keep track of time for gesturing
+int gesture_progress = 0;  // How many steps of the Cruise Mode gesture have you completed successfully (from Fly Mode)
+long steer_fullrange_long = (long)steer_fullrange;
+const long adcrange_long = (long)adcrange;
+long joy_deadband_bot_h_long = (long)joy_deadband_bot_h;
+long joy_deadband_top_h_long = (long)joy_deadband_top_h;
+
+// State variables
+//
+int runmode = SHUTDOWN;  // Variable to store what mode we're in
+int oldmode = SHUTDOWN;  // So we can tell when the mode has just changed
+bool we_just_switched_modes = true;  // For mode logic to set things up upon first entry into mode
+bool shutdown_complete = true;  // Shutdown mode has completed its work and can stop activity
+
+// Initialization of global variables, tunable parameters, etc.
+int output_bits = 8;
+bool output_signed = false;
+
+boolean RecordOn = false;
+
+unsigned long tach_old_time, speedo_old_time, tach_time, speedo_time;
+
+// Instantiate objects
+Adafruit_FT6206 touchpanel = Adafruit_FT6206();
+Adafruit_ILI9341 tft = Adafruit_ILI9341(tft_cs_pin, tft_dc_pin);
+static Servo steer_motor;
+static Servo gas_servo;
+FastPID brake_pid(brake_kp, brake_ki, brake_kd, brake_pid_freq, output_bits, output_signed);
+SdFat SD;         // SD card filesystem
+
+// Instantiate PID loops
+//
+// Steering:
+//   Setpoint Value: Proportional to Joystick Horz ADC value.  0V = Full Left, 2.5V = Stop, 5V = Full Right
+//   Measured Value: We have no feedback.  Try using a value proportional to the current actuator output
+//   Actuator Output Value: PWM signal to Steering Jaguar unit.  0% duty = Full Left, 50% = Stop, 100% = Full Right
+//   Limits: Reed switch limit signals for left and right may be handled by us, or by the jaguar controller
+//   Setpoint scaling: Kp/Ki/Kd values should decrease appropriately as a function of vehicle speed (safety)
+//
+//   Notes: The steering has no feedback sensing, other than two digital limit switches at the ends of travel.
+//   So just consider the error to be the difference between the joystick position and the last output value.
+//
+// Brakes:
+//   Setpoint Value: * Default: Setpoint proportional to Joystick Vert distance from center when below center.
+//       * In Hold Mode: Brake adjusts automatically to keep car stopped, as long as joystick below center
+//       * In Cruise Mode: Brake is disabled
+//   Measured Value: Analog voltage from brake fluid pressure sensor. 0-3.3V proportional to 0-1000psi
+//   Actuator Output Value: PWM signal to Brake Jaguar unit.
+//       0% duty = Full speed extend (less brake), 50% = Stop, 100% = Full speed Retract (more brake)
+//   Position: Analog 0-3.3V proportional to the travel length of the actuator (not used as feedback)
+//
+// Gas:
+//   Setpoint Value: * Default: Servo angle is proportional to Joystick Vert distance from center when above center.
+//       * In Cruise Mode: Upward or downward joy vert motions modify vehicle speed setpoint
+//   Measured Value: * Default: Engine speed determined from tach pulses
+//   Actuator Output Value: PWM signal to throttle servo
+//       0% duty = Fully close throttle.  This will idle.  100% duty = Fully open throttle.
+//
+// Cruise:
+//   Setpoint Value: * Default: Set to the current vehicle speed when mode is entered.
+//       * In Cruise Mode: Upward or downward joy vert motions modify vehicle speed setpoint
+//   Measured Value: * Vehicle speed determined from tach pulses
+//   Actuator Output Value: Cruise PID output values are setpoint values for the Gas PID above
+//       0% duty = Car stopped.  100% duty = Car max speed.
+
+static Servo steering_servo;
+// Setup code runs once at boot time
+
+bool encb_isr_flag = false;
+bool enca_isr_flag = false;
+bool encsw_isr_flag = false;
+bool tach_isr_flag = false;
+bool speedo_isr_flag = false;
+
+void setup() {
+    pinMode(enc_b_pin, INPUT_PULLUP);
+    pinMode(enc_a_pin, INPUT_PULLUP);
+    pinMode(brake_pwm_pin, OUTPUT);
+    pinMode(steer_pwm_pin, OUTPUT);
+    pinMode(tft_dc_pin, OUTPUT);
+    pinMode(enc_sw_pin, INPUT_PULLUP);
+    pinMode(gas_pwm_pin, OUTPUT);
+    pinMode(ignition_pin, INPUT);
+    pinMode(neutral_pin, INPUT_PULLUP);
+    pinMode(basicmodesw_pin, INPUT_PULLUP);
+    pinMode(tach_pulse_pin, INPUT);
+    pinMode(speedo_pulse_pin, INPUT);
+    pinMode(joy_horz_pin, INPUT);
+    pinMode(joy_vert_pin, INPUT);
+    pinMode(brake_pres_pin, INPUT);
+    pinMode(brake_pos_pin, INPUT);
+    pinMode(mule_volt_pin, INPUT);
+    pinMode(usd_cs_pin, OUTPUT);
+    pinMode(tft_cs_pin, OUTPUT);
+    //pinMode(tft_ledk_pin, OUTPUT);
+    //pinMode(tp_irq_pin, INPUT);
+
+    // Set all outputs to known sensible values
+    digitalWrite(tft_cs_pin, HIGH);   // Prevent bus contention
+    digitalWrite(usd_cs_pin, HIGH);   // Prevent bus contention
+    digitalWrite(tft_dc_pin, LOW);
+    analogWrite(brake_pwm_pin, 128);   // Write values range from 0 to 255
+    analogWrite(steer_pwm_pin, 128);
+    analogWrite(gas_pwm_pin, throttle_min_closed);
+
+    // Timers T0 (8b): pins 4,13 - T1 (16b): pins 11,12 - T2 (8b): pins 9,10 - T3 (16b): pins 2,3,5 - T4 (16b): pins 6,7,8 - T5 (16b): pins 44,45,46
+    // Registers: TCCRx = Timer control, prescalar, TCNTx = Timer value, OCRx = Output compare, ICRx = Input Capture, TIMSKx = Int mask, TIFRx = Int Flags
+    TCCR0B = TCCR0B & B11111000 | B00000101; // set timer 0 divisor to  1024 for PWM frequency of    61.04 Hz (Gas Servo)
+    TCCR4B = TCCR4B & B11111000 | B00000100; // set timer 4 divisor to   256 for PWM frequency of   122.55 Hz (Brake & Steering)
+
+    //while (!Serial);     // needed for debugging?!
+    Serial.begin(115200);
+    tft.begin();
+
+    Serial.println(F("Captouch initialization"));
+    if (! touchpanel.begin(40)) {     // pass in 'sensitivity' coefficient
+        Serial.println("Couldn't start FT6206 touchscreen controller");
+        while (1);
+    }
+    Serial.println("Capacitive touchscreen started");
+    tft.fillScreen(BLACK);
+    tft.setRotation(1);  // origin = left,top landscape (USB left upper)
+
+    Serial.print(F("Initializing filesystem..."));  // SD card is pretty straightforward, a single call.
+    if (! SD.begin(usd_cs_pin, SD_SCK_MHZ(25))) {   // ESP32 requires 25 MHz limit
+        Serial.println(F("SD begin() failed"));
+        for(;;); // Fatal error, do not continue
+    }
+    Serial.println(F("Filesystem started"));
+    //tft.fillScreen(ILI9341_BLUE);  // Fill screen blue.
+
+    steer_motor.attach(steer_pwm_pin);
+    gas_servo.attach(gas_pwm_pin);
+
+    // Set up our interrupts
+    attachInterrupt(digitalPinToInterrupt(enc_b_pin), EncoderB_ISR, CHANGE);
+    attachInterrupt(digitalPinToInterrupt(enc_a_pin), EncoderA_ISR, CHANGE);
+    attachInterrupt(digitalPinToInterrupt(enc_sw_pin), EncoderSw_ISR, CHANGE);
+    attachInterrupt(digitalPinToInterrupt(tach_pulse_pin), Tach_ISR, RISING);
+    attachInterrupt(digitalPinToInterrupt(speedo_pulse_pin), Speedo_ISR, RISING);
+
+    int i;
+    for (i=0; i<=arraysize(telemetry); i++)  {
+        writeline(i+1, telemetry[i]);
+    }
+}
+
+void EncoderB_ISR(void) {
+    encb_isr_flag = true;
+}
+void EncoderA_ISR(void) {
+    enca_isr_flag = true;
+}
+void EncoderSw_ISR(void) {
+    encsw_isr_flag = true;
+}
+void Tach_ISR(void) {
+    tach_time = millis();  // This might screw up things.  Anders would remember
+    tach_isr_flag = true;
+}
+void Speedo_ISR(void) {
+    speedo_time = millis();  // This might screw up things.  Anders would remember
+    speedo_isr_flag = true;
+}
+
+void set_steer_pwm(int target_in_degrees) {
+    steer_motor.write(target_in_degrees);
+}
+
+void set_gas_pwm(int target_in_degrees) {
+    gas_servo.write(target_in_degrees);
+}
+
+// Returns: The value read on the joystick from 0 (maximum left) to 1024 (maximum right).
+// Zero values correspond to the deadband at the center position.
+int read_joy_vert(void)  {
+    int adc_value = analogRead(joy_vert_pin);
+    int joy_vert;
+
+    if ((adc_value > joy_deadband_bot_v) && (adc_value < joy_deadband_top_v)) {
+        joy_vert = adcmidscale;  // return 0 input if value is in the deadband
+    }
+    else  {
+        joy_vert = adcrange-adc_value;
+    }
+    return joy_vert;
+}
+
+int read_joy_horz(void)  {
+    int adc_value = analogRead(joy_horz_pin);
+    int joy_horz;
+
+    if ((adc_value > joy_deadband_bot_h) && (adc_value < joy_deadband_top_h)) {
+        joy_horz = adcmidscale;  // return 0 input if value is in the deadband
+    }
+    else  {
+        joy_horz = adcrange-adc_value;
+    }
+    return joy_horz;
+}
+
+// Inputs: The joystick input between -512 and 512.
+// Returns: The joystick target to output from 0 to 180 degrees.
+int joy_horz_to_target(int joy_input) {
+    int target;
+    long joy_input_long = (long)joy_input;
+    long max_target_deg_long = (long)steer_fullrange;
+    long adcrange_long = (long)adcrange;
+
+    target = (int)((joy_input_long*max_target_deg_long)/adcrange_long);
+    return target;
+}
+
+int joy_vert_to_target(int joy_input) {
+    int target;
+    long joy_input_long = (long)joy_input;
+
+    long max_target_deg_long;
+    long adcrange_long = (long)adcrange;
+    if (joy_input > joy_deadband_top_v)  {
+        max_target_deg_long = (long)engine_rpm_max_redline;
+    }
+    else  {
+        max_target_deg_long = (long)brake_max_pressure;
+    }
+    target = (int)((joy_input_long*max_target_deg_long)/(2*adcrange_long));
+    return target;
+}
+/*int* read_joy_values(void) {
+    int joy_raw_values[2];
+    joy_raw_values[H] = analogRead(joy_horz_pin);
+    joy_raw_values[V] = analogRead(joy_vert_pin);
+
+    int joy_values[2];
+
+    for (axis=H; axis<=V; axis++) {
+        //check if the value is in the deadband, return 0 input if it is
+        if ((joy_raw_values[axis] > joy_deadband_bot_) && (joy_raw_values[axis] < joy_deadband_top[axis])) {
+            joy_values[axis] = adcmidscale;
+        }
+        else {
+            joy_values[axis] = adcrange-joy_raw_values[axis];
+        }
+    return joy_values;
+}
+
+int *joy_to_actuator_targets(int *joy_values) {
+    long servo_max_target_deg_long = (long)steer_fullrange;
+    long adcmidscale_long = (long)adcmidscale;
+    long adcrange_long = (long)adcrange;
+
+    int actuator_targets[3];
+    long joy_values_long[2];
+
+    joy_values_long[H] = (long)joy_values[H];
+    joy_values_long[V] = (long)joy_values[V];
+
+    actuator_targets[STR] = (int)((joy_values_long[H]*servo_max_target_deg_long)/adcrange_long);
+
+    //actuator_targets[BRK] = BRAKE_FULL_EXTEND_TARGET;
+    //if (joy_values[V] < adcmidscale)
+    //{
+    //  actuator_targets[BRK] = (int)((adcmidscale_long-joy_values_long[V])*servo_max_target_deg_long/adcmidscale_long);
+    //}
+
+    actuator_targets[GAS] = throttle_min_closed;
+    if (joy_values[V] > adcmidscale) {
+        actuator_targets[GAS] = (int)((joy_values_long[V]-adcmidscale_long)/adcmidscale_long);
+    }
+
+    return actuator_targets;
+}
+*/
+
+void writevalue(int lineno, char *value) {
+    tft.fillRect(80, (lineno-1)*disp_lineheight, 55, disp_lineheight, BLACK);
+    //tft.fillRect(0, (lineno-1)*disp_lineheight, disp_linewidth, disp_lineheight, ILI9341_BLACK);
+    tft.setCursor(86, ((lineno-1)*disp_lineheight)+disp_lineheight/2);
+    tft.setTextColor(WHITE);
+    tft.setTextSize(1);
+    tft.println(value);
+    RecordOn = false;
+}
+
+void writeline(int lineno, char *value) {
+    tft.fillRect(0, (lineno-1)*disp_lineheight, disp_linewidth, disp_lineheight, BLACK);
+    tft.setCursor(6, ((lineno-1)*disp_lineheight)+disp_lineheight/2);
+    tft.setTextColor(WHITE);
+    tft.setTextSize(1);
+    tft.println(value);
+    RecordOn = false;
+}
+
+void loop() {
+    // Main loop.  Each time through we do these eight steps:
+    //
+    // 1) Gather new telemetry
+    // 2) Check if our current runmode has been overridden by certain specific conditions
+    // 3) Read joystick horizontal and determine new steering setpoint
+    // 4) Do actions based on which runmode we are in (including gas & brake setpoint), and possibly change runmode
+    // 5) Step the PID loops and update the actuation outputs
+    // 6) Service the user interface
+    // 7) Log to SD card
+    // 8) Do the control loop bookkeeping at the end of each loop
+
+    // Initialize variables
+    //
+    int joy_vert, joy_horz, old_joy_vert, old_joy_horz;
+    char buffer[6];
+    int steer_target, brake_target, gas_target, old_steer_target, old_brake_target, old_gas_target;
+    int steer_pwm, brake_pwm, gas_pwm, old_steer_pwm, old_brake_pwm, old_gas_pwm;
+    int engine_rpm, old_engine_rpm, car_speed, old_car_speed;
+    int before, after;
+    int old_brake_pressure;
+    int pwmtimer;
+
+    // 1) Gather new telemetry
+    //
+    int now = millis();
+    if (tach_isr_flag == true) {       // Update engine rpm value
+        float engine_rpm = 60*1000/(4*(tach_time-tach_old_time));
+        tach_old_time = tach_time;
+        tach_isr_flag = false;
+    }
+    else if (now-tach_old_time > engine_rpm_timeout) {
+        engine_rpm = 0;
+    }
+    if (speedo_isr_flag == true) {   // Update car speed value
+        float car_speed = 60*1000/(8*(speedo_time-speedo_old_time));
+        speedo_old_time = speedo_time;
+        speedo_isr_flag = false;
+    }  // Mule gearing:  Total -19.845x (lo) ( Converter: -3.5x to -0.96x Tranny -3.75x (lo), -1.821x (hi), Final drive -5.4x )
+    else if (now-speedo_old_time > car_speed_timeout) {
+        car_speed = 0;
+    }
+
+    // Read the sensors
+    int brake_pressure = analogRead(brake_pres_pin);
+    int brake_pos = analogRead(brake_pos_pin);
+    int mule_voltage = analogRead(mule_volt_pin);
+    int neutral = digitalRead(neutral_pin);
+    int ignition = digitalRead(ignition_pin);
+    //int basicmodesw = digitalRead(basicmodesw_pin);
+    int basicmodesw = 1;  // Disable for now until connected
+
+    // 2) Check if our current runmode has been overridden by certain specific conditions
+    //
+    if (ignition == 0)  {           // if ignition off --> Shutdown Mode
+        runmode = SHUTDOWN;
+    }
+    else if (basicmodesw == 0) {    // elif basicmode switch on --> Basic Mode
+        runmode = BASIC;
+    }
+    else if (engine_rpm == 0)  {    // elif engine not running --> Stall Mode
+        runmode = STALL;
+    }
+
+    // 3) Read joystick horizontal and determine new steering setpoint
+    //
+    joy_vert = read_joy_vert();
+    joy_horz = read_joy_horz();
+
+    if (!(runmode == SHUTDOWN && (car_speed == 0 or shutdown_complete == true)))  { // The only time we don't want steering
+        long joy_horz_long = (long)joy_horz;
+         if (joy_horz < joy_deadband_bot_h)  {    // If we are trying to turn left
+            // This should determine how hard we're trying to turn (0 to 1 range)
+            int temp = (int)((joy_deadband_bot_h_long-joy_horz_long)/joy_deadband_bot_h_long);
+            int steer_target = (steer_fullrange_long/2)*(1-temp);
+        }
+        else if (joy_horz > joy_deadband_top_h)  {  // If we are trying to turn right
+            // This should determine how hard we're trying to turn (0 to 1 range)
+            int temp = (int)((joy_horz_long-joy_deadband_top_h_long)/joy_deadband_bot_h_long);
+            int steer_target = (steer_fullrange_long/2)*(1+temp);
+            //int steer_target = (steer_fullrange_long/2)*(1+(int)((joy_horz_long-joy_deadband_top_h_long)/joy_deadband_bot_h_long));
+        }
+        else {
+            steer_target = steer_stop;  // adjust to actual center
+        }
+    }
+    // Try this with the commented function above
+    // joy_values = read_joy_values();
+    // joy_horz = joy_values[0];
+    // joy_vert = joy_values[1];
+
+    // Old: steer_target = joy_horz_to_target(joy_horz);
+    // Old: gas_target = joy_vert_to_target(joy_vert);
+
+    // 4) Do actions based on which runmode we are in (and set gas/brake setpoints), and possibly change runmode
+    //
+    now = millis();
+
+    if (runmode == SHUTDOWN)  {
+        if (basicmodesw == 0)  {   // If basic mode switch is enabled
+            shutdown_complete = true;
+        }
+        else if (we_just_switched_modes == true)  {  // If basic switch is off, we need to stop the car then release brakes and gas before shutting down
+            gas_target = engine_rpm_min_idle;  //  Begin Letting off the gas all the way
+            shutdown_complete = false;
+            if (car_speed > 0)  {
+                brake_target = brake_hold_initial;  // More brakes, etc. to stop the car
+                brake_timer = now;
+            }
+        }
+        if (shutdown_complete == false)  {  // If we haven't yet stopped the car and released the brakes and gas all the way
+            if (car_speed == 0)  {  // Car is stopped, but maybe we still need to release the brakes
+                brake_target = brake_min_release;  // Start to Fully release brakes
+                if (brake_pressure == brake_min_release)  {
+                    shutdown_complete = true;  // With this set, we will do nothing from here on out (until mode changes, i.e. ignition)
+                }
+            }
+            else if (brake_timer-now > brake_increment_interval)  {
+                brake_target += brake_hold_increment;  // Slowly add more brakes until car stops
+                brake_timer = now;
+            }
+        }
+    }
+    else if (runmode == BASIC)  {
+        if (basicmodesw == 1 && engine_rpm > 0)  {  // If we turned off the basic mode switch with engine running, go to Hold mode
+            runmode = HOLD;   //  If engine is not running, we'll end up in Stall Mode automatically
+        }
+    }
+    else if (runmode == STALL)  {   // In stall mode, the gas doesn't have feedback}
+        // PID limits don't apply any more when not using engine rpm as fb.  Fix this
+        if (engine_rpm > 0)  {  //  enter Hold Mode if we started the car
+            runmode = HOLD;
+        }
+        else {
+            gas_target = engine_rpm_min_idle;  // Default when joystick not pressed
+            brake_target = brake_min_release;  // Default when joystick not pressed
+            if (joy_vert > joy_deadband_top_v)  { //  If we are pushing up
+                gas_target = engine_rpm_fullrange*(joy_vert-joy_deadband_top_v)/joy_deadband_bot_v;
+            }
+            else if (joy_vert < joy_deadband_bot_v)  {  // If we are pushing down
+                brake_target = brake_fullrange*(joy_deadband_bot_v-joy_vert)/joy_deadband_bot_v;
+            }
+        }
+    }
+    else if (runmode == HOLD)  {
+        if (joy_vert > joy_deadband_top_v)  { // Enter Fly Mode if joystick is pushed up
+            runmode = FLY;
+        }
+        else if (we_just_switched_modes == true)  {  // Release throttle and push brake upon entering hold mode
+            gas_target = engine_rpm_min_idle;  // Let off gas
+            brake_target = brake_hold_initial;  // Apply brakes to preset setpoint
+            brake_timer = now;
+        }
+        else if (car_speed > 0 && brake_timer-now > brake_increment_interval)  {  // If car starts moving again, push harder
+            brake_target += brake_hold_increment; //  Increase the brake pressure slowly when trying to stop car
+            brake_timer = now;
+        }
+    }
+    else if (runmode == FLY)  {
+        if (we_just_switched_modes == true)  {
+            gesture_progress = 0;
+            gesture_timer = now;
+        }
+        // else if (gesture motion detected)  {   // If next motion of gesture completed in time
+        //     // Add checks to identify conditions to satisfy steps of the gesture
+        //     gesture_progress++;
+        //     gesture_timer = now;
+        // }
+        else if (gesture_progress == gesture_complete)  { // Series of motions is complete
+            runmode = CRUISE;
+        }
+        else if (now-gesture_timer > gesture_adj_timeout)  {  // Gesture timed out
+            gesture_progress = 0;
+        }
+        if ((car_speed == 0 && joy_vert < joy_deadband_bot_v) || neutral)  {
+            runmode = HOLD;  // Back to Hold Mode if we have braked to a stop or shifted out of gear
+        }
+        else  {  // Use PID to drive
+            gas_target = engine_rpm_min_idle;  // Default when joystick not pressed
+            brake_target = brake_min_release;  // Default when joystick not pressed
+            if (joy_vert > joy_deadband_top_v)  {  // If we are trying to accelerate
+                gas_target = engine_rpm_fullrange*(joy_vert-joy_deadband_top_v)/joy_deadband_bot_v;
+            }
+            else if (joy_vert < joy_deadband_bot_v)  {  // If we are trying to brake
+                brake_target = brake_fullrange*(joy_deadband_bot_v-joy_vert)/joy_deadband_bot_v;
+            }
+        }
+    }
+    else if (runmode == CRUISE)  { // Maybe the elif clauses within here should be if clauses.  Maybe doesn't matter
+        if (car_speed == 0 or neutral > 0)  {  // this should never happen
+            runmode = HOLD;  // Back to Hold Mode because we don't understand what's going on
+        }
+        else if (we_just_switched_modes == true)  {
+            cruise_target = car_speed;
+            brake_target = brake_min_release;
+            gesture_extent = adcrange/2;  // Variable to keep track of our joystick motion for speed adjustments
+        }
+        if (joy_deadband_bot_v < joy_vert && joy_deadband_top_v > joy_vert)  {  // joystick within deadband near center: do speed adjustment, and reset gesture timer
+            if (gesture_extent > joy_deadband_top_v && now-gesture_timer < gesture_adj_timeout)  { // Adjust speed upward based on how high we were pushed
+                cruise_target += cruise_max_change*(gesture_extent-joy_deadband_top_v)/joy_deadband_bot_v;
+            }
+            else if (gesture_extent < joy_deadband_bot_v && now-gesture_timer < gesture_adj_timeout)  {
+                cruise_target -= cruise_max_change*(joy_deadband_bot_v-gesture_extent)/joy_deadband_bot_v;
+            }
+            gesture_extent = adcrange/2;  // Reset for next time
+            gesture_timer = now;
+        }
+        else if (joy_vert == joy_min_v && now-gesture_timer < gesture_fly_timeout)  { // If joystick quickly pushed down
+            runmode = FLY;
+        }
+        else if (joy_vert > joy_deadband_top_v && joy_vert > gesture_extent)  {  // We pushed it even further up
+            gesture_extent = joy_vert;  // Remember our highest point
+        }
+        else if (joy_vert < joy_deadband_bot_v && joy_vert < gesture_extent)  {  // We pushed it even further down
+            gesture_extent = joy_vert;  // Remember our lowest point
+        }
+        // Commented out during initial debug
+        //gas_target = cruise_pid.step(feedback=car_speed, setpoint = cruise_target);
+    }
+    else  { // Obviously this should never happen
+        runmode = HOLD;
+    }
+
+    // 5) Step the pids, update the actuator outputs
+    //
+    // The pid approach is the most cool.  Fix and uncomment these as we can
+    if (! (runmode == SHUTDOWN && shutdown_complete == true))  {  // The only time we don't want steering
+        // steering_pwm = steering_pid.step(feedback=none, setpoint = steer_target);
+
+        if (! basicmodesw == 0)  {  // If basicmode switch is disabled we want brake and gas
+            // brake_pwm = brake_pid.step(feedback=brake_pressure, setpoint = brake_target);
+
+            if (runmode == STALL)  {
+                // gas_pwm = throttle_pid.step(feedback=none, setpoint = gas_target);
+            }
+            else {
+                // gas_pwm = throttle_pid.step(feedback=engine_rpm, setpoint = gas_target);
+            }
+        }
+    }
+
+    now = millis();
+
+    // Update gas and steering output only every 15ms
+    if (now-pwmtimer > 15)  {
+        set_steer_pwm(steer_target);
+        set_gas_pwm(gas_target);
+        pwmtimer = now;
+    }
+
+    // Step the brake pid
+    before = micros();
+    brake_target = brake_pid.step(brake_min_release, brake_pressure);
+    // uint8_t brake_target = brake_pid.step(brake_min_release, brake_pressure);
+    after = micros();
+    analogWrite(brake_pwm_pin, brake_pwm);
+    Serial.print("runtime: ");
+    Serial.print(after - before);
+    Serial.print(" sp: ");
+    Serial.print(brake_min_release);
+    Serial.print(" fb: ");
+    Serial.print(brake_pressure);
+    Serial.print(" out: ");
+    Serial.println(brake_target);
+
+    // Try this later
+    // if (car_speed != old_car_speed) {
+    //    writevalue(11, (char*)car_speed;
+    //    old_car_speed = car_speed;
+    // }
+
+    set_steer_pwm(steer_target);
+    set_gas_pwm(gas_target);
+
+    // 6) Service the user interface
+    //
+    // Act on any encoder action
+
+    // See if someone is groping the touch screen
+    if (touchpanel.touched())  {  // See if there's any  touch data for us
+        TS_Point touchpoint = touchpanel.getPoint();   // Retreive a point
+        touchpoint.x = map(touchpoint.x, 0, 240, 240, 0);  // Rotate touch coordinates to match tft
+        touchpoint.y = map(touchpoint.y, 0, 320, 320, 0);
+        int y = tft.height()-touchpoint.x;
+        int x = touchpoint.y;
+        memset(buffer,0,strlen(buffer));
+        itoa(x, buffer, 10);
+        writevalue(13, buffer);
+        memset(buffer,0,strlen(buffer));
+        itoa(y, buffer, 10);
+        writevalue(14, buffer);
+    }
+
+    // Write new values to the screen
+
+    if (runmode != oldmode) {
+        writevalue(1, modecard[runmode]);
+    }
+    if (car_speed != old_car_speed) {
+        memset(buffer,0,strlen(buffer));
+        itoa(car_speed, buffer, 10);
+        writevalue(2, buffer);
+        old_car_speed = car_speed;
+    }
+    if (joy_horz != old_joy_horz) {
+        memset(buffer,0,strlen(buffer));
+        itoa(joy_horz, buffer, 10);
+        writevalue(3, buffer);
+        old_joy_horz = joy_horz;
+    }
+    if (joy_vert != old_joy_vert) {
+        memset(buffer,0,strlen(buffer));
+        itoa(joy_vert, buffer, 10);
+        writevalue(4, buffer);
+        old_joy_vert = joy_vert;
+    }
+    if (steer_target != old_steer_target) {
+        memset(buffer,0,strlen(buffer));
+        itoa(steer_target, buffer, 10);
+        writevalue(5, buffer);
+        old_steer_target = steer_target;
+    }
+    if (steer_pwm != old_steer_pwm) {
+        memset(buffer,0,strlen(buffer));
+        itoa(steer_pwm, buffer, 10);
+        writevalue(6, buffer);
+        old_steer_pwm = steer_pwm;
+    }
+    if (brake_target != old_brake_target) {
+        memset(buffer,0,strlen(buffer));
+        itoa(brake_target, buffer, 10);
+        writevalue(7, buffer);
+        old_brake_target = brake_target;
+    }
+    if (brake_pwm != old_brake_pwm) {
+        memset(buffer,0,strlen(buffer));
+        itoa(brake_pwm, buffer, 10);
+        writevalue(8, buffer);
+        old_brake_pwm = brake_pwm;
+    }
+    if (brake_pressure != old_brake_pressure) {
+        memset(buffer,0,strlen(buffer));
+        itoa(brake_pressure, buffer, 10);
+        writevalue(9, buffer);
+        old_brake_pressure = brake_pressure;
+    }
+    if (gas_target != old_gas_target) {
+        memset(buffer,0,strlen(buffer));
+        itoa(gas_target, buffer, 10);
+        writevalue(10, buffer);
+        old_gas_target = gas_target;
+    }
+    if (gas_pwm != old_gas_pwm) {
+        memset(buffer,0,strlen(buffer));
+        itoa(gas_pwm, buffer, 10);
+        writevalue(11, buffer);
+        old_gas_pwm = gas_pwm;
+    }
+    if (engine_rpm != old_engine_rpm) {
+        memset(buffer,0,strlen(buffer));
+        itoa(engine_rpm, buffer, 10);
+        writevalue(12, buffer);
+        old_engine_rpm = engine_rpm;
+    }
+    memset(buffer,0,strlen(buffer));
+    itoa(analogRead(joy_horz_pin), buffer, 10);
+    writevalue(15, buffer);
+
+    // 7) Log to SD card
+    //
+    // [write current state with timestamp to the SD card]
+
+    // 8) Do the control loop bookkeeping at the end of each loop
+    //
+    old_joy_vert = joy_vert;
+    if (! oldmode == runmode)  {      // If changing runmode
+        we_just_switched_modes = true;    // So new mode logic can perform initial actions
+    }
+    else {
+        we_just_switched_modes = false;    // Reset this variable
+    }
+    oldmode = runmode;   // remember what mode we're in for next time
+
+    delay(10);
+
+}


Switched to use hmtl_next_sensor()

Switched to use hmtl_next_sensor()

--- utilities/HMTL_Command_CLI/HMTL_Command_CLI.ino
+++ utilities/HMTL_Command_CLI/HMTL_Command_CLI.ino
@@ -133,13 +133,12 @@
   switch (msg->type) {
     case MSG_TYPE_SENSOR: {
       DEBUG1_PRINTLN(" * Sensor data:");
-      byte *curr_ptr = (uint8_t *)(msg + 1);
-      do {
-        msg_sensor_data_t *sense = (msg_sensor_data_t *)(curr_ptr);
+
+      msg_sensor_data_t *sense = NULL;
+      while (sense = hmtl_next_sensor(msg, sense)) {
         DEBUG1_VALUE(" ptr:", (int)sense);
         DEBUG1_VALUE(" type:", sense->sensor_type);
         DEBUG1_VALUE(" datalen:", sense->data_len);
-        curr_ptr += sizeof (msg_sensor_data_t) + sense->data_len;

         switch (sense->sensor_type) {
           case HMTL_SENSOR_SOUND: {
@@ -167,8 +166,7 @@
         }

         DEBUG_PRINT_END();
-      } while (curr_ptr < (byte *)msg + msg->length);
-
+      }
       break;
     }
     case MSG_TYPE_POLL: {


neopixel: add example file

neopixel: add example file

Just to show that it works.

Signed-off-by: Sean Cross <d7e19930cc1f42c2d0781f4d9e6f1fe5891bf9cf@xobs.io>

error: pathspec 'html/examples/chibitronics/neopixel/neopixel.ino' did not match any file(s) known to git

--- html/examples/chibitronics/neopixel/neopixel.ino
+++ html/examples/chibitronics/neopixel/neopixel.ino
@@ -0,0 +1,143 @@
+#include "Adafruit_NeoPixel.h"
+
+// Parameter 1 = number of pixels in strip
+// Parameter 2 = Arduino pin number (most are valid)
+// Parameter 3 = pixel type flags, add together as needed:
+//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
+//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
+//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
+//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
+//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
+Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, LED_BUILTIN_RGB,
+                                            NEO_GRB + NEO_KHZ800);
+
+// Input a value 0 to 255 to get a color value.
+// The colours are a transition r - g - b - back to r.
+uint32_t Wheel(byte WheelPos) {
+  WheelPos = 255 - WheelPos;
+  if(WheelPos < 85) {
+    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
+  }
+  if(WheelPos < 170) {
+    WheelPos -= 85;
+    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
+  }
+  WheelPos -= 170;
+  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
+}
+
+// Fill the dots one after the other with a color
+void colorWipe(uint32_t c, uint8_t wait) {
+  for(uint16_t i=0; i<strip.numPixels(); i++) {
+    strip.setPixelColor(i, c);
+    strip.show();
+    delay(wait);
+  }
+}
+
+void rainbow(uint8_t wait) {
+  uint16_t i, j;
+
+  for(j=0; j<256; j++) {
+    for(i=0; i<strip.numPixels(); i++) {
+      strip.setPixelColor(i, Wheel((i+j) & 255));
+    }
+    strip.show();
+    delay(wait);
+  }
+}
+
+// Slightly different, this makes the rainbow equally distributed throughout
+void rainbowCycle(uint8_t wait) {
+  uint16_t i, j;
+
+  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
+    for(i=0; i< strip.numPixels(); i++) {
+      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
+    }
+    strip.show();
+    delay(wait);
+  }
+}
+
+//Theatre-style crawling lights.
+void theaterChase(uint32_t c, uint8_t wait) {
+  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
+    for (int q=0; q < 3; q++) {
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        strip.setPixelColor(i+q, c);    //turn every third pixel on
+      }
+      strip.show();
+
+      delay(wait);
+
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        strip.setPixelColor(i+q, 0);        //turn every third pixel off
+      }
+    }
+  }
+}
+
+//Theatre-style crawling lights with rainbow effect
+void theaterChaseRainbow(uint8_t wait) {
+  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
+    for (int q=0; q < 3; q++) {
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
+      }
+      strip.show();
+
+      delay(wait);
+
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        strip.setPixelColor(i+q, 0);        //turn every third pixel off
+      }
+    }
+  }
+}
+
+// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
+// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
+// and minimize distance between Arduino and first pixel.  Avoid connecting
+// on a live circuit...if you must, connect GND first.
+
+void setup() {
+  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
+  #if defined (__AVR_ATtiny85__)
+    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
+  #endif
+  // End of trinket special code
+
+
+  strip.begin();
+  strip.show(); // Initialize all pixels to 'off'
+}
+
+extern "C" double sin(double f);
+#define double float
+double test_d = 4.3L;
+int test_thingy(int start) {
+  static int val = start;
+
+  test_d += sin(start);
+  return ++val;
+}
+
+void loop() {
+  printf("Hello, world!\r\n");
+
+  test_thingy(micros());
+  // Some example procedures showing how to display to the pixels:
+  colorWipe(strip.Color(255, 0, 0), 50); // Red
+  colorWipe(strip.Color(0, 255, 0), 50); // Green
+  colorWipe(strip.Color(0, 0, 255), 50); // Blue
+//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
+  // Send a theater pixel chase in...
+  theaterChase(strip.Color(127, 127, 127), 50); // White
+  theaterChase(strip.Color(127, 0, 0), 50); // Red
+  theaterChase(strip.Color(0, 0, 127), 50); // Blue
+
+  rainbow(20);
+  rainbowCycle(20);
+  theaterChaseRainbow(50);
+}


neo pixel strandtest initi commit

neo pixel strandtest initi commit

error: pathspec 'strandtest/strandtest.ino' did not match any file(s) known to git

--- strandtest/strandtest.ino
+++ strandtest/strandtest.ino
@@ -0,0 +1,160 @@
+// A basic everyday NeoPixel strip test program.
+
+// NEOPIXEL BEST PRACTICES for most reliable operation:
+// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
+// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
+// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
+// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
+//   connect GROUND (-) first, then +, then data.
+// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
+//   a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
+// (Skipping these may work OK on your workbench but can fail in the field)
+
+#include <Adafruit_NeoPixel.h>
+#ifdef __AVR__
+ #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
+#endif
+
+// Which pin on the Arduino is connected to the NeoPixels?
+// On a Trinket or Gemma we suggest changing this to 1:
+#define LED_PIN    6
+
+// How many NeoPixels are attached to the Arduino?
+#define LED_COUNT 8
+
+// Declare our NeoPixel strip object:
+Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
+// Argument 1 = Number of pixels in NeoPixel strip
+// Argument 2 = Arduino pin number (most are valid)
+// Argument 3 = Pixel type flags, add together as needed:
+//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
+//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
+//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
+//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
+//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
+
+
+// setup() function -- runs once at startup --------------------------------
+
+void setup() {
+  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
+  // Any other board, you can remove this part (but no harm leaving it):
+#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
+  clock_prescale_set(clock_div_1);
+#endif
+  // END of Trinket-specific code.
+
+  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
+  strip.show();            // Turn OFF all pixels ASAP
+  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
+}
+
+
+// loop() function -- runs repeatedly as long as board is on ---------------
+
+void loop() {
+  // Fill along the length of the strip in various colors...
+  colorWipe(strip.Color(255,   0,   0), 50); // Red
+  delay(1000);
+  colorWipe(strip.Color(  0, 255,   0), 50); // Green
+  delay(1000);
+  colorWipe(strip.Color(  0,   0, 255), 50); // Blue
+  delay(1000);
+  colorWipe(strip.Color(  255,   0, 255), 50); // Purple
+  delay(1000);
+  colorWipe(strip.Color(  0,   255, 255), 50); // Cyan
+  delay(1000);
+  colorWipe(strip.Color(  255,   255, 255), 50); // White
+  delay(1000);
+  colorWipe(strip.Color(  127,   127, 127), 50); // White
+  delay(1000);
+  colorWipe(strip.Color(  255,   255, 0), 50); // Yellow
+  delay(1000);
+
+  // Do a theater marquee effect in various colors...
+  theaterChase(strip.Color(127, 127, 127), 50); // White, half brightness
+  theaterChase(strip.Color(127,   0,   0), 50); // Red, half brightness
+  theaterChase(strip.Color(  0,   0, 127), 50); // Blue, half brightness
+
+  rainbow(10);             // Flowing rainbow cycle along the whole strip
+  theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
+}
+
+
+// Some functions of our own for creating animated effects -----------------
+
+// Fill strip pixels one after another with a color. Strip is NOT cleared
+// first; anything there will be covered pixel by pixel. Pass in color
+// (as a single 'packed' 32-bit value, which you can get by calling
+// strip.Color(red, green, blue) as shown in the loop() function above),
+// and a delay time (in milliseconds) between pixels.
+void colorWipe(uint32_t color, int wait) {
+  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
+    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
+    strip.show();                          //  Update strip to match
+    delay(wait);                           //  Pause for a moment
+  }
+}
+
+// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
+// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
+// between frames.
+void theaterChase(uint32_t color, int wait) {
+  for(int a=0; a<10; a++) {  // Repeat 10 times...
+    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
+      strip.clear();         //   Set all pixels in RAM to 0 (off)
+      // 'c' counts up from 'b' to end of strip in steps of 3...
+      for(int c=b; c<strip.numPixels(); c += 3) {
+        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
+      }
+      strip.show(); // Update strip with new contents
+      delay(wait);  // Pause for a moment
+    }
+  }
+}
+
+// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
+void rainbow(int wait) {
+  // Hue of first pixel runs 5 complete loops through the color wheel.
+  // Color wheel has a range of 65536 but it's OK if we roll over, so
+  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
+  // means we'll make 5*65536/256 = 1280 passes through this outer loop:
+  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
+    for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
+      // Offset pixel hue by an amount to make one full revolution of the
+      // color wheel (range of 65536) along the length of the strip
+      // (strip.numPixels() steps):
+      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
+      // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
+      // optionally add saturation and value (brightness) (each 0 to 255).
+      // Here we're using just the single-argument hue variant. The result
+      // is passed through strip.gamma32() to provide 'truer' colors
+      // before assigning to each pixel:
+      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
+    }
+    strip.show(); // Update strip with new contents
+    delay(wait);  // Pause for a moment
+  }
+}
+
+// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
+void theaterChaseRainbow(int wait) {
+  int firstPixelHue = 0;     // First pixel starts at red (hue 0)
+  for(int a=0; a<30; a++) {  // Repeat 30 times...
+    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
+      strip.clear();         //   Set all pixels in RAM to 0 (off)
+      // 'c' counts up from 'b' to end of strip in increments of 3...
+      for(int c=b; c<strip.numPixels(); c += 3) {
+        // hue of pixel 'c' is offset by an amount to make one full
+        // revolution of the color wheel (range 65536) along the length
+        // of the strip (strip.numPixels() steps):
+        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
+        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
+        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
+      }
+      strip.show();                // Update strip with new contents
+      delay(wait);                 // Pause for a moment
+      firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
+    }
+  }
+}


wled06_usermod.ino

wled06_usermod.ino

This section is used to read the temperature from the sensor and prints it using the MQTT service started by Aircoookie.

error: pathspec 'usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino' did not match any file(s) known to git

--- usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino
+++ usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino
@@ -0,0 +1,41 @@
+//starts Dallas Temp service on boot
+void userSetup()
+{
+// Start the DS18B20 sensor
+  sensors.begin();
+}
+
+//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
+void userConnected()
+{
+
+}
+
+void userLoop()
+{
+  temptimer = millis();
+
+// Timer to publishe new temperature every 60 seconds
+  if (temptimer - lastMeasure > 60000) {
+    lastMeasure = temptimer;
+
+//Check if MQTT Connected, otherwise it will crash the 8266
+    if (mqtt != nullptr){
+      sensors.requestTemperatures();
+
+//Gets prefered temperature scale based on selection in definitions section
+      #ifdef Celsius
+      float board_temperature = sensors.getTempCByIndex(0);
+      #else
+      float board_temperature = sensors.getTempFByIndex(0);
+      #endif
+
+//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server.
+      char subuf[38];
+      strcpy(subuf, mqttDeviceTopic);
+      strcat(subuf, "/temperature");
+      mqtt->publish(subuf, 0, true, String(board_temperature).c_str());
+    return;}
+  return;}
+return;
+}


Fixed a missing variable declaration.

Fixed a missing variable declaration.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/tectronics/turbull-encabulator.git/'


add arduino sketch

add arduino sketch

error: pathspec 'arduino/muzejni-noc.ino' did not match any file(s) known to git

--- arduino/muzejni-noc.ino
+++ arduino/muzejni-noc.ino
@@ -0,0 +1,63 @@
+const int numSensors = 5;
+const int numReadings = 10;
+const int printFrequency = 10;
+
+int sensors[numSensors] = {
+  A0, A1, A2, A3, A4
+};
+int readings[numSensors][numReadings];
+int indices[numSensors];
+int total[numSensors];
+int average[numSensors];
+int printCounter = 0;
+
+void setup() {
+  for (int i = 0; i < numSensors; i++) {
+    pinMode(sensors[i], INPUT);
+    indices[i] = 0;
+    total[i] = 0;
+    average[i] = 0;
+    for (int j = 0; j < numReadings; j++) {
+      readings[i][j] = 0;
+    }
+  }
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for Leonardo only
+  }
+}
+
+void loop() {
+  printCounter += 1;
+
+  for (int i = 0; i < numSensors; i++) {
+    updateDataForSensor(i, analogRead(sensors[i]));
+    if (printCounter >= printFrequency) {
+      Serial.print(average[i]);
+      Serial.print(";");
+    }
+  }
+
+  if (printCounter >= printFrequency) {
+    printCounter = 0;
+    Serial.println();
+  }
+
+  delay(10);
+}
+
+void updateDataForSensor(int sensorIndex, int value) {
+  int index = indices[sensorIndex];
+
+  total[sensorIndex] = total[sensorIndex] - readings[sensorIndex][index];
+  readings[sensorIndex][index] = value;
+  total[sensorIndex] = total[sensorIndex] + readings[sensorIndex][index];
+  index += 1;
+
+  average[sensorIndex] = total[sensorIndex] / numReadings;
+
+  if (index >= numReadings) {
+    index = 0;
+  }
+  indices[sensorIndex] = index;
+}


Getting started

Getting started

error: pathspec 'Arduino/weather-tree.ino' did not match any file(s) known to git

--- Arduino/weather-tree.ino
+++ Arduino/weather-tree.ino
@@ -0,0 +1,46 @@
+// This #include statement was automatically added by the Spark IDE.
+#include "neopixel/neopixel.h"
+
+#define PIN 6
+
+Adafruit_NeoPixel strip = Adafruit_NeoPixel(24, PIN, WS2812);
+
+
+class Snow {
+
+    unsigned long interval;
+    unsigned long previousMillis;
+
+    public:
+    Snow(unsigned long interval) {
+        interval = interval;
+        previousMillis = 0;
+    }
+
+    void Run() {
+        // int randomPixel = random(0, Adafruit_NeoPixel::numPixels());
+
+        unsigned long currentMillis = millis();
+
+        if(currentMillis - previousMillis > interval) {
+            previousMillis = currentMillis;
+            // Adafruit_NeoPixel::setPixelColor(randomPixel, Adafruit_NeoPixel::Color(127, 127, 127));
+            // Adafruit_NeoPixel::show();
+        }
+
+        // Adafruit_NeoPixel::setPixelColor(randomPixel, 0);
+    }
+
+};
+
+
+void setup() {
+    strip.begin();
+    strip.show(); // Initialize all pixels to 'off'
+    randomSeed(analogRead(0));
+}
+
+
+void loop() {
+
+}


sent temp via mqttn on arduino

sent temp via mqttn on arduino

error: pathspec 'arduino/arduiview-lm35-mqtt/arduiview-lm35-mqtt.ino' did not match any file(s) known to git

--- arduino/arduiview-lm35-mqtt/arduiview-lm35-mqtt.ino
+++ arduino/arduiview-lm35-mqtt/arduiview-lm35-mqtt.ino
@@ -0,0 +1,204 @@
+#include <Adafruit_SleepyDog.h>
+#include <Adafruit_CC3000.h>
+#include <ccspi.h>
+#include <SPI.h>
+#include "utility/debug.h"
+#include "Adafruit_MQTT.h"
+#include "Adafruit_MQTT_CC3000.h"
+
+#define CC3000_IRQ   3
+#define CC3000_VBAT  5
+#define CC3000_CS    10
+
+
+// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
+#define WLAN_SECURITY   WLAN_SEC_WPA2
+// cannot be longer than 32 characters!
+#define WLAN_SSID       "HUAWEI-DevTech"
+// For connecting router or AP, don't forget to set the SSID and password here!!
+#define WLAN_PASS       "Autodesk123"
+
+
+/************************* mqtt server setup *********************************/
+// http://www.cloudmqtt.com
+
+
+#define AIO_SERVER      "m11.cloudmqtt.com"
+#define AIO_SERVERPORT  15521
+#define AIO_USERNAME    "danieldu"
+#define AIO_KEY         "Autodesk123"
+
+// Store the MQTT server, username, and password in flash memory.
+// This is required for using the Adafruit MQTT library.
+const char MQTT_SERVER[] PROGMEM    = AIO_SERVER;
+const char MQTT_USERNAME[] PROGMEM  = AIO_USERNAME;
+const char MQTT_PASSWORD[] PROGMEM  = AIO_KEY;
+
+
+
+//The sensor id in mongoDB, currently we have only one sensor
+#define SENSOR_ID "561083be06dd6162658ae8c8"
+
+Adafruit_CC3000 cc3000 = Adafruit_CC3000(
+                           CC3000_CS,
+                           CC3000_IRQ,
+                           CC3000_VBAT,
+                           SPI_CLOCK_DIVIDER); // you can change this clock speed
+
+
+// Setup the CC3000 MQTT class by passing in the CC3000 class and MQTT server and login details.
+Adafruit_MQTT_CC3000 mqtt(&cc3000, MQTT_SERVER, AIO_SERVERPORT, MQTT_USERNAME, MQTT_PASSWORD);
+
+
+// You don't need to change anything below this line!
+#define halt(s) { Serial.println(F( s )); while(1);  }
+
+
+/****************************** Feeds ***************************************/
+
+// Setup a feed called 'temperature-center' for publishing.
+const char TEMPERATURE_FEED[] PROGMEM = "temperature-center";
+Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, TEMPERATURE_FEED);
+
+
+static Adafruit_CC3000_Client client;
+
+
+uint32_t ip = 0;
+
+unsigned long lastConnectionTime = 0;            // last time you connected to the server, in milliseconds
+const unsigned long postingInterval = 5L * 1000L; // delay between updates, in milliseconds
+
+unsigned long lastConnectWifiTime = 0;
+const unsigned long checkNetworkInterval = 30L * 1000L * 60L; //reconnect WIFI every 20 min
+
+void setup() {
+
+  Serial.begin(115200);
+
+  initWifiConnection();
+}
+
+void loop() {
+
+  if (millis() - lastConnectWifiTime > checkNetworkInterval) {
+
+    initWifiConnection();
+
+    lastConnectWifiTime = millis();
+  }
+
+
+
+
+
+
+    // Make sure to reset watchdog every loop iteration!
+    Watchdog.reset();
+
+    // Ensure the connection to the MQTT server is alive (this will make the first
+    // connection and automatically reconnect when disconnected).  See the MQTT_connect
+    // function definition further below.
+    MQTT_connect();
+
+
+    //get temperature value
+    float temp = 0.0;
+    // get the current temperature from sensor
+    int reading = analogRead(0);
+    temp = reading * 0.0048828125 * 100;
+    Serial.print(F("Current temp"));
+    Serial.println(temp);
+
+
+    // Now we can publish stuff!
+    Serial.print(F("\nSending temperature val "));
+    Serial.print(temp);
+    Serial.print("...");
+    if (! photocell.publish(temp)) {
+      Serial.println(F("Failed"));
+    } else {
+      Serial.println(F("OK!"));
+    }
+
+    // ping the server to keep the mqtt connection alive
+    if(! mqtt.ping()) {
+      Serial.println(F("MQTT Ping failed."));
+    }
+
+
+}
+
+
+void initWifiConnection()
+{
+
+
+  /* Initialise the module */
+  //Serial.println(F("\nInitialising the CC3000 ..."));
+  if (!cc3000.begin())
+  {
+    Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
+    while (1);
+  }
+
+  /* Attempt to connect to an access point */
+  char *ssid = WLAN_SSID;             /* Max 32 chars */
+  Serial.print(F("\nAttempting to connect to "));
+  Serial.println(ssid);
+
+  /* NOTE: Secure connections are not available in 'Tiny' mode!
+   By default connectToAP will retry indefinitely, however you can pass an
+   optional maximum number of retries (greater than zero) as the fourth parameter.
+   */
+  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
+    Serial.println(F("Failed!"));
+    while (1);
+  }
+
+
+  //Serial.println("\nconnected to WIFI.");
+
+  /* Wait for DHCP to complete */
+  //Serial.println("\nRequest DHCP");
+  while (!cc3000.checkDHCP())
+  {
+    delay(1000); // ToDo: Insert a DHCP timeout!
+  }
+
+
+  //Serial.println("\nDHCP is OK");
+
+
+  Serial.println("Initialization completed.\n");
+
+}
+
+
+
+// Function to connect and reconnect as necessary to the MQTT server.
+// Should be called in the loop function and it will take care if connecting.
+void MQTT_connect() {
+  int8_t ret;
+
+  // Stop if already connected.
+  if (mqtt.connected()) {
+    return;
+  }
+
+  Serial.print("Connecting to MQTT... ");
+
+  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
+       Serial.println(mqtt.connectErrorString(ret));
+       if (ret < 0){
+            //lets connect to wifi again
+            initWifiConnection();
+       }
+       Serial.println("Retrying MQTT connection in 5 seconds...");
+       mqtt.disconnect();
+       delay(5000);  // wait 5 seconds
+  }
+  Serial.println("MQTT Connected!");
+}
+
+


Added shofanduino2 program, much simpler since
some of the calculations are now done in the
sensor (filtering out noisy values).

Instead of 'avg' and 'max', we use envelope min
and max values that try to hug the humidity with
a slow moving function. The slow moving function
is based on the half-life equation, so the
parameters are more intuitive.

error: pathspec 'shofanduino2/shofanduino2.ino' did not match any file(s) known to git

--- shofanduino2/shofanduino2.ino
+++ shofanduino2/shofanduino2.ino
@@ -0,0 +1,192 @@
+//#include <Time.h>
+#include <DHT22.h>
+
+#define DHT22_PIN 6
+
+// consts for controlling relays
+const int K1 = 11;
+const int K2 = 12;
+const int milliseconds_switch_delay = 100;
+const int NormallyConnected = HIGH;
+const int NormallyOpen = LOW;
+typedef enum {FAN_OFF, FAN_NOCHANGE, FAN_ON};
+
+// DHT consts/variables
+DHT22 myDHT22(DHT22_PIN);
+
+// consts/variables for humidity sensor and averages etc.
+const int sensorPin = A0;    // select the input pin for the potentiometer
+const int milliseconds_sleep = 5000;
+
+float thresholdLow = 0.0;
+float thresholdHigh = 0.0;
+float env_min = 0.0;
+float env_max = 0.0;
+float humidity = 0.0;
+float temperature = 0.0;
+float on_off = 0;
+
+// variables for decay
+ // number of seconds after which the difference between the humidity and the max envelope should have been halved.
+const float p_halflife_max = 604800.0;
+const float p_halflife_min = 86400.0; // same for the min envelope. This is shorter because it needs to change faster.
+float env_max_decay = 0.0;
+float env_min_decay = 0.0;
+
+// variables to decide the state of the fan
+int state = FAN_OFF; // action to take, based on sensor reading
+
+void getValues() {
+  DHT22_ERROR_t errorCode;
+
+  // The sensor can only be read from every 1-2s, and requires a minimum
+  // 2s warm-up after power-on.
+  delay(milliseconds_sleep);
+
+  errorCode = myDHT22.readData();
+  humidity = myDHT22.getHumidity();
+  temperature = myDHT22.getTemperatureC();
+
+  switch(errorCode) {
+  case DHT_ERROR_NONE:
+    break;
+  case DHT_ERROR_CHECKSUM:
+    Serial.print("E: checksum ");
+    Serial.print(myDHT22._lastCheckSum);
+    Serial.print(" != ");
+    Serial.print(myDHT22._lastExpectedCheckSum);
+    Serial.print(". ");
+    Serial.print(temperature);
+    Serial.print("C ");
+    Serial.print(humidity);
+    Serial.println("%. Are you really sure you set the clock to 8Mhz?");
+    break;
+  case DHT_BUS_HUNG:
+    Serial.println("E: BUS Hung");
+    break;
+  case DHT_ERROR_NOT_PRESENT:
+    Serial.println("E: Sensor not found");
+    break;
+  case DHT_ERROR_ACK_TOO_LONG:
+    Serial.println("E: ACK time out");
+    break;
+  case DHT_ERROR_SYNC_TIMEOUT:
+    Serial.println("E: Sync Timeout");
+    break;
+  case DHT_ERROR_DATA_TIMEOUT:
+    Serial.println("E: Data Timeout");
+    break;
+  case DHT_ERROR_TOOQUICK:
+    Serial.println("E: Polled to quick");
+    break;
+  }
+}
+
+void setup() {
+  Serial.begin(9600);
+
+  // set relays to normally connected
+  pinMode(K1, OUTPUT);
+  pinMode(K2, OUTPUT);
+  digitalWrite(K1, NormallyConnected);
+  digitalWrite(K2, NormallyConnected);
+
+  Serial.println("DHT22 temperature/humidity. Did you set the clock to 8Mhz?");
+
+  // initialize sensor
+  pinMode(sensorPin, INPUT);
+  getValues();
+  env_min = humidity;
+  env_max = humidity;
+
+  // calculate decay
+  float t = milliseconds_sleep / 1000.0f;
+
+  // calculate what would happen after one step, to unit value (1.0)
+  // the result is the scale factor we can apply to the envelope max/min
+  env_max_decay = 1.0f * pow(2, -t / p_halflife_max);
+  env_min_decay = 1.0f * pow(2, -t / p_halflife_min);
+}
+
+void loop() {
+  getValues();
+
+  // clamp the envelope min/max, decay otherwise
+  if (env_min > humidity) {
+    env_min = humidity;
+  } else {
+    // this actually decays the difference between humidity
+    // and env_min, so this means env_min goes up!
+    env_min = humidity - (humidity - env_min) * env_min_decay;
+  }
+  if (env_max < humidity) {
+    env_max = humidity;
+  } else {
+    env_max = humidity + (env_max - humidity) * env_max_decay;
+  }
+
+  // New thresholds based on the envelope values. We
+  // want the fan to turn on pretty quickly, but we don't
+  // want it to turn off quickly. This explains the difference
+  // in factors between low and high.
+  thresholdLow = env_min + (env_max - env_min) / 20.0f;
+  thresholdHigh = env_min + (env_max - env_min) / 2.0f;
+
+  state = FAN_NOCHANGE;
+  if (humidity < thresholdLow) {
+    state = FAN_OFF;
+  } else if (humidity > thresholdHigh) {
+    state = FAN_ON;
+  }
+
+  // switch the relays!
+  switch (state) {
+    case FAN_OFF:
+      // because we don't know the status of the 3-way switch, we switch both
+      // relays at the same time and hope there is no short circuit. This should
+      // not be an issue, but anyway...
+      digitalWrite(K1, NormallyConnected);
+      digitalWrite(K2, NormallyConnected);
+      break;
+    case FAN_ON:
+      digitalWrite(K2, NormallyOpen);
+      // since we don't know how the switch is switched, we first switch K2 to
+      // non-connected, then wait a while for it to settle, before we switch
+      // K1 to connected L. This should prevent any shorts (don't know if that
+      // is actually an issue, but better safe than sorry).
+      delay(milliseconds_switch_delay);
+      digitalWrite(K1, NormallyOpen);
+      break;
+  }
+
+  // display the current state in the graph
+  // don't use absolute values, so the graph can zoom in and show more information
+  switch (state) {
+    case FAN_OFF:
+      on_off = 0.0;
+      break;
+    case FAN_ON:
+      on_off = 1.0;
+      break;
+    case FAN_NOCHANGE:
+      on_off = 0.5;
+      break;
+  }
+
+  // print out the values for debugging/plotting
+  Serial.print("humidity = ");
+  Serial.print(humidity, 1);
+  Serial.print(", threshold_low = ");
+  Serial.print(thresholdLow, 3);
+  Serial.print(", threshold_hi = ");
+  Serial.print(thresholdHigh, 3);
+  Serial.print(", envelope_min = ");
+  Serial.print(env_min, 3);
+  Serial.print(", envelope_max = ");
+  Serial.print(env_max, 3);
+  Serial.print(", temperature = ");
+  Serial.print(temperature, 1);
+  Serial.print(", on_off = ");
+  Serial.println(on_off, 1);
+}
+


Create pin_change_interrupt.ino

Create pin_change_interrupt.ino

error: pathspec 'interrupts/pin_change_interrupt.ino' did not match any file(s) known to git

--- interrupts/pin_change_interrupt.ino
+++ interrupts/pin_change_interrupt.ino
@@ -0,0 +1,39 @@
+#include <Arduino.h>
+
+#define INTERRUPT_PIN 9
+
+/*
+Pin Change Interrupt Example
+
+James H. Bannock, 2017
+Norges teknisk-naturvitenskapelige universitet (NTNU)
+
+Notes:
+  - A button is attached to Pin 9 and GND
+  - INPUT_PULLUP is used to pin the voltage of the input to 5V while the Button
+    is open
+  - On the Uno there are three ports, Pin 9 is on PORT B (PORTB)
+  - There are Pin Change Interrupts associated to each port, PCINT0 is for PORTB
+  - PCINT0 must be activated by enabling Pin Change Interrupt Enable 0 (PCIE0)
+    in the Pin Change Interrupt Control Register (PCICR)
+  - Pin 9 corresponds to Pin Change Interrupt 1 (PCINT1) and must be enabled in
+    the Pin Change Mask 0 (PCMSK0)
+*/
+
+void setup() {
+  Serial.begin(9600); (!Serial); // setup the serial port
+  pinMode(INTERRUPT_PIN, INPUT_PULLUP); // pull-up to pin voltage to 5V
+  setupPinInterrupt(); // abstract pin interrupt setup
+}
+
+ISR (PCINT0_vect) { // Interrupt Service Routine (ISR) for Pin Change Interrupt
+                    // Request 0 (PCINT0)
+  Serial.println("Button State Change Detected!"); // print an output
+}
+
+void loop() {}
+
+void setupPinInterrupt() {
+  PCICR |= (1 << PCIE0); // enable PCINT0 ISR
+  PCMSK0 |= (1 << PCINT1); // enable interrupt on PCINT1 (Pin 9)
+}


Added MiniDora sketch for the Arduino Uno

error: pathspec 'Navigation/base_controller/minidora.ino' did not match any file(s) known to git

--- Navigation/base_controller/minidora.ino
+++ Navigation/base_controller/minidora.ino
@@ -0,0 +1,73 @@
+#include <Servo.h>
+  Servo posBack;
+  Servo posFront;
+  Servo driveR;
+  Servo driveL;
+  String SerialInput="";
+  String Svalue;
+  int value;
+void setup()
+{
+  Serial.begin(9600);
+  posBack.attach(7);// 1 dot
+  posFront.attach(8);// 4 dot
+  driveR.attach(9);// 2 dots
+  driveL.attach(10);// 3 dots
+}
+
+/*
+MiniDora EVX 2 (EVX 3014)
+NOTE: Longer treads are the front treads; Shorter treads are the back treads.
+
+driveR: Calibs for right side drive motors through the ESC.
+FullSpeedForward:0-1349; x-y (x >> 10000)
+Forward:1350(high speed)-1484 (lowest speed)
+Braking:1485-1555
+1556-1557: Not reliable
+Reverse:1558(lowest speed)-1900 (highest speed)
+FullSpeedReverse:1900-x
+
+driveL: Calibs for left side drive motors through the ESC.
+FullSpeedForward:1930-z
+Forward:1572(slowest)-1930 (fastest)
+Braking:1475-1570
+1571-1572:Not reliable
+Reverse:1474 (slowest)- 1320(fastest)
+FullSpeedReverse:-1474
+*/
+void loop()
+{
+
+   if(Serial.available())
+                              {
+                               for(int i=1;i<=3;i++)
+                               {
+                                   SerialInput = Serial.readStringUntil(' ');
+                                if(SerialInput!="")
+                                {
+                                  Serial.print("Processing Command:");Serial.print(SerialInput);Serial.println();
+                                  char motor=SerialInput.charAt(0);
+                                 // Serial.print("LED=");Serial.print(LED);Serial.println();
+                                  for(int j=1;SerialInput.charAt(j)!='\0';j++)
+                                   Svalue+=SerialInput.charAt(j);
+                                  // Serial.print("Svalue=");Serial.print(Svalue);Serial.println();
+                                   value=Svalue.toInt();//In case the value input is not in the range of 0-255, the input value is converted
+                                  // into integer with truncation and written to the Pin(s).
+                                  // Serial.print("value=");Serial.print(value);Serial.println();
+                                   switch(motor)
+                                       {
+                                         case 'r': {Serial.print("Writing");Serial.print(value);Serial.print("to motor 'r' ");Serial.println();driveR.writeMicroseconds(value);break;}
+                                         case 'l': {Serial.print("Writing");Serial.print(value);Serial.print("to motor 'l' ");Serial.println();driveL.writeMicroseconds(value);break;}
+                                         //case 'b': {BlueValue=value;break;}
+                                        default: {Serial.print("Wrong Command. Please check your input");Serial.println();break;}
+                                       }
+                                 }
+                                 Svalue="";
+                                }
+
+                                SerialInput="";
+                              }
+
+
+}
+


Created sketch

Created sketch

This is still work in progress. Various code-sources are used to create
this. Current instance is unlikely to work.

error: pathspec 'Vsens/src/sketch.ino' did not match any file(s) known to git

--- Vsens/src/sketch.ino
+++ Vsens/src/sketch.ino
@@ -0,0 +1,120 @@
+/*--------------------------------------------------------------
+  Adapted from : volt_measure
+                 22 May 2013 by W.A. Smith, http://startingelectronics.com
+
+  Modified by  : M. Hendrix
+--------------------------------------------------------------*/
+
+// number of samples per measurement
+#define numSamples 10
+#define measurePin A0
+
+int sumSamples = 0;             // sum of samples taken
+int cntSamples = 0;             // current sample number
+float voltage = 0.0;            // calculated voltage
+
+const float ref5V = 5.000;     // calibration of reference value for 5.0V ADC
+
+
+void setup()
+{
+    Serial.begin(9600);
+}
+
+void loop()
+{
+    // take a number of analog samples and add them up
+    for (cntSamples = 0; cntSamples <= numSamples; cntSamples++) {
+        sumSamples += analogRead(measurePin);
+        delay(10);
+    }
+    // calculate the voltage
+    // use 5.0 for a 5.0V ADC reference voltage
+    // 5.015V is the calibrated reference voltage
+    voltage = ((float)sumSamples / (float)numSamples * ref5V) / 1024.0;
+    // send voltage for display on Serial Monitor
+    // voltage multiplied by 11 when using voltage divider that
+    // divides by 11. 11.132 is the calibrated voltage divide
+    // value
+    Serial.print(voltage * 11.000);
+    Serial.println (" V");
+    cntSamples = 0;
+    sumSamples = 0;
+}
+
+
+/*
+  DisplayMoreThan5V sketch
+  prints the voltage on analog pin to the serial port
+  Do not connect more than 5 volts directly to an Arduino pin.
++/
+const float referenceVolts = 5;        // the default reference on a 5-volt board
+
+const float R1 = 1000; // value for a maximum voltage of 10 volts
+const float R2 = 1000;
+// determine by voltage divider resistors, see text
+const float resistorFactor = 1023.0 * (R2/(R1 + R2));
+const int batteryPin = A0;         // +V from battery is connected to analog pin 0
+
+const int led[6] = {3, 5, 6, 9, 10, 11};
+const int sensPin = A0;
+const int maxoutput = 191;
+const float factor = (6.0*maxoutput)/1024.0;
+
+void setup() {
+  Serial.begin(9600);
+  for (int i = 0; i<=5; i++) {
+    pinMode(led[i],OUTPUT);
+    digitalWrite(led[i],HIGH);
+  }
+  delay(500);
+  ledsoff();
+}
+
+void loop() {
+
+   int val = analogRead(sensPin);  // read the value from the sensor
+   Serial.print(val);
+   Serial.print(" = ");
+   float volts = (val / resistorFactor) * referenceVolts ; // calculate the ratio
+   Serial.println(volts);  // print the value in volts
+  ledon(val * factor);
+  /* pulse
+  for (int i = 0; i <= 6*maxoutput; i++) {
+    ledon(i);
+    //delay(0.5);
+  }
+  for (int i = 6*maxoutput; i >= 0; i--) {
+    ledon(i);
+    delay(1);
+  }
+  +/
+  delay(1000);
+}
+
+void ledsoff() {
+  for (int i = 0; i<=5; i++) {
+    digitalWrite(led[i],LOW);
+  }
+}
+
+void ledon(int v) {
+  int w;
+  //Serial.print(v);
+  //Serial.print(": ");
+  if (v > 6* maxoutput) { v=6*maxoutput; }
+
+  for (int out=5; out >= 0; out = out -1) {
+    if (v > out * maxoutput) {
+      w = v - out * maxoutput;
+   //     Serial.print(w);
+   //     Serial.print(" ");
+      analogWrite(led[out], w);
+      v = v- w;
+    }
+    else { analogWrite(led[out],0); }
+  }
+  //Serial.println(".");
+}
+
+*/


Training Mix With Pin Change Interrupts, Servo Control, and Relay Control

error: pathspec 'TrainingMix/TrainingMix/TrainingMix.ino' did not match any file(s) known to git

--- TrainingMix/TrainingMix/TrainingMix.ino
+++ TrainingMix/TrainingMix/TrainingMix.ino
@@ -0,0 +1,421 @@
+/*
+ * Include libraries at the top of the source document. These signal the compiler
+ * to look in the the specified libraries for keywords and function calls that we
+ * will be using.
+ *
+ * Servo.h:         allows us to easily write a PWM signal directly to servos.
+ *
+ * PinChangeInt.h:  extends the Arduino unit's capabilities by "listening" for
+ *                  changes in the incoming signals. The attachInterrupt() function
+ *                  in the base library will listen for an entire duty cycle. For
+ *                  our purposes this is a waste of resources and will not allow us
+ *                  to realize control of the 12 actuators necessary for the full
+ *                  project. More on how to use it later in the file.
+ */
+
+#include <Servo.h>
+#include <PinChangeInt.h>
+
+/*
+ * Pre-define the pins used on the Arduino board. All pins defined are digital pins.
+ *
+ * REL_IN_PIN:      Input pin receiveing the incoming PWM signal from the X8R
+ *                  receiver(the remote control) that controls the linear actuator.
+ *
+ * RELAY1_OUT_PIN:  Pin outputting to relay 1.
+ * RELAY2_OUT_PIN:  Pin outputting to relay 2.
+ *
+ * REL_FLAG:        Bit flag to let the Arduino system know there is a change in the
+ *                  state of the incoming signal (Changing from HIGH to LOW,
+ *                  or vice versa).
+ *
+ * **Note:  BIT FLAGS are represented in base 10 in this source code
+ *          (1, 2, 4, up to 256). In base 2 we can treat a 8-bit unsigned integer as
+ *          a quick and dirty 8-element boolean array. In this version of the code.
+ *
+ *          EX: 1     = 00000001
+ *              2     = 00000010
+ *              1 + 2 = 00000011
+ *
+ *          We may use a 16-bit number for double the flags, however we only two
+ *          flags for this project.
+ */
+
+#define REL_IN_PIN 10
+#define RELAY1_OUT_PIN 7
+#define RELAY2_OUT_PIN 8
+#define REL_FLAG 2
+
+/*
+ * END_IN_PIN:    Input pin receiveing the incoming PWM signal from X8R receiver
+ *                (the remote control) that controls the end effector.
+ *
+ * END_OUT_PIN:   Pin outputting to end effector.
+ *
+ * END_FLAG:      Bit flag to let the Arduino system know there is a change in the
+ *                state of the incoming signal (Changing from HIGH to LOW,
+ *                or vice versa).
+ */
+
+#define END_IN_PIN 11
+#define END_OUT_PIN 2
+#define END_FLAG 1
+
+/*
+ * bUpdateFlagsShared is the variable that holds the bit flags.
+ *
+ * **NOTE:  "volatile" keyword tells the compiler to allow this variable to be
+ *          modified outside of the MAIN control loop. Normally, the Arduino will
+ *          "lock-out" other processes. We need these variables to be open so we can
+ *          allow sensors to intrrupt the main loop and modify them.
+ */
+
+volatile uint8_t bUpdateFlagsShared;
+
+/*
+ * volatile uint16_t usEndInShared: 16-bit, unsigned, integer that stores how long
+ *                                  the incloming PWM signalfor the end effector is
+ *                                  HIGH. A 16-bit variable is called a 'short'.
+ *
+ * volatile uint16_t usRelInShared: 16-bit unsigned integer that stores how long the
+ *                                  incloming PWM signal for the relay is high.
+ *
+ * **NOTE:  On the naming convention,
+ *          [if unsigned][bits][what it does][if shared (read volatile)]
+ *          So, usEndInShared means [unsigned][short][end effector input][shared]
+ *
+ * **NOTE:  The duty cycle of these servos is 20,000 microseconds. Therefore, an
+ *          8-bit, unsigned variable will not work because the maximum value it can
+ *          hold is 256. The next size variable available is 16-bits which can store
+ *          an unsigned integer between 0 and 65,536.
+ */
+
+volatile uint16_t usEndInShared;
+volatile uint16_t usRelInShared;
+
+/*
+ * uint32_t ulEndStart: [unsigned][long][end effector signal start-time]
+ *                      When the Arduino "senses" a change in the end effector input
+ *                      pin from LOW to HIGH, the system time in microseconds, is
+ *                      saveded to the 32-bit integer variable ulEndStart. More on
+ *                      this later in the code.
+ *
+ * uint32_t ulRelStart: [unsigned][long][relay signal start-time]
+ *                      Just like the end effector, this is the variable that saves
+ *                      the start-time for the realy controlling the linear actuator
+ *
+ *                      Later,
+ *                      when the signal changes back from HIGH to LOW, the ulEndStart
+ *                      is subtracted from the current time. Like a timer, the
+ *                      difference gives us the signal time length to tell the end
+ *                      effector servo. I
+ */
+
+uint32_t ulEndStart;
+uint32_t ulRelStart;
+
+/*
+ * Servo servoEnd: [class][name of instance]
+ *                 Here we are telling the compiler that there will be a instance of
+ *                 the Servo class. Creating servoEnd and attaching it to the output
+ *                 pin for the end effector will allow us to control the servo in it.
+ *                 The methods in the Servo class will handle all of the background
+ *                 magic to make it all happen.
+ */
+
+Servo servoEnd;
+
+void setup()
+{
+  /*
+   * Start the default serial port at standard baudrate 9600.
+   * This opens communication with the serial monitor on
+   * the computer connected to the arduino unit through the
+   * USB port.
+  */
+  Serial.begin(9600);
+
+  /*
+   * Before we can begin communication to the end effector, we must "attach" the
+   * output pin to the instance of the Servo class we created earlier. This will
+   * allow us to manipulate the servo.
+   */
+
+  servoEnd.attach(END_OUT_PIN);
+
+  /*
+   * Because the linear actuator is controlled by two relays, we cannot use the Servo
+   * class to manage how we control it. Instead, we must control the relays directly.
+   * Don't worry, it's very simple. Much like the Servo class, first we much declare
+   * which pins are the outputs for the two relays.
+   */
+
+  pinMode(RELAY1_OUT_PIN, OUTPUT);
+  pinMode(RELAY2_OUT_PIN, OUTPUT);
+
+  /*
+   * This code tells the compiler that we will be listeing to REL_IN_PIN and
+   * END_IN_PIN for changes in the signal (remember, LOW to HIGH). When there is a
+   * change in signal, execute the function calcRel or calcEnd, respective to the
+   * pin.
+   */
+
+  PCintPort::attachInterrupt(REL_IN_PIN, calcRel, CHANGE);
+  PCintPort::attachInterrupt(END_IN_PIN, calcEnd, CHANGE);
+}
+
+void loop() {
+
+  /*
+   * static uint16_t usEndIn: [unsigned][short: 16-bit][end effector input]
+   * static uint16_t usRelIn: [unsigned][short: 16-bit][end relay input]
+   *                          Local variables get created and destroyed every time
+   *                          the function they reside in is called, so the value is
+   *                          not retained. Making the variable 'static' retains the
+   *                          variable through subsequent iterations of the main
+   *                          loop, so the value inside is intact.
+   *
+   * **NOTE:  We will be using these variables to hold copies of the raw input values
+   *          from the receiver. We have to hold copies of the input values in
+   *          usEndInShared, usRelInShared, and bUpdateFlagsShared because we cannot
+   *          guarantee we will have read access to these variables when it comes
+   *          time to use them; also, we cannot guarantee the values won't be updated
+   *          mid-loop.
+   */
+
+  static uint16_t usEndIn;
+  static uint16_t usRelIn;
+
+  static uint8_t bUpdateFlags;
+
+  /*
+   * This is an if statement. It means
+   * if (such and such is true) {
+   *     do is...
+   *     and this...
+   * }
+   *
+   * In the following statement there is some programming trickery.
+   * if(bUpdateFlagsShared), means: if bUpdateFlagsShared has any value other than
+   * 0, do the following code. In Arduino, indeed in all of the languages I've
+   * encountered, false is represented as 0. So the following code will be executed
+   * provided there are bit-flags indicating there are values to process.
+   */
+
+  if(bUpdateFlagsShared) {
+
+    /*
+     * Remember those copies I mentioned just before? This is where we do it.
+     * "noInterrupts()" tells the system that we won't be taking any new values while
+     * we make our copies.
+     */
+    noInterrupts();
+
+    //make a copy of the bit-flags
+    bUpdateFlags = bUpdateFlagsShared;
+
+    /*
+     * Here we are checking if there is flag for the relay input.
+     * "bUpdateFlags & REL_FLAG", is a Bitwise AND operation. The condition checks if
+     * there is bit-flag for the relay operation.
+     *
+     * To visualize:
+     *
+     * If there was only one flag indicating we had a new input signal for the relay
+     *
+     * 0  0  1  0    bUpdateFlags
+     * 0  0  1  0    REL_FLAG
+     * ----------
+     * 0  0  1  0    (bUpdateFlags & REL_FLAG) = returned result
+     *
+     * (4 in binary which is not 0, so the result is TRUE)
+     *
+     * We now will go and get a copy of the input value for the relay.
+     *
+     * What if bUpdateFlags was 0?
+     *
+     * 0  0  0  0    bUpdateFlags
+     * 0  0  1  0    REL_FLAG
+     * ----------
+     * 0  0  0  0    (bUpdateFlags & REL_FLAG) = returned result
+     *
+     * (0 in binary, so the result is FALSE)
+     *
+     * What if bUpdateFlags only had a bit flag for the end effector?
+     *
+     * 0  0  0  1    bUpdateFlags
+     * 0  0  1  0    REL_FLAG
+     * ----------
+     * 0  0  0  0    (bUpdateFlags & REL_FLAG) = returned result
+     *
+     * (0 in binary, so the result is FALSE)
+     */
+    if(bUpdateFlags & REL_FLAG)
+    {
+      usRelIn = usRelInShared;
+    }
+
+    if(bUpdateFlags & END_FLAG)
+    {
+      usEndIn = usEndInShared;
+    }
+
+    //We've processed all of the necessary flags. Reset bUpdateFlagsShared to 0 so it
+    //can receive new flags from the interrupts.
+    bUpdateFlagsShared = 0;
+
+    //resume allowing interrupts to write data.
+    interrupts();
+  }
+
+  /*
+   * Now that we have shiny new values to play with, lets use them to control our
+   * end effector and relay.
+   *
+   * First we check for a flag to process the respective data.
+   */
+
+  if(bUpdateFlags & REL_FLAG)
+  {
+    // This line is commented out because it was used for debug purposes
+    // Serial.println(usRelIn);
+
+    /*
+     * usRelIn has holds the length of time the input signal was HIGH. If the input
+     * signal is HIGH for less than 1100 microseconds, we retract the linear actuator
+     * connected to the relay. if the signal is greater than 1900 microseconds, we
+     * extend the linear actuator. If the value is anwhere in between, we simply turn
+     * both relays off and the linear actuator will not move.
+     *
+     * digitalWrite(RELAY2_OUT_PIN,1);          // Turns Relay Off
+     * digitalWrite(RELAY1_OUT_PIN,0);          // Turns ON Relay 1
+     *
+     * The linear actuator exends and retracts depending on the polarity of the
+     * eletricity flowing to it. To "flip" the polarity, we connected 2 relays in an
+     * H-Bridge configuration. Turning relay 1 on and relay 2 off makes the
+     * the electricity flow in one direction. if we turn relay 1 off and relay 2 on,
+     * the polarity reverses. If we turn both relays off, the circuit is broken and
+     * the linear actuator does nothing.
+     */
+
+    if(usRelIn < 1100) {
+      //sometimes the signal is missed between toggles
+      //confirm relay 2 is off. otherwise no movement, relays cancel out
+      digitalWrite(RELAY2_OUT_PIN,1);          // Turns Relay Off
+      digitalWrite(RELAY1_OUT_PIN,0);          // Turns ON Relay 1
+    }
+    else if(usRelIn > 1900) {
+      //cofnirm relay 1 is off
+      digitalWrite(RELAY1_OUT_PIN,1);          // Turns Relay Off
+      digitalWrite(RELAY2_OUT_PIN,0);          // Turns ON Relay 2
+    }
+    else {
+      digitalWrite(RELAY1_OUT_PIN,1);          // Turns Relay 1 Off
+      digitalWrite(RELAY2_OUT_PIN,1);          // Turns Relay 2 Off
+    }
+  }
+
+  //check for an end effector flag
+  if(bUpdateFlags & END_FLAG)
+  {
+    /*
+     * Here we are checking fit the old value for servoEnd is not equal to the new
+     * If there is no change, there is no reason to update the value we are sending
+     * to the end effector servo.
+     */
+
+    if(servoEnd.readMicroseconds() != usEndIn)
+    {
+      /*
+       * The servo in the end effector can only handle signal values that are natural
+       * numbers between 1000 and and 2000. being an unsigned integer automatically
+       * handles the natural number part. But what about the upper and lower bounds?
+       * Sometimes the receiver will get pulses that are less than 1000 and greater
+       * than 2000 (on testing, I received signals ranging from 994 and 2015
+       * microseconds). Sending a signal that exceeds the bounds of the servo could
+       * damage it. The following conditions automatically set any incoming signal
+       * that is excessive or deficient to its respective bound. All other values in
+       * between are untouched.
+       */
+      if(usEndIn < 1000)
+        usEndIn = 1000;
+      else if(usEndIn > 2000)
+        usEndIn = 2000;
+
+      /*
+       * The end effector is tough but it's not tough enough to withstand constant
+       * pressure from the servo within. We can't limit the servo with conditions
+       * like we did before because the dial would only work within conditions
+       * So, we have to limit servo yet again witout losing the range of output of
+       * the dial controlling the opening and closing of the fingers. If the fingers
+       * close too tight, we risk damaging the lower plate, washer, and spacers.
+       * If the servo opens too wide, we risk cracking the casing and damaging the
+       * lower plate.
+       *
+       * The solution is to map the values between valid inputs, 1000 and 2000, to a
+       * restricted, safe range. After testing, I found the acceptable ranges to be
+       * 1220 to 1480.
+       */
+
+      usEndIn = map(usEndIn, 1000, 2000, 1220, 1480);
+
+      //print the final output for debugging purposes.
+      //Serial.println(usEndIn);
+
+      //And finally, we update the servo with the new signal value, and it will
+      //rotate to the new position.
+      servoEnd.writeMicroseconds(usEndIn);
+    }
+  }
+
+  //Now that we've processed the incoming signals, zero out the bit-flag variable
+  //so it's ready for the next loop.
+  bUpdateFlags = 0;
+}
+
+/*
+ * These are the functions called when a new inturrupt arrives. Remember these?
+ *
+ * PCintPort::attachInterrupt(REL_IN_PIN, calcRel, CHANGE);
+ * PCintPort::attachInterrupt(END_IN_PIN, calcEnd, CHANGE);
+ */
+
+void calcRel()
+{
+
+  //if the input signal is HIGH,
+  if(digitalRead(REL_IN_PIN) == HIGH)
+  {
+    //get the current system time in microseconds and save it
+    ulRelStart = micros();
+  }
+  else
+  {
+    /*
+     * Else the signal must be LOW and we can calculate the signal length
+     * Get the current system time, subtract the start time we saved earlier,
+     * and save the result for processing.
+     */
+    usRelInShared = (uint16_t)(micros() - ulRelStart);
+
+    //Remove the relay bit flag from the bit-flag variable.
+    bUpdateFlagsShared |= REL_FLAG;
+  }
+}
+
+/*
+ * calcEnd does the same thing as calcRel, just with different variables.
+ * See the previous function for details.
+ */
+void calcEnd()
+{
+  if(digitalRead(END_IN_PIN) == HIGH)
+  {
+    ulEndStart = micros();
+  }
+  else
+  {
+    usEndInShared = (uint16_t)(micros() - ulEndStart);
+    bUpdateFlagsShared |= END_FLAG;
+  }
+}