-Re-factored api calls. (less nesting)

-Re-factored api calls. (less nesting)

--- webservertask.ino
+++ webservertask.ino
@@ -28,26 +28,655 @@

   server.on( "/api/login", HTTP_POST, []( AsyncWebServerRequest * request )
   {
-    if ( request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
-    {
-      request->send( 200, textPlainHeader, "Logged in." );
+    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
+    {
+      return request->requestAuthentication();
+    }
+    request->send( 200 );
+  });
+
+  // / or 'index.htm'
+  server.on( "/", HTTP_GET, [] ( AsyncWebServerRequest * request )
+  {
+    request->send_P( 200, textHtmlHeader, index_htm );
+  });
+
+  //  /channels or 'channels.htm'
+  server.on( "/channels", HTTP_GET, [] ( AsyncWebServerRequest * request )
+  {
+    request->send_P( 200, textHtmlHeader, channels_htm);
+  });
+
+  //  /editor or 'editor.htm'
+  server.on( "/editor", HTTP_GET, [] ( AsyncWebServerRequest * request )
+  {
+    request->send_P( 200, textHtmlHeader, editor_htm );
+  });
+
+  //  /setup or 'setup.htm'
+  server.on( "/setup", HTTP_GET, [] ( AsyncWebServerRequest * request )
+  {
+    request->send_P( 200, textHtmlHeader, setup_htm );
+  });
+
+  //  /filemanager or 'fileman.htm'
+  server.on( "/filemanager", HTTP_GET, [] ( AsyncWebServerRequest * request )
+  {
+    request->send_P( 200, textHtmlHeader, fileman_htm );
+  });
+
+  /**********************************************************************************************
+      api device get calls
+  **********************************************************************************************/
+
+  server.on( "/api/deletefile", HTTP_POST, []( AsyncWebServerRequest * request)
+  {
+    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
+    {
+      return request->requestAuthentication();
+    }
+
+    if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime ) )
+    {
+      return request->send( 501, textPlainHeader, "SPI Bus not available" );
+    }
+
+    if ( !request->hasArg( "filename" ) )
+    {
+      xSemaphoreGive( x_SPI_Mutex );
+      return request->send( 400, textPlainHeader, "Invalid filename." );
+    }
+
+    String path;
+
+    if ( !request->arg( "filename" ).startsWith( "/" ) )
+    {
+      path = "/" + request->arg( "filename" );
     }
     else
     {
-      request->send( 401, textPlainHeader, "Not logged in." );
-    }
+      path = request->arg( "filename" );
+    }
+
+    if ( !SD.exists( path ) )
+    {
+      path = request->arg( "filename" ) + " not found.";
+      xSemaphoreGive( x_SPI_Mutex );
+      return request->send( 404, textPlainHeader, path );
+    }
+    SD.remove( path );
+    path = request->arg( "filename" ) + " deleted.";
+    xSemaphoreGive( x_SPI_Mutex );
+    request->send( 200, textPlainHeader, path );
+  });
+
+  server.on( "/api/getdevice", HTTP_GET, []( AsyncWebServerRequest * request)
+  {
+    char content[1024];
+
+    if ( request->hasArg( "boottime" ) )
+    {
+      snprintf( content, sizeof( content ), "%s", asctime( localtime( &systemStart.tv_sec ) ) );
+    }
+    else if ( request->hasArg( "channelcolors" ) )
+    {
+      uint8_t charCount = 0;
+      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
+      {
+        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", channel[channelNumber].color.c_str() );
+      }
+    }
+    else if ( request->hasArg( "channelnames" ) )
+    {
+      uint8_t charCount = 0;
+      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
+      {
+        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", channel[channelNumber].name.c_str() );
+      }
+    }
+    else if ( request->hasArg( "diskspace" ) )
+    {
+      if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime  ) )
+      {
+        return request->send( 501, textPlainHeader, "SPI bus not available" );
+      }
+      if ( sdcardPresent )
+      {
+        snprintf( content, sizeof( content ), "%llu" , SD.totalBytes() - SD.usedBytes() );
+      }
+      else
+      {
+        xSemaphoreGive( x_SPI_Mutex );
+        return request->send( 501, textPlainHeader, "SD card not available" );
+      }
+      xSemaphoreGive( x_SPI_Mutex );
+    }
+    else if ( request->hasArg( "files" ) )
+    {
+      if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime ) )
+      {
+        return request->send( 501, textPlainHeader, "SPI bus not available" );
+      }
+      File root = SD.open( "/" );
+      if ( !root )
+      {
+        xSemaphoreGive( x_SPI_Mutex );
+        return request->send( 503, textPlainHeader, "SD card not available." );
+      }
+      if ( !root.isDirectory() )
+      {
+        xSemaphoreGive( x_SPI_Mutex );
+        return request->send( 400, textPlainHeader, "Not a directory");
+      }
+      File file = root.openNextFile();
+      uint16_t charCount = 0;
+      content[0] = 0;        /* solves webif filemanager showing garbage when no files on sd */
+      while ( file )
+      {
+        if ( !file.isDirectory() )
+        {
+          size_t fileSize = file.size();
+          charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s,%s\n", file.name(), humanReadableSize( fileSize ).c_str() );
+        }
+        file = root.openNextFile();
+      }
+      xSemaphoreGive( x_SPI_Mutex );
+    }
+    else if ( request->hasArg( "hostname" ) )
+    {
+      snprintf( content, sizeof( content ), "%s", hostName );
+    }
+    else if ( request->hasArg( "minimumlevels" ) )
+    {
+      uint8_t charCount = 0;
+      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
+      {
+        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%.2f\n", channel[channelNumber].minimumLevel );
+      }
+    }
+    else if ( request->hasArg( "oledcontrast" ) )
+    {
+      snprintf( content, sizeof( content ), "%i", oledContrast );
+    }
+    else if ( request->hasArg( "oledorientation" ) )
+    {
+      if ( OLED_ENABLED )
+      {
+        snprintf( content, sizeof( content ), "%s", oledOrientation == OLED_ORIENTATION_NORMAL ? "normal" : "upsidedown" );
+      }
+      else
+      {
+        return request->send( 501, textPlainHeader, "Not present." );
+      }
+    }
+    else if ( request->hasArg( "pwmdepth" ) )
+    {
+      snprintf( content, sizeof( content ), "%i", ledcNumberOfBits );
+    }
+    else if ( request->hasArg( "pwmfrequency" ) )
+    {
+      snprintf( content, sizeof( content ), "%.0f", ledcActualFrequency );
+    }
+
+    else if ( request->hasArg( "tftorientation" ) )
+    {
+      if ( tftPresent )
+      {
+        snprintf( content, sizeof( content ), "%s", ( tftOrientation == TFT_ORIENTATION_NORMAL ) ? "normal" : "upsidedown" );
+      }
+      else
+      {
+        return request->send( 501, textPlainHeader, "Not present." );
+      }
+    }
+
+    else if ( request->hasArg( "sensor" ) )
+    {
+      if ( !numberOfFoundSensors )
+      {
+        return request->send( 501, textPlainHeader, "No sensors present." );
+      }
+
+      if ( !request->hasArg( "number" ) )
+      {
+        return request->send( 400, textPlainHeader, "No sensornumber" );
+      }
+
+      uint8_t sensorNumber = request->arg( "number" ).toInt();
+
+      if ( sensorNumber >= numberOfFoundSensors )
+      {
+        return request->send( 400, textPlainHeader, "Invalid sensornumber" );
+      }
+
+      uint8_t charCount = 0;
+
+      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", sensor[sensorNumber].name );
+      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%f\n", sensor[sensorNumber].temp / 16.0 );
+      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%x\n", sensor[sensorNumber].addr );
+    }
+
+    else if ( request->hasArg( "sensorname" ) )
+    {
+      if ( !numberOfFoundSensors )
+      {
+        return request->send( 501, textPlainHeader, "No sensors present." );
+      }
+
+      if ( !request->hasArg( "number" ) )
+      {
+        return request->send( 400, textPlainHeader, "No sensornumber" );
+      }
+
+      uint8_t sensorNumber = request->arg( "number" ).toInt();
+
+      if ( sensorNumber >= numberOfFoundSensors )
+      {
+        return request->send( 400, textPlainHeader, "Invalid sensornumber" );
+      }
+      snprintf( content, sizeof( content ), "%s", sensor[sensorNumber].name );
+    }
+
+
+
+
+
+    else if ( request->hasArg( "status" ) )
+    {
+      uint8_t charCount = 0;
+      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
+      {
+        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%.2f\n", channel[channelNumber].currentPercentage );
+      }
+      time_t now = time(0);
+      charCount += strftime( content + charCount, sizeof( content ) - charCount, "%T\n", localtime( &now ) );
+      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", lightStatus.c_str() );
+      if ( numberOfFoundSensors )
+      {
+        for ( uint8_t sensorNumber = 0; sensorNumber < numberOfFoundSensors; sensorNumber++ )
+        {
+          charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s,%.1f\n", sensor[sensorNumber].name, sensor[sensorNumber].temp / 16 );
+        }
+      }
+    }
+    else if ( request->hasArg( "tftbrightness" ) )
+    {
+      if ( tftPresent )
+      {
+        snprintf( content, sizeof( content ), "%.2f", tftBrightness );
+      }
+      else
+      {
+        return request->send( 501, textPlainHeader, "Not present." );
+      }
+    }
+    else if ( request->hasArg( "timezone" ) )
+    {
+      snprintf( content, sizeof( content ), "%s", getenv( "TZ" ) );
+    }
+    else
+    {
+      return request->send( 400, textPlainHeader, "Invalid option" );
+    }
+    request->send( 200, textPlainHeader, content );
+  });
+
+  server.on( "/api/setchannel", HTTP_POST, []( AsyncWebServerRequest * request )
+  {
+    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
+    {
+      return request->requestAuthentication();
+    }
+
+    uint8_t channelNumber;
+    char   content[100];
+
+    channelNumber = checkChannelNumber( request);
+    if ( channelNumber == -1 )
+    {
+      return request->send( 400, textPlainHeader, "Invalid channel" );
+    }
+
+    if ( request->hasArg( "color" ) )
+    {
+      for ( uint8_t currentChar = 0; currentChar < request->arg( "color" ).length(); currentChar++ )
+      {
+        if ( !isxdigit( request->arg( "color" )[currentChar] ) )
+        {
+          return request->send( 400, textPlainHeader, "Invalid char" );
+        }
+      }
+      channel[ channelNumber ].color = "#" + request->arg( "color" );
+
+
+      //pre-use 'content' buffer
+      snprintf( content, sizeof( content ), "channelcolor%i", channelNumber );
+      saveStringNVS( content, channel[channelNumber].color );
+
+      snprintf( content, sizeof( content ), "channel %i color set to %s", channelNumber + 1, channel[ channelNumber ].color.c_str() );
+    }
+
+
+
+    else if ( request->hasArg( "minimum" ) )
+    {
+      float minLevel = request->arg( "minimum" ).toFloat();
+      if ( minLevel < 0 || minLevel > 0.991 )
+      {
+        return request->send( 400, textPlainHeader, "Invalid level" );
+      }
+      channel[ channelNumber ].minimumLevel = minLevel;
+
+      //pre-use 'content' buffer
+      snprintf( content, sizeof( content ), "channelminimum%i", channelNumber );
+      saveFloatNVS( content, channel[channelNumber].minimumLevel );
+
+      snprintf( content, sizeof( content ), "channel %i minimum set to %.2f%%", channelNumber + 1, channel[ channelNumber ].minimumLevel );
+    }
+
+    else if ( request->hasArg( "name" ) )
+    {
+      for ( uint8_t currentChar = 0; currentChar < request->arg( "name" ).length(); currentChar++ )
+      {
+        if ( request->arg( "name" )[currentChar] != 0x20  && !isalnum( request->arg( "name" )[currentChar] ) )
+        {
+          snprintf( content, sizeof( content ), "Invalid character '%c'.", request->arg( "name" )[currentChar] );
+          return request->send( 400, textPlainHeader, content );
+        }
+      }
+      if ( request->arg( "name" ) != "" )
+      {
+        channel[ channelNumber ].name = request->arg( "name" );
+      }
+      else
+      {
+        channel[ channelNumber ].name = "Channel" + String( channelNumber + 1 );
+      }
+
+      //pre-use 'content' buffer
+      snprintf( content, sizeof( content ), "channelname%i", channelNumber );
+      saveStringNVS( content, channel[channelNumber].name );
+
+      snprintf( content, sizeof( content ), "channel %i name set to '%s'", channelNumber + 1, channel[ channelNumber ].name.c_str() );
+    }
+
+
+
+    else
+    {
+      return request->send( 400, textPlainHeader, "Invalid option" );
+    }
+
+    request->send( 200, textPlainHeader, content );
+  });
+
+  server.on( "/api/setdevice", HTTP_POST, []( AsyncWebServerRequest * request )
+  {
+    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
+    {
+      return request->requestAuthentication();
+    }
+
+    char content[100];
+
+    if ( request->hasArg( "clearnvs" ) )
+    {
+      clearNVS();
+      snprintf( content, sizeof( content ), "NVS cleared" );
+    }
+
+
+
+    else if ( request->hasArg( "hostname" ) )
+    {
+      if ( !setupMDNS( request->arg( "hostname" ) ) )
+      {
+        return request->send( 400, textPlainHeader, "name not available." );
+      }
+      snprintf( hostName , sizeof( hostName ), "%s", request->arg( "hostname" ).c_str() );
+      saveStringNVS( "hostname", hostName );
+      snprintf( content, sizeof( content ), "%s", hostName );
+    }
+
+
+
+    else if ( request->hasArg( "lightsoff" ) )
+    {
+      vTaskSuspend( x_dimmerTaskHandle );
+      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
+      {
+        channel[channelNumber].currentPercentage = 0;
+        ledcWrite( channelNumber, 0 );
+      }
+      lightStatus = "LIGHTS OFF ";
+      snprintf( content, sizeof( content ), "%s", lightStatus.c_str() );
+    }
+
+
+
+    else if ( request->hasArg( "lightson" ) )
+    {
+      vTaskSuspend( x_dimmerTaskHandle );
+      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
+      {
+        channel[channelNumber].currentPercentage = 100;
+        ledcWrite( channelNumber, ledcMaxValue );
+      }
+      lightStatus = " LIGHTS ON ";
+      snprintf( content, sizeof( content ), "%s", lightStatus.c_str() );
+    }
+
+
+
+    else if ( request->hasArg( "lightsprogram" ) )
+    {
+      lightStatus = "LIGHTS AUTO";
+      snprintf( content, sizeof( content ), "%s", lightStatus.c_str() );
+      vTaskResume( x_dimmerTaskHandle );
+    }
+
+
+
+    else if ( request->hasArg( "loadtimers" ) )
+    {
+      snprintf( content, sizeof( content ), "%s", defaultTimersLoaded() ? "Timers loaded." : "Not loaded." );
+    }
+
+
+
+    else if ( request->hasArg( "oledcontrast" ) )
+    {
+      request->arg( "oledcontrast" );
+      int8_t contrast = request->arg( "oledcontrast" ).toInt();
+      if ( contrast < 0 || contrast > 15 )
+      {
+        return request->send( 400, textPlainHeader, "Invalid contrast." );
+      }
+      oledContrast = contrast;
+      OLED.setContrast( contrast << 4 );
+      saveUint8NVS( "oledcontrast", oledContrast );
+      snprintf( content, sizeof( content ), "%i", contrast );
+    }
+
+
+
+    else if ( request->hasArg( "oledorientation" ) )
+    {
+      if ( request->arg( "oledorientation" ) == "upsidedown" )
+      {
+        oledOrientation = OLED_ORIENTATION_UPSIDEDOWN;
+      }
+      else if ( request->arg( "oledorientation" ) == "normal" )
+      {
+        oledOrientation = OLED_ORIENTATION_NORMAL;
+      }
+      else
+      {
+        return request->send( 400, textPlainHeader, "Invalid orientation" );
+      }
+      OLED.end();
+      OLED.init();
+      OLED.setContrast( oledContrast << 0x04 );
+      oledOrientation == OLED_ORIENTATION_NORMAL ? OLED.normalDisplay() : OLED.flipScreenVertically();
+      saveStringNVS( "oledorientation", ( oledOrientation == OLED_ORIENTATION_NORMAL ? "normal" : "upsidedown" ) );
+      snprintf( content, sizeof( content ), "%s", oledOrientation == OLED_ORIENTATION_NORMAL ? "normal" : "upsidedown" );
+    }
+
+
+
+    else if ( request->hasArg( "password" ) )
+    {
+      //TODO:check password is valid
+      if ( request->arg( "password") == "" )
+      {
+        return request->send( 400, textPlainHeader, "Supply a password. Password not changed." );
+      }
+
+      //some more tests...
+
+      saveStringNVS( passwordKeyNVS, request->arg( "password") );
+
+      strncpy( content, "Password saved.", sizeof( content ) );
+    }
+
+
+
+    else if ( request->hasArg( "pwmdepth" ) )
+    {
+      uint8_t newPWMDepth = request->arg( "pwmdepth" ).toInt();
+      if ( newPWMDepth < 11 || newPWMDepth > 16 )
+      {
+        return request->send( 400, textPlainHeader, "Invalid PWM depth" );
+      }
+      if ( ledcNumberOfBits != newPWMDepth )
+      {
+        setupDimmerPWMfrequency( LEDC_MAXIMUM_FREQ, newPWMDepth );
+        saveInt8NVS( "pwmdepth" , ledcNumberOfBits );
+      }
+      snprintf( content, sizeof( content ), "%i", ledcNumberOfBits );
+    }
+
+
+
+    else if ( request->hasArg( "pwmfrequency" ) )
+    {
+      double tempPWMfrequency = request->arg( "pwmfrequency" ).toFloat();
+      if ( tempPWMfrequency < 100 || tempPWMfrequency > LEDC_MAXIMUM_FREQ )
+      {
+        request->send( 200, textPlainHeader, "Invalid PWM frequency" );
+        return;
+      }
+      if ( tempPWMfrequency != ledcActualFrequency )
+      {
+        setupDimmerPWMfrequency( tempPWMfrequency, ledcNumberOfBits );
+        saveDoubleNVS( "pwmfrequency", ledcActualFrequency );
+      }
+      snprintf( content, sizeof( content ), "%.0f", ledcActualFrequency );
+    }
+
+
+
+    else if ( request->hasArg( "sensorname" ) )
+    {
+      if ( !request->hasArg( "number" ) )
+      {
+        return request->send( 400, textPlainHeader, "No sensornumber" );
+      }
+
+      uint8_t sensorNumber = request->arg( "number" ).toInt();
+
+      if ( sensorNumber > numberOfFoundSensors )
+      {
+        return request->send( 400, textPlainHeader, "Invalid sensornumber" );
+      }
+
+      snprintf( sensor[sensorNumber].name, sizeof( sensor[sensorNumber].name ), "%s", request->arg( "sensorname" ).c_str() );
+
+      snprintf( content, sizeof( content ), "sensorname%i", sensorNumber );
+      saveStringNVS( content, request->arg( "sensorname" ).c_str() );
+
+      Serial.printf( " Saved '%s' as '%s'\n", request->arg( "sensorname" ).c_str(), content );
+
+      snprintf( content , sizeof( content ), "%s", request->arg( "sensorname" ).c_str() );
+    }
+
+
+
+    else if ( request->hasArg( "tftorientation" ) )
+    {
+      if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime ) )
+      {
+        return request->send( 501, textPlainHeader, "SPI bus not available" );
+      }
+
+      if (  request->arg( "tftorientation" ) == "normal" )
+      {
+        tftOrientation = TFT_ORIENTATION_NORMAL;
+      }
+      else if ( request->arg( "tftorientation" ) == "upsidedown" )
+      {
+        tftOrientation = TFT_ORIENTATION_UPSIDEDOWN;
+      }
+      else
+      {
+        return request->send( 400, textPlainHeader, "Invalid tft orientation." );
+      }
+      tft.setRotation( tftOrientation );
+      tft.fillScreen( ILI9341_BLACK );
+      saveStringNVS( "tftorientation", ( tftOrientation == TFT_ORIENTATION_NORMAL ) ? "normal" : "upsidedown" );
+      snprintf( content, sizeof( content ), "%s", ( tftOrientation == TFT_ORIENTATION_NORMAL ) ? "normal" : "upsidedown" );
+
+      xSemaphoreGive( x_SPI_Mutex );
+    }
+
+
+
+    else if ( request->hasArg( "tftbrightness" ) )
+    {
+      float brightness = request->arg( "tftbrightness" ).toFloat();
+      if ( brightness < 0 || brightness > 100 )
+      {
+        return request->send( 400, textPlainHeader, "Invalid tft brightness." );
+      }
+      tftBrightness = brightness;
+      saveInt8NVS( "tftbrightness", brightness );
+      snprintf( content, sizeof( content ), "%.2f", tftBrightness );
+    }
+
+
+
+    else if ( request->hasArg( "timezone" ) )
+    {
+      //Serial.printf( request->arg( "timezone" ) );
+      urlDecode( request->arg( "timezone" ) );
+      if ( 0 == setenv( "TZ",  request->arg( "timezone" ).c_str(), 1 )  )
+      {
+        saveStringNVS( "timezone", getenv( "TZ" ) );
+      }
+      else
+      {
+        return request->send( 400, textPlainHeader, "Error setting timezone." );
+      }
+      snprintf( content, sizeof( content ), "%s", getenv( "TZ" ) );
+    }
+
+
+
+    else
+    {
+      return request->send( 400, textPlainHeader, "Invalid option" );
+    }
+
+    request->send( 200, textPlainHeader, content );
   });

   server.on( "/api/upload", HTTP_POST, []( AsyncWebServerRequest * request )
   {
-    if ( request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
-    {
-      request->send( 200 );
-    }
-    else
-    {
-      request->requestAuthentication();
-    }
+    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
+    {
+      return request->requestAuthentication();
+    }
+    request->send( 200 );
   },
   []( AsyncWebServerRequest * request, String filename, size_t index, uint8_t *data, size_t len, bool final )
   {
@@ -104,657 +733,6 @@
       Serial.printf( "Upload %iBytes in %.2fs which is %.2ikB/s.\n", index, ( millis() - startTimer ) / 1000.0, index / ( millis() - startTimer ) );
     }
     xSemaphoreGive( x_SPI_Mutex );
-  });
-
-  // / or 'index.htm'
-  server.on( "/", HTTP_GET, [] ( AsyncWebServerRequest * request )
-  {
-    request->send_P( 200, textHtmlHeader, index_htm );
-  });
-
-  //  /channels or 'channels.htm'
-  server.on( "/channels", HTTP_GET, [] ( AsyncWebServerRequest * request )
-  {
-    request->send_P( 200, textHtmlHeader, channels_htm);
-  });
-
-  //  /editor or 'editor.htm'
-  server.on( "/editor", HTTP_GET, [] ( AsyncWebServerRequest * request )
-  {
-    request->send_P( 200, textHtmlHeader, editor_htm );
-  });
-
-  //  /setup or 'setup.htm'
-  server.on( "/setup", HTTP_GET, [] ( AsyncWebServerRequest * request )
-  {
-    request->send_P( 200, textHtmlHeader, setup_htm );
-  });
-
-  //  /filemanager or 'fileman.htm'
-  server.on( "/filemanager", HTTP_GET, [] ( AsyncWebServerRequest * request )
-  {
-    request->send_P( 200, textHtmlHeader, fileman_htm );
-  });
-
-  /**********************************************************************************************
-      api device get calls
-  **********************************************************************************************/
-
-  server.on( "/api/getdevice", HTTP_GET, []( AsyncWebServerRequest * request)
-  {
-    char content[1024];
-
-    if ( request->hasArg( "boottime" ) )
-    {
-      snprintf( content, sizeof( content ), "%s", asctime( localtime( &systemStart.tv_sec ) ) );
-    }
-    else if ( request->hasArg( "channelcolors" ) )
-    {
-      uint8_t charCount = 0;
-      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
-      {
-        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", channel[channelNumber].color.c_str() );
-      }
-    }
-    else if ( request->hasArg( "channelnames" ) )
-    {
-      uint8_t charCount = 0;
-      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
-      {
-        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", channel[channelNumber].name.c_str() );
-      }
-    }
-    else if ( request->hasArg( "diskspace" ) )
-    {
-      if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime  ) )
-      {
-        return request->send( 501, textPlainHeader, "SPI bus not available" );
-      }
-      if ( sdcardPresent )
-      {
-        snprintf( content, sizeof( content ), "%llu" , SD.totalBytes() - SD.usedBytes() );
-      }
-      else
-      {
-        xSemaphoreGive( x_SPI_Mutex );
-        return request->send( 501, textPlainHeader, "SD card not available" );
-      }
-      xSemaphoreGive( x_SPI_Mutex );
-    }
-    else if ( request->hasArg( "files" ) )
-    {
-      if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime ) )
-      {
-        return request->send( 501, textPlainHeader, "SPI bus not available" );
-      }
-      File root = SD.open( "/" );
-      if ( !root )
-      {
-        xSemaphoreGive( x_SPI_Mutex );
-        return request->send( 503, textPlainHeader, "SD card not available." );
-      }
-      if ( !root.isDirectory() )
-      {
-        xSemaphoreGive( x_SPI_Mutex );
-        return request->send( 400, textPlainHeader, "Not a directory");
-      }
-      File file = root.openNextFile();
-      uint16_t charCount = 0;
-      content[0] = 0;        /* solves webif filemanager showing garbage when no files on sd */
-      while ( file )
-      {
-        if ( !file.isDirectory() )
-        {
-          size_t fileSize = file.size();
-          charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s,%s\n", file.name(), humanReadableSize( fileSize ).c_str() );
-        }
-        file = root.openNextFile();
-      }
-      xSemaphoreGive( x_SPI_Mutex );
-    }
-    else if ( request->hasArg( "hostname" ) )
-    {
-      snprintf( content, sizeof( content ), "%s", hostName );
-    }
-    else if ( request->hasArg( "minimumlevels" ) )
-    {
-      uint8_t charCount = 0;
-      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
-      {
-        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%.2f\n", channel[channelNumber].minimumLevel );
-      }
-    }
-    else if ( request->hasArg( "oledcontrast" ) )
-    {
-      snprintf( content, sizeof( content ), "%i", oledContrast );
-    }
-    else if ( request->hasArg( "oledorientation" ) )
-    {
-      if ( OLED_ENABLED )
-      {
-        snprintf( content, sizeof( content ), "%s", oledOrientation == OLED_ORIENTATION_NORMAL ? "normal" : "upsidedown" );
-      }
-      else
-      {
-        return request->send( 501, textPlainHeader, "Not present." );
-      }
-    }
-    else if ( request->hasArg( "pwmdepth" ) )
-    {
-      snprintf( content, sizeof( content ), "%i", ledcNumberOfBits );
-    }
-    else if ( request->hasArg( "pwmfrequency" ) )
-    {
-      snprintf( content, sizeof( content ), "%.0f", ledcActualFrequency );
-    }
-
-    else if ( request->hasArg( "tftorientation" ) )
-    {
-      if ( tftPresent )
-      {
-        snprintf( content, sizeof( content ), "%s", ( tftOrientation == TFT_ORIENTATION_NORMAL ) ? "normal" : "upsidedown" );
-      }
-      else
-      {
-        return request->send( 501, textPlainHeader, "Not present." );
-      }
-    }
-
-    else if ( request->hasArg( "sensor" ) )
-    {
-      if ( !numberOfFoundSensors )
-      {
-        return request->send( 501, textPlainHeader, "No sensors present." );
-      }
-
-      if ( !request->hasArg( "number" ) )
-      {
-        return request->send( 400, textPlainHeader, "No sensornumber" );
-      }
-
-      uint8_t sensorNumber = request->arg( "number" ).toInt();
-
-      if ( sensorNumber >= numberOfFoundSensors )
-      {
-        return request->send( 400, textPlainHeader, "Invalid sensornumber" );
-      }
-
-      uint8_t charCount = 0;
-
-      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", sensor[sensorNumber].name );
-      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%f\n", sensor[sensorNumber].temp / 16.0 );
-      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%x\n", sensor[sensorNumber].addr );
-    }
-
-    else if ( request->hasArg( "sensorname" ) )
-    {
-      if ( !numberOfFoundSensors )
-      {
-        return request->send( 501, textPlainHeader, "No sensors present." );
-      }
-
-      if ( !request->hasArg( "number" ) )
-      {
-        return request->send( 400, textPlainHeader, "No sensornumber" );
-      }
-
-      uint8_t sensorNumber = request->arg( "number" ).toInt();
-
-      if ( sensorNumber >= numberOfFoundSensors )
-      {
-        return request->send( 400, textPlainHeader, "Invalid sensornumber" );
-      }
-      snprintf( content, sizeof( content ), "%s", sensor[sensorNumber].name );
-    }
-
-
-
-
-
-    else if ( request->hasArg( "status" ) )
-    {
-      uint8_t charCount = 0;
-      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
-      {
-        charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%.2f\n", channel[channelNumber].currentPercentage );
-      }
-      time_t now = time(0);
-      charCount += strftime( content + charCount, sizeof( content ) - charCount, "%T\n", localtime( &now ) );
-      charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s\n", lightStatus.c_str() );
-      if ( numberOfFoundSensors )
-      {
-        for ( uint8_t sensorNumber = 0; sensorNumber < numberOfFoundSensors; sensorNumber++ )
-        {
-          charCount += snprintf( content + charCount, sizeof( content ) - charCount, "%s,%.1f\n", sensor[sensorNumber].name, sensor[sensorNumber].temp / 16 );
-        }
-      }
-    }
-    else if ( request->hasArg( "tftbrightness" ) )
-    {
-      if ( tftPresent )
-      {
-        snprintf( content, sizeof( content ), "%.2f", tftBrightness );
-      }
-      else
-      {
-        return request->send( 501, textPlainHeader, "Not present." );
-      }
-    }
-    else if ( request->hasArg( "timezone" ) )
-    {
-      snprintf( content, sizeof( content ), "%s", getenv( "TZ" ) );
-    }
-    else
-    {
-      return request->send( 400, textPlainHeader, "Invalid option" );
-    }
-    request->send( 200, textPlainHeader, content );
-  });
-
-  /**********************************************************************************************
-      api device set calls
-  **********************************************************************************************/
-
-  server.on( "/api/setdevice", HTTP_POST, []( AsyncWebServerRequest * request )
-  {
-    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
-    {
-      return request->requestAuthentication();
-    }
-
-    char content[100];
-
-    if ( request->hasArg( "clearnvs" ) )
-    {
-      clearNVS();
-      snprintf( content, sizeof( content ), "NVS cleared" );
-    }
-
-
-
-    else if ( request->hasArg( "hostname" ) )
-    {
-      if ( !setupMDNS( request->arg( "hostname" ) ) )
-      {
-        return request->send( 400, textPlainHeader, "name not available." );
-      }
-      snprintf( hostName , sizeof( hostName ), "%s", request->arg( "hostname" ).c_str() );
-      saveStringNVS( "hostname", hostName );
-      snprintf( content, sizeof( content ), "%s", hostName );
-    }
-
-
-
-    else if ( request->hasArg( "lightsoff" ) )
-    {
-      vTaskSuspend( x_dimmerTaskHandle );
-      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
-      {
-        channel[channelNumber].currentPercentage = 0;
-        ledcWrite( channelNumber, 0 );
-      }
-      lightStatus = "LIGHTS OFF ";
-      snprintf( content, sizeof( content ), "%s", lightStatus.c_str() );
-    }
-
-
-
-    else if ( request->hasArg( "lightson" ) )
-    {
-      vTaskSuspend( x_dimmerTaskHandle );
-      for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
-      {
-        channel[channelNumber].currentPercentage = 100;
-        ledcWrite( channelNumber, ledcMaxValue );
-      }
-      lightStatus = " LIGHTS ON ";
-      snprintf( content, sizeof( content ), "%s", lightStatus.c_str() );
-    }
-
-
-
-    else if ( request->hasArg( "lightsprogram" ) )
-    {
-      lightStatus = "LIGHTS AUTO";
-      snprintf( content, sizeof( content ), "%s", lightStatus.c_str() );
-      vTaskResume( x_dimmerTaskHandle );
-    }
-
-
-
-    else if ( request->hasArg( "loadtimers" ) )
-    {
-      snprintf( content, sizeof( content ), "%s", defaultTimersLoaded() ? "Timers loaded." : "Not loaded." );
-    }
-
-
-
-    else if ( request->hasArg( "oledcontrast" ) )
-    {
-      request->arg( "oledcontrast" );
-      int8_t contrast = request->arg( "oledcontrast" ).toInt();
-      if ( contrast < 0 || contrast > 15 )
-      {
-        return request->send( 400, textPlainHeader, "Invalid contrast." );
-      }
-      oledContrast = contrast;
-      OLED.setContrast( contrast << 4 );
-      saveUint8NVS( "oledcontrast", oledContrast );
-      snprintf( content, sizeof( content ), "%i", contrast );
-    }
-
-
-
-    else if ( request->hasArg( "oledorientation" ) )
-    {
-      if ( request->arg( "oledorientation" ) == "upsidedown" )
-      {
-        oledOrientation = OLED_ORIENTATION_UPSIDEDOWN;
-      }
-      else if ( request->arg( "oledorientation" ) == "normal" )
-      {
-        oledOrientation = OLED_ORIENTATION_NORMAL;
-      }
-      else
-      {
-        return request->send( 400, textPlainHeader, "Invalid orientation" );
-      }
-      OLED.end();
-      OLED.init();
-      OLED.setContrast( oledContrast << 0x04 );
-      oledOrientation == OLED_ORIENTATION_NORMAL ? OLED.normalDisplay() : OLED.flipScreenVertically();
-      saveStringNVS( "oledorientation", ( oledOrientation == OLED_ORIENTATION_NORMAL ? "normal" : "upsidedown" ) );
-      snprintf( content, sizeof( content ), "%s", oledOrientation == OLED_ORIENTATION_NORMAL ? "normal" : "upsidedown" );
-    }
-
-
-
-    else if ( request->hasArg( "password" ) )
-    {
-      //TODO:check password is valid
-      if ( request->arg( "password") == "" )
-      {
-        return request->send( 400, textPlainHeader, "Supply a password. Password not changed." );
-      }
-
-      //some more tests...
-
-      saveStringNVS( passwordKeyNVS, request->arg( "password") );
-
-      strncpy( content, "Password saved.", sizeof( content ) );
-    }
-
-
-
-    else if ( request->hasArg( "pwmdepth" ) )
-    {
-      uint8_t newPWMDepth = request->arg( "pwmdepth" ).toInt();
-      if ( newPWMDepth < 11 || newPWMDepth > 16 )
-      {
-        return request->send( 400, textPlainHeader, "Invalid PWM depth" );
-      }
-      if ( ledcNumberOfBits != newPWMDepth )
-      {
-        setupDimmerPWMfrequency( LEDC_MAXIMUM_FREQ, newPWMDepth );
-        saveInt8NVS( "pwmdepth" , ledcNumberOfBits );
-      }
-      snprintf( content, sizeof( content ), "%i", ledcNumberOfBits );
-    }
-
-
-
-    else if ( request->hasArg( "pwmfrequency" ) )
-    {
-      double tempPWMfrequency = request->arg( "pwmfrequency" ).toFloat();
-      if ( tempPWMfrequency < 100 || tempPWMfrequency > LEDC_MAXIMUM_FREQ )
-      {
-        request->send( 200, textPlainHeader, "Invalid PWM frequency" );
-        return;
-      }
-      if ( tempPWMfrequency != ledcActualFrequency )
-      {
-        setupDimmerPWMfrequency( tempPWMfrequency, ledcNumberOfBits );
-        saveDoubleNVS( "pwmfrequency", ledcActualFrequency );
-      }
-      snprintf( content, sizeof( content ), "%.0f", ledcActualFrequency );
-    }
-
-
-
-    else if ( request->hasArg( "sensorname" ) )
-    {
-      if ( !request->hasArg( "number" ) )
-      {
-        return request->send( 400, textPlainHeader, "No sensornumber" );
-      }
-
-      uint8_t sensorNumber = request->arg( "number" ).toInt();
-
-      if ( sensorNumber > numberOfFoundSensors )
-      {
-        return request->send( 400, textPlainHeader, "Invalid sensornumber" );
-      }
-
-      snprintf( sensor[sensorNumber].name, sizeof( sensor[sensorNumber].name ), "%s", request->arg( "sensorname" ).c_str() );
-
-      snprintf( content, sizeof( content ), "sensorname%i", sensorNumber );
-      saveStringNVS( content, request->arg( "sensorname" ).c_str() );
-
-      Serial.printf( " Saved '%s' as '%s'\n", request->arg( "sensorname" ).c_str(), content );
-
-      snprintf( content , sizeof( content ), "%s", request->arg( "sensorname" ).c_str() );
-    }
-
-
-
-    else if ( request->hasArg( "tftorientation" ) )
-    {
-      if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime ) )
-      {
-        return request->send( 501, textPlainHeader, "SPI bus not available" );
-      }
-
-      if (  request->arg( "tftorientation" ) == "normal" )
-      {
-        tftOrientation = TFT_ORIENTATION_NORMAL;
-      }
-      else if ( request->arg( "tftorientation" ) == "upsidedown" )
-      {
-        tftOrientation = TFT_ORIENTATION_UPSIDEDOWN;
-      }
-      else
-      {
-        return request->send( 400, textPlainHeader, "Invalid tft orientation." );
-      }
-      tft.setRotation( tftOrientation );
-      tft.fillScreen( ILI9341_BLACK );
-      saveStringNVS( "tftorientation", ( tftOrientation == TFT_ORIENTATION_NORMAL ) ? "normal" : "upsidedown" );
-      snprintf( content, sizeof( content ), "%s", ( tftOrientation == TFT_ORIENTATION_NORMAL ) ? "normal" : "upsidedown" );
-
-      xSemaphoreGive( x_SPI_Mutex );
-    }
-
-
-
-    else if ( request->hasArg( "tftbrightness" ) )
-    {
-      float brightness = request->arg( "tftbrightness" ).toFloat();
-      if ( brightness < 0 || brightness > 100 )
-      {
-        return request->send( 400, textPlainHeader, "Invalid tft brightness." );
-      }
-      tftBrightness = brightness;
-      saveInt8NVS( "tftbrightness", brightness );
-      snprintf( content, sizeof( content ), "%.2f", tftBrightness );
-    }
-
-
-
-    else if ( request->hasArg( "timezone" ) )
-    {
-      //Serial.printf( request->arg( "timezone" ) );
-      urlDecode( request->arg( "timezone" ) );
-      if ( 0 == setenv( "TZ",  request->arg( "timezone" ).c_str(), 1 )  )
-      {
-        saveStringNVS( "timezone", getenv( "TZ" ) );
-      }
-      else
-      {
-        return request->send( 400, textPlainHeader, "Error setting timezone." );
-      }
-      snprintf( content, sizeof( content ), "%s", getenv( "TZ" ) );
-    }
-
-
-
-    else
-    {
-      return request->send( 400, textPlainHeader, "Invalid option" );
-    }
-
-    request->send( 200, textPlainHeader, content );
-  });
-
-  server.on( "/api/setdevice", HTTP_GET, []( AsyncWebServerRequest * request )
-  {
-    return request->send( 404, textPlainHeader, "Not found." );
-  });
-
-  /**********************************************************************************************
-      api channel set calls
-  **********************************************************************************************/
-
-  server.on( "/api/setchannel", HTTP_POST, []( AsyncWebServerRequest * request )
-  {
-    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
-    {
-      return request->requestAuthentication();
-    }
-
-    uint8_t channelNumber;
-    char   content[100];
-
-    channelNumber = checkChannelNumber( request);
-    if ( channelNumber == -1 )
-    {
-      return request->send( 400, textPlainHeader, "Invalid channel" );
-    }
-
-    if ( request->hasArg( "color" ) )
-    {
-      for ( uint8_t currentChar = 0; currentChar < request->arg( "color" ).length(); currentChar++ )
-      {
-        if ( !isxdigit( request->arg( "color" )[currentChar] ) )
-        {
-          return request->send( 400, textPlainHeader, "Invalid char" );
-        }
-      }
-      channel[ channelNumber ].color = "#" + request->arg( "color" );
-
-
-      //pre-use 'content' buffer
-      snprintf( content, sizeof( content ), "channelcolor%i", channelNumber );
-      saveStringNVS( content, channel[channelNumber].color );
-
-      snprintf( content, sizeof( content ), "channel %i color set to %s", channelNumber + 1, channel[ channelNumber ].color.c_str() );
-    }
-
-
-
-    else if ( request->hasArg( "minimum" ) )
-    {
-      float minLevel = request->arg( "minimum" ).toFloat();
-      if ( minLevel < 0 || minLevel > 0.991 )
-      {
-        return request->send( 400, textPlainHeader, "Invalid level" );
-      }
-      channel[ channelNumber ].minimumLevel = minLevel;
-
-      //pre-use 'content' buffer
-      snprintf( content, sizeof( content ), "channelminimum%i", channelNumber );
-      saveFloatNVS( content, channel[channelNumber].minimumLevel );
-
-      snprintf( content, sizeof( content ), "channel %i minimum set to %.2f%%", channelNumber + 1, channel[ channelNumber ].minimumLevel );
-    }
-
-    else if ( request->hasArg( "name" ) )
-    {
-      for ( uint8_t currentChar = 0; currentChar < request->arg( "name" ).length(); currentChar++ )
-      {
-        if ( request->arg( "name" )[currentChar] != 0x20  && !isalnum( request->arg( "name" )[currentChar] ) )
-        {
-          snprintf( content, sizeof( content ), "Invalid character '%c'.", request->arg( "name" )[currentChar] );
-          return request->send( 400, textPlainHeader, content );
-        }
-      }
-      if ( request->arg( "name" ) != "" )
-      {
-        channel[ channelNumber ].name = request->arg( "name" );
-      }
-      else
-      {
-        channel[ channelNumber ].name = "Channel" + String( channelNumber + 1 );
-      }
-
-      //pre-use 'content' buffer
-      snprintf( content, sizeof( content ), "channelname%i", channelNumber );
-      saveStringNVS( content, channel[channelNumber].name );
-
-      snprintf( content, sizeof( content ), "channel %i name set to '%s'", channelNumber + 1, channel[ channelNumber ].name.c_str() );
-    }
-
-
-
-    else
-    {
-      return request->send( 400, textPlainHeader, "Invalid option" );
-    }
-
-    request->send( 200, textPlainHeader, content );
-  });
-
-  /**********************************************************************************************/
-
-  server.on( "/api/deletefile", HTTP_POST, []( AsyncWebServerRequest * request)
-  {
-    if ( !xSemaphoreTake( x_SPI_Mutex, SPI_MutexMaxWaitTime ) )
-    {
-      return request->send( 501, textPlainHeader, "SPI Bus not available" );
-    }
-
-    if ( !request->authenticate( www_username, readStringNVS( passwordKeyNVS, www_default_passw ).c_str() ) )
-    {
-      xSemaphoreGive( x_SPI_Mutex );
-      return request->requestAuthentication();
-    }
-
-    if ( !request->hasArg( "filename" ) )
-    {
-      xSemaphoreGive( x_SPI_Mutex );
-      return request->send( 400, textPlainHeader, "Invalid filename." );
-    }
-
-    String path;
-
-    if ( !request->arg( "filename" ).startsWith( "/" ) )
-    {
-      path = "/" + request->arg( "filename" );
-    }
-    else
-    {
-      path = request->arg( "filename" );
-    }
-
-    if ( !SD.exists( path ) )
-    {
-      path = request->arg( "filename" ) + " not found.";
-      xSemaphoreGive( x_SPI_Mutex );
-      return request->send( 404, textPlainHeader, path );
-    }
-    SD.remove( path );
-    path = request->arg( "filename" ) + " deleted.";
-    xSemaphoreGive( x_SPI_Mutex );
-    request->send( 200, textPlainHeader, path );
   });

   server.onNotFound( []( AsyncWebServerRequest * request )


Changing telemetry for testing

Changing telemetry for testing

--- examples/quadcopter/quadcopter.ino
+++ examples/quadcopter/quadcopter.ino
@@ -4,6 +4,8 @@
 #include "platforms/Quadcopter.h"

 boolean calibrated = false;
+boolean altHold = false;
+float outputThrottle;
 uint32_t calStartTime;
 const uint8_t CHANNEL_MIN = 32;
 const uint8_t CHANNEL_MAX = 148;
@@ -27,6 +29,11 @@
     return down && left;
 });

+//
+uint32_t loopstart;
+uint32_t loopcount;
+//
+
 void setup() {
     setupQuad();
     setState(DISARMED);
@@ -35,9 +42,12 @@
 void setState(State s){
     state = s;
     comms.sendString(stateString[s]);
+    loopstart = micros();
+    loopcount = 0;
 }

 void loop() {
+    loopcount++;
     //always running updates
     loopQuad();
     sendTelemetry();
@@ -66,6 +76,7 @@
             if(calStartTime + 2000 < millis()) {
                 orientation.calibrate(false);
                 yawTarget = orientation.getYaw();
+                altitudeCalcInit();
                 output.standby();
                 setState(FLYING);
             }
@@ -84,12 +95,11 @@
 }

 void fly(){
-    static bool altHold = false;
     const float pitchCmd = ((float)APMRadio::get(RADIO_PITCH)-90) /-70.0;
     const float rollCmd  = ((float)APMRadio::get(RADIO_ROLL)-90)  /-70.0;
     const float yawCmd   = ((float)APMRadio::get(RADIO_YAW)-90)   / 90.0;
     const float throttle = ((float)APMRadio::get(RADIO_THROTTLE)-25)/130.0;
-    const bool  gearCmd  = (APMRadio::get(4) > 90);
+    const bool  gearCmd  = (APMRadio::get(RADIO_GEAR) > 90);

     // check for low throttle standby mode
     if(APMRadio::get(RADIO_THROTTLE) <= CHANNEL_MIN){
@@ -102,7 +112,6 @@
     // switch into and out of altitude hold mode
     if(gearCmd == false){
         //switch to manual mode
-        comms.sendString("Manual");
         altHold = false;
     } else if (altHold == false && gearCmd == true){
         comms.sendString("Alt Hold");
@@ -116,12 +125,13 @@
         yawTarget = truncateRadian(yawTarget);
     }

+    altitudeCalculation();
     // calculate target throttle
     float throttleOut = (altHold)? altHoldUpdate(throttle) :
                                    throttleCurve.get(throttle);

     // set output targets
-    comms.sendTelem(AMPERAGE, throttleOut);
+    outputThrottle = throttleOut;
     horizon.set(pitchCmd, rollCmd, yawTarget, throttleOut);
 }

@@ -135,7 +145,6 @@
 //

 void altHoldInit(float curThrottle){
-    altitudeEst = baro.getAltitude();
     altitudeSetpoint = altitudeEst;
     hover = curThrottle;
     oldIntegral = 0.0;
@@ -143,14 +152,35 @@
     throttleOutput = 0.0;
 }

+void altitudeCalcInit(){
+    altitudeEst = baro.getAltitude();
+}
+
+void altitudeCalculation(){
+    static auto timer = Interval::every(10);
+    if(timer()){
+        //float dt = (micros() - nextUpdate + UPDATE_MICROS) / 1e6;
+        const float dt = 0.01;
+
+        float C0 = AHP.C0;
+        float C1 = AHP.C1;
+
+        float barometer = baro.getAltitude();
+        float altitude = barometer*C0 + altitudeEst*(1.0-C0);
+        float newvelocity = (altitude-altitudeEst) / dt;
+        float velocity = newvelocity*C1 + velocityEst*(1.0-C1);
+
+        altitudeEst = altitude;
+        velocityEst = velocity;
+    }
+}
+
 float altHoldUpdate(float throttleCMD){
     static auto timer = Interval::every(10);
     if(timer()){
         //float dt = (micros() - nextUpdate + UPDATE_MICROS) / 1e6;
         const float dt = 0.01;

-        float C0 = AHP.C0;
-        float C1 = AHP.C1;
         float K0 = AHP.K0;
         float K1 = AHP.K1;
         float K2 = AHP.K2;
@@ -158,24 +188,12 @@
         //adjust setpoint
         float th = (throttleCMD-0.5);
         if(fabs(th) > 0.1) altitudeSetpoint += th/4.0;
-        float barometer = baro.getAltitude();
-
-        float altitude = barometer*C0 + altitudeEst*(1.0-C0);
-        float newvelocity = (altitude-altitudeEst) / dt;
-        float velocity = newvelocity*C1 + velocityEst*(1.0-C1);
-
-        float error = altitudeSetpoint-altitude;
+
+        float error = altitudeSetpoint-altitudeEst;
         float newIntegral = oldIntegral + error * dt;
-        throttleOutput = (K0/10.0)*(error + K1*velocity + K2*newIntegral) + hover;
-
-        altitudeEst = altitude;
-        velocityEst = velocity;
+        throttleOutput = (K0/10.0)*(error + K1*velocityEst + K2*newIntegral) + hover;
+
         oldIntegral = newIntegral;
-
-        comms.sendTelem(ALTITUDE, altitudeEst);
-        comms.sendTelem(ALTITUDE+1, velocityEst);
-        comms.sendTelem(ALTITUDE+2, oldIntegral);
-        comms.sendTelem(ALTITUDE+3, altitudeSetpoint);
     }

     return throttleOutput;
@@ -193,21 +211,24 @@
 }

 void sendTelemetry(){
-    static auto timer = Interval::every(50);
+    static auto timer = Interval::every(100);
     if(timer()){
         float voltage  = float((analogRead(67)/1024.l)*5.l*10.1f);
         float amperage = float((analogRead(66)/1024.l)*5.l*17.0f);

         using namespace Protocol;
-        comms.sendTelem(LATITUDE   , state);
-        comms.sendTelem(LONGITUDE  , ((float)APMRadio::get(RADIO_THROTTLE)-25)/130.0 );
+        comms.sendTelem(LATITUDE   , (micros()-loopstart)/loopcount);
+        comms.sendTelem(LONGITUDE  , altHold );
         comms.sendTelem(HEADING    , toDeg(orientation.getYaw()));
         comms.sendTelem(PITCH      , toDeg(orientation.getPitch()));
         comms.sendTelem(ROLL       , toDeg(orientation.getRoll()));
-        comms.sendTelem(GROUNDSPEED, profileTime(0));
-        comms.sendTelem(VOLTAGE    , dVdA(voltage,amperage));
-        //comms.sendTelem(AMPERAGE   , safe());
-        //comms.sendTelem(ALTITUDE   , altitude.get());
+        comms.sendTelem(GROUNDSPEED, outputThrottle);
+        comms.sendTelem(VOLTAGE    , voltage);
+        comms.sendTelem(AMPERAGE   , amperage);
+        comms.sendTelem(ALTITUDE, altitudeEst);
+        comms.sendTelem(ALTITUDE+1, velocityEst);
+        comms.sendTelem(ALTITUDE+2, oldIntegral);
+        comms.sendTelem(ALTITUDE+3, altitudeSetpoint);

         Serial.println();
         Serial.flush();


Ajout du code pour fio v3 +  code quasi final

--- src/deplacement/deplacement.ino
+++ src/deplacement/deplacement.ino
@@ -1,41 +1,76 @@
 #include <Servo.h>

-Servo roueGauche;
-Servo roueDroite;
-int zeroDroite;
-int zeroGauche;
+Servo bras;
+int zeroBras;
+char recv[10];
+int noRecv;
+int nbModules;
+int posX;
+double angle;
+unsigned long backTime;
+extern volatile unsigned long timer0_overflow_count;
+
 void setup() {
   // put your setup code here, to run once:
-  roueGauche.attach(5);
-  roueDroite.attach(3);
-  zeroDroite = roueDroite.read();
-  zeroGauche = roueGauche.read();
-  roueDroite.write(zeroDroite);
-  roueGauche.write(zeroGauche);
-  Serial.begin(9600);
+  backTime = 0;
+  bras.attach(3);
+  bras.write(90);
+  Serial1.begin(9600);
+  Serial1.write("NEW");
+  reset();
+  noRecv = 0;
+  nbModules = 1;
+  posX = 3;
 }

 void loop() {
   // put your main code here, to run repeatedly:
+  if(Serial1.available() > 0)
+  {
+    handleSerial();
+  }
+  unsigned long time = timer0_overflow_count;
+  if((time-backTime) >= 200)
+  {
+    backTime = time;
+    double angle = sin(time + posX);
+    int angleDeg = (angle * 4068) / 71;
+    bras.write(90+angleDeg);
+  }
 }

-void serialEvent()
+void handleSerial()
 {
-  Serial.println("Event serie");
-  int lu = Serial.read();
-  if(lu == 71) // Roue gauche
+  char lu = Serial1.read();
+  if(noRecv < 2)
   {
-    roueDroite.write(0);
-    roueGauche.write(180);
+    recv[noRecv] = lu;
+    noRecv++;
   }
-  else if(lu == 68) // Roue droite
+  else
   {
-    roueGauche.write(0);
-    roueDroite.write(180);
+    recv[noRecv] = lu;
+    if(recv[0] == 'R' && recv[1] == 'E' && recv[2] == 'S')
+    {
+        reset();
+    }
+    else if(recv[0] == 'N' && recv[1] == 'E' && recv[2] == 'W')
+    {
+        reset();
+        nbModules++;
+    }
+    else if(recv[0] == 'M' && recv[1] == 'A' && recv[2] == 'J')
+    {
+       char nbModulesRecv = Serial1.read();
+       nbModules = nbModulesRecv;
+    }
+    noRecv = 0;
   }
-  else if(lu == 83)
-  {
-    roueGauche.write(90);
-    roueDroite.write(90);
-  }
+
 }
+
+void reset()
+{
+  bras.write(90);
+  timer0_overflow_count = 0;
+}


remove serial port reporting from dButtonMove()

--- OS_HelperFunctions.ino
+++ OS_HelperFunctions.ino
@@ -80,10 +80,6 @@
 // execute a motion of the appropriate magnitude, accelaration, and speed.

 void dButtonMove(int axis, int direction){
-  byte verbose=0;
-
-  if (verbose)
-   Serial.println("Doing D-Button move");

   //Prepare for the step
   digitalWrite(stageLEDs[axis],HIGH);
@@ -323,4 +319,4 @@

   return rangedValue;
 } //End fscale
-
+


modified reporting every 1 minute

modified reporting every 1 minute

--- mws/mws.ino
+++ mws/mws.ino
@@ -193,7 +193,7 @@

   ss.begin(9600); //Begin listening to GPS over software serial at 9600. This should be the default baud of the module.
   Serial.print(F("lon,lat,altitude,sats,date,GMTtime,winddir"));
-  Serial.print(F(",windspeedms,windgustms,windspdms_avg2m,winddir_avg2m,windgustms_10m,windgustdir_10m"));
+  Serial.print(F(",windspeedms,windgustms,windgustdir,windspdms_avg2m,winddir_avg2m,windgustms_10m,windgustdir_10m"));
   Serial.print(F(",humidity,tempc,raindailymm,rainhourmm,rain5mmm,rainindicate,pressure,batt_lvl,light_lvl"));

   pinMode(STAT1, OUTPUT); //Status LED Blue


test both ADCs separatedly, reset settings after each test

improve all testing printing

--- examples/adc_test/adc_test.ino
+++ examples/adc_test/adc_test.ino
@@ -7,117 +7,159 @@

 #if defined(ADC_TEENSY_LC) // teensy LC
 #define PINS 13
-#define DIG_PINS 13
+#define DIG_ADC_0_PINS 13
+#define DIG_ADC_1_PINS 0
 #define PINS_DIFF 2
 uint8_t adc_pins[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9,A10,A11, A12};
-uint8_t adc_pins_dig[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9,A10,A11, A12};
+uint8_t adc_pins_dig_ADC_0[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9,A10,A11, A12};
+uint8_t adc_pins_dig_ADC_1[] = {};
 uint8_t adc_pins_diff[] = {A10, A11};

 #elif defined(ADC_TEENSY_3_0)  // teensy 3.0
 #define PINS 14
-#define DIG_PINS 10
+#define DIG_ADC_0_PINS 10
+#define DIG_ADC_1_PINS 0
 #define PINS_DIFF 4
 uint8_t adc_pins[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13};
-uint8_t adc_pins_dig[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9};
+uint8_t adc_pins_dig_ADC_0[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9};
+uint8_t adc_pins_dig_ADC_1[] = {};
 uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

 #elif defined(ADC_TEENSY_3_1) || defined(ADC_TEENSY_3_2)  // teensy 3.1/3.2
 #define PINS 21
-#define DIG_PINS 16
+#define DIG_ADC_0_PINS 10
+#define DIG_ADC_1_PINS 8
 #define PINS_DIFF 4
-uint8_t adc_pins[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,
-                      A14, A15,A16,A17,A18,A19,A20 };
-uint8_t adc_pins_dig[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9, A15,A16,A17,A18,A19,A20 };
+uint8_t adc_pins_dig_ADC_0[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,};
+uint8_t adc_pins_dig_ADC_1[] = {A2,A3, A15,A16,A17,A18,A19,A20 };
 uint8_t adc_pins_diff[] = {A10, A11, A12, A13};

 #elif defined(ADC_TEENSY_3_5)  // Teensy 3.5
 #define PINS 27
-#define DIG_PINS 21
+#define DIG_ADC_0_PINS 12
+#define DIG_ADC_1_PINS 11
 #define PINS_DIFF 2
-uint8_t adc_pins[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9, A10,
-                      A11,A12,A13,A14,A15,A16,A17,A18,A19,A20,A21,A22,A23,A24, A25, A26};
-uint8_t adc_pins_dig[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9, A12,A13,A14,A15,A16,A17,A18,A19,A20,A23,A24};
+uint8_t adc_pins_dig_ADC_0[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9,
+                                A14,A15};
+uint8_t adc_pins_dig_ADC_1[] = {A2,A3, A12,A13, A16,A17,A18,A19, A20,A23,A24};
 uint8_t adc_pins_diff[] = {A10, A11};

 #elif defined(ADC_TEENSY_3_6)  // Teensy 3.6
 #define PINS 25
-#define DIG_PINS 21
+#define DIG_ADC_0_PINS 12
+#define DIG_ADC_1_PINS 11
 #define PINS_DIFF 2
-uint8_t adc_pins[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9, A10,
-                      A11,A12,A13,A14,A15,A16,A17,A18,A19,A20,A21,A22,A23,A24};
-uint8_t adc_pins_dig[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9, A12,A13,A14,A15,A16,A17,A18,A19,A20,A23,A24};
+uint8_t adc_pins_dig_ADC_0[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9,
+                                A14,A15};
+uint8_t adc_pins_dig_ADC_1[] = {A2,A3, A12,A13, A16,A17,A18,A19, A20,A23,A24};
 uint8_t adc_pins_diff[] = {A10, A11};

 #elif defined(ADC_TEENSY_4)  // Teensy 4
 #define PINS 14
-#define DIG_PINS 10
+#define DIG_ADC_0_PINS 10
+#define DIG_ADC_1_PINS 10
 #define PINS_DIFF 0
-uint8_t adc_pins[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9, A10,
-                      A11,A12,A13};
-uint8_t adc_pins_dig[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9};
+uint8_t adc_pins_dig_ADC_0[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9};
+uint8_t adc_pins_dig_ADC_1[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8, A9};
 uint8_t adc_pins_diff[] = {};
 #endif // defined

-int value = 0;
+long int value = 0;
+
+#ifdef ADC_TEENSY_4
+const uint32_t num_samples = 10000;
+#else
+const uint32_t num_samples = 100;
+#endif

 // test the analog pins that also have a digital function
-bool test_pullup_down(bool pullup) {
+bool test_pullup_down(bool pullup, bool debug=false) {
     uint8_t mode = pullup ? INPUT_PULLUP : INPUT_PULLDOWN;

     const int max_val = adc->getMaxValue(ADC_0);

     bool pass_test = true;

-    for (int i=0;i<DIG_PINS;i++) {
-        pinMode(adc_pins_dig[i], mode);
-        delay(50); // settle time
-        value = adc->analogRead(adc_pins_dig[i]);
+    for (auto pin : adc_pins_dig_ADC_0) {
+      pinMode(pin, mode);
+      delay(10); // settle time
+
+        value = adc->adc0->analogRead(pin);
         bool fail_condition = pullup ? (value < 0.95*max_val) : (value > 0.05*max_val);
         if (fail_condition) {
-            Serial.print("A"); Serial.print(i); Serial.print(": ");
+          if (debug) {
+            Serial.print("Pin "); Serial.print(pin); Serial.print(": ");
             Serial.print("FAILED "); Serial.print(pullup ? "INPUT_PULLUP" : "INPUT_PULLDOWN");
             Serial.print("("); Serial.print(value); Serial.print(")");
+            Serial.print("max_val: "); Serial.print(pullup ? (0.95*max_val) : (0.05*max_val));
             Serial.println(". ");
+          }
             pass_test = false;
         }
     }
+    for (auto pin : adc_pins_dig_ADC_1) {
+      pinMode(pin, mode);
+      delay(10); // settle time
+
+        value = adc->adc1->analogRead(pin);
+        bool fail_condition = pullup ? (value < 0.95*max_val) : (value > 0.05*max_val);
+        if (fail_condition) {
+          if (debug) {
+            Serial.print("Pin "); Serial.print(pin); Serial.print(": ");
+            Serial.print("FAILED "); Serial.print(pullup ? "INPUT_PULLUP" : "INPUT_PULLDOWN");
+            Serial.print("("); Serial.print(value); Serial.print(")");
+            Serial.print("max_val: "); Serial.print(pullup ? (0.95*max_val) : (0.05*max_val));
+            Serial.println(". ");
+          }
+            pass_test = false;
+        }
+    }

     return pass_test;
 }

 const uint8_t pin_cmp = A0;

-bool test_compare() {
+bool test_compare(bool debug=false) {
     bool pass_test = true;

     // measurement will be ready if value < 1.0V
-    adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 0, ADC_0);
+    adc->enableCompare((int)(1.0/3.3*adc->getMaxValue(ADC_0)), 0, ADC_0);

     pinMode(pin_cmp, INPUT_PULLUP); // set to max
+    delay(10);
     // this should fail
     value = adc->analogRead(pin_cmp, ADC_0);
     if (adc->adc0->fail_flag != ADC_ERROR::COMPARISON) {
+      if (debug) {
         Serial.println("Comparison didn't fail.");
         Serial.println(adc->adc0->getError());
+      }
         pass_test = false;
     }
     adc->adc0->resetError();

     pinMode(pin_cmp, INPUT_PULLDOWN); // set to min
+    delay(50);
     // this should be ok
     value = adc->analogRead(pin_cmp, ADC_0);
-    if(adc->adc0->fail_flag != ADC_ERROR::CLEAR) {
-        Serial.println("Some other error happened when comparison should have succeeded.");
+    if(adc->adc0->fail_flag == ADC_ERROR::COMPARISON) {
+      if (debug) {
+        Serial.println("Comparison didn't succeed.");
         Serial.println(adc->adc0->getError());
+        Serial.println(value);
+      }
         pass_test = false;
     }
     adc->adc0->resetError();

+    adc->adc0->disableCompare();
+
     return pass_test;
 }

-bool test_compare_range() {
+bool test_compare_range(bool debug=false) {
     bool pass_test = true;

     // ready if value lies out of [1.0,2.0] V
@@ -128,8 +170,10 @@
     // this should be ok
     value = adc->analogRead(pin_cmp, ADC_0);
     if(adc->adc0->fail_flag != ADC_ERROR::CLEAR) {
+      if (debug) {
         Serial.println("Some other error happened when comparison should have succeeded.");
         Serial.println(adc->adc0->getError());
+      }
         pass_test = false;
     }

@@ -138,21 +182,23 @@
     // this should be ok
     value = adc->analogRead(pin_cmp, ADC_0);
     if(adc->adc0->fail_flag != ADC_ERROR::CLEAR) {
+      if (debug) {
         Serial.println("Some other error happened when comparison should have succeeded.");
         Serial.println(adc->adc0->getError());
+      }
         pass_test = false;
     }
     adc->adc0->resetError();

+    adc->adc0->disableCompare();

     return pass_test;
 }

-bool test_averages() {
+bool test_averages(bool debug=false) {
     elapsedMicros timeElapsed;
     bool pass_test = true;
-    const uint32_t num_samples = 10000;
     const uint8_t averages[] = {4, 8, 16, 32};
     float avg_times[4];
     volatile int value = 0;
@@ -187,84 +233,84 @@
     // this is even worse for Teensy LC, where 4 avgs take about 2.6 more than none.
     if((avg_times[0] < 2*one_avg_time) || (avg_times[0] > 4*one_avg_time)) {
         pass_test = false;
-        Serial.print("4 averages should take about 4 times longer than one, but it took ");
-        Serial.print(avg_times[0]/one_avg_time); Serial.println(" times longer");
-    }
-    // check that the times are between 90% and 110% their theoretical value.
+        if (debug) {
+          Serial.print("4 averages should take about 4 times longer than one, but it took ");
+          Serial.print(avg_times[0]/one_avg_time); Serial.println(" times longer");
+        }
+    }
+    // check that the times are between 80% and 120% their theoretical value.
     for(uint8_t i=1; i<4; i++) {
-        if((avg_times[i] < 2*0.9*avg_times[i-1]) || (avg_times[i] > 2*1.1*avg_times[i-1])) {
+        if((avg_times[i] < 2*0.8*avg_times[i-1]) || (avg_times[i] > 2*1.2*avg_times[i-1])) {
             pass_test = false;
-            Serial.print(averages[i]); Serial.print(" averages should take about twice as long as "); Serial.print(averages[i-1]);
-            Serial.print(", but it took "); Serial.print(avg_times[i]/avg_times[i-1]); Serial.println(" times longer.");
+            if (debug) {
+              Serial.print(averages[i]); Serial.print(" averages should take about twice as long as "); Serial.print(averages[i-1]);
+              Serial.print(", but it took "); Serial.print(avg_times[i]/avg_times[i-1]); Serial.println(" times longer.");
+            }
         }
     }

     return pass_test;
 }

+void resetSettings() {
+  adc->setAveraging(16); // set number of averages
+  adc->setResolution(12); // set bits of resolution
+  adc->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed
+  adc->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed
+
+  adc->setAveraging(16, ADC_1); // set number of averages
+  adc->setResolution(12, ADC_1); // set bits of resolution
+  adc->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED, ADC_1); // change the conversion speed
+  adc->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED, ADC_1); // change the sampling speed
+  adc->adc1->recalibrate();
+
+}
+
 void setup() {

     pinMode(LED_BUILTIN, OUTPUT);

     Serial.begin(9600);
-

     ///// ADC0 ////
     // reference can be ADC_REFERENCE::REF_3V3, ADC_REFERENCE::REF_1V2 (not for Teensy LC) or ADC_REFERENCE::REF_EXT.
     //adc->setReference(ADC_REFERENCE::REF_1V2, ADC_0); // change all 3.3 to 1.2 if you change the reference to 1V2

     adc->setAveraging(16); // set number of averages
-    adc->setResolution(16); // set bits of resolution
-
-    // it can be any of the ADC_CONVERSION_SPEED enum: VERY_LOW_SPEED, LOW_SPEED, MED_SPEED, HIGH_SPEED_16BITS, HIGH_SPEED or VERY_HIGH_SPEED
-    // see the documentation for more information
-    // additionally the conversion speed can also be ADACK_2_4, ADACK_4_0, ADACK_5_2 and ADACK_6_2,
-    // where the numbers are the frequency of the ADC clock in MHz and are independent on the bus speed.
-    adc->setConversionSpeed(ADC_CONVERSION_SPEED::LOW_SPEED); // change the conversion speed
-    // it can be any of the ADC_MED_SPEED enum: VERY_LOW_SPEED, LOW_SPEED, MED_SPEED, HIGH_SPEED or VERY_HIGH_SPEED
-    adc->setSamplingSpeed(ADC_SAMPLING_SPEED::LOW_SPEED); // change the sampling speed
-
-    // If you enable interrupts, notice that the isr will read the result, so that isComplete() will return false (most of the time)
-    //adc->enableInterrupts(ADC_0);
-
+    adc->setResolution(12); // set bits of resolution
+    adc->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed
+    adc->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed
+    adc->adc0->recalibrate();

     ////// ADC1 /////
     #if ADC_NUM_ADCS>1
     adc->setAveraging(16, ADC_1); // set number of averages
-    adc->setResolution(16, ADC_1); // set bits of resolution
-    adc->setConversionSpeed(ADC_CONVERSION_SPEED::LOW_SPEED, ADC_1); // change the conversion speed
-    adc->setSamplingSpeed(ADC_SAMPLING_SPEED::LOW_SPEED, ADC_1); // change the sampling speed
-
-    //adc->setReference(ADC_REFERENCE::REF_1V2, ADC_1);
-
-
-    // If you enable interrupts, note that the isr will read the result, so that isComplete() will return false (most of the time)
-    //adc->enableInterrupts(ADC_1);
-
-    #endif
-
-    adc->adc0->recalibrate();
-    #if ADC_NUM_ADCS>1
+    adc->setResolution(12, ADC_1); // set bits of resolution
+    adc->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED, ADC_1); // change the conversion speed
+    adc->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED, ADC_1); // change the sampling speed
     adc->adc1->recalibrate();
     #endif

     delay(2000);

     ////// START TESTS /////////////
-
-    bool pullup_test = test_pullup_down(true);
+    // do the tests
+
+    bool pullup_test = test_pullup_down(true, false);
+    resetSettings();
     Serial.print("PULLUP TEST "); Serial.println(pullup_test ? "PASS" : "FAIL");
-    bool pulldown_test = test_pullup_down(false);
+    bool pulldown_test = test_pullup_down(false, false);
+    resetSettings();
     Serial.print("PULLDOWN TEST "); Serial.println(pulldown_test ? "PASS" : "FAIL");
-    bool compare_test = test_compare();
+    bool compare_test = test_compare(true);
+    resetSettings();
     Serial.print("COMPARE TEST "); Serial.println(compare_test ? "PASS" : "FAIL");
-    bool compare_range_test = test_compare_range();
+    bool compare_range_test = test_compare_range(true);
+    resetSettings();
     Serial.print("COMPARE RANGE TEST "); Serial.println(compare_range_test ? "PASS" : "FAIL");
-    bool averages_test = test_averages();
+    bool averages_test = test_averages(true);
+    resetSettings();
     Serial.print("AVERAGES TEST "); Serial.println(averages_test ? "PASS" : "FAIL");
-    if (pullup_test & pulldown_test & compare_test & compare_range_test & averages_test) {
-      Serial.println("ALL TEST PASSED.");
-    }
 }


bring back info about LED connections

bring back info about LED connections

--- ksp-gegi-mini/ksp-gegi-mini.ino
+++ ksp-gegi-mini/ksp-gegi-mini.ino
@@ -4,6 +4,7 @@
 // IDE: Arduino Mini, ATmega168

 // dł kabelków 15-20cm
+// diody LED z uC: niebieskie (bez rezystora, krótka nóżka) do GND, białe (z rezystorem, długa nóżka) do pinu
 // przełączniki2: niebieski do GND, biały do pinu, wszystkie zamontowane tak, aby niebieski był w środku, biały na dole, a niepodłączone u góry (wtedy gałka w górę==załączenie)
 // przełączniki3: środkowy (ciemny) do GND, boczne do pinów, zamontowany bokiem


Generalize Barometric data to a typedefined struct and adjust functions

--- payload/payload.ino
+++ payload/payload.ino
@@ -1,8 +1,4 @@
-#include <Wire.h>
-#include <Adafruit_BMP085.h>
-
-/*
- * BMP180 setup instructions:
+/* BMP180 setup for Arduino UNO:
  * ----------------------------------------
  * Connect BMP180 V-in   to 3.3V or 5.0V
  * Connect BMP180 GND    to Ground
@@ -11,61 +7,81 @@
  * ----------------------------------------
 */

+#include <Wire.h>
+#include <Adafruit_BMP085.h>
+
+
 Adafruit_BMP085 bmp;

 void setup() {
-  Serial.begin(2000000); // bits/second
+  // Start serial communication.
+  Serial.begin(9600); // in bits/second
+
+  // Start the BMP-180. Trap the thread if no sensor is found.
   if (!bmp.begin()) {
-  	Serial.println("No BMP085 sensor found. Program will not proceed.");
+  	Serial.println("No BMP sensor found. Program will not proceed.");
   	while (1) { /* Trap the thread. */ }
   }
 }
+
+void writeBmpData() {}
+
+struct BmpData {

-void printBaroData() {
+  // Temperature.
+  // // UNIT: degrees Celcius
+  float temperature;
+
+  // Pressure at the sensor.
+  // UNIT: pascal (N/m^2)
+  int32_t ambient_pressure;
+
+  // Pressure altitude: altitude with altimeter setting at 101325 Pascals == 1013.25 millibars
+  // == 29.92 inches mercury (i.e., std. pressure) // For pressure conversions, visit NOAA
+  // at: https://www.weather.gov/media/epz/wxcalc/pressureConversion.pdf.
+  // (Pressure altitude is NOT correct for non-standard pressure or temperature.)
+  // UNIT: meters
+  float pressure_altitude;
+
+  // Only if a humidity sensor is viable, TODO (Nolan Holden):
+  // Add density altitude:
+  // pressure altitude corrected for nonstandard temperature.
+  // Remember: higher density altitude (High, Hot, and Humid) means decreased performance.
+
+  BmpData(float _temperature, int32_t _ambient_pressure, float _pressure_altitude) {
+    temperature = _temperature;
+    ambient_pressure = _ambient_pressure;
+    pressure_altitude = _pressure_altitude;
+  }
+};
+
+BmpData& getBmpData(void) {
+  return BmpData(bmp.readTemperature(), bmp.readPressure(), bmp.readAltitude());
+}
+
+void printBmpData(void) {

   // Get temperature.
   Serial.print("Temperature = ");
   Serial.print(bmp.readTemperature());
-  Serial.println(" *C");
+  Serial.println(" °C");

   // Get pressure at sensor.
   Serial.print("Pressure = ");
   Serial.print(bmp.readPressure());
   Serial.println(" Pa");

-  // Calculate pressure at 0 MSL. (0 meters mean sea-level)
-  Serial.print("Calculated pressure at 0 MSL = ");
-  Serial.print(bmp.readSealevelPressure());
-  Serial.println(" Pa");

-  // Get pressure altitude:
-  // altitude with (default) altimeter setting at 101325 Pascals == 1013.25 millibars
-  // == 29.92 inches mercury (i.e., std. pressure)
   Serial.print("Pressure altitude = ");
   Serial.print(bmp.readAltitude());
   Serial.println(" meters");

-  // TODO:
-  // Density altitude:
-  // pressure altitude corrected for nonstandard temperature
-  // High density altitude (High, Hot, and Humid) means decreased performance.

-  // Get indicated altitude:
-  // pressure altitude corrected for non-standard pressure, with
-  // altimeter setting (in Pascals) as argument
-  // (29.92 inches mercury == 101325 Pascals == 1013.25 millibars)
-  // For pressure conversions, visit NOAA at: https://www.weather.gov/media/epz/wxcalc/pressureConversion.pdf.
-  Serial.print("Indicated altitude =                                  ");
-  Serial.print(bmp.readAltitude());
-  Serial.println(" meters");
-  Serial.print("Indicated altitude with sensor pressure adjustment =  ");
-  Serial.print(bmp.readAltitude( 101400 /* (float)bmp.readPressure() */));
-  Serial.println(" meters");

   Serial.println();
 }

 void loop() {
   printBaroData();
-  delay(200);
+  delay(1000);
 }


I2C code updated

I2C code updated

Tested master-slave communication. OK

--- Lifetester/I2C.ino
+++ Lifetester/I2C.ino
@@ -74,15 +74,15 @@

   // assign elements of the buffer

-  BUFFER_WRITE(0.5 * (LTChannelA.timer + LTChannelB.timer));
-  BUFFER_WRITE(LTChannelA.IVData.v);
-  BUFFER_WRITE(LTChannelA.IVData.iTransmit);
-  BUFFER_WRITE(LTChannelB.IVData.v);
-  BUFFER_WRITE(LTChannelB.IVData.iTransmit);
-  BUFFER_WRITE(TSense.raw_data);
-  BUFFER_WRITE(analogRead(LdrPin));
-  BUFFER_WRITE(LTChannelA.error);
-  BUFFER_WRITE(LTChannelB.error);
+  BUFFER_WRITE(0.5 * (uint32_t)(LTChannelA.timer + LTChannelB.timer));
+  BUFFER_WRITE((uint16_t)LTChannelA.IVData.v);
+  BUFFER_WRITE((uint16_t)LTChannelA.IVData.iTransmit);
+  BUFFER_WRITE((uint16_t)LTChannelB.IVData.v);
+  BUFFER_WRITE((uint16_t)LTChannelB.IVData.iTransmit);
+  BUFFER_WRITE((uint16_t)TSense.raw_data);
+  BUFFER_WRITE((uint16_t)analogRead(LdrPin));
+  BUFFER_WRITE((uint8_t)LTChannelA.error);
+  BUFFER_WRITE((uint8_t)LTChannelB.error);

   #if DEBUG
     I2C_PrintByteArray(I2CByteBuffer, bufferIdx);


Remove duplicates

Remove duplicates

--- ButtonHandler/ButtonHandler.ino
+++ ButtonHandler/ButtonHandler.ino
@@ -1,53 +0,0 @@
-/*
-Framework to handle button presses in a logical way.
- */
-
-#include <SerialLCD.h>
-#include <SoftwareSerial.h> //this is a must
-
-
-SerialLCD slcd(11,12); //Initialize the lcd lib.
-
-const int inPin1 = 5; //right red button
-const int inPin2 = 4; //left red button
-const int inPin3 = 3; //green menu button
-const int inPin4 = 8; //yellow enter button, I/O 2
-const int inPin5 = 7; //left limit switch. Green/White twisted pair.
-const int inPin6 = 6; //right limit switch. Blue/White twisted pair.
-
-//const int Hallpin = 2;               // wired to Hall Effect sensor output
-//const int CWpin  = 10;                // wired to MD01B pin INa
-//const int CCWpin = 9;                // wired to MD01B pin INb
-
-
-int loopCounter = 0; //Loop counter to refresh screen.
-unsigned long lcdUpdate;
-unsigned long lastLcdUpdate;
-int refreshScreen = 0; //boolean counter to clear screen.
-
-int menuMode = 0; //Initialize the controller in setup mode.
-
-//measurement variables.
-unsigned int targetSteps = 32; //inital target for slew mode and setup mode.
-unsigned int testVar = 0; //counter for jogmode
-
-int stepIncrements = 0;
-int stepMultiplier = 1;
-
-void setup() { //int yer inpins
-
-  pinMode(inPin1, INPUT);
-  pinMode(inPin2, INPUT);
-  pinMode(inPin3, INPUT);
-  pinMode(inPin4, INPUT);
-  pinMode(inPin5, INPUT);
-  pinMode(inPin6, INPUT);
-
-  Serial.begin(9600);
-  slcd.begin();
-  slcd.backlight();
-}
-
-void loop() {
-
-}


First step motor and button

First step motor and button

--- Stepdir/stepdir_v1/stepdir_v1.ino
+++ Stepdir/stepdir_v1/stepdir_v1.ino
@@ -5,7 +5,7 @@
  * @email vas@vingrad.ru
 */

-//#define DEBUG
+#define DEBUG

 /**
 * Системные модули
@@ -25,28 +25,41 @@
 // #include "functions.h"

 void setup() {
+  pinMode(2, INPUT_PULLUP);
 #ifdef DEBUG
   Serial.begin(9600);
 #endif
   //первый мотор
-  Stm1.setRunMode(FOLLOW_POS);
+  Stm1.setRunMode(KEEP_SPEED);
   //устанавливаем скорость
   Stm1Speed = constrain(Stm1Speed, _MIN_SPEED_FP, MAX_STEP_SPEED);
   Stm1.setSpeed(Stm1Speed);
-  //установка макс. скорости
-  Stm1.setMaxSpeed(MAX_STEP_SPEED);
   //установка ускорения в шагах/сек/сек
-  Stm1.setAcceleration(300);
+  Stm1.setAcceleration(0);
   //отключать мотор при достижении цели
   Stm1.autoPower(true);
   // //включить мотор (если указан пин en)
   // Stm1.enable();
 }

+bool _work;
+double _speed;
+
 void loop() {
-
+
   if (!Stm1.tick()) {
-    Stm1Dir = !Stm1Dir;
-    Stm1.setTarget(Stm1Dir ? Stm1Steps : -Stm1Steps, RELATIVE);
+    Stm1.setSpeed(Stm1Speed);
   }
+
+  if (digitalRead(2) == LOW) {
+    Stm1.stop();
+    Serial.println("break");
+    Stm1Speed = Stm1Speed * -1;
+    Serial.println(Stm1Speed);
+  } else {
+    Serial.print("tick = ");
+    _speed = Stm1.getSpeed();
+    Serial.println(_speed);
+  }
+
 }


Update ac-toggle.ino

Update ac-toggle.ino

commended code

--- arduino/ac-toggle/ac-toggle.ino
+++ arduino/ac-toggle/ac-toggle.ino
@@ -1,6 +1,9 @@
+// brendan@horan.hk
+// ac-toggle
+
 // define the switches
-int relay_1 = 2;
-int relay_2 = 3;
+int relay_1 = 2; // Physical pin D2
+int relay_2 = 3; // Physical pin D3

 // string to store the incoming serial data
 String serial_in;
@@ -23,6 +26,7 @@
   if (serial_in == "relay_1") {
    Serial.println("in relay_1");
    digitalWrite(relay_1, HIGH);
+   // wait 200ms for the relay to tigger
    delay(200);
    digitalWrite(relay_1, LOW);
    Serial.println("exit relay_1");
@@ -32,6 +36,7 @@
   if (serial_in == "relay_2") {
    Serial.println("in relay_2");
    digitalWrite(relay_2, HIGH);
+   // wait 200ms for the relay to tigger
    delay(200);
    digitalWrite(relay_2, LOW);
    Serial.println("out relay_2");


Removed SPI interface and restored serial

--- arduino_motor/arduino_motor.ino
+++ arduino_motor/arduino_motor.ino
@@ -1,39 +1,23 @@
-#include <Wire.h>
-#include <SPI.h>
 #include <Adafruit_MotorShield.h>
-#include "utility/Adafruit_MS_PWMServoDriver.h"
-
-volatile boolean go;
-char command;
+//#include "utility/Adafruit_MS_PWMServoDriver.h"

 Adafruit_MotorShield AFMS = Adafruit_MotorShield();
 Adafruit_DCMotor *M1 = AFMS.getMotor(1);
 Adafruit_DCMotor *M2 = AFMS.getMotor(2);
 Adafruit_DCMotor *M3 = AFMS.getMotor(3);
 Adafruit_DCMotor *M4 = AFMS.getMotor(4);
-int inByte = 0;

 void setup() {
   Serial.begin(9600);           // set up Serial library at 9600 bps
   Serial.println("OK!");

-  SPCR |= bit (SPE); // slave mode;
-  pinMode(MISO, OUTPUT);
-  go = false;
-  SPI.attachInterrupt();
-
   AFMS.begin();  // create with the default frequency 1.6KHz
 }

-ISR (SPI_STC_vect) {
-  command = SPDR;
-  go = true;
-}
-
-
 void loop() {
-  if (go) {
-     go = false;
+  int command;
+  if (Serial.available()) {
+     command = Serial.read();
      switch (command) {
        case 'f':
          do_forward();


Battery icon

Battery icon

--- battery_indicator/battery_indicator.ino
+++ battery_indicator/battery_indicator.ino
@@ -24,25 +24,53 @@
   // initialize the robot
   Robot.begin();

-
   // initialize the screen
   Robot.beginTFT();

-  Robot.beginSD();
+  // Black screen
+  Robot.background(0,0,0);
+
+  batteryDraw();
+  batteryUpdate();
+}
+void loop()
+{
+  batteryUpdate();
+  delay(3000);
+}
+
+void batteryDraw()
+{
+  Robot.stroke(255, 255, 255);
+  // Body
+  Robot.line(115, 0, 115, 6);
+  Robot.line(115, 0, 125, 0);
+  Robot.line(115, 5, 125, 5);
+  // Tip
+  Robot.line(125, 1, 127, 1);
+  Robot.line(125, 4, 127, 4);
+  Robot.line(127, 1, 127, 5);
+}
+
+void batteryUpdate()
+{
+  Robot.noStroke();
+  value_old = value;
+  value = readVcc();
+  int level = map(value, 4400, 5000, 1, 11);
+  if (level < 3) {
+    Robot.fill(255,0,0);
+  } else if (level < 7) {
+    Robot.fill(0,0,255);
+  } else {
+    Robot.fill(0,255,0);
+  }
+  Robot.rect(116, 1, level, 4);

-    //The image is pulled from sd card
-  Robot.drawBMP("intro.bmp",0,0);
-
-}
-void loop(){
-  value_old = value;
-
-  // read a value
-  value = readVcc();
-
-
+  // Large number center screen
+  // @todo: remove
   // Clear
-  Robot.stroke(255,255,255);
+  Robot.stroke(0,0,0);
   Robot.textSize(3);
   Robot.text(value_old, 15, 50);
   // Write
@@ -52,12 +80,10 @@

   Robot.textSize(2);
   Robot.text("mV", 92, 58);
-
-
-  delay(3000);
 }

-long readVcc() {
+long readVcc()
+{
   // Read 1.1V reference against AVcc
   // set the reference to Vcc and the measurement to the internal 1.1V reference
   #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)


Testing sending binary struct to receiver

--- arduinoSensor/arduinoSensor.ino
+++ arduinoSensor/arduinoSensor.ino
@@ -4,14 +4,13 @@
 #include "printf.h"
 #include "common/PlantData.h"

-// TRANSMITTER
-
 RF24 radio(7, 8);

-
-//const byte rxAddr[6] = "00001";
 const unsigned char rxAddr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
 unsigned long time;
+
+// Device ID (HARDCODED FTW)
+unsigned short ID = 1;

 PlantData pd;

@@ -22,7 +21,6 @@
   printf("Starting transmitter!\n");

   radio.begin();
-
   radio.setPALevel(palevel);

   radio.setDataRate(dataRate);
@@ -33,16 +31,31 @@
   radio.openWritingPipe(rxAddr);

   radio.stopListening();
+
+  uint8_t payloadSize = radio.getPayloadSize();
+  printf("payload size: %d\n", payloadSize);
+
 }

 void loop()
 {
-  //time = millis();
-  const char text[] = "Hello World";
-  radio.write(&text, sizeof(text));
+  pd.id = ID;
+  pd.soilHumidity = 11;
+  pd.airTemperature = 3.14f;
+  pd.airHumidity = 0.6f;
+  pd.light = 66;
+
+  /*printf("pd.id -> %i\n", sizeof(pd.id));
+  printf("pd.solidHumidity -> %i\n", sizeof(pd.soilHumidity));
+  printf("pd.airTemperature -> %i\n", sizeof(pd.airTemperature));
+  printf("pd.airHumidity -> %i\n", sizeof(pd.airHumidity));
+  printf("pd.light -> %i\n", sizeof(pd.light));*/
+
+  //const char text[] = "Hello World";
+  radio.write(&pd, sizeof(pd));
   //radio.write(&time, sizeof(time));
   //printf("Sent: %s, with time; %ul\n", text, time);
-  printf("Sent: %s\n", text);
+  printf("Sent: %i bytes of data\n", sizeof(pd));

   delay(1000);
 }


Updates root layout

Updates root layout

--- njmakerday_20160318/route_root.ino
+++ njmakerday_20160318/route_root.ino
@@ -3,7 +3,7 @@

 void handleRoot() {
  digitalWrite ( ledWiFi, 0 );
-  char temp[1024];
+  char temp[1536];
   int sec = millis() / 1000;
   int min = sec / 60;
   int hr = min / 60;
@@ -16,23 +16,33 @@
   dtostrf(getTempFahrenheit(), 3, 2, tf);
   dtostrf(getHumidityRH(), 3, 2, hm);

-  snprintf ( temp, 1024,
+  snprintf ( temp, 1536,

-"\
-  \
-    <meta http-equiv='refresh' content='5'/>\
-    <title>ESP8266 Temp/Humidity Sensors</title>\
-    \
-  \
-  \
-
Hello from ESP8266!
\
-
Uptime: %02d:%02d:%02d
\
-
Celsius: %s C
\
-
Fahrenheit: %s F
\
-
Humidity: %s RH
\
-  \
+"<meta charset=\"utf-8\">\
+<meta http-equiv=\"refresh\" content=\"13\">\
+<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
+<title>ESP8266 Temp/Humidity Sensors</title>\
+\
+<link rel=\"stylesheet\" href=\"//cdn.jsdelivr.net/foundation/6.2.0/foundation.min.css\">\
+\
+\
+
ESP8266 Temp/Humidity Sensors

Uptime: %02d:%02d:%02d
\
+
\
+

%s

Celsius

\
+

%s

Fahrenheit

\
+

%s

Relative Humidity

\
+
\
+\
+\
+\
+\
 ",

     hr, min % 60, sec % 60, tc, tf, hm


Fixed pin mapping & ground reference.

Fixed pin mapping & ground reference.

--- ethernet-tester.ino
+++ ethernet-tester.ino
@@ -1,21 +1,65 @@
 #include <Bounce.h>

 // -----------------------------------------------------------------------------
-// Hardware Mapping
+// Hardware Mapping - Arduino Leonardo

-static const byte sPins[8] =
+template<int Pin>
+class ToggleButton : public Bounce
+{
+public:
+    static const int sDebouncingTimeMs = 10;
+
+public:
+    inline ToggleButton()
+        : Bounce(Pin, sDebouncingTimeMs)
+        , mState(false)
+    {
+
+    }
+
+public:
+    static inline void init()
+    {
+        pinMode(Pin, INPUT_PULLUP);
+    }
+
+    inline bool process()
+    {
+        const bool status = update();
+
+        if (fallingEdge())
+        {
+            mState = true; // Pressed
+        }
+        else if (risingEdge())
+        {
+            mState = false; // Released
+        }
+        if (mState != mPreviousState)
+        {
+            mPreviousState = mState;
+            return mState;
+        }
+        return false;
+    }
+private:
+    bool mState;
+    bool mPreviousState;
+};
+
+static const byte sOutputPins[8] =
 {
     0, 1, 2, 3, 4, 5, 6, 7
 };

-static const byte sPinMode = 14; // A0
-static const byte sPinIncr = 15; // A1
-static const byte sPinDecr = 16; // A2
+static const byte sModePin = 20; // A2
+static const byte sDecrPin = 21; // A3
+static const byte sIncrPin = 22; // A4
+static const byte sGndPin  = 23; // A5

-static const unsigned long sDebouncingTime = 10; // ms
-Bounce sModeButton(sPinMode, sDebouncingTime);
-Bounce sIncrButton(sPinIncr, sDebouncingTime);
-Bounce sDecrButton(sPinDecr, sDebouncingTime);
+ToggleButton<sModePin> sModeButton;
+ToggleButton<sIncrPin> sIncrButton;
+ToggleButton<sDecrPin> sDecrButton;

 // -----------------------------------------------------------------------------

@@ -42,10 +86,11 @@

     for (unsigned i = 0; i < numIterations; ++i)
     {
-        sModeButton.update();
-        if (sModeButton.fallingEdge())
+        if (sModeButton.process())
         {
             sRunningMode = !sRunningMode;
+            Serial.print("Running Mode: ");
+            Serial.println(sRunningMode ? "manual" : "automatic");
             return true;
         }
         delay(sSleepTime);
@@ -59,8 +104,16 @@
 {
     for (byte i = 0; i < 8; ++i)
     {
-        digitalWrite(sPins[i], LOW);
+        digitalWrite(sOutputPins[i], LOW);
     }
+}
+
+void updatePins(byte inPrevious)
+{
+    digitalWrite(sOutputPins[inPrevious],   LOW);
+    digitalWrite(sOutputPins[sCurrentStep], HIGH);
+    Serial.print("Pin ");
+    Serial.println(sCurrentStep + 1);
 }

 // -----------------------------------------------------------------------------
@@ -69,10 +122,9 @@
 {
     while (true)
     {
-        const unsigned current = sCurrentStep;
+        const unsigned previous = sCurrentStep;
         sCurrentStep = (sCurrentStep + 1) & 0x7;
-        digitalWrite(sPins[current],        LOW);
-        digitalWrite(sPins[sCurrentStep],   HIGH);
+        updatePins(previous);
         if (yield(1000))
         {
             reset();
@@ -85,21 +137,19 @@
 {
     while (true)
     {
-        const unsigned current = sCurrentStep;
-        sIncrButton.update();
-        sDecrButton.update();
+        const unsigned previous = sCurrentStep;
+        const bool incr = sIncrButton.process();
+        const bool decr = sDecrButton.process();

-        if (sIncrButton.fallingEdge())
+        if (incr)
         {
             sCurrentStep = (sCurrentStep + 1) & 0x7;
-            digitalWrite(sPins[current],        LOW);
-            digitalWrite(sPins[sCurrentStep],   HIGH);
+            updatePins(previous);
         }
-        else if (sDecrButton.fallingEdge())
+        else if (decr)
         {
             sCurrentStep = (sCurrentStep - 1) & 0x7;
-            digitalWrite(sPins[current],        LOW);
-            digitalWrite(sPins[sCurrentStep],   HIGH);
+            updatePins(previous);
         }

         if (yield(0))
@@ -114,14 +164,19 @@

 void setup()
 {
-    pinMode(sPinMode, INPUT_PULLUP);
-    pinMode(sPinIncr, INPUT_PULLUP);
-    pinMode(sPinDecr, INPUT_PULLUP);
+    sModeButton.init();
+    sIncrButton.init();
+    sDecrButton.init();
+    pinMode(sGndPin, OUTPUT); // GND reference

     for (byte i = 0; i < 8; ++i)
     {
-        pinMode(sPins[i], OUTPUT);
+        pinMode(sOutputPins[i], OUTPUT);
     }
+    Serial.begin(115200);
+    while (!Serial);
+    Serial.print("Ready. Running mode: ");
+    Serial.println(sRunningMode ? "manual" : "automatic");
 }

 void loop()


volume to max

volume to max

--- shira-pumpkin/shira-pumpkin.ino
+++ shira-pumpkin/shira-pumpkin.ino
@@ -7,7 +7,7 @@
 #include "DFRobotDFPlayerMini.h"

-#define VOLUME 10
+#define VOLUME 30
 #define NR_LEDS 5

 LED leds[] = {


Update template.ino

Update template.ino

Fixed the millis() rollover

--- files/template.ino
+++ files/template.ino
@@ -145,7 +145,7 @@
   // your last connection, then connect again and send data:
   uploadSuccess = false;
   currentTime = millis();
-  if(!client.connected() && (currentTime - lastConnectionTime > postingInterval)) {
+  if(!client.connected() && ((unsigned long)(currentTime - lastConnectionTime) >= postingInterval)) {
     Serial.println("-----------------------------------");
     Serial.print("Connecting and posting sensor sample (case ");
     Serial.print(sampleType);


refactored html generation

refactored html generation

--- ardesp/waterelf/waterelf.ino
+++ ardesp/waterelf/waterelf.ino
@@ -4,42 +4,6 @@
 #include <WiFiClient.h>
 #include <ESP8266WebServer.h>
 #include <DNSServer.h>
-
-// TODO refactor to create header from title
-// char* header() {
-//
-// }
-const char* ssid = "WaterElf";
-const char* html =
-  "<title>WaterElf Aquaponics Helper</title>"
-  ""
-  ""
-  "
Welcome to WaterElf
"
-  "
Control
"
-  "

"
-  "
join a wifi network
"
-  "
actuator links go here...
"
-  "

"
-  "
Monitor
"
-  "

"
-  "
data goes here...
"
-  "

"
-  "";
-const char* apChoice =
-  "<title>WaterElf Aquaponics Helper: Wifi Config</title>"
-  ""
-  ""
-  "
Choose a wifi access point to join

<form method='POST' action='chz'> "
-  "_ITEMS_
Pass key: <input type='textarea' name='key'>

 "
-  "<input type='submit' value='Submit'></form>
";
-const char* wstatus =
-  "<title>WaterElf Aquaponics Helper: Wifi Status</title>"
-  ""
-  ""
-  "
Wifi Status

";
-const char* footer =
-  "
WaterElf Home
"
-  "
Fish4Tea
";

 const byte DNS_PORT = 53;
 IPAddress apIP(192, 168, 1, 1);
@@ -47,6 +11,29 @@
 DNSServer dnsServer;
 ESP8266WebServer server(80);

+const char* ssid = "WaterElf";
+
+const char* pageTop =
+  "<title>WaterElf Aquaponics Helper";
+const char* pageTop2 = "</title>\n"
+  ""
+  "\n";
+const char* pageDefault =
+  "
Welcome to WaterElf
\n"
+  "
Control
"
+  "

"
+  "
join a wifi network
"
+  "
wifi status
"
+  "
TODO actuator links go here...
"
+  "

"
+  "
Monitor
"
+  "

"
+  "
TODO data goes here...
"
+  "

\n";
+const char* pageFooter =
+  "\n
WaterElf   "
+  "Fish4Tea
";
+
 void setup() {
   Serial.begin(115200);
   // Serial.setDebugOutput(true);
@@ -67,9 +54,9 @@

   server.on("/", handle_root);
   server.on("/generate_204", handle_root); // Android support
-  server.on("/L0", handle_L0);
-  server.on("/L2", handle_L2);
-  server.on("/ALL", handle_ALL);
+  server.on("/L0", handle_root);
+  server.on("/L2", handle_root);
+  server.on("/ALL", handle_root);
   server.onNotFound(handleNotFound);

   server.on("/wifi", handle_wifi);
@@ -88,38 +75,38 @@
 void handleNotFound() {
   Serial.print("\t\t\t\t URI Not Found: ");
   Serial.println(server.uri());
-  server.send ( 200, "text/plain", "URI Not Found" );
+  // TODO send redirect to /? or just use handle_root?
+  server.send(200, "text/plain", "URI Not Found");
 }

 void handle_root() {
   Serial.println("serving page notionally at /");
-  String toSend = html;
-  //toSend.replace("TGT0", LEDstate[0] ? "y" : "b");
-  //toSend.replace("TGT2", LEDstate[2] ? "y" : "b");
+  String toSend = pageTop;
+  toSend += pageTop2;
+  toSend += pageDefault;
+  toSend += pageFooter;
   server.send(200, "text/html", toSend);
   delay(100);
 }

 String genAPForm() {
-  String f = apChoice;
-  String buf = "";
+  String f = pageTop;
+  f += ": Wifi Config";
+  f += pageTop2;
+  f += "
Choose a wifi access point to join

\n";
+
   const char *checked = " checked";

   int n = WiFi.scanNetworks();
   Serial.println("scan done");
   if (n == 0) {
     Serial.println("no networks found");
-    f =
-      "<title>WaterElf Aquaponics Helper: Wifi Config</title>"
-      ""
-      "
No AP data :-(
"
-      "Back
Try again
"
-      "
WaterElf Home "
-      "Fish4Tea
"
-      "";
+    f += "No wifi access points found :-( ";
+    f += "Back
Try again?
\n";
   } else {
     Serial.print(n);
     Serial.println(" networks found");
+    f += "<form method='POST' action='chz'> ";
     for (int i = 0; i < n; ++i) {
       // print SSID and RSSI for each network found
       Serial.print(i + 1);
@@ -131,19 +118,21 @@
       Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*");
       delay(10);

-      buf.concat("<input type='radio' name='ssid' value='");
-      buf.concat(WiFi.SSID(i));
-      buf.concat("'");
-      buf.concat(checked);
-      buf.concat(">");
-      buf.concat(WiFi.SSID(i));
-      buf.concat("
\n");
+      f.concat("<input type='radio' name='ssid' value='");
+      f.concat(WiFi.SSID(i));
+      f.concat("'");
+      f.concat(checked);
+      f.concat(">");
+      f.concat(WiFi.SSID(i));
+      f.concat("
\n");
       checked = "";
     }
-    f.replace("_ITEMS_", buf);
+    f += "
Pass key: <input type='textarea' name='key'>

 ";
+    f += "<input type='submit' value='Submit'></form>
";
   }
   Serial.println("");

+  f += pageFooter;
   return f;
 }

@@ -156,7 +145,12 @@

 void handle_wifistatus() {
   Serial.println("serving page at /wifistatus");
-  String toSend = wstatus;
+
+  String toSend = pageTop;
+  toSend += ": Wifi Status";
+  toSend += pageTop2;
+  toSend += "
Wifi Status

";
+
   toSend += "
SSID: ";
   toSend += WiFi.SSID();
   toSend += "
";
@@ -180,14 +174,17 @@
        toSend += "unknown
";
   }
   toSend += "

";
-  toSend += footer;
+
+  toSend += pageFooter;
   server.send(200, "text/html", toSend);
   delay(100);
 }

 void handle_chz() {
   Serial.println("serving page at /chz");
-  String toSend = "
Done! Joining...
";
+  String toSend = pageTop;
+  toSend += ": joining wifi network";
+  toSend += pageTop2;
   String ssid = "";
   String key = "";

@@ -200,8 +197,11 @@
   }

   if(ssid == "") {
-    toSend = "
Ooops, no SSID...?
";
+    toSend += "
Ooops, no SSID...?
";
+    toSend += "
Looks like a bug :-(
";
   } else {
+    toSend += "
Done! Joining...
";
+    toSend += "
Check wifi status here.
";
     char ssidchars[sizeof(ssid)];
     char keychars[sizeof(key)];
     ssid.toCharArray(ssidchars, sizeof(ssid));
@@ -209,10 +209,7 @@
     WiFi.begin(ssidchars, keychars);
   }

-  server.send(200, "text/html", toSend);
-  delay(100);
-}
-
-void handle_L0() { handle_root(); }
-void handle_L2() { handle_root(); }
-void handle_ALL() { handle_root(); }
+  toSend += pageFooter;
+  server.send(200, "text/html", toSend);
+  delay(100);
+}


Removed unnecessary pin write

Removed unnecessary pin write

--- arduino_atmega328_burner.ino
+++ arduino_atmega328_burner.ino
@@ -64,6 +64,7 @@
 inline void setDataBus(byte val) FORCE_INLINE;
 inline void setWriteMode() FORCE_INLINE;
 inline void setReadMode() FORCE_INLINE;
+inline void writePage(ushort address, byte *buffer, int num) FORCE_INLINE;
 inline void writeToAddr(ushort address, byte value) FORCE_INLINE;
 inline byte readFromAddr(ushort address) FORCE_INLINE;
 inline byte readDataBus() FORCE_INLINE;
@@ -216,7 +217,6 @@
 // -------------

 void writePage(ushort address, byte *buffer, int num) {
-   digitalWrite(READ_PIN, LOW);
    digitalWrite(CHIP_PIN, HIGH);
    digitalWrite(WRITE_PIN, HIGH);
    digitalWrite(READ_PIN, HIGH);
@@ -236,7 +236,6 @@
 // Writes a byte using BUFFER_SIZE write timing.
 // Data pins must be in write mode before calling this.
 void writeToAddr(ushort address, byte value) {
-   digitalWrite(READ_PIN, LOW);
    digitalWrite(CHIP_PIN, HIGH);
    digitalWrite(WRITE_PIN, HIGH);
    digitalWrite(READ_PIN, HIGH);


Finished up dev

Finished up dev


Median filter enable/disable

Median filter enable/disable

--- tag/tag.ino
+++ tag/tag.ino
@@ -8,6 +8,8 @@
 #define CALIBRATE_OFFSET false
 #define CALIBRATE_MULTIPLIER false
 #define CALIBRATION_DISTANCE 1.0
+
+#define USE_MEDIAN_FILT true

 #define BEACON_1_ADDRESS 0x1A1A
 #define BEACON_2_ADDRESS 0x2A1A
@@ -178,8 +180,7 @@
     range *= CALIBRATE_MULTIPLIER?1.0f:BEACON_1_MULTIPLIER;
     range += CALIBRATE_OFFSET?0.0f:BEACON_1_OFFSET;
     cir_enqueue(cir1,range);
-    //dist[0] = cir_median(cir1);
-    dist[0] = range;
+    dist[0] = (USE_MEDIAN_FILT)?cir_median(cir1):range;

     if(CALIBRATE_OFFSET){
       if(dist[0] < shortestDistanceBeacon1 && dist[0] > 0){
@@ -191,8 +192,7 @@
     range *= CALIBRATE_MULTIPLIER?1.0f:BEACON_2_MULTIPLIER;
     range += CALIBRATE_OFFSET?0.0f:BEACON_2_OFFSET;
     cir_enqueue(cir2,range);
-    //dist[1] = cir_median(cir2);
-    dist[1] = range;
+    dist[1] = (USE_MEDIAN_FILT)?cir_median(cir2):range;

     if(CALIBRATE_OFFSET){
       if(dist[1] < shortestDistanceBeacon2 && dist[1] > 0){
@@ -204,8 +204,7 @@
     range *= CALIBRATE_MULTIPLIER?1.0f:BEACON_3_MULTIPLIER;
     range += CALIBRATE_OFFSET?0.0f:BEACON_3_OFFSET;
     cir_enqueue(cir3,range);
-    //dist[2] = cir_median(cir3);
-    dist[2] = range;
+    dist[2] = (USE_MEDIAN_FILT)?cir_median(cir3):range;

     if(CALIBRATE_OFFSET){
       if(dist[2] < shortestDistanceBeacon3 && dist[2] > 0){


Fixed included libraries and links.

Fixed included libraries and links.

--- aquacontrol32.ino
+++ aquacontrol32.ino
@@ -5,10 +5,11 @@
 #include <ESPmDNS.h>               /* should be installed together with ESP32 Arduino install */
 #include <Preferences.h>           /* should be installed together with ESP32 Arduino install */
 #include "Adafruit_ILI9341.h"      /* Install via 'Manage Libraries' in Arduino IDE */
+#include "Adafruit_GFX.h"          /* Install via 'Manage Libraries' in Arduino IDE */
 #include "OneWire.h"               /* Install via 'Manage Libraries' in Arduino IDE */
 /* or use stickbreakers library:      https://github.com/stickbreaker/OneWire */
 #include "SSD1306.h"               /* https://github.com/squix78/esp8266-oled-ssd1306 */
-#include <AsyncTCP.h>              /* https://github.com/me-no-dev/ESPAsyncTCP */
+#include <AsyncTCP.h>              /* https://github.com/me-no-dev/AsyncTCP */
 #include <ESPAsyncWebServer.h>     /* https://github.com/me-no-dev/ESPAsyncWebServer */
 #include <XPT2046_Touchscreen.h>   /* https://github.com/PaulStoffregen/XPT2046_Touchscreen */
 #include <MoonPhase.h>             /* https://github.com/CelliesProjects/MoonPhase */


Increase delay to 20ms

Increase delay to 20ms

--- HHI_switches_gripper/HHI_switches_gripper.ino
+++ HHI_switches_gripper/HHI_switches_gripper.ino
@@ -42,7 +42,7 @@
 void loop(){
     // Read value to apply from serial
     // Read until newline
-    delay(10);  //delay to allow buffer to fill
+    delay(20);  //delay to allow buffer to fill
     while (Serial.available()) {
         if (Serial.available() >0) {
             char c = Serial.read();  //gets one byte from serial buffer


Further clean up debug print routines; they were not compiling in the IDE

--- MapTheThings-Arduino/MapTheThings-Arduino.ino
+++ MapTheThings-Arduino/MapTheThings-Arduino.ino
@@ -16,28 +16,28 @@

 extern "C"{
   void debugPrint(const char *msg);
-  void debugLog(char *msg, uint16_t value);
-  void debugLogData(char *msg, uint8_t data[], uint16_t len);
+  void debugLog(const char *msg, uint16_t value);
+  void debugLogData(const char *msg, uint8_t data[], uint16_t len);
 }

 extern "C" {
-void debugPrint(const char *msg) {
-  Log.Debug(msg);
-  Log.Debug(CR);
+  void debugPrint(const char *msg) {
+    Log.Debug(msg);
+    Log.Debug(CR);
   }
-}

-void debugLog(const char *msg, uint16_t value) {
-  Log.Debug("%s %x" CR, msg, value);
-}
+  void debugLog(const char *msg, uint16_t value) {
+    Log.Debug("%s %x" CR, msg, value);
+  }

-void debugLogData(const char *msg, uint8_t data[], uint16_t len) {
-  Log.Debug("%s: ", msg);
-  for(int i=0; i<len; ++i) {
-    Log.Debug("%x", data[i]);
+  void debugLogData(const char *msg, uint8_t data[], uint16_t len) {
+    Log.Debug("%s: ", msg);
+    for(int i=0; i<len; ++i) {
+      Log.Debug("%x", data[i]);
+    }
+    Log.Debug(CR);
   }
-  Log.Debug(CR);
-}
+} // extern "C".

 #define CMD_DISCONNECT 1


Better output when box is connected to serial port and waiting to be connected to the CAN bus