Eliminate BLYNK_LOG

Eliminate BLYNK_LOG

--- examples/BoardsAndShields/ESP8266_Standalone_SmartConfig/ESP8266_Standalone_SmartConfig.ino
+++ examples/BoardsAndShields/ESP8266_Standalone_SmartConfig/ESP8266_Standalone_SmartConfig.ino
@@ -52,8 +52,8 @@
       while (1) {
         delay(1000);
         if (WiFi.smartConfigDone()) {
-          Serial.println("");
-          BLYNK_LOG("SmartConfig: Success");
+          Serial.println();
+          Serial.println("SmartConfig: Success");
           break;
         }
         Serial.print("|");


finalized code for test run with plane

finalized code for test run with plane

--- audible-altimeter/audible-altimeter.ino
+++ audible-altimeter/audible-altimeter.ino
@@ -10,7 +10,7 @@

 enum period_t sleepPeriod; // how long the processor should sleep for
 double basePressure, previousPressure, previousAltitude; // pressure (mb): 0 AGL
-
+boolean inPlaneTriggered;
 //3.33 fpm elevator

 void BMPSetup() {
@@ -54,13 +54,13 @@
   delay(100); //Allow for serial print to complete.

   BMPSetup();
+  inPlaneTriggered = false;

   sleepPeriod = SLEEP_8S;

   Serial.println("Initialisation complete.");
   delay(100); //Allow for serial print to complete.
 }
-

 /***************************************************
  *  Name:        loop
@@ -80,25 +80,38 @@
   digitalWrite(LED_PIN, HIGH);

   currentPressure = getPressure();
+
+  Serial.print("Current pressure:");
+  Serial.println(currentPressure);
+
   currentAltitude = pressure.altitude(currentPressure, basePressure);
+
+  Serial.print("Current altitude:");
+  Serial.println(currentAltitude);

-  if (currentAltitude < 2 && currentAltitude > -2) {
+  if (currentAltitude < 5 && currentAltitude > -5 && !inPlaneTriggered) {
+    Serial.println("On ground - adjusting for altitude");
     sleepPeriod = SLEEP_8S;
     // consider this noise, set as base
     basePressure = currentPressure;
   } else {
+    Serial.println("In plane");
+    inPlaneTriggered = true;
     // we're in the air
     sleepPeriod = SLEEP_1S;

     // human falls at 53 m/s
     // open canopy, full flight 5 m/s
     // refresh rate is 1 s on arduino
-    while (previousAltitude - currentAltitude > 35 && // m/s
-           currentAltitude < 1370) { // 4500 ft
+    Serial.print("DIFF:");
+    Serial.println(previousAltitude - currentAltitude);
+    if (previousAltitude - currentAltitude > 5 && // m/s
+           currentAltitude < 1524) { // 3000 ft
+      Serial.println("TRIGGER!");
       // beep and wait for slowing down
-      tone(2, 2048);
-      LowPower.powerSave(SLEEP_1S, ADC_OFF, BOD_ON, TIMER2_ON);
-      noTone(2);
+//      tone(2, 2048);
+//      LowPower.powerSave(SLEEP_1S, ADC_OFF, BOD_ON, TIMER2_ON);
+//      noTone(2);
     }
   }

@@ -106,6 +119,8 @@
   previousAltitude = currentAltitude;

   digitalWrite(LED_PIN, LOW);
+
+  delay(100); // for console

   LowPower.powerDown(sleepPeriod, ADC_OFF, BOD_ON);
 }


removed the SPI and added client.available protection before reading the tcp connection

--- libraries/Ethernet/examples/DhcpChatServer/DhcpChatServer.ino
+++ libraries/Ethernet/examples/DhcpChatServer/DhcpChatServer.ino
@@ -4,30 +4,27 @@
  A simple server that distributes any incoming messages to all
  connected clients.  To use telnet to  your device's IP address and type.
  You can see the client's input in the serial monitor as well.
- Using an Arduino Wiznet Ethernet shield.

  THis version attempts to get an IP address using DHCP
-
- Circuit:
- * Ethernet shield attached to pins 10, 11, 12, 13

  created 21 May 2011
  by Tom Igoe
  Based on ChatServer example by David A. Mellis
+
+ modified 9 Feb 2014
+ by Craig Hollabaugh

  */

-#include <SPI.h>
 #include <Ethernet.h>

 // Enter a MAC address and IP address for your controller below.
 // The IP address will be dependent on your local network.
 // gateway and subnet are optional:
-byte mac[] = {
-  0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
-IPAddress ip(192,168,1, 177);
-IPAddress gateway(192,168,1, 1);
-IPAddress subnet(255, 255, 0, 0);
+byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
+IPAddress ip(192,168,1,177);
+IPAddress gateway(192,168,1,1);
+IPAddress subnet(255,255,255,0);

 // telnet defaults to port 23
 EthernetServer server(23);
@@ -36,6 +33,7 @@
 void setup() {
    // open the serial port
   Serial.begin(9600);
+  Serial.println("\n\nDhcpChatServer setup");
   // start the Ethernet connection:
   Serial.println("Trying to get an IP address using DHCP");
   if (Ethernet.begin(mac) == 0) {
@@ -69,12 +67,12 @@
       gotAMessage = true;
     }

-    // read the bytes incoming from the client:
-    char thisChar = client.read();
-    // echo the bytes back to the client:
-    server.write(thisChar);
-    // echo the bytes to the server as well:
-    Serial.print(thisChar);
+    if (client.available() > 0) {
+      // read the bytes incoming from the client:
+      char thisChar = client.read();
+      // echo the char to the console
+      Serial.print(thisChar);
+    }
   }
 }


Tmeter: print voltages.

Tmeter: print voltages.

--- microcontrollers/arduino-mega2560/mega-tmeter/mega-tmeter.ino
+++ microcontrollers/arduino-mega2560/mega-tmeter/mega-tmeter.ino
@@ -4,11 +4,20 @@
 unsigned gate_output = 0;       // control voltage to the Gate pin of transistor
 unsigned gate_input;            // measured voltage of the Gate pin
 unsigned drain_input;           // measured voltage of the Drain pin
-unsigned volt5_input;           // measured voltage of +5V power source
-unsigned volt15_input;          // measured voltage of +15V power source
-
+unsigned power5_input;          // measured voltage of +5V power source
+unsigned power15_input;         // measured voltage of +15V power source
+
+//
 // Use Timer3, pins 2, 3 and 5 on Mega 2560 board.
+//
 static const int gate_output_pin = 2;
+
+//
+// Coefficients of resistor dividers.
+//
+#define DRAIN_MULT      (((33 + 10) / 10.0) * (11.33 / 11.21)) // resistors 33k and 10k
+#define POWER5_MULT     (((10 + 10) / 10.0) * (5.00  / 5.08))  // resistors 10k and 10k
+#define POWER15_MULT    (((33 + 10) / 10.0) * (11.39 / 11.06)) // resistors 33k and 10k

 /*
  * Read a character from the serial port.
@@ -41,6 +50,15 @@
 #endif

 /*
+ * Print volts.
+ */
+void print_mv(int mv)
+{
+    Serial.print(mv / 1000.0, 2);
+    Serial.write("mV");
+}
+
+/*
  * Print the current state of the device.
  */
 void show_state()
@@ -48,18 +66,43 @@
     // Update input values.
     gate_input = analogRead(A0);
     drain_input = analogRead(A1);
-    volt5_input = analogRead(A2);
-    volt15_input = analogRead(A3);
+    power5_input = analogRead(A2);
+    power15_input = analogRead(A3);
+
+    // Compute voltages.
+    int gate_mv = gate_input * (5000.0 / 1024);
+    int drain_mv = drain_input * ((5000.0 / 1024) * DRAIN_MULT);
+    int power5_mv = power5_input * ((5000.0 / 1024) * POWER5_MULT);
+    int power15_mv = power15_input * ((5000.0 / 1024) * POWER15_MULT);

     // Display all values.
-    Serial.write("\r\n Gate output: "); Serial.print(gate_output);
-    Serial.write("\r\n  Gate input: "); Serial.print(gate_input);
-    Serial.write("\r\n Drain input: "); Serial.print(drain_input);
-    Serial.write("\r\n  Voltage +5: "); Serial.print(volt5_input);
-    Serial.write("\r\n Voltage +15: "); Serial.print(volt15_input);
-    //print_binary("\r\n TCCR3A = ", TCCR3A); print_binary(", TCCR3B = ", TCCR3B);; print_binary(", TCCR3C = ", TCCR3C);
-    //print_binary("\r\n  OCR3A = ", OCR3A); print_binary(", OCR3B = ", OCR3B);
-    //print_binary("\r\n   ICR3 = ", ICR3);
+    Serial.write("\r\nGate output: ");
+    Serial.print(gate_output);
+
+    Serial.write("\r\n Gate input: ");
+    print_mv(gate_mv);
+    Serial.write(" (");
+    Serial.print(gate_input);
+    Serial.write(")");
+
+    Serial.write("\r\nDrain input: ");
+    print_mv(drain_mv);
+    Serial.write(" (");
+    Serial.print(drain_input);
+    Serial.write(")");
+
+    Serial.write("\r\n  Power +5V: ");
+    print_mv(power5_mv);
+    Serial.write(" (");
+    Serial.print(power5_input);
+    Serial.write(")");
+
+    Serial.write("\r\n Power +15V: ");
+    print_mv(power15_mv);
+    Serial.write(" (");
+    Serial.print(power15_input);
+    Serial.write(")");
+
     Serial.write("\r\n");
 }

@@ -147,9 +190,9 @@
     // Configure pins.
     // D2 - gate output
     // A0 - gate input
-    // A1 - drain input with divisor 1:3
+    // A1 - drain input with divisor 1:4.3
     // A2 - +5V input with divisor 1:2
-    // A3 - +15V input with divisor 1:4
+    // A3 - +15V input with divisor 1:4.3
     //
     pinMode(gate_output_pin, OUTPUT);


remove germ buster

remove germ buster

--- use_cases/germ_buster/soap.ino
+++ use_cases/germ_buster/soap.ino
@@ -1,60 +0,0 @@
-#include <Process.h>
-// constants won't change. They're used here to
-// set pin numbers:
-const int buttonPin = 2;     // the number of the pushbutton pin
-const int ledPin =  13;      // the number of the LED pin
-
-// variables will change:
-int buttonState = 0;         // variable for reading the pushbutton status
-int oneClick = 0;
-
-void setup() {
-
-  // Bridge takes about two seconds to start up
-  // it can be helpful to use the on-board LED
-  // as an indicator for when it has initialized
-  pinMode(13, OUTPUT);
-  digitalWrite(13, LOW);
-  Bridge.begin();
-  digitalWrite(13, HIGH);
-
-  SerialUSB.begin(9600);
-
-  while (!SerialUSB); // wait for a serial connection
-}
-
-void runCurl(String value){
-  Process p;
-  p.runShellCommand("curl -X POST -H \"X-Device-Secret: 12345\" \"http://54.246.192.181:8080/report?key=restroom_1" + value+"\" ");
-  while (p.available() > 0) {
-    char c = p.read();
-    SerialUSB.print(c);
-  }
-  // Ensure the last bit of data is sent.
-  SerialUSB.flush();
-}
-
-void loop() {
-  // read the state of the pushbutton value:
-  buttonState = digitalRead(buttonPin);
-
-  // check if the pushbutton is pressed.
-  // if it is, the buttonState is HIGH:
-  if (buttonState == HIGH) {
-    // turn LED on:
-    digitalWrite(ledPin, HIGH);
-    if(oneClick == 0){
-      SerialUSB.print("Soap 1 pressed\n");
-      runCurl("&label=soap_1&value=1");
-      SerialUSB.print("http sent");
-      oneClick = 1;
-    }
-  } else {
-    // turn LED off:
-    digitalWrite(ledPin, LOW);
-    if(oneClick == 1 ){
-      SerialUSB.print("end soap\n");
-      oneClick=0;
-     }
-  }
-}


Startup button for v3.1 . Shitt triggers has invertors!

--- code/PABS/PABS.ino
+++ code/PABS/PABS.ino
@@ -36,11 +36,13 @@
 	SystemMethodsObject.PlaySound(1000, 1000);
 	Serial.write("Started!");

+	SystemMethodsObject.initDisplay();
+
 	int state = 0;
-	while (digitalRead(Constants.adminStartButton) == HIGH)
+	while (digitalRead(Constants.adminStartButton) != HIGH)
 	{
 		SystemMethodsObject.SetDisplayNumber(state);
-		if (digitalRead(Constants.adminResetButton) != HIGH)
+		if (digitalRead(Constants.adminResetButton) == HIGH)
 		{
 			state++;
 			if (state > 4)


Corrections after testing arduino serial connection. Something is not working 100% still on recv or sending serial data.

--- arduino/morTimmy/morTimmy.ino
+++ arduino/morTimmy/morTimmy.ino
@@ -79,9 +79,9 @@
             void initialize()
             {
                 Serial.println("Initializing robot");
-                leftMotors.setSpeed(255);
-                rightMotors.setSpeed(255);
-                state = stateMoving;
+                leftMotors.setSpeed(0);
+                rightMotors.setSpeed(0);
+                state = stateRemote;
             }

@@ -100,6 +100,11 @@
           //    haveRemoteCmd, remoteCmd.left, remoteCmd.right, remoteCmd.key);

               if (remoteControlled()) {
+                // send the current distance to the raspberry
+                Serial.print("distance: ");
+                Serial.println(distance);
+
+                // Check if we received a command
                 if (haveRemoteCmd) {
                   switch (remoteCmd.key) {
                     case RemoteControlDriver::command_t::keyF1:
@@ -108,6 +113,7 @@
                       break;
                   case RemoteControlDriver::command_t::keyNone:
                       // this is a directional command
+                      Serial.println("received direction");
                       leftMotors.setSpeed(remoteCmd.left);
                       rightMotors.setSpeed(remoteCmd.right);
                       break;


More LED controls.

More LED controls.

--- arduino/surfcube/surfcube.ino
+++ arduino/surfcube/surfcube.ino
@@ -14,6 +14,9 @@
 #define RED_PIN 3
 #define YELLOW_PIN 5
 #define GREEN_PIN 6
+#define RED_MASK 1
+#define YELLOW_MASK ( 1 << 1 )
+#define GREEN_MASK ( 1 << 2 )

 Adafruit_MotorShield AFMS = Adafruit_MotorShield();
 Adafruit_StepperMotor *myStepper = AFMS.getStepper(200, 2);
@@ -36,9 +39,11 @@
 unsigned long servoDelay = 2;

 unsigned long keepAliveNextCheck = 0;
-unsigned long redLEDNextCheck = 0;
-unsigned long yellowLEDNextCheck = 0;
-unsigned long greenLEDNextCheck = 0;
+
+int LEDBreathingState = 0;
+int LEDRedValue = 0;
+int LEDYellowValue = 0;
+int LEDGreenValue = 0;

 void setup() {
   Serial.begin(9600);
@@ -87,6 +92,17 @@
       tide(UP, val);
     } else if (command == "tidedown") {
       tide(DOWN, val);
+    } else if (command == "breathingstate") {
+      LEDBreathingState = val;
+    } else if (command == "red") {
+      LEDBreathingState &= ~RED_MASK;
+      analogWrite(RED_PIN, val);
+    } else if (command == "yellow") {
+      LEDBreathingState &= ~YELLOW_MASK;
+      analogWrite(YELLOW_PIN, val);
+    } else if (command == "green") {
+      LEDBreathingState &= ~GREEN_MASK;
+      analogWrite(GREEN_PIN, val);
     } else {
       Serial.println("Command not understood!");
     }
@@ -134,9 +150,21 @@

 int counter = 0;

+void updateLED() {
+  float val = (exp(sin(now/2000.0*PI)) - 0.36787944)*108.0;
+  if(LEDBreathingState & RED_MASK) {
+    analogWrite(RED_PIN, val);
+  }
+  if(LEDBreathingState & YELLOW_MASK) {
+    analogWrite(YELLOW_PIN, val);
+  }
+  if(LEDBreathingState & GREEN_MASK) {
+    analogWrite(GREEN_PIN, val);
+  }
+}
+
 void loop() {
   now = millis();
-  float val;
   if(now >= serialNextCheck) {
     checkSerial();
   }
@@ -150,22 +178,8 @@
     Serial.println("I'm alive");
     keepAliveNextCheck = now + 2000;
   }
-  val = (exp(sin(millis()/2000.0*PI)) - 0.36787944)*108.0;
-  switch(counter % 3) {
-    case 0:
-      analogWrite(RED_PIN, val);
-      break;
-    case 1:
-      analogWrite(YELLOW_PIN, val);
-      break;
-    case 2:
-      analogWrite(GREEN_PIN, val);
-      break;
-  }
-
-  if(val == 0) {
-    //++counter;
-    //delay(1);
+  if(LEDBreathingState) {
+    updateLED();
   }
 }


buoy: Remove unused vars and functions.

buoy: Remove unused vars and functions.

--- arduino_buoy/arduino_buoy.ino
+++ arduino_buoy/arduino_buoy.ino
@@ -47,8 +47,8 @@

 uint8_t buf[60];
 uint8_t lock =0, sats = 0, nolock=0, psm_status = 0;
-int16_t GPSerror = 0,navmode = 0,lat_int=0,lon_int=0;
-int32_t lat = 0, lon = 0, alt = 0, lat_dec = 0, lon_dec =0 ,tslf=0;
+int16_t GPSerror = 0,lat_int=0,lon_int=0;
+int32_t lat = 0, lon = 0, alt = 0, lat_dec = 0, lon_dec =0;

 #ifdef ENABLE_BATTV_SENSOR
 float sampleBattv() {
@@ -297,31 +297,6 @@

   return crc;
 }
-uint8_t gps_check_nav(void)
-{
-  uint8_t request[8] = {
-    0xB5, 0x62, 0x06, 0x24, 0x00, 0x00, 0x2A, 0x84 };
-  sendUBX(request, 8);
-
-  // Get the message back from the GPS
-  gps_get_data();
-
-  // Verify sync and header bytes
-  if( buf[0] != 0xB5 || buf[1] != 0x62 ){
-    GPSerror = 41;
-  }
-  if( buf[2] != 0x06 || buf[3] != 0x24 ){
-    GPSerror = 42;
-  }
-  // Check 40 bytes of message checksum
-  if( !_gps_verify_checksum(&buf[2], 40) ) {
-    GPSerror = 43;
-  }
-
-
-  // Return the navigation mode and let the caller analyse it
-  navmode = buf[8];
-}
 void gps_get_data()
 {
   Serial.flush();


Better output for uptime. Fixed typo in ouput if humidity out>in.

--- cellar-climate-controller.ino
+++ cellar-climate-controller.ino
@@ -195,8 +195,8 @@
     lcd.clear();

     lcd.setCursor(0,0);
-    lcd.print("Up:   ");lcd.print((millis()/1000/60/60));lcd.print("h ");lcd.print(((millis()/1000/60)%60));lcd.print("min");
-
+    long minutes = millis() / 1000 / 60;
+    lcd.print("Up: ");lcd.print((minutes/60/24));lcd.print("d ");lcd.print(((minutes/60)%24));lcd.print("h ");lcd.print((minutes%60));lcd.print("m          ");
     lcd.setCursor(0,1);
     lcd.print("V-In: ");lcd.print(doRound2(volt), 2); lcd.print("V");
 }
@@ -218,7 +218,7 @@
     lcd.setCursor(0, 0);
     lcd.print("***FanControl***");
     lcd.setCursor(0, 1);
-    lcd.print("***   1.33   ***");
+    lcd.print("***   1.34   ***");
     time = millis() - time;
     Serial.print("Took "); Serial.print(time); Serial.println(" ms");
     lcd.setBacklight(WHITE);
@@ -399,10 +399,10 @@
             why = why + "%";
         } else if ((absHumidityOutside >= absHumidityInside) && fanRunning) {
             fanOff();
-            why = "A-Humi: Out > In";
+            why = "Humi: Out > In";
         } else if (((absHumidityOutside + ABS_HUMIDITY_LIMIT_TOLERANCE) >= absHumidityInside) && !fanRunning) {
             fanOff();
-            why = "A-Humi: Out > In";
+            why = "Humi: Out > In";
         } else if ((absHumidityOutside < absHumidityInside) && fanRunning)  {
             fanOn();
             why = "Drying ...";


Humidity

Humidity

--- zg01/zg01.ino
+++ zg01/zg01.ino
@@ -65,11 +65,11 @@
         uint8_t chk = buffer[0] + buffer[1] + buffer[2];

         // send CO2 measurement over RF24
-        if (buffer[0] == 'P') {
+        if (buffer[0] == 'P' || buffer[0] == 'A') {
             // construct buffer: [length]"CO_2"[ppm high byte][ppm low byte]
             uint8_t buf[7];
             buf[0] = 6;
-            memcpy(&buf[1], "CO_2", 4);
+            memcpy(&buf[1], (buffer[0] == 'P' ? "CO_2" : "HUMI"), 4);
             memcpy(&buf[5], &buffer[1], 2);
             // dump send buffer
             Serial.print("Send:");


Refactor API call '/api/hostname'.

Refactor API call '/api/hostname'.

Url decode the request.
Compare the request hostname with the actual hostname, and if they are the same, return without setting the hostname.

--- webserver.ino
+++ webserver.ino
@@ -102,12 +102,17 @@
     if ( webServer.arg( "newhostname" ) != "" ) {
       String newHostName = webServer.arg( "newhostname" );
       newHostName.trim();
+      newHostName = webServer.urlDecode( newHostName );
       //check for illegal characters --legal chars are alphanumeric
       for ( byte thisChar = 0; thisChar < newHostName.length(); thisChar++ ) {
-        if ( !isAlphaNumeric( newHostName[thisChar] ) || isSpace( newHostName[thisChar] ) ) {
+        if ( !isAlphaNumeric( newHostName[thisChar] ) ) {
           webServer.send( 200, FPSTR( textplainHEADER ), F( "ERROR - Invalid character in hostname." ) );
           return;
         }
+      }
+      if ( newHostName == WiFi.hostname() ) {
+        webServer.send( 200, FPSTR( textplainHEADER ), "Hostname already set to " + WiFi.hostname() );
+        return;
       }
       WIFIhostname = newHostName;
       hostNameChanged = true;


Send CO2 ppm value over RF24

Send CO2 ppm value over RF24

--- zg01/zg01.ino
+++ zg01/zg01.ino
@@ -1,12 +1,21 @@
+#include <RF24.h>
+#include <nRF24L01.h>
+#include <RF24_config.h>
+
+#include <SPI.h>
+
 #include <stdint.h>

 #include "zg01_fsm.h"

 #define PIN_CLOCK   2
 #define PIN_DATA    3
-#define PIN_LED     13
+
+static long int address = 0x66996699L;  // So that's 0x0066996699
+const int tries = 1;

 static uint8_t buffer[5];
+static RF24 rf(/*ce*/ 8, /*cs*/ 10);

 void setup(void)
 {
@@ -14,15 +23,18 @@
     pinMode(PIN_CLOCK, INPUT);
     pinMode(PIN_DATA, INPUT);

-    // initialize LED
-    pinMode(PIN_LED, OUTPUT);
-
     // initialize serial port
     Serial.begin(9600);
     Serial.println("Hello world!\n");

     // initialize ZG01 finite state machine
     zg01_init(buffer);
+
+    // init RF24
+    rf.begin();
+    rf.setRetries(15, 15);
+    rf.enableDynamicPayloads();
+    rf.openWritingPipe(address);
 }

 void loop(void)
@@ -30,9 +42,6 @@
     // wait until clock is low
     while (digitalRead(PIN_CLOCK) != LOW);

-    // indicate activity on LED
-    digitalWrite(PIN_LED, HIGH);
-
     // sample data and process in the ZG01 state machine
     uint8_t data = (digitalRead(PIN_DATA) == HIGH) ? 1 : 0;
     unsigned long ms = millis();
@@ -40,18 +49,41 @@

     // process data if ready
     if (ready) {
+        // dump buffer to serial
         for (int i = 0; i < 5; i++) {
             Serial.print(buffer[i], HEX);
             Serial.print(" ");
         }
+
+        // get binary value
+        uint16_t val = buffer[1] << 8 | buffer[2];
+        Serial.print(val, DEC);
         Serial.println();
+
+        // calculate checksum
+        uint8_t chk = buffer[0] + buffer[1] + buffer[2];
+
+        // send CO2 measurement over RF24
+        if (buffer[0] == 'P') {
+            // construct buffer: [length]"CO_2"[ppm high byte][ppm low byte]
+            uint8_t buf[8];
+            buf[0] = 6;
+            memcpy(&buf[1], "CO_2", 4);
+            memcpy(&buf[5], &buffer[1], 2);
+            // dump send buffer
+            Serial.print("Send:");
+            for (int i = 0; i < 6; i++) {
+                Serial.print(buf[i], HEX);
+                Serial.print(" ");
+            }
+            Serial.println();
+            // send it
+            rf.write(&buf, 7);
+        }
     }

     // wait until clock is high again
     while (digitalRead(PIN_CLOCK) == LOW);
-
-    // indicate activity on LED
-    digitalWrite(PIN_LED, LOW);
 }


Update BrewDuino.ino

Update BrewDuino.ino

Konsolenausgaben optimiert

--- BrewDuino.ino
+++ BrewDuino.ino
@@ -30,6 +30,7 @@
 #define MAX_LONG 2147483647L;
 #define TEMP_ARRAY_SIZE 5
 #define ENC_HALFSTEP
+#define SERIAL N

 //Display einrichten
 //Old LCD5110 lcd(8, 9, 10, 11, 12);
@@ -91,7 +92,9 @@
   lcd.print(COPYRIGHT, LEFT, 32);
   lcd.print(COPYRIGHT2, RIGHT, 40);

-  Serial.begin(9600);
+  #if SERIAL != "N"
+    Serial.begin(9600);
+  #endif

   pinMode(PIN_BG_LIGHT, OUTPUT);
   pinMode(PIN_HEATER, OUTPUT);


Update Serial2.ino

Update Serial2.ino

--- examples/Advanced/Serial2/Serial2.ino
+++ examples/Advanced/Serial2/Serial2.ino
@@ -2,26 +2,26 @@

 void setup() {
-
+
   M5.begin();

-  Serial.begin(9600);
+  Serial.begin(115200);

   // Serial2.begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert)
-  Serial2.begin(9600, SERIAL_8N1, 16, 17);
+  Serial2.begin(115200, SERIAL_8N1, 16, 17);
   pinMode(5, OUTPUT);
   digitalWrite(5, 1);
 }

 void loop() {

-//  if(Serial.available()) {
-//    int ch = Serial.read();
-//    Serial2.write(ch);
-//  }
-//
-//  if(Serial2.available()) {
-//    int ch = Serial2.read();
-//    Serial.write(ch);
-//  }
+ if(Serial.available()) {
+   int ch = Serial.read();
+   Serial2.write(ch);
+ }
+
+ if(Serial2.available()) {
+   int ch = Serial2.read();
+   Serial.write(ch);
+ }
 }


add NUM_LEDS constant

add NUM_LEDS constant

--- first_one/first_one.ino
+++ first_one/first_one.ino
@@ -33,6 +33,8 @@
 bool SYS_DEBUG = true;

 #define MORSE_UNIT_TIME SLEEP_250MS
+#define NUM_LEDS 9
+#define LDR_PIN A0

 unsigned long arTime = millis();
 unsigned long reset = millis();
@@ -71,7 +73,7 @@
 void setup() {
   randomSeed(analogRead(1));

-  for(unsigned int i=0;i<9;i++){
+  for(unsigned int i=0;i<NUM_LEDS;i++){
     pinModeFast(ledPins[i], OUTPUT);
     digitalWriteFast(ledPins[i], LOW);
   }
@@ -182,7 +184,7 @@
  *
  */
 void checkLEDS(){
-  for(unsigned int i=0;i<9;i++){
+  for(unsigned int i=0;i<NUM_LEDS;i++){
     digitalWriteFast(ledPins[i], LOW);
     if(ledPins[i] == actualPin){
       digitalWriteFast(ledPins[i], HIGH);
@@ -209,7 +211,7 @@
   if(DEBUG){
     Serial.println(bl);
   }
-  for(unsigned int i=0;i<9;i++){
+  for(unsigned int i=0;i<NUM_LEDS;i++){
     if(i <= bl){
       digitalWriteFast(ledPins[i], HIGH);
     }else{
@@ -249,7 +251,7 @@
  */
 void binaryLED(){

-  for(unsigned int i=0;i<9;i++){
+  for(unsigned int i=0;i<NUM_LEDS;i++){
     if(bitRead(binaryCounter,i) == 0){
       digitalWriteFast(ledPins[i], LOW);
     }else if(bitRead(binaryCounter,i) == 1){
@@ -276,7 +278,7 @@
  */
 void binaryLEDRand(){

-  for(unsigned int i=0;i<9;i++){
+  for(unsigned int i=0;i<NUM_LEDS;i++){
     if(bitRead(binaryCounter,i) == 0){
       digitalWriteFast(ledPinsRAND[i], LOW);
     }else if(bitRead(binaryCounter,i) == 1){
@@ -303,7 +305,7 @@
  *
  */
 void lynchLED(){
-   for(unsigned int i=0;i<9;i++){
+   for(unsigned int i=0;i<NUM_LEDS;i++){
     if(lynchON){
       digitalWriteFast(ledPins[i], HIGH);
     }else{
@@ -370,7 +372,7 @@
  *
  */
 void readLDR(){
-  LDR = analogRead(A0);
+  LDR = analogRead(LDR_PIN);

   if(LDR < ldrMinLight){
     weAreTheNight = true;
@@ -404,8 +406,8 @@
  *
  */
 void shuffleLEDS(){
-  for (int a=0; a<9; a++){
-    int r = random(a,8);
+  for (int a=0; a<NUM_LEDS; a++){
+    int r = random(a,NUM_LEDS-1);
     int temp = ledPinsRAND[a];
     ledPinsRAND[a] = ledPinsRAND[r];
     ledPinsRAND[r] = temp;
@@ -417,7 +419,7 @@
  *
  */
 void LEDSOFF(){
-   for(unsigned int i=0;i<9;i++){
+   for(unsigned int i=0;i<NUM_LEDS;i++){
     digitalWriteFast(ledPins[i], LOW);
   }
 }
@@ -427,7 +429,7 @@
  *
  */
 void LEDSON(){
-   for(unsigned int i=0;i<9;i++){
+   for(unsigned int i=0;i<NUM_LEDS;i++){
     digitalWriteFast(ledPins[i], HIGH);
   }
 }


cleanup. changed accelProfileIdx to uint8_t

--- GeheimTuer.ino
+++ GeheimTuer.ino
@@ -3,6 +3,7 @@
 /* config */

 // MAXPIX must fit uint8_t loop variable in ledFade, ledBlink
+// MAXPIX+EXTRAPIX must fit uint8_t loop variable in setLeds
 #define MAXPIX 20  // LED stripe length
 #define EXTRAPIX 3 // animation buffer, read window moves back and forth EXTRAPIX pixels

@@ -156,7 +157,7 @@
 enum motorDir { forward, backward } motorDir;

 const struct accelProfile *accelProfile; // pointer to current profile
-int accelProfileIdx = 0;
+uint8_t accelProfileIdx = 0;

 /* WS2812 LEDs */
@@ -230,17 +231,16 @@
 unsigned long debounceMillis = 0;

 // TODO: use structured profiles
+unsigned long ledMillis = 0;
 const unsigned long ledSolidInterval = 250; // constantly update LED stripe in solid mode
 const unsigned long ledFadeInterval = 20;
 const unsigned long ledBlinkInterval = 500;
-//const unsigned long ledMoveInterval = 250; // shift LED window every X ms
 unsigned long ledMoveInterval; // shift LED window every X ms - varied by drivePWM
-unsigned long ledMillis = 0;
-const unsigned int ledMoveMin = 50; // minimum move interval if drivePWM == 255
-const unsigned int ledMoveMax = 305; // maximum move interval if drivePWM == 0
-
-bool swFront, swBack, swTrigger, motorDiagA, motorDiagB, pirTrigger; // debounced inputs
-bool swFrontDeglitch, swBackDeglitch, swTriggerDeglitch, motorDiagADeglitch, motorDiagBDeglitch, pirDeglitch; // TODO: quick hack. document
+const unsigned long ledMoveMin = 50; // minimum move interval if drivePWM == 255
+const unsigned long ledMoveMax = 305; // maximum move interval if drivePWM == 0
+
+bool swFront, swBack, swTrigger, motorDiagA, motorDiagB, pirTrigger; // debounced inputs, can be reset to wait for next cycle
+bool swFrontDeglitch, swBackDeglitch, swTriggerDeglitch, motorDiagADeglitch, motorDiagBDeglitch, pirDeglitch; // debounced inputs, reset by every detected edge
 uint16_t swFrontDebounce, swBackDebounce, swTriggerDebounce, motorDiagADebounce, motorDiagBDebounce, pirDebounce; // debounce shift registers


Logic Analyzer confirms: PWM freq is ~31kHz

--- GeheimTuer.ino
+++ GeheimTuer.ino
@@ -269,10 +269,10 @@
   digitalWrite(motorInBPin, LOW);
   pinMode(motorInBPin, OUTPUT);

-// TODO: measure real PWM frequeny. any value other than 1 makes the motor whine
-// the code says base frequency is ~31kHz
-// VNH3SP30 datasheet limits the input PWM frequency to 10kHz - and yet it works. MOSFETs in thermal danger?
-setPwmFrequency(motorPWMPin, 1);
+  // TODO: check driver power dissipation
+  // base PWM frequency is ~31kHz. any divisor other than 1 makes the motor whine
+  // VNH3SP30 datasheet limits the input PWM frequency to 10kHz. MOSFETs in thermal danger?
+  setPwmFrequency(motorPWMPin, 1);

   pinMode(switchFront, INPUT_PULLUP);
   pinMode(switchBack, INPUT_PULLUP);


v1.4 -- improved averaging with double precision

Changed the accumulation of sums and squared variables to double precision to prevent overflow.

--- EM27cover.ino
+++ EM27cover.ino
@@ -123,7 +123,8 @@
 int verbose_logging = VERBOSE_DEFAULT;
 float duration, rain_mv;
 int i;
-float rain_mv_sum, rain_mv_sumsqr, rain_mv_avg, rain_mv_std;
+double rain_mv_sum, rain_mv_sumsqr;
+float rain_mv_avg, rain_mv_std;

 AccelStepper stepper(AccelStepper::DRIVER, DPIN_STEP, DPIN_DIR);  // sets up 2-wire stepper driver

@@ -131,7 +132,7 @@
 void setup() {
   // Start serial for input / output
   Serial.begin(9600);
-  Serial.println("EM27Sun Cover Driver v1.3");
+  Serial.println("EM27Sun Cover Driver v1.4");
   Serial.print("Rain threshold set at ");
   Serial.print(RAIN_THRESHOLD_MV);
   Serial.println("mV");


updated sample project

updated sample project

--- TinyfontSample/TinyfontSample.ino
+++ TinyfontSample/TinyfontSample.ino
@@ -6,9 +6,8 @@
 char allLetters[101];

 void setup() {
-  // put your setup code here, to run once:
-
-    arduboy.begin();
+  arduboy.begin();
+    Serial.begin(9600);

     // create all ascii letters from 32-126
     size_t newLineCounter = 0;
@@ -28,12 +27,18 @@
   // put your main code here, to run repeatedly:

   arduboy.clear();
-  // for comparison
-  arduboy.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-  tinyfont.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, 8);
+  tinyfont.print("THE QUICK BROWN FOX JUMPS\nOVER THE LAZY DOG.", 1, 1);
+  tinyfont.print("The quick brown fox jumps\nover the lazy dog.", 1, 13);

   // all letters
-  tinyfont.print(allLetters, 0, 12);
+  tinyfont.print(allLetters, 1, 22);
+
+  // for comparison
+  arduboy.setCursor(1, 52);
+  arduboy.print("THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.");
+

+  // screen capture
+  Serial.write(arduboy.getBuffer(), 128 * 64 / 8);
   arduboy.display();
 }


Update cash-register.ino

Update cash-register.ino

--- arduino/cash-register.ino
+++ arduino/cash-register.ino
@@ -1,46 +1,20 @@
-#include <avr/sleep.h>
+#include "LowPower.h"

 const int OBSTACLE_SENSOR = 2;
-const int OBSTACLE_BATTERY = 5;
-const int BUZZER = 3;
+const int OBSTACLE_BATTERY = 3;
+const int LED = 5;
+const int BUZZER = 9;
 const int BUZZER_BATTERY = 6;
 const int SOUND_DURATION = 40;
 const int RANDOM_ERROR_COUNT = 20;
+const int COUNT_BEFORE_SLEEP = 30;
 int lastObstacleValue;
-
-/**
- * Sleep mode
- */
-void sleep() {
-  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
-
-  noInterrupts();
-  sleep_enable();
-  attachInterrupt(digitalPinToInterrupt(OBSTACLE_SENSOR), onObstacleChanged, CHANGE);
-  interrupts();
-
-  sleep_cpu();
-}
-
-/**
- * Wake up
- */
-void wake() {
-  sleep_disable();
-  detachInterrupt(digitalPinToInterrupt(OBSTACLE_SENSOR));
-}
-
-/**
- * Obstacle sensor is changed
- */
-void onObstacleChanged() {
-  wake();
-}

 /**
  * Setup
  */
 void setup() {
+  pinMode(LED, OUTPUT);
   pinMode(BUZZER, OUTPUT);
   pinMode(BUZZER_BATTERY, OUTPUT);
   pinMode(OBSTACLE_SENSOR, INPUT);
@@ -53,21 +27,56 @@
  * Main loop
  */
 void loop() {
+  start:
+
+  // Turn on batteries
   digitalWrite(OBSTACLE_BATTERY, 255);
   digitalWrite(BUZZER_BATTERY, 255);
-  int obstacleValue = digitalRead(OBSTACLE_SENSOR);
-
-  if (lastObstacleValue == HIGH && obstacleValue == LOW) {
+
+
+  attachInterrupt(digitalPinToInterrupt(OBSTACLE_SENSOR), wake, LOW);
+
+  int obstacleValue = HIGH;
+  int count = COUNT_BEFORE_SLEEP;
+  while (count > 0) {
+    obstacleValue = digitalRead(OBSTACLE_SENSOR);
+    if (lastObstacleValue == HIGH && obstacleValue == LOW) {
+      detachInterrupt(digitalPinToInterrupt(OBSTACLE_SENSOR));
+
       int randNumber = random(RANDOM_ERROR_COUNT);
       if (randNumber == 0) {
           soundError();
       } else {
           soundOk();
       }
+    }
+    lastObstacleValue = obstacleValue;
+    if (obstacleValue == LOW) {
+
+      delay(500);
+      goto start;
+      break;
+    }
+
+    count--;
+    LowPower.powerSave(SLEEP_2S, ADC_OFF, BOD_OFF, TIMER2_ON);
   }
-  lastObstacleValue = obstacleValue;
+  detachInterrupt(digitalPinToInterrupt(OBSTACLE_SENSOR));
+

-  sleep();
+  // Last light before sleep
+  analogWrite(LED, 10);
+  delay(2000);
+  analogWrite(LED, 0);
+
+  // Turn off batteries
+  digitalWrite(OBSTACLE_BATTERY, 0);
+  digitalWrite(BUZZER_BATTERY, 0);
+
+  // Sleep and wait sensor battery
+  attachInterrupt(digitalPinToInterrupt(OBSTACLE_SENSOR), wake, RISING);
+  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
+  detachInterrupt(digitalPinToInterrupt(OBSTACLE_SENSOR));
 }

 /**
@@ -108,3 +117,10 @@
         delay(1);
     }
 }
+
+/**
+ * Wake up
+ */
+void wake() {
+
+}


Improved adalight show() handling

Improved adalight show() handling

--- wled00/wled04_file.ino
+++ wled00/wled04_file.ino
@@ -16,12 +16,13 @@
 void handleSerial()
 {
   static auto state = AdaState::Header_A;
-  static unsigned int count = 0;
-  static unsigned int pixel = 0;
+  static uint16_t count = 0;
+  static uint16_t pixel = 0;
   static byte check = 0x00;
   static byte red   = 0x00;
   static byte green = 0x00;
-  static byte blue  = 0x00;
+  static bool changed = false;
+
   while (Serial.available() > 0)
   {
     byte next = Serial.read();
@@ -61,18 +62,23 @@
         state = AdaState::Data_Blue;
         break;
       case AdaState::Data_Blue:
-        blue  = next;
+        byte blue  = next;
+        changed = true;
         setRealtimePixel(pixel++, red, green, blue, 0);
         if (--count > 0) state = AdaState::Data_Red;
         else             state = AdaState::Header_A;
         break;
     }
+  }

+  if (changed)
+  {
     if (!realtimeActive && bri == 0) strip.setBrightness(briLast);
     arlsLock(realtimeTimeoutMs);

     yield();
     strip.show();
+    changed = false;
   }
 }


SetTime: keep track of timer inaccuracy

SetTime: keep track of timer inaccuracy

--- SetTime/SetTime.ino
+++ SetTime/SetTime.ino
@@ -156,6 +156,8 @@
 volatile int writeCurBit = 0;
 volatile int writeCurByte = 0;

+uint32_t previousNtp = 0;
+uint32_t previousNtpFractions = 0;
 volatile uint32_t curTime = 0;
 volatile uint32_t curTimeFractions = 0;
 bool knowTime = false;
@@ -166,8 +168,10 @@
 static const unsigned int finetuning = 50; // fine-tuned with an oscilloscope
 static const unsigned int timerPreload = 65536 - systemHz / prescaler / (1 / bitLength) + finetuning;

+static const double two_to_32 = 4294967296.0;
+
 // how much to advance curTimeFractions in the interrupt handler
-volatile uint32_t fractionsPerOverflow = 4294967296.0 * bitLength;
+volatile uint32_t fractionsPerOverflow = two_to_32 * bitLength;

 void setupTimer()
 {
@@ -213,10 +217,11 @@
   pinMode(debug, OUTPUT);
   pinMode(in, INPUT);
   digitalWrite(out, HIGH);
-  digitalWrite(debug, HIGH);
+  digitalWrite(debug, LOW);
   ethernet.setup(mac, ip, port);
   Serial.begin(9600);
   Serial.println("Start.");
+  Serial.print("Fractions per overflow: "); Serial.println(fractionsPerOverflow);
   setupTimer();
 }

@@ -312,6 +317,39 @@
   }
 }

+// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1207226548
+void printDouble( double val, byte precision){
+  // prints val with number of decimal places determine by precision
+  // precision is a number from 0 to 6 indicating the desired decimial places
+  // example: printDouble( 3.1415, 2); // prints 3.14 (two decimal places)
+
+  Serial.print (int(val));  //prints the int part
+  if( precision > 0) {
+    Serial.print("."); // print the decimal point
+    unsigned long frac;
+    unsigned long mult = 1;
+    byte padding = precision -1;
+    while(precision--)
+	 mult *=10;
+
+    if(val >= 0)
+	frac = (val - int(val)) * mult;
+    else
+	frac = (int(val)- val ) * mult;
+    unsigned long frac1 = frac;
+    while( frac1 /= 10 )
+	padding--;
+    while(  padding--)
+	Serial.print("0");
+    Serial.print(frac,DEC) ;
+  }
+}
+
+double fixedPointDistance(uint32_t a1, uint32_t a2, uint32_t b1, uint32_t b2)
+{
+  return ((int64_t) b2 - (int64_t) b1) / two_to_32 + (int32_t) (b1 - a1);
+}
+
 void loop() {
   static bool flag = true;
   if (flag) {
@@ -319,6 +357,7 @@
     flag = false;
   }
   checkTime();
+
   uint16_t length = es.ES_enc28j60PacketReceive(bufferSize, buf);
   if (length && length >= 42 /* IP packet min length */ && length <= UDP_DATA_P + NTP_PACKET_SIZE) {
     if (es.ES_eth_type_is_arp_and_my_ip(buf, length)) {
@@ -328,8 +367,8 @@
                buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V) {
       es.ES_make_echo_reply_from_request(buf, length);
     } else if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_DST_PORT_L_P] == ntpPort && length == UDP_DATA_P + NTP_PACKET_SIZE) {
+      digitalWrite(debug, HIGH);
       // NOTE: requires disabling the pass-only-arp-broadcast filter from enc28j60
-      Serial.println("Received ntp packet");
       uint32_t a = buf[UDP_DATA_P + TRANSMIT_TIMESTAMP_P + 0];
       uint32_t b = buf[UDP_DATA_P + TRANSMIT_TIMESTAMP_P + 1];
       uint32_t c = buf[UDP_DATA_P + TRANSMIT_TIMESTAMP_P + 2];
@@ -341,18 +380,40 @@
       d = buf[UDP_DATA_P + TRANSMIT_TIMESTAMP_P + 7];
       uint32_t t2 = ((a << 24) | (b << 16) | (c << 8) | d);
       noInterrupts();
+      uint32_t oldTime = curTime;
+      uint32_t oldTimeFractions = curTimeFractions;
       curTime = t1;
       curTimeFractions = t2;
       interrupts();
-      struct tm tm;
-      gmtime_r(t1, &tm);
-      Serial.println(tm.tm_mday);
-      Serial.println(tm.tm_mon + 1);
-      Serial.println(tm.tm_year);
-      Serial.println(tm.tm_hour);
-      Serial.println(tm.tm_min);
-      Serial.println(tm.tm_sec);
+      digitalWrite(debug, LOW);
+      if (knowTime) {
+        Serial.print(oldTime);
+        Serial.print(" ");
+        Serial.print(oldTimeFractions);
+        Serial.print(" ");
+        Serial.print(t1);
+        Serial.print(" ");
+        Serial.print(t2);
+        Serial.print(" ");
+        Serial.print(t1 - oldTime);
+        double error = fixedPointDistance(t1, t2, oldTime, oldTimeFractions);
+        Serial.print(" ");
+        printDouble(error, 6);
+        double sinceLastNtp = fixedPointDistance(t1, t2, previousNtp, previousNtpFractions);
+        Serial.print(" ");
+        printDouble(sinceLastNtp, 6);
+        Serial.print(" ");
+        double errorPerSecond = error / sinceLastNtp;
+        printDouble(errorPerSecond, 9);
+        Serial.println();
+        fractionsPerOverflow += errorPerSecond * 100000;
+        Serial.print("Fractions per overflow: ");
+        Serial.println(fractionsPerOverflow);
+      }
+      previousNtp = t1;
+      previousNtpFractions = t2;
       knowTime = true;
     }
   }
-}
+  digitalWrite(debug, LOW);
+}


Final version

Final version

--- oven.ino
+++ oven.ino
@@ -32,7 +32,7 @@
 S7S s7s(s7sAddress);

 // Will be used with sprintf to create strings
-char tempString[10];
+char tempString[10];

 // Piezo buzzer pin number
 int buzzerPin = 7;
@@ -40,13 +40,13 @@
 // Place to store current time
 unsigned long now;
 long offset = 0;
-
+
 // The time (in ms) when the button was pressed down
 unsigned long buttonDownTime;
 boolean cycleColors = true;

 // Amount of time (in ms) to hold the button down to set the clock
-const int buttonSetClockDuration = 5000;  // 10 seconds
+const int buttonSetClockDuration = 8000;  // 8 seconds

 const int MODE_CLOCK = 0;
@@ -58,17 +58,17 @@

 void setup() {
   Serial.begin(115200);
-
+
   // Attach interrupts for rotary encoder
   re_setup();
-
+
   // Enable push button in LOW state
   pinMode(buttonPin, INPUT);
   digitalWrite(buttonPin, LOW);

   // Set the RGB LEB pins as output
   rgb.setup();
-
+
   // Initialize hardware I2C pins for S7S
   Wire.begin();

@@ -77,7 +77,7 @@

   // Set baud rate higher
   s7s.setBaudRate(6);
-
+
   // Set brightness
   s7s.setBrightness(255);

@@ -93,7 +93,7 @@

 void loop() {
   doButtonCheck();
-
+
   switch (mode) {
     case MODE_CLOCK:
       displayClock();
@@ -107,7 +107,7 @@
     case MODE_SET_CLOCK:
       setClock();
       break;
-  }
+  }
 }

@@ -132,13 +132,13 @@
         // Set time setup goes here
         rgb.setColor(0, 128, 255);
         cycleColors = false;
-
+
         //offset = (pulses / 4) * 1000 * 60;
         long pulses = offset / 60 / 1000 * 4;
         re_setPulses(pulses);
     }
   }
-
+
   // Save the previous state for the next loop
   lastButtonState = buttonState;
 }
@@ -148,20 +148,20 @@
   if (wasLongPress) {
     mode = MODE_SET_CLOCK;
     wasLongPress = false;
-
+
   } else {
     // Normal mode switch
     mode++;
     mode = mode % 3;
-    Serial.print("Now in mode: ");
+    Serial.print("Now in mode: ");
     Serial.println(mode);
-
+
     // Reset pulses for normal mode switch
     re_resetPulses();
-
+
     cycleColors = true;
   }
-
+
   // Always clear display before switching modes
   s7s.clearDisplay();
 }
@@ -173,37 +173,38 @@
   int minutes = (now / 1000 / 60) % 60;
   int hours24 = (now / 1000 / 60 / 60) % 24;
   int hours = hours24 % 12;
-
+  int cycleSpeed = 500;
+
   if (hours == 0) {
     hours = 12;
   }
-
+
   sprintf(tempString, "%2d%02d", hours, minutes);
   s7s.sendString(tempString);
-
+
   if (mode == MODE_SET_CLOCK) {
     // Decimals
     if (hours24 >= 12) {
-      s7s.setDecimals(0b00011000);
+      s7s.setDecimals(0b00011000);
     } else {
-      s7s.setDecimals(0b00010000);
+      s7s.setDecimals(0b00010000);
     }
   } else {
     // Decimals
     if (seconds % 2 == 0) {
       if (hours24 >= 12) {
-        s7s.setDecimals(0b00011000);
+        s7s.setDecimals(0b00011000);
       } else {
-        s7s.setDecimals(0b00010000);
+        s7s.setDecimals(0b00010000);
       }
     } else {
       if (hours24 >= 12) {
-        s7s.setDecimals(0b00001000);
+        s7s.setDecimals(0b00001000);
       } else {
         s7s.setDecimals(0b00000000);
       }
     }
-
+
     // Brightness
     int s7sBrightness = 255;
     int buttonBrightness = 255;
@@ -213,12 +214,20 @@
     }
     s7s.setBrightness(s7sBrightness);

+    if (minutes == 0) {
+      cycleColors = false;
+    } else {
+      cycleColors = true;
+    }
+
     // Cycle colors
     if (cycleColors) {
-      int v = (now / 100) % 360;
+      int v = (now / cycleSpeed) % 360;
       rgb.cycleColor(v, buttonBrightness);
-    }
-  }
+    } else {
+      rgb.setColor(174, 255, 255);
+    }
+  }
 }

@@ -253,7 +262,7 @@

   displayValue--;
   updateDisplay();
-
+
   if (displayValue <= 0) {
     soupsReady();
   } else {
@@ -271,8 +280,8 @@
 void soupsReady() {
   // Switch to clock mode after song and dance
   mode = MODE_CLOCK;
-
-  displayValue = 0;
+
+  displayValue = 0;
   re_resetPulses();

   // notes in the melody:
@@ -300,13 +309,13 @@
     "0000",
     "0  0"
   };
-
+
   int numberOfNotes = sizeof(melody) / sizeof(int);
-
+
   // iterate over the notes of the melody:
   for (int thisNote = 0; thisNote < numberOfNotes; thisNote++) {

-    // to calculate the note duration, take one second
+    // to calculate the note duration, take one second
     // divided by the note type.
     //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
     int noteDuration = 1800/noteDurations[thisNote];
@@ -322,7 +331,7 @@
     delay(pauseBetweenNotes);
     // stop the tone playing:
     noTone(buzzerPin);
-  }
+  }
 }

 void updateDisplay() {
@@ -333,9 +342,3 @@
   // This will output the tempString to the S7S
   s7s.sendString(tempString);
 }
-
-
-
-
-
-


some code cleanup

some code cleanup


Update bb-8-Head-stabiliser-controller.ino

--- Head/Stabiliser/Arduino-firmware/bb-8-Head-stabiliser-controller/bb-8-Head-stabiliser-controller.ino
+++ Head/Stabiliser/Arduino-firmware/bb-8-Head-stabiliser-controller/bb-8-Head-stabiliser-controller.ino
@@ -4,7 +4,7 @@
  ###########################################

  https://github.com/Gruniek/OPEN-BB-X
- Made by Daniel M/Gruniek/MOUS
+ Made by Daniel M/Gruniek/

  Compatible only with an Arduino Nano and the specific PCB.
  PCB Link : https://github.com/Gruniek/OPEN-BB-X/tree/master/Head/Stabiliser/PCB
@@ -21,7 +21,7 @@
  To do list
  ==========
  - Add Z rotation controller
- - Add Rx/Tx with i2c from the motherboard for change de coordinate of the head,
+ - Add Rx/Tx from the motherboard for change de coordinate of the head,
   rotate the head and report to the remote the status off all sensors/positiom.

@@ -30,20 +30,44 @@

 */

-#define pinEnable 8 // Activation du driver/pilote
-
-#define pinStep    2 //2 Signal de PAS (avancement)
-#define pinDir     5 //5 Direction
-
-
-
-#define pinStepY    4 // 4Signal de PAS (avancement)
-#define pinDirY     7 // 7Direction
-
-
 #include "Wire.h"
-#include "I2Cdev.h"
 #include "MPU6050.h"
+
+
+
+// MOTOR CONFIGURATION // YOU CAN TOUCH THIS
+
+#define pinEnable        4      // Activation off all stepper
+
+#define pinStepX         3      // PIN STEP FOR X
+#define pinDirX          2      // PIN DIRECTION FOR X
+
+#define pinStepY         6      // PIN STEP FOR Y
+#define pinDirY          5      // PIN DIRECTION FOR Y
+
+#define pinStepY         8      // PIN STEP FOR Y
+#define pinDirY          7      // PIN DIRECTION FOR Y
+
+#define Z_POSITION       9      // PIN FOR THE DIGITAL INPUT FOR THE HEAD POSITION
+
+bool    invertX        = false; // INVERT THE X MOTOR DIRECTION
+bool    invertY        = true;  // INVERT THE Y MOTOR DIRECTION
+bool    invertXY       = false; // INVERT X Y AXIAL
+
+int     minX           = 45;    // SET THE MIN INCLINAISON FOR X
+int     maxX           = 135;   // SET THE MAX INCLINAISON FOR X
+int     minY           = 45;    // SET THE MIN INCLINAISON FOR Y
+int     maxY           = 135;   // SET THE MAX INCLINAISON FOR Y
+
+int     etalonX        = 0;     // AJUSTEMENT FOR X
+int     etalonY        = -6;    // AJUSTEMENT FOR Y
+
+int     hysteresis     = 1;     // HYSTERESSIS FOR THE ANGLE CALCULATION
+
+
+//=====================//
+// NOW YOU CAN'T TOUCH //
+//=====================//

 // MPU6050
 MPU6050 accelgyro;
@@ -53,34 +77,21 @@

 // I2C
 int adress = 2;
-int LED    = 13; // PIN
+int PING   = 13; // PIN

 // VARIABLE
 int x              = 0;
 int y              = 0;
 int vx             = 0;
 int vy             = 0;
-int minX           = 45;
-int maxX           = 135;
-int minY           = 45;
-int maxY           = 135;
+
 int speedX         = 0;
 int speedY         = 0;
-int etalonX        = 0;
-int etalonY        = -6;
+
 int setpointX      = 90;
 int setpointY      = 90;
-int hysteresis     = 1;
-
-int motorX         = 2;  // PIN 5
-int enableX        = 8;  // PIN7
-int dirX           = 5;  // PIN6
-int motorY         = 4;  // PIN 8
-int enableY        = 9; // PIN10
-int dirY           = 7;  // PIN9
-int motorZ         = 3;  // PIN3
-int dirZ           = 10;  // PIN2
-int enableZ        = 4;  // PIN4
+
+

 int multiplier     = 15;
 int emergencyStop  = 11; // PIN
@@ -90,6 +101,10 @@
 bool runY          = false;
 bool directX       = false; // false = Gauche / true = droite
 bool directY       = false; // false = Gauche / true = droite
+
+bool xok           = false;
+bool yok           = false;
+bool zok           = false;

 // PID
 unsigned long previousMillisX = 0;
@@ -97,24 +112,41 @@
 long                    intX  = 0;
 long                    intY  = 0;

-bool start = false;
-
-
+
+unsigned long trigGyro = 100;
+unsigned long trigSec  = 1000;
+unsigned long trigG    = 0;
+unsigned long trigS    = 0;
+
+
+bool production = false;
+bool LED_PING   = false;
+bool BOOT = false;
+
+
+
+
+//=============================================================================//
+//   SETUP
+//=============================================================================//
 void setup()
 {
-  pinMode( pinEnable, OUTPUT );
-  pinMode( pinDir   , OUTPUT );
-  pinMode( pinStep  , OUTPUT );
-  pinMode( pinDirY   , OUTPUT );
-  pinMode( pinStepY  , OUTPUT );
-
+	pinMode( pinEnable , OUTPUT );
+    pinMode( pinDirX   , OUTPUT );
+    pinMode( pinStepX  , OUTPUT );
+    pinMode( pinDirY   , OUTPUT );
+    pinMode( pinStepY  , OUTPUT );
+    pinMode( PING      , OUTPUT );
+    pinMode( Z_POSITION, INPUT  );
+
+
     Serial.begin(9600);

-  Serial.println("Initializing I2C devices...");
-  Serial.print("I2C Adress = ");
-  Serial.println(adress);
-
-  Wire.begin(adress);
+    Serial.println("Initializing I2C devices...");
+    Serial.print("I2C Adress = ");
+    Serial.println(adress);
+
+    Wire.begin(adress);
     //Wire.onReceive(); // register event

@@ -124,69 +156,171 @@
     Serial.println("Testing device connections...");
     Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

-    pinMode(LED, OUTPUT);
-  pinMode(motorX, OUTPUT);
-  pinMode(motorY, OUTPUT);
-  pinMode(dirX, OUTPUT);
-  pinMode(dirY, OUTPUT);
-
 }

+//=============================================================================//
+//   LOOP
+//=============================================================================//
 void loop()
 {
-  // GET MOTION
-    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
-
-  x = (((ax/180)+etalonX)+90);
-  y = (((ay/180)+etalonY)+90);
-
-   char msg[30];
-  if (Serial.available() > 0)
-  {
-    Serial.readBytesUntil('\n', msg, sizeof msg);
-
-    if (strcmp(strtok(msg, " "), "SET") == 0)
+    // GET MOTION
+    //-------------------------------------------------------------------------//
+    unsigned long currentG = millis(); // millis();
+
+    if (currentG - trigG  >= trigGyro)
     {
-      Serial.println("UPDATE SETPOINT");
-      // trouvé le message
-      char *p;
-      while ((p = strtok(NULL, " ")) != NULL)
-      {
-        int val = atoi(p + 1);
-        switch (*p)
-        {
-          case 'x': setpointX = val; break;
-          case 'y': setpointY = val; break;
-      	  case 'm': multiplier = val; break;
-        }
-      }
+    	trigG = currentG;
+
+
+    	accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
+		if(invertXY)
+		{
+			y = (((ax/180)+etalonX)+90);
+  			x = (((ay/180)+etalonY)+90);
+		}
+		else
+		{
+			x = (((ax/180)+etalonX)+90);
+  			y = (((ay/180)+etalonY)+90);
+		}
     }
-
-    if (strcmp(strtok(msg, " "), "RUN") == 0)
+
+    // GET SERIAL DATA
+    //-------------------------------------------------------------------------//
+
+    char msg[30];
+    if (Serial.available() > 0)
     {
-      Serial.println("RUN");
-      start = true;
-    }
-
-    if (strcmp(strtok(msg, " "), "STOP") == 0)
-    {
-      Serial.println("STOP");
-      start = false;
-    }
+    	Serial.readBytesUntil('\n', msg, sizeof msg);
+
+    	if (strcmp(strtok(msg, " "), "SET") == 0)
+    	{
+        	Serial.println("UPDATE SETPOINT");
+      		// trouvé le message
+      		char *p;
+      		while ((p = strtok(NULL, " ")) != NULL)
+      		{
+        		int val = atoi(p + 1);
+        		switch (*p)
+        		{
+          			case 'x': setpointX  = val; break;
+          			case 'y': setpointY  = val; break;
+      	  			case 'm': multiplier = val; break;
+      				case 'i': invertXY   = val; break;
+      				case 'j': invertX    = val; break;
+      				case 'k': invertX    = val; break;
+      				case 'h': hysteresis = val; break;
+        		}
+      		}
+    	}
+
+    	if (strcmp(strtok(msg, " "), "RUN") == 0)
+    	{
+        	Serial.println("ROll BB-8 ROLL !");
+      	    production = true;
+    	}
+
+    	if (strcmp(strtok(msg, " "), "STOP") == 0)
+    	{
+    		Serial.println("STOP");
+    	    production = false;
+    	}
+
+    	if (strcmp(strtok(msg, " "), "BOOT") == 0)
+    	{
+    		Serial.println("STOP");
+    	    BOOT = true;
+    	    Serial.println("BOOTING UP...");
+    	    delay(2000);
+    	}
+
+
   }

-//  Serial.print(x); Serial.print("/"); Serial.println(y);
-
-  if(start)  //-
-  {
+   if(boot)
+   {
+   	    // SET X IN POSITION
+   		if(x < 90) directX = false;
+  		else directX = true;
+
+  		if(invertX)
+  		{
+    		if(directX) directX = false;
+    		else directX = true;
+  		}
+  		digitalWrite(pinDirX, directX); // dirX
+
+  		if(x > (setpointX + hysteresis) || (x < setpointX - hysteresis ))
+  		{
+  	        digitalWrite(pinStepX, 1);
+        	delayMicroseconds( 500 );
+    	    digitalWrite(pinStepX, 0);
+    	    delayMicroseconds( 500 );
+  		}
+  		else
+  		{
+  			if(!xok)
+  			{
+  				Serial.println(STAT x1);
+  				xok = true;
+  				delay(2000);
+  			}
+  		}
+
+
+  		// SET Y IN POSITION
+   		if(y < 90) directY = false;
+  		else directY = true;
+
+  		if(invertY)
+  		{
+    		if(directY) directY = false;
+    		else directY = true;
+  		}
+  		digitalWrite(pinDirY, directY); // dirX
+
+  		if(x > (setpointY + hysteresis) || (y < setpointY - hysteresis ))
+  		{
+  	        digitalWrite(pinStepY, 1);
+        	delayMicroseconds( 500 );
+    	    digitalWrite(pinStepY, 0);
+    	    delayMicroseconds( 500 );
+  		}
+  		else
+  		{
+  			if(!yok)
+  			{
+  				Serial.println(STAT y1);
+  				yok = true;
+  				delay(2000);
+  			}
+  		}
+
+  		// SET W IN POSITION
+
+  		if()
+
+
+   }
+
+	// PRODUCTION CODE
+    //-------------------------------------------------------------------------//
+    if(production)  //-
+    {

   // X //

   // Direction
   if(x < 90) directX = false;
   else directX = true;
-
-  digitalWrite(pinDir, directX); // dirX
+
+  if(invertX)
+  {
+    if(directX) directX = false;
+    else directX = true;
+  }
+
+  digitalWrite(pinDirX, directX); // dirX

   // Si ont doit demarer le moteur
   if(x > (setpointX + hysteresis) || (x < setpointX - hysteresis ))
@@ -203,8 +337,8 @@
   if(x > 90) vx = (x - 90);

   speedX = 1000 / (vx);
-    speedX = speedX / multiplier; // DEL
-  //if(speedX < 15) speedX = 15;
+  speedX = speedX * multiplier; // DEL
+  if(speedX < 15) speedX = 15;

   // Mise en route du moteur
@@ -217,14 +351,10 @@

       if(runX)
       {
-        digitalWrite(pinStep, 1);
+        digitalWrite(pinStepX, 1);
         delayMicroseconds( 500 );
-        digitalWrite(pinStep, 0);
+        digitalWrite(pinStepX, 0);
         delayMicroseconds( 500 );
-        /*Serial.print(">X< : STEP ");
-        Serial.print(directX);
-        Serial.print(" ");
-        Serial.println(x);*/
       }
   }

@@ -233,7 +363,11 @@
   // Direction
   if(y < 90) directY = false;
   else directY = true;
-
+  if(invertY)
+  {
+    if(directY) directY = false;
+    else directY = true;
+  }
   digitalWrite(pinDirY, directY);

   // Si ont doit demarer le moteur
@@ -250,8 +384,8 @@
   if(y > 90) vy = (y - 90);

   speedY = 1000 / (vy);
-  speedY = speedY / multiplier; // DEL
-  //if(speedY < 15) speedY = 15;
+  speedY = speedY * multiplier; // DEL
+  if(speedY < 15) speedY = 15;

   // Mise en route du moteur
@@ -267,12 +401,41 @@
         delayMicroseconds( 500 );
         digitalWrite(pinStepY, 0);
         delayMicroseconds( 500 );
-       /* Serial.print("<Y> : STEP ");
-        Serial.print(directY);
-        Serial.print(" ");
-        Serial.println(y);*/
       }
   }

   } //-
+
+
+  	// SEND SERIAL DATA
+    //-------------------------------------------------------------------------//
+    unsigned long currentS = millis(); // millis();
+
+    if (currentS - trigS  >= trigSec)
+    {
+    	trigS = currentS;
+
+
+
+    	// PING
+    	if(LED_PING) LED_PING = false;
+    	else LED_PING = true;
+    	digitalWrite(PING, LED_PING);
+
+
+    	// SEND STATUT
+    	Serial.print("SEND x");
+    	Serial.print(x);
+    	Serial.print(" y");
+    	Serial.print(y);
+    	Serial.print(" i");
+    	Serial.print(invertXY);
+    	Serial.print(" j");
+    	Serial.print(invertX);
+    	Serial.print(" k");
+    	Serial.print(invertY);
+    	Serial.print(" m");
+    	Serial.print(multiplier);
+    	Serial.println(" ");
+    }
 }


Improve Arduino code - Constrain receiver input values before mapping them (fix) - Reset calibration after values have been sent to the phone - Require phone to send calibration values to activate receiver - Tweak various constants

--- arduino/arduino_sketch/arduino_sketch.ino
+++ arduino/arduino_sketch/arduino_sketch.ino
@@ -14,7 +14,7 @@
 const byte CHAR_ARRAY_SIZE = BYTE_MAX;

 //The allowed margin from the cutover min/max input
-const byte CUTOVER_MARGIN = 125;
+const byte CUTOVER_MARGIN = 100;

 //Number of cutover samples to use for cutoverInAverage
 const byte CUTOVER_SAMPLE_SIZE = 10;
@@ -37,9 +37,12 @@
 //TODO use better calibration default
 const int RECEIVER_INPUT_DEFAULT = 1500;

+//Allowed distance from the default for a valid receiver input value
+const int RECEIVER_INPUT_DISTANCE = 800;
+
 //Min and max possible values to filter for receiver servo input
-const int RECEIVER_INPUT_MAX = 2300;
-const int RECEIVER_INPUT_MIN = 700;
+const int RECEIVER_INPUT_MAX = RECEIVER_INPUT_DEFAULT + RECEIVER_INPUT_DISTANCE;
+const int RECEIVER_INPUT_MIN = RECEIVER_INPUT_DEFAULT - RECEIVER_INPUT_DISTANCE;

 //Sets the baud rate of the Arduino
 const long BAUD_RATE = 115200;
@@ -194,16 +197,15 @@
       if (!receiverControl) {
         calibrationMode = (boolean) root["calibrationMode"];
         sendJsonBoolean("calibrationMode", calibrationMode);
-        if (calibrationMode) {
-          resetCalibration();
-        } else {
+        if (!calibrationMode) {
           if (calibrationComplete()){
-            isCalibrated = true;
             sendCalibrationJsonData();
           } else {
             sendJsonString("error", "Calibration not complete");
           }
         }
+        //Reset the calibration values (they will later be sent back from the attached USB device)
+        resetCalibration();
       } else {
         sendJsonString("error", "Disable receiver control to change calibration mode");
       }
@@ -213,10 +215,6 @@
     if (root.containsKey("aileron")) {
       processServoJson(root["aileron"], aileron, "aileron", listenForAileron, "Unassigned aileron pin");
     }
-    if (root.containsKey("cutover")) {
-      //Cutover is input only, so output only function parameters can be null
-      processServoJson(root["cutover"], cutover, "", listenForCutover, "");
-    }
     if (root.containsKey("elevator")) {
       processServoJson(root["elevator"], elevator, "elevator", listenForElevator, "Unassigned elevator pin");
     }
@@ -226,64 +224,16 @@
     if (root.containsKey("throttle")) {
       processServoJson(root["throttle"], throttle, "throttle", listenForThrottle, "Unassigned throttle pin");
     }
+    if (root.containsKey("cutover")) {
+      //Cutover is input only, so output-only function parameters can be null
+      processServoJson(root["cutover"], cutover, "", listenForCutover, "");
+    }
+
+    //If the receiver inputs are not yet calibrated, but calibration values were just received, set isCalibrated to true
+    if (!isCalibrated && calibrationComplete()) isCalibrated = true;

     //Reset newData to false so we can read new serial input
     newData = false;
-  }
-}
-
-//Processes received JSON for a given servo (e.g. ServoData struct)
-//The inputISR is the interrupt service routine function for triggering action upon receiver input
-void processServoJson(JsonObject& servoJson, ServoData& servoData, const char servoType[], void inputISR(), const char servoError[]) {
-  if (!receiverControl) {
-    //Check for any output configuration parameters for this servo
-    if (servoJson.containsKey("outputConfig")) {
-      JsonObject& outputConfig = servoJson["outputConfig"];
-      if (outputConfig.containsKey("max")) {
-        servoData.outputMax = (byte) outputConfig["max"];
-      }
-      if (outputConfig.containsKey("min")) {
-        servoData.outputMin = (byte) outputConfig["min"];
-      }
-      if (outputConfig.containsKey("pin")) {
-        if (servoData.servo.attached()) servoData.servo.detach();
-        byte servoOutPin = (byte) outputConfig["pin"];
-        servoData.servo.attach(servoOutPin);
-      }
-    }
-    //Check for any input configuration parameters for this servo
-    if (servoJson.containsKey("inputConfig")) {
-      JsonObject& inputConfig = servoJson["inputConfig"];
-      if (inputConfig.containsKey("max")) {
-        servoData.inputMax = (uint16_t) inputConfig["max"];
-      }
-      if (inputConfig.containsKey("min")) {
-        servoData.inputMin = (uint16_t) inputConfig["min"];
-      }
-      if (inputConfig.containsKey("pin")) {
-        if (servoData.inputPin != BYTE_MAX) PCintPort::detachInterrupt(servoData.inputPin);
-        servoData.inputPin = (byte) inputConfig["pin"];
-        PCintPort::attachInterrupt(servoData.inputPin, inputISR, CHANGE);
-      }
-      if (inputConfig.containsKey("receiverOnly")) {
-        servoData.receiverInputOnly = (boolean) inputConfig["receiverOnly"];
-      }
-    }
-    //Only write to servo if the phone input for this servo has permission to do so
-    if (!servoData.receiverInputOnly) {
-      //Check for any servo output values to write to the servo
-      if (servoJson.containsKey("value")) {
-        if (servoData.servo.attached()) {
-          byte value = (byte) servoJson["value"];
-          servoData.servo.write(value);
-          sendJsonServoOutput(servoType, value);
-        } else {
-          sendJsonString("error", servoError);
-        }
-      }
-    }
-  } else {
-    sendJsonString("error", "Disable receiver control to configure servos");
   }
 }

@@ -469,6 +419,61 @@
   cutover.inputMin= RECEIVER_INPUT_MAX;
 }

+//Processes received JSON for a given servo (e.g. ServoData struct)
+//The inputISR is the interrupt service routine function for triggering action upon receiver input
+void processServoJson(JsonObject& servoJson, ServoData& servoData, const char servoType[], void inputISR(), const char servoError[]) {
+  if (!receiverControl) {
+    //Check for any output configuration parameters for this servo
+    if (servoJson.containsKey("outputConfig")) {
+      JsonObject& outputConfig = servoJson["outputConfig"];
+      if (outputConfig.containsKey("max")) {
+        servoData.outputMax = (byte) outputConfig["max"];
+      }
+      if (outputConfig.containsKey("min")) {
+        servoData.outputMin = (byte) outputConfig["min"];
+      }
+      if (outputConfig.containsKey("pin")) {
+        if (servoData.servo.attached()) servoData.servo.detach();
+        byte servoOutPin = (byte) outputConfig["pin"];
+        servoData.servo.attach(servoOutPin);
+      }
+    }
+    //Check for any input configuration parameters for this servo
+    if (servoJson.containsKey("inputConfig")) {
+      JsonObject& inputConfig = servoJson["inputConfig"];
+      if (inputConfig.containsKey("max")) {
+        servoData.inputMax = (uint16_t) inputConfig["max"];
+      }
+      if (inputConfig.containsKey("min")) {
+        servoData.inputMin = (uint16_t) inputConfig["min"];
+      }
+      if (inputConfig.containsKey("pin")) {
+        if (servoData.inputPin != BYTE_MAX) PCintPort::detachInterrupt(servoData.inputPin);
+        servoData.inputPin = (byte) inputConfig["pin"];
+        PCintPort::attachInterrupt(servoData.inputPin, inputISR, CHANGE);
+      }
+      if (inputConfig.containsKey("receiverOnly")) {
+        servoData.receiverInputOnly = (boolean) inputConfig["receiverOnly"];
+      }
+    }
+    //Only write to servo if the phone input for this servo has permission to do so
+    if (!servoData.receiverInputOnly) {
+      //Check for any servo output values to write to the servo
+      if (servoJson.containsKey("value")) {
+        if (servoData.servo.attached()) {
+          byte value = (byte) servoJson["value"];
+          servoData.servo.write(value);
+          sendJsonServoOutput(servoType, value);
+        } else {
+          sendJsonString("error", servoError);
+        }
+      }
+    }
+  } else {
+    sendJsonString("error", "Disable receiver control to configure servos");
+  }
+}
+
 //Interrupt Service Routine (ISR) for aileron receiver input
 void listenForAileron() {
   static uint32_t aileronInStart;
@@ -526,8 +531,7 @@

 //Determines whether a given value is valid receiver input
 boolean isValidReceiverInput(uint16_t receiverInput) {
-  if (receiverInput < RECEIVER_INPUT_MAX && receiverInput > RECEIVER_INPUT_MIN) return true;
-  return false;
+  return receiverInput < RECEIVER_INPUT_MAX && receiverInput > RECEIVER_INPUT_MIN;
 }

 //Processes receiver input for a given servo and writes the converted output to the servo
@@ -540,6 +544,8 @@
     //Only write to servo if the receiver input for this servo has permission to do so
     } else if (receiverControl || (sharedControl && servoData.receiverInputOnly)) {
       if (servoData.servo.attached()) {
+        //Constrain the servo input value to be within the calibration range (since mapping won't constrain it)
+        servoInputValue = constrain(servoInputValue, servoData.inputMin, servoData.inputMax);
         //The receiverInput (in microseconds) is mapped to the min and max for the Servo.write function (degrees)
         byte convertedServoValue = map(servoInputValue, servoData.inputMin, servoData.inputMax, servoData.outputMin, servoData.outputMax);
         servoData.servo.write(convertedServoValue);