REV2SecureTempSensor201912

Creating and documenting and capturing simple secure remote
temperature sensor based on REV2.

error: pathspec 'Arduino/hardware/REV2/REV2SecureTempSensor201912/REV2SecureTempSensor201912.ino' did not match any file(s) known to git

--- Arduino/hardware/REV2/REV2SecureTempSensor201912/REV2SecureTempSensor201912.ino
+++ Arduino/hardware/REV2/REV2SecureTempSensor201912/REV2SecureTempSensor201912.ino
@@ -0,0 +1,923 @@
+/*
+The OpenTRV project licenses this file to you
+under the Apache Licence, Version 2.0 (the "Licence");
+you may not use this file except in compliance
+with the Licence. You may obtain a copy of the Licence at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the Licence is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the Licence for the
+specific language governing permissions and limitations
+under the Licence.
+
+Author(s) / Copyright (s): Damon Hart-Davis 2019
+*/
+
+
+
+/*
+ * WILL BE...
+ *
+ * REV2 as remote temperature sensor with secure (AES128 enc/auth) link.
+ *
+ * Used at 16WW to collect outside temperatures from REV2 device in porch,
+ * replacing earlier REV2 of which the radio TX apparently failed 2019-12.
+ *
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+ * NOTES
+ *****************************************************************************/
+/*
+    WARNING! PRELIMINARY INO! DO NOT LOAD ON BOARD! DE20171201
+    WILL NOT EVEN COMPILE!
+
+  V0p2 (V0.2) core.
+
+  DHD20130417: hardware setup on bare board.
+    * 1MHz CPU clock (from 8MHz internal RC clock with /8 prescaler) ATmega328P running at 1.8V--5V (typically 2V--3.3V).
+    * Fuse set for BOD-managed additional clock settle time, ie as fast a restart from sleep as possible.
+    * All unused pins unconnected and nominally floating (though driven low as output where possible).
+    * 32768Hz xtal between pins XTAL1 and XTAL2, async timer 2, for accurate timekeeping and low-power sleep.
+    * All unused system modules turned off.
+
+  Basic AVR power consumption ticking an (empty) control loop at ~0.5Hz should be ~1uA.
+ */
+
+
+/******************************************************************************
+ * INCLUDES
+ *****************************************************************************/
+////// GLOBAL flags that alter system build and behaviour.
+
+// If defined, do extra checks and serial logging.  Will take more code space and power.
+#undef DEBUG
+
+// *** Global flag for REVx configuration here *** //
+#define V0p2_REV 2
+
+// --------------------------------------------
+// I/O pin allocation and setup: include ahead of I/O module headers.
+#include <OTV0p2_Board_IO_Config.h>
+
+
+////// MAIN LIBRARIES
+
+#include <Arduino.h>
+#include <OTV0p2Base.h>
+#include <OTRadioLink.h>
+
+
+////// ADDITIONAL LIBRARIES/PERIPHERALS
+
+#include <OTRadValve.h>
+#include <OTRFM23BLink.h>
+#include <OTAESGCM.h>
+
+
+////// ADDITIONAL USER HEADERS
+
+
+/******************************************************************************
+ * GENERAL CONSTANTS AND VARIABLES
+ * Core constants and variables required for V0p2 loop.
+ *****************************************************************************/
+// Controller's notion/cache of seconds within major cycle.
+static uint_fast8_t TIME_LSD;
+
+// 'Elapsed minutes' count of minute/major cycles; cheaper than accessing RTC and not tied to real time.
+// Starts at or just above zero (within the first 4-minute cycle) to help avoid collisions between units after mass power-up.
+// Wraps at its maximum (0xff) value.
+static uint8_t minuteCount;
+
+// Controller's view of Least Significant Digits of the current (local) time, in this case whole seconds.
+// TIME_LSD ranges from 0 to TIME_CYCLE_S-1, also major cycle length.
+constexpr uint_fast8_t TIME_CYCLE_S = 60;
+
+// Period in minutes for simple learned on-time; strictly positive (and less than 256).
+static constexpr uint8_t LEARNED_ON_PERIOD_M = 60;
+// Period in minutes for simple learned on-time with comfort bias; strictly positive (and less than 256).
+// Defaults to twice LEARNED_ON_PERIOD_M.
+// Should be no shorter/less than LEARNED_ON_PERIOD_M to avoid confusion.
+static constexpr uint8_t LEARNED_ON_PERIOD_COMFORT_M = (OTV0P2BASE::fnmin(2*(LEARNED_ON_PERIOD_M),255));
+
+// Send the underlying stats binary/text 'whitened' message.
+// This must be terminated with an 0xff (which is not sent),
+// and no longer than STATS_MSG_MAX_LEN bytes long in total (excluding the terminating 0xff).
+// This must not contain any 0xff and should not contain long runs of 0x00 bytes.
+// The message to be sent must be written at an offset of STATS_MSG_START_OFFSET from the start of the buffer.
+// This routine will alter the content of the buffer for transmission,
+// and the buffer should not be re-used as is.
+//   * doubleTX  double TX to increase chance of successful reception
+//   * RFM23BfriendlyPremable  if true then add an extra preamble
+//     to allow RFM23B-based receiver to RX this
+// This will use whichever transmission medium/carrier/etc is available.
+static constexpr uint8_t RFM22_PREAMBLE_BYTES = 5; // Recommended number of preamble bytes for reliable reception.
+static constexpr uint8_t RFM22_SYNC_MIN_BYTES = 3; // Minimum number of sync bytes.
+static constexpr uint8_t STATS_MSG_START_OFFSET = (RFM22_PREAMBLE_BYTES + RFM22_SYNC_MIN_BYTES);
+static constexpr uint8_t STATS_MSG_MAX_LEN = (64 - STATS_MSG_START_OFFSET);
+
+// Mask for Port D input change interrupts.
+constexpr uint8_t SERIALRX_INT_MASK = 0b00000001; // Serial RX
+#if BUTTON_MODE_L > 7
+  #error BUTTON_MODE_L expected to be on port D
+#endif
+constexpr uint8_t MODE_INT_MASK = (1 << (BUTTON_MODE_L&7));
+constexpr uint8_t MASK_PD = (SERIALRX_INT_MASK | MODE_INT_MASK); // MODE button interrupt (et al).
+
+// Counter containing current no. of resets.
+static uint8_t resetCount;
+
+////// SCRATCHSPACE
+
+// Create scratch space for secure stats TX
+// Buffer need be no larger than leading length byte + typical 64-byte radio module TX buffer limit + optional terminator.
+constexpr uint8_t MSG_BUF_SIZE = 1 + 64 + 1;
+constexpr uint8_t bufEncJSONlen = OTRadioLink::ENC_BODY_SMALL_FIXED_PTEXT_MAX_SIZE + 1;
+constexpr uint8_t ptextBuflen = bufEncJSONlen + 2;
+static_assert(ptextBuflen == 34, "ptextBuflen wrong");
+constexpr uint8_t scratchSpaceNeeded = MSG_BUF_SIZE + ptextBuflen;
+constexpr size_t StatsTX_WorkspaceSize = OTRadioLink::SimpleSecureFrame32or0BodyTXBase::encodeValveFrame_total_scratch_usage_OTAESGCM_2p0 + scratchSpaceNeeded;
+static_assert(StatsTX_WorkspaceSize == 384, "StatsTX workspace size wrong!");  // Correct as of 20170704
+
+// Create a scratchspace for CLI
+// Suggested minimum buffer size for pollUI() to ensure maximum-sized commands can be received.
+static constexpr uint8_t MAXIMUM_CLI_RESPONSE_CHARS = 1 + OTV0P2BASE::CLI::MAX_TYPICAL_CLI_BUFFER;
+static constexpr uint8_t PollUI_WorkspaceSize = 1 + MAXIMUM_CLI_RESPONSE_CHARS;
+
+// Create workspace
+union GlobalWorkSpace {
+    uint8_t statsTX[StatsTX_WorkspaceSize];
+    uint8_t cli[PollUI_WorkspaceSize];
+};
+static GlobalWorkSpace globalWorkSpace;
+
+
+/******************************************************************************
+ * PERIPHERALS
+ * Peripheral drivers and config
+ *****************************************************************************/
+
+////// FORWARD DECLARATIONS
+
+// Valve physical UI controller.
+typedef OTRadValve::ModeButtonAndPotActuatorPhysicalUI valveUI_t;
+extern valveUI_t valveUI;  // Extern as otherwise compiler attempts to instantiate.
+
+
+////// RADIOS
+
+// Config
+// Nodes talking on fast GFSK channel 0.
+static constexpr uint8_t nPrimaryRadioChannels = 1;
+static const OTRadioLink::OTRadioChannelConfig RFM23BConfigs[nPrimaryRadioChannels] = {
+    // GFSK channel 0 full config, RX/TX, not in itself secure.
+    OTRadioLink::OTRadioChannelConfig(OTRFM23BLink::StandardRegSettingsGFSK57600, true), };
+
+// Brings in necessary radio libs.
+static constexpr uint8_t RFM23B_RX_QUEUE_SIZE = OTV0P2BASE::fnmax(uint8_t(2), uint8_t(OTRFM23BLink::DEFAULT_RFM23B_RX_QUEUE_CAPACITY)) - 1;
+static constexpr int8_t RFM23B_IRQ_PIN = PIN_RFM_NIRQ;
+static constexpr bool RFM23B_allowRX = false;
+OTRFM23BLink::OTRFM23BLink<OTV0P2BASE::V0p2_PIN_SPI_nSS, RFM23B_IRQ_PIN, RFM23B_RX_QUEUE_SIZE, RFM23B_allowRX> PrimaryRadio;
+
+////// SENSORS
+
+// Sensor for supply (eg battery) voltage in millivolts.
+OTV0P2BASE::SupplyVoltageCentiVolts Supply_cV;
+
+// Support for general timed and multi-input occupancy detection / use.
+typedef OTV0P2BASE::PseudoSensorOccupancyTracker OccupancyTracker;
+// Singleton implementation for entire node.
+OccupancyTracker Occupancy;
+
+// Sensor for temperature potentiometer/dial UI control.
+typedef OTV0P2BASE::SensorTemperaturePot
+    <
+    decltype(Occupancy), &Occupancy,
+    48, 296, // Correct for DORM1/TRV1 with embedded REV7.
+    false // REV7 does not drive pot from IO_POWER_UP.
+    > TempPot_t;
+TempPot_t TempPot;
+
+// Sensor for ambient light level; 0 is dark, 255 is bright.
+typedef OTV0P2BASE::SensorAmbientLight AmbientLight;
+AmbientLight AmbLight;
+
+// Dummy humidity sensor
+OTV0P2BASE::DummyHumiditySensor RelHumidity;
+
+// Ambient/room temperature sensor, usually on main board.
+OTV0P2BASE::RoomTemperatureC16_TMP112 TemperatureC16;
+
+
+////// ACTUATORS
+
+// Electric heating
+static constexpr uint8_t relayPin = OUT_HEATCALL;
+static constexpr bool isActiveHigh = false;
+// Driver for controlling electric heating with an SSR or a relay.
+// Provides binary control (no PWM).
+// NOTE: The output polarity can be set by changing isActiveHigh.
+//       Defaults to assuming heating will activate when the ouput pin is low.
+OTRadValve::BinaryRelayDirect<relayPin, isActiveHigh> ValveDirect;
+
+////// CONTROL
+
+// Singleton non-volatile stats store instance.
+OTV0P2BASE::EEPROMByHourByteStats eeStats;
+
+// Singleton stats-updater object.
+typedef
+    OTV0P2BASE::ByHourSimpleStatsUpdaterSampleStats <
+      decltype(eeStats), &eeStats,
+      decltype(Occupancy), &Occupancy,
+      decltype(AmbLight), &AmbLight,
+      decltype(TemperatureC16), &TemperatureC16,
+      decltype(RelHumidity), &RelHumidity,
+      2
+      > StatsU_t;
+// Stats updater singleton.
+StatsU_t statsU;
+
+// Singleton scheduler instance.
+// Dummy scheduler to simplify coding.
+typedef OTRadValve::NULLValveSchedule Scheduler_t;
+Scheduler_t Scheduler;
+
+// Radiator valve mode (FROST, WARM, BAKE).
+OTRadValve::ValveMode valveMode;
+
+// Settings for room TRV. TODO Make sure these are relevant to electric heating/REV2
+typedef OTRadValve::DEFAULT_ValveControlParameters PARAMS;
+// Choose which subtype to use depending on enabled settings and board type.
+typedef OTRadValve::TempControlTempPot<decltype(TempPot), &TempPot, PARAMS, decltype(RelHumidity), &RelHumidity> TempControl_t;
+// Temperature control object.
+TempControl_t tempControl;
+
+static OTV0P2BASE::EEPROMByHourByteStats ebhs;
+
+// Create setback lockout if needed.
+typedef bool(*setbackLockout_t)();
+// If allowing setback lockout, eg for testing, then inject suitable lambda.
+static bool setbackLockout() { return(0 != OTRadValve::getSetbackLockout()); }
+
+// Algorithm for computing target temperature.
+constexpr OTRadValve::ModelledRadValveComputeTargetTempBasic<
+  PARAMS,
+  &valveMode,
+  decltype(TemperatureC16),   &TemperatureC16,
+  decltype(tempControl),      &tempControl,
+  decltype(Occupancy),        &Occupancy,
+  decltype(AmbLight),         &AmbLight,
+  decltype(valveUI),          &valveUI,
+  decltype(Scheduler),        &Scheduler,
+  decltype(ebhs),             &ebhs,
+  decltype(RelHumidity),      &RelHumidity,
+  setbackLockout>
+  cttBasic;
+
+// Internal model of controlled radiator valve position.
+OTRadValve::ModelledRadValve NominalRadValve(
+  &cttBasic,
+  &valveMode,
+  &tempControl,
+  &ValveDirect,
+    false,
+    100);
+
+// Valve physical UI controller.
+valveUI_t valveUI(
+  &valveMode,
+  &tempControl,
+  &NominalRadValve,
+  &Occupancy,
+  &AmbLight,
+  &TempPot,
+  &Supply_cV,
+  OTV0P2BASE::LED_HEATCALL_ON,
+  OTV0P2BASE::LED_HEATCALL_OFF,
+  OTV0P2BASE::LED_HEATCALL_ON_ISR_SAFE);
+
+////// MESSAGING
+
+// Managed JSON stats.
+OTV0P2BASE::SimpleStatsRotation<12> ss1; // Configured for maximum different stats.
+
+// Error reporting
+extern OTV0P2BASE::ErrorReport OTV0P2BASE::ErrorReporter;
+
+
+/******************************************************************************
+ * INTERRUPT SERVICE ROUTINES
+ *****************************************************************************/
+
+// Previous state of port D pins to help detect changes.
+static volatile uint8_t prevStatePD;
+// Interrupt service routine for PD I/O port transition changes (including RX).
+ISR(PCINT2_vect)
+{
+    const uint8_t pins = PIND;
+    const uint8_t changes = pins ^ prevStatePD;
+    prevStatePD = pins;
+    // Mode button detection is on the falling edge (button pressed).
+    if((changes & MODE_INT_MASK) && !(pins & MODE_INT_MASK)) { valveUI.startBakeFromInt(); }
+    // If an interrupt arrived from the serial RX then wake up the CLI.
+    // Use a nominally rising edge to avoid spurious trigger when other interrupts are handled.
+    // The will ensure that it is possible to wake the CLI subsystem with an extra CR or LF.
+    // It is OK to trigger this from other things such as button presses.
+    if((changes & SERIALRX_INT_MASK) && !(pins & SERIALRX_INT_MASK)) { OTV0P2BASE::CLI::resetCLIActiveTimer(); }
+}
+
+
+/******************************************************************************
+ * LOCAL FUNCTIONS
+ *****************************************************************************/
+
+////// PANIC
+
+// Indicate that the system is broken in an obvious way (distress flashing the main LED).
+// DOES NOT RETURN.
+// Tries to turn off most stuff safely that will benefit from doing so, but nothing too complex.
+// Tries not to use lots of energy so as to keep distress beacon running for a while.
+void panic() {
+    // Reset radio and go into low-power mode.
+    PrimaryRadio.panicShutdown();
+    // Power down almost everything else...
+    OTV0P2BASE::minimisePowerWithoutSleep();
+    pinMode(OTV0P2BASE::LED_HEATCALL_L, OUTPUT);
+    for( ; ; ) {
+        OTV0P2BASE::LED_HEATCALL_ON();
+        OTV0P2BASE::nap(WDTO_15MS);
+        OTV0P2BASE::LED_HEATCALL_OFF();
+        OTV0P2BASE::nap(WDTO_120MS);
+    }
+}
+
+// Panic with fixed message.
+void panic(const __FlashStringHelper *s) {
+    OTV0P2BASE::serialPrintlnAndFlush(); // Start new line to highlight error.  // May fail.
+    OTV0P2BASE::serialPrintAndFlush('!'); // Indicate error with leading '!' // May fail.
+    OTV0P2BASE::serialPrintlnAndFlush(s); // Print supplied detail text. // May fail.
+    panic();
+}
+
+////// SENSORS
+
+// Update sensors with historic/trailing statistics information where needed.
+// Should be called at least hourly after all stats have been updated,
+// but can also be called whenever the user adjusts settings for example.
+void updateSensorsFromStats() {
+    // Update with rolling stats to adapt to sensors and local environment...
+    // ...and prevailing bias, so may take a while to adjust.
+    AmbLight.setTypMinMax(
+          eeStats.getByHourStatRTC(OTV0P2BASE::NVByHourByteStatsBase::STATS_SET_AMBLIGHT_BY_HOUR_SMOOTHED),
+          eeStats.getMinByHourStat(OTV0P2BASE::NVByHourByteStatsBase::STATS_SET_AMBLIGHT_BY_HOUR_SMOOTHED),
+          eeStats.getMaxByHourStat(OTV0P2BASE::NVByHourByteStatsBase::STATS_SET_AMBLIGHT_BY_HOUR_SMOOTHED),
+          !tempControl.hasEcoBias());
+}
+
+
+////// CONTROL
+
+// Call this to do an I/O poll if needed; returns true if something useful definitely happened.
+// This call should typically take << 1ms at 1MHz CPU.
+// Does not change CPU clock speeds, mess with interrupts (other than possible brief blocking), or sleep.
+// Should also do nothing that interacts with Serial.
+// Limits actual poll rate to something like once every 8ms, unless force is true.
+//   * force if true then force full poll on every call (ie do not internally rate-limit)
+// Note that radio poll() can be for TX as well as RX activity.
+// Not thread-safe, eg not to be called from within an ISR.
+bool pollIO(const bool force = false) {
+    static volatile uint8_t _pO_lastPoll;
+    // Poll RX at most about every ~8ms.
+    const uint8_t sct = OTV0P2BASE::getSubCycleTime();
+    if(force || (sct != _pO_lastPoll)) {
+        _pO_lastPoll = sct;
+        // Poll for inbound frames.
+        // If RX is not interrupt-driven then
+        // there will usually be little time to do this
+        // before getting an RX overrun or dropped frame.
+        PrimaryRadio.poll();
+    }
+    return(false);
+}
+
+// Run tasks needed at the end of each day (nominal midnight).
+// Should be run once at a fixed slot in the last minute of the last hour of each day.
+// Will be run after all stats for the current hour have been updated.
+static void endOfDayTasks() {
+    OTRadValve::countDownSetbackLockout();
+}
+
+
+////// MESSAGING
+
+// Do bare stats transmission.
+// Output should be filtered for items appropriate
+// to current channel security and sensitivity level.
+// This may be binary or JSON format.
+//   * allowDoubleTX  allow double TX to increase chance of successful reception
+// Sends stats on primary radio channel 0 with possible duplicate to secondary channel.
+// If sending encrypted then ID/counter fields (eg @ and + for JSON) are omitted
+// as assumed supplied by security layer to remote recipent.
+void bareStatsTX() {
+
+    // Capture heavy stack usage from local allocations here.
+    OTV0P2BASE::MemoryChecks::recordIfMinSP();
+    const bool neededWaking = OTV0P2BASE::powerUpSerialIfDisabled<>();
+    static_assert(OTV0P2BASE::FullStatsMessageCore_MAX_BYTES_ON_WIRE <= STATS_MSG_MAX_LEN, "FullStatsMessageCore_MAX_BYTES_ON_WIRE too big");
+    static_assert(OTV0P2BASE::MSG_JSON_MAX_LENGTH+1 <= STATS_MSG_MAX_LEN, "MSG_JSON_MAX_LENGTH too big"); // Allow 1 for trailing CRC.
+    OTV0P2BASE::ScratchSpaceL sW(globalWorkSpace.statsTX, sizeof(globalWorkSpace.statsTX));
+
+    bool sendingJSONFailed = false; // Set true and stop attempting JSON send in case of error.
+
+    uint8_t *const buf = sW.buf;
+    // Leading length offset in bytes
+    // When sending on a channel with framing, do not explicitly send the frame length byte.
+    constexpr uint8_t offset = 1;
+    // Where to write the real frame content, leaving space for leading
+    // frame-length byte for encrypted frame.
+    uint8_t *const realTXFrameStart = buf + offset;
+    // If forcing encryption or if unconditionally suppressed
+    // then suppress the "@" ID field entirely,
+    // assuming that the encrypted commands will carry the ID, ie in the 'envelope'.
+    ss1.setID(V0p2_SENSOR_TAG_F(""));
+    // Managed JSON stats.
+    // Enable "+" count field for diagnostic purposes, eg while TX is lossy,
+    // if the primary radio channel does not include a sequence number itself.
+    // Assume that an encrypted channel will provide its own (visible) sequence counter.
+    ss1.enableCount(false);  // Always encrypted
+    ss1.put(TemperatureC16);
+    ss1.put(Occupancy.twoBitTag(), Occupancy.twoBitOccupancyValue()); // Reduce spurious TX cf percentage.
+    ss1.put(Occupancy.vacHSubSensor);
+    // OPTIONAL items
+    // Only TX supply voltage for units apparently not mains powered, and TX with low priority as slow changing.
+    ss1.put(Supply_cV, true);
+    ss1.put(AmbLight); // Always send ambient light level (assuming sensor is present).
+    // Show TRV-related stats since enabled.
+    ss1.put(NominalRadValve); // Show modelled value to be able to deduce call-for-heat.
+    ss1.put(NominalRadValve.targetTemperatureSubSensor);
+    ss1.put(NominalRadValve.setbackSubSensor);
+    // Show state of setback lockout.
+    ss1.put(V0p2_SENSOR_TAG_F("gE"), OTRadValve::getSetbackLockout(), true);
+
+    // Show reset counter. Low priority.
+    ss1.put(V0p2_SENSOR_TAG_F("R"), resetCount, true);
+    // Send error reports.
+    ss1.put(OTV0P2BASE::ErrorReporter, true);
+
+    const uint8_t privacyLevel = OTV0P2BASE::stTXalwaysAll;
+    // Redirect JSON output appropriately.
+    // Part of sW, directly after the message buffer.
+    // |    0    |     1    | 2 |  3:n | n+1 | n+2 | n is the end of the stats message. n+2 <= 34
+    // | valvePC | hasStats | { | json | '}' | 0x0 |
+    // ptextBuf is the entire frame
+    uint8_t * const ptextBuf = sW.buf + MSG_BUF_SIZE;
+    // Redirect JSON output appropriately.
+    uint8_t *const bufJSON = ptextBuf + 2;
+    const uint8_t bufJSONlen = bufEncJSONlen;
+    // Number of bytes written for body.
+    // For non-secure, this is the size of the JSON text.
+    // For secure this is overridden with the secure frame size.
+    int8_t wrote = 0;
+    // Generate JSON text.
+    if(!sendingJSONFailed) {
+        // Generate JSON and write to appropriate buffer:
+        // direct to TX buffer if not encrypting, else to separate buffer.
+        wrote = ss1.writeJSON(bufJSON, bufJSONlen, privacyLevel, true); // make best use of JSON stats;
+        if(0 == wrote) { sendingJSONFailed = true; }
+    }
+    // Push the JSON output to Serial.
+    if(!sendingJSONFailed) {
+        // Insert synthetic full ID/@ field for local stats, but no sequence number for now.
+        Serial.print(F("{\"@\":\""));
+        for(int i = 0; i < OTV0P2BASE::OpenTRV_Node_ID_Bytes; ++i) { Serial.print(eeprom_read_byte((uint8_t *)V0P2BASE_EE_START_ID+i), HEX); }
+        Serial.print(F("\","));
+        Serial.write(bufJSON+1, wrote-1);
+        Serial.println();
+        OTV0P2BASE::flushSerialSCTSensitive(); // Ensure all flushed since system clock may be messed with...
+    }
+    // Get the 'building' key for stats sending.
+    uint8_t key[16];
+    if(!sendingJSONFailed) {
+        if(!OTV0P2BASE::getPrimaryBuilding16ByteSecretKey(key)) {
+            sendingJSONFailed = true;
+            OTV0P2BASE::serialPrintlnAndFlush(F("!TX key")); // Know why TX failed.
+        }
+    }
+    // If doing encryption
+    // then build encrypted frame from raw JSON.
+    if(!sendingJSONFailed) {
+        // Explicit-workspace version of encryption.
+        const OTRadioLink::SimpleSecureFrame32or0BodyTXBase::fixed32BTextSize12BNonce16BTagSimpleEnc_fn_t &eW = OTAESGCM::fixed32BTextSize12BNonce16BTagSimpleEnc_DEFAULT_WITH_LWORKSPACE;
+        // Create subscratch space for encryption functions
+        OTV0P2BASE::ScratchSpaceL subScratch(sW, scratchSpaceNeeded);
+        constexpr uint8_t txIDLen = OTRadioLink::ENC_BODY_DEFAULT_ID_BYTES;
+
+        // Struct to pass data into encode function.
+        OTRadioLink::OTEncodeData_T fd(
+            ptextBuf,
+            ptextBuflen,
+            buf,            // (realTXFrameStart - offset),
+            MSG_BUF_SIZE);  // (MSG_BUF_SIZE - (realTXFrameStart-buf) + offset));
+
+        // Assumed to be at least one free writeable byte ahead of bptr.
+        // Get current modelled valve position.
+        const uint8_t valvePC = NominalRadValve.get();
+        const uint8_t bodylen = OTRadioLink::SimpleSecureFrame32or0BodyTXV0p2::getInstance().encodeValveFrame(
+                                    fd,
+                                    txIDLen,
+                                    valvePC,
+                                    eW,
+                                    subScratch,
+                                    key);
+        sendingJSONFailed = (0 == bodylen);
+        wrote = bodylen - offset;
+        Serial.println(wrote);
+        for(auto ip = fd.outbuf; ip != fd.outbuf+fd.outbufSize; ++ip) { Serial.print(*ip, HEX); Serial.print(F(" "));}
+        Serial.println();
+
+    }
+    // Send directly to the primary radio...
+    if((!sendingJSONFailed) && (!PrimaryRadio.queueToSend(realTXFrameStart, wrote))) { sendingJSONFailed = true; }
+    if(neededWaking) { OTV0P2BASE::flushSerialProductive(); OTV0P2BASE::powerDownSerial(); }
+}
+
+
+////// UI
+
+// Dump some brief CLI usage instructions to serial TX, which must be up and running.
+// If this gets too big there is a risk of overrunning and missing the next tick...
+static void dumpCLIUsage()
+{
+    OTV0P2BASE::CLI::InvalidIgnored(); // Minimal placeholder.
+    Serial.println();
+}
+
+// Used to poll user side for CLI input until specified sub-cycle time.
+// Commands should be sent terminated by CR *or* LF; both may prevent 'E' (exit) from working properly.
+// A period of less than (say) 500ms will be difficult for direct human response on a raw terminal.
+// A period of less than (say) 100ms is not recommended to avoid possibility of overrun on long interactions.
+// Times itself out after at least a minute or two of inactivity.
+// NOT RENTRANT (eg uses static state for speed and code space).
+
+void pollCLI(const uint8_t maxSCT, const bool startOfMinute, const OTV0P2BASE::ScratchSpace &s)
+{
+    // Perform any once-per-minute operations.
+    if(startOfMinute) { OTV0P2BASE::CLI::countDownCLI(); }
+    const bool neededWaking = OTV0P2BASE::powerUpSerialIfDisabled<V0P2_UART_BAUD>();
+    // Wait for input command line from the user (received characters may already have been queued)...
+    // Read a line up to a terminating CR, either on its own or as part of CRLF.
+    // (Note that command content and timing may be useful to fold into PRNG entropy pool.)
+    // A static buffer generates better code but permanently consumes previous SRAM.
+    const uint8_t n = OTV0P2BASE::CLI::promptAndReadCommandLine(maxSCT, s, [](){pollIO();});
+    char *buf = (char *)s.buf;
+    if(n > 0) {
+        // Got plausible input so keep the CLI awake a little longer.
+        OTV0P2BASE::CLI::resetCLIActiveTimer();
+        // Process the input received, with action based on the first char...
+        switch(buf[0]) {
+            // Explicit request for help, or unrecognised first character.
+            // Avoid showing status as may already be rather a lot of output.
+            default: case '?': { dumpCLIUsage(); break; }
+            // Exit/deactivate CLI immediately.
+            // This should be followed by JUST CR ('\r') OR LF ('\n')
+            // else the second will wake the CLI up again.
+            case 'E': { OTV0P2BASE::CLI::makeCLIInactive(); break; }
+            // Show/set generic parameter values (eg "G N [M]").
+            case 'G': { OTV0P2BASE::CLI::GenericParam().doCommand(buf, n); break; }
+            // Reset or display ID.
+            case 'I': { OTV0P2BASE::CLI::NodeIDWithSet().doCommand(buf, n); break; }
+            // Switch to FROST mode OR set FROST/setback temperature (even with temp pot available).
+            // With F! force to frost and holiday (long-vacant) mode.  Useful for testing and for remote CLI use.
+            case 'F': {
+                valveMode.setWarmModeDebounced(false); // No parameter supplied; switch to FROST mode.
+                break;
+            }
+            /**
+            * Set secret key.
+            * @note  The OTRadioLink::SimpleSecureFrame32or0BodyTXV0p2::resetRaw3BytePersistentTXRestartCounterCond
+            *        function pointer MUST be passed here to ensure safe handling of the key and the Tx message
+            *        counter.
+            */
+            case 'K': { OTV0P2BASE::CLI::SetSecretKey(OTRadioLink::SimpleSecureFrame32or0BodyTXV0p2::resetRaw3BytePersistentTXRestartCounterCond).doCommand(buf, n); break; }
+            // Switch to WARM (not BAKE) mode OR set WARM temperature.
+            case 'W':{
+                valveMode.cancelBakeDebounced(); // Ensure BAKE mode not entered.
+                valveMode.setWarmModeDebounced(true); // No parameter supplied; switch to WARM mode.
+                break;
+            }
+            // Zap/erase learned statistics.
+            case 'Z': { OTV0P2BASE::CLI::ZapStats().doCommand(buf, n); break; }
+        }
+        // Else show ack of command received.
+        Serial.println(F("OK"));
+    } else { Serial.println(); } // Terminate empty/partial CLI input line after timeout.
+    // Force any pending output before return / possible UART power-down.
+    OTV0P2BASE::flushSerialSCTSensitive();
+    if(neededWaking) { OTV0P2BASE::powerDownSerial(); }
+}
+
+
+/******************************************************************************
+ * SETUP
+ *****************************************************************************/
+
+// Setup routine: runs once after reset.
+// Does some limited board self-test and will panic() if anything is obviously broken.
+void setup()
+{
+    // Set appropriate low-power states, interrupts, etc, ASAP.
+    OTV0P2BASE::powerSetup();
+    // IO setup for safety, and to avoid pins floating.
+    OTV0P2BASE::IOSetup();
+    // Init the relay to the off position before anything can panic.
+    ValveDirect.setup();
+
+    // Restore previous RTC state if available.
+    OTV0P2BASE::restoreRTC();
+    OTV0P2BASE::serialPrintAndFlush(F("\r\nOpenTRV: ")); // Leading CRLF to clear leading junk, eg from bootloader.
+    V0p2Base_serialPrintlnBuildVersion();
+    // Count resets to detect unexpected crashes/restarts.
+    const uint8_t oldResetCount = eeprom_read_byte((uint8_t *)V0P2BASE_EE_START_RESET_COUNT);
+    resetCount = oldResetCount + 1;
+    eeprom_write_byte((uint8_t *)V0P2BASE_EE_START_RESET_COUNT, 1 + oldResetCount);
+#if 0  // Print reset count. Intended for testing purposes.
+    OTV0P2BASE::serialPrintAndFlush(F("\rResets: "));
+    OTV0P2BASE::serialPrintAndFlush(oldResetCount);
+    OTV0P2BASE::serialPrintlnAndFlush();
+#endif
+
+    // Have 32678Hz clock at least running before going any further.
+    // Check that the slow clock is running reasonably OK, and tune the fast one to it.
+    if(!::OTV0P2BASE::HWTEST::calibrateInternalOscWithExtOsc()) { panic(F("Xtal")); } // Async clock not running or can't tune.
+
+    // Signal that xtal is running AND give it time to settle.
+    OTV0P2BASE::sleepLowPowerMs(1000);
+    OTV0P2BASE::LED_HEATCALL_OFF();
+    OTV0P2BASE::sleepLowPowerMs(250);
+    OTV0P2BASE::LED_HEATCALL_ON();
+    OTV0P2BASE::sleepLowPowerMs(1000);
+
+    // Initialise the radio, if configured, ASAP because it can suck a lot of power until properly initialised.
+    PrimaryRadio.preinit(NULL);
+    // Check that the radio is correctly connected; panic if not...
+    if(!PrimaryRadio.configure(nPrimaryRadioChannels, RFM23BConfigs) || !PrimaryRadio.begin()) { panic(F("r1")); }
+
+    // Buttons should not be activated DURING boot for user-facing boards; an activated button implies a fault.
+    // Check buttons not stuck in the activated position.
+    if(fastDigitalRead(BUTTON_MODE_L) == LOW) { panic(F("b")); }
+
+    // Collect full set of environmental values before entering loop() in normal mode.
+    // This should also help ensure that sensors are properly initialised.
+    TemperatureC16.read();
+    AmbLight.read();
+    TempPot.read();
+    Supply_cV.read();
+    OTV0P2BASE::seedPRNGs();
+    // Update targets, output to TRV and boiler, etc, to be sensible before main loop starts.
+    NominalRadValve.read();
+    // Ensure that the unique node ID is set up (mainly on first use).
+    // Have one attempt (don't want to stress an already failing EEPROM) to force-reset if not good, then panic.
+    // Needs to have had entropy gathered, etc.
+    if(!OTV0P2BASE::ensureIDCreated()) {
+        if(!OTV0P2BASE::ensureIDCreated(true)) { panic(F("ID")); } // Force reset.
+    }
+    // Initialised: turn main/heatcall UI LED off.
+    OTV0P2BASE::LED_HEATCALL_OFF();
+    // Do OpenTRV-specific (late) setup.
+    // Radio not listening to start with.
+    // Ignore any initial spurious RX interrupts for example.
+    PrimaryRadio.listen(false);
+    // Set up async edge interrupts.
+    ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
+        //PCMSK0 = PB; PCINT  0--7    (LEARN1 and Radio)
+        //PCMSK1 = PC; PCINT  8--15
+        //PCMSK2 = PD; PCINT 16--24   (Serial RX and LEARN2 and MODE and Voice)
+        PCICR = 4 | 0; // 0x4 enables PD/PCMSK2.
+        PCMSK2 = MASK_PD;
+    }
+
+    // Wire components together, eg. for occupancy sensing.
+    AmbLight.setOccCallbackOpt([](bool prob){if(prob){Occupancy.markAsPossiblyOccupied();}else{Occupancy.markAsJustPossiblyOccupied();}});
+    // Callbacks to set various mode combinations.
+    // Typically at most one call would be made on any appropriate pot adjustment.
+    TempPot.setWFBCallbacks([](bool x){valveUI.setWarmModeFromManualUI(x);},
+                            [](bool x){valveUI.setBakeModeFromManualUI(x);});
+    // Initialise sensors with stats info where needed.
+    updateSensorsFromStats();
+
+    // Do early 'wake-up' stats transmission if possible.
+    // when everything else is set up and ready and allowed (TODO-636)
+    // including all set-up and inter-wiring of sensors/actuators.
+    // Attempt to maximise chance of reception with a double TX.
+    // Assume not in hub mode (yet).
+    // Send all possible formats, binary first (assumed complete in one message).
+    bareStatsTX();
+    // Send JSON stats repeatedly (typically once or twice)
+    // until all values pushed out (no 'changed' values unsent)
+    // or limit reached.
+    for(uint8_t i = 5; --i > 0; ) {
+        ::OTV0P2BASE::nap(WDTO_120MS, false); // Sleep long enough for receiver to have a chance to process previous TX.
+        bareStatsTX();
+        if(!ss1.changedValue()) { break; }
+    }
+    // Start local counters in randomised positions to help avoid inter-unit collisions,
+    // eg for mains-powered units starting up together after a power cut,
+    // but without (eg) breaking any of the logic about what order things will be run first time through.
+    // Uses some decent noise to try to start the units separated.
+    const uint8_t b = OTV0P2BASE::getSecureRandomByte(); // randRNG8();
+    // Start within bottom half of minute (or close to); sensor readings happen in second half.
+    OTV0P2BASE::setSeconds(b >> 2);
+    // Start anywhere in first 4 minute cycle.
+    minuteCount = b & 3;
+    // Set appropriate loop() values just before entering it.
+    TIME_LSD = OTV0P2BASE::getSecondsLT();
+}
+
+
+/******************************************************************************
+ * MAIN LOOP
+ *****************************************************************************/
+
+void loop()
+{
+    OTV0P2BASE::stackCheck();
+
+    // Main loop for OpenTRV radiator control.
+    // Note: exiting and re-entering can take a little while, handling Arduino background tasks such as serial.
+    // Sensor readings are taken late in each minute (where they are taken)
+    // and if possible noise and heat and light should be minimised in this part of each minute to improve readings.
+    //  const bool sensorReading30s = (TIME_LSD >= 30);
+    // Sensor readings and (stats transmissions) are nominally on a 4-minute cycle.
+    const uint8_t minuteFrom4 = (minuteCount & 3);
+    // The 0th minute in each group of four is always used for measuring where possible (possibly amongst others)
+    // and where possible locally-generated noise and heat and light should be minimised in this minute
+    // to give the best possible readings.
+    // True if this is the first (0th) minute in each group of four.
+    const bool minute0From4ForSensors = (0 == minuteFrom4);
+    // Note last-measured battery status.
+    const bool batteryLow = Supply_cV.isSupplyVoltageLow();
+    // Run some tasks less often when not demanding heat (at the valve or boiler), so as to conserve battery/energy.
+    // Spare the batteries if they are low, or the unit is in FROST mode, or if the room/area appears to be vacant.
+    // Stay responsive if the valve is open and/or we are otherwise calling for heat.
+    // Run at full speed until not nominally demanding heat, eg even during FROST mode or pre-heating.
+    const bool conserveBattery = (batteryLow || !valveMode.inWarmMode() || Occupancy.longVacant()) && (!NominalRadValve.isCallingForHeat());
+    // Try if very near to end of cycle and thus causing an overrun.
+    // Conversely, if not true, should have time to safely log outputs, etc.
+    const uint8_t nearOverrunThreshold = OTV0P2BASE::GSCT_MAX - 8; // ~64ms/~32 serial TX chars of grace time...
+
+    // Go to sleep until the start of the next cycle.
+    TIME_LSD = OTV0P2BASE::sleepUntilNewCycle<>(TIME_LSD);
+
+    // Reset and immediately re-prime the RTC-based watchdog.
+    OTV0P2BASE::resetRTCWatchDog();
+    OTV0P2BASE::enableRTCWatchdog(false);
+
+    // START LOOP BODY
+    // ===============
+    // High-priority UI handing, every other/even second.
+    // Show status if the user changed something significant.
+    // Must take ~300ms or less so as not to run over into next half second if two TXs are done.
+    bool recompute = false; // Set true if an extra recompute of target temperature should be done.
+    // Run the OpenTRV button/LED UI if required.
+    if(0 != valveUI.read()) { recompute = true; }
+    // Handling the UI may have taken a little while, so process I/O a little.
+    if(recompute || valveUI.veryRecentUIControlUse()) {
+        // Force immediate recompute of target temperature for (UI) responsiveness.
+        NominalRadValve.computeTargetTemperature();
+        // Keep dynamic adjustment of sensors up to date.
+        updateSensorsFromStats();
+    }
+
+    // DO SCHEDULING
+
+    // Once-per-minute tasks: all must take << 0.3s unless particular care is taken.
+    // Run tasks spread throughout the minute to be as kind to batteries (etc) as possible.
+    // Only when runAll is true run less-critical tasks that be skipped sometimes when particularly conserving energy.
+    // Run all for first full 4-minute cycle, eg because unit may start anywhere in it.
+    // Note: ensure only take ambient light reading at times when all LEDs are off (or turn them off).
+    // TODO: coordinate temperature reading with time when radio and other heat-generating items are off for more accurate readings.
+    const bool runAll = (!conserveBattery) || minute0From4ForSensors || (minuteCount < 4);
+    switch(TIME_LSD) { // With V0P2BASE_TWO_S_TICK_RTC_SUPPORT only even seconds are available.
+        case 0: {
+            // Tasks that must be run every minute.
+            ++minuteCount; // Note simple roll-over to 0 at max value.
+            // Force to user's programmed schedule(s), if any, at the correct time.
+            Scheduler.applyUserSchedule(&valveMode, OTV0P2BASE::getMinutesSinceMidnightLT());
+            // Ensure that the RTC has been persisted promptly when necessary.
+            OTV0P2BASE::persistRTC();
+            // Run hourly tasks at the end of the hour.
+            if((59 == OTV0P2BASE::getMinutesLT() ) && (23 == OTV0P2BASE::getHoursLT() )) { endOfDayTasks(); }
+            break;
+        }
+        // Churn/reseed PRNG(s) a little to improve unpredictability in use: should be lightweight.
+        case 2: { if(runAll) { OTV0P2BASE::seedRNG8(minuteCount ^ OTV0P2BASE::getCPUCycleCount() ^ (uint8_t)Supply_cV.get(), OTV0P2BASE::_getSubCycleTime() ^ AmbLight.get(), (uint8_t)TemperatureC16.get()); } break; }
+        // Force read of supply/battery voltage; measure and recompute status (etc) less often when already thought to be low, eg when conserving.
+        case 4: { if(runAll) { Supply_cV.read(); } break; }
+        // Periodic transmission of stats if NOT driving a local valve (else stats can be piggybacked onto that).
+        // Randomised somewhat between slots and also within the slot to help avoid collisions.
+        static uint8_t txTick;
+        case 6: { txTick = OTV0P2BASE::randRNG8() & 7; break; } // Pick which of the 8 slots to use.
+        case 8: case 10: case 12: case 14: case 16: case 18: case 20: case 22: {
+            // Only the slot where txTick is zero is used.
+            if(0 != txTick--) { break; }
+            // Stats TX in the minute (#1) after all sensors should have been polled
+            // (so that readings are fresh) and evenly between.
+            // Usually send one frame every 4 minutes, 2 if this is a valve.
+            // No extra stats TX for changed data to reduce information/activity leakage.
+            // Note that all O frames contain the current valve percentage,
+            // which implies that any extra stats TX also speeds response to call-for-heat changes.
+            // DHD20170113: was once every 4 minutes, but can make boiler response too slow.
+            if(0 == (minuteFrom4 & 1)) { break; }
+            // Sleep randomly up to ~25% of the minor cycle
+            // to spread transmissions and thus help avoid collisions.
+            // (Longer than 25%/0.5s could interfere with other ops such as FHT8V TXes.)
+            const uint8_t stopBy = 1 + (((OTV0P2BASE::GSCT_MAX >> 2) | 7) & OTV0P2BASE::randRNG8());
+            while(OTV0P2BASE::getSubCycleTime() <= stopBy) { OTV0P2BASE::nap(WDTO_15MS, true); } // Sleep a little.
+            // Send stats!
+            // Try for double TX for extra robustness unless:
+            //   * this is a speculative 'extra' TX
+            //   * battery is low
+            //   * this node is a hub so needs to listen as much as possible
+            // This doesn't generally/always need to send binary/both formats
+            // if this is controlling a local FHT8V on which the binary stats can be piggybacked.
+            // Ie, if doesn't have a local TRV then it must send binary some of the time.
+            // Any recently-changed stats value is a hint that a strong transmission might be a good idea.
+            bareStatsTX();
+            break;
+        }
+        // SENSOR READ AND STATS
+        //
+        // All external sensor reads should be in the second half of the minute (>32) if possible.
+        // This is to have them as close to stats collection at the end of the minute as possible,
+        // and to allow randomisation of the start-up cycle position in the first 32s to help avoid inter-unit collisions.
+        // Also all sources of noise, self-heating, etc, may be turned off for the 'sensor read minute'
+        // and thus will have diminished by this point.
+
+        // Sample the user-selected WARM temperature target at a fixed rate.
+        // This allows the unit to stay reasonably responsive to adjusting the temperature dial.
+        case 48: { TempPot.read(); break; }
+        // Read all environmental inputs, late in the cycle.
+        // Poll ambient light level at a fixed rate.
+        // This allows the unit to respond consistently to (eg) switching lights on (eg TODO-388).
+        // Force all UI lights off before sampling ambient light level.
+        case 52: { OTV0P2BASE::LED_HEATCALL_OFF(); AmbLight.read(); break; }
+        // At a hub, sample temperature regularly as late as possible in the minute just before recomputing valve position.
+        // Force a regular read to make stats such as rate-of-change simple and to minimise lag.
+        // TODO: optimise to reduce power consumption when not calling for heat.
+        // TODO: optimise to reduce self-heating jitter when in hub/listen/RX mode.
+        case 54: { TemperatureC16.read(); break; }
+        // Compute targets and heat demand based on environmental inputs and occupancy.
+        // This should happen as soon after the latest readings as possible (temperature especially).
+        case 56: {
+            // Update occupancy status (fresh for target recomputation) at a fixed rate.
+            Occupancy.read();
+            // Recompute target, valve position and call for heat, etc.
+            // Should be called once per minute to work correctly.
+            NominalRadValve.read();
+            // Update/age errors and warnings.
+            OTV0P2BASE::ErrorReporter.read();
+            break;
+        }
+        // Stats samples; should never be missed.
+        case 58: {
+            // Update non-volatile stats.
+            // Make the final update as near the end of the hour as possible to reduce glitches (TODO-1086),
+            // and with other optional non-full samples evenly spaced throughout the hour.
+            // Race-free.
+            const uint_least16_t msm = OTV0P2BASE::getMinutesSinceMidnightLT();
+            const uint8_t mm = msm % 60;
+            if(59 == mm) { statsU.sampleStats(true, uint8_t(msm / 60)); }
+            else if((statsU.maxSamplesPerHour > 1) && (29 == mm)) { statsU.sampleStats(false, uint8_t(msm / 60)); }
+            break;
+        }
+    }
+
+    // Command-Line Interface (CLI) polling.
+    // If a reasonable chunk of the minor cycle remains after all other work is done
+    // AND the CLI is / should be active OR a status line has just been output
+    // then poll/prompt the user for input
+    // using a timeout which should safely avoid overrun, ie missing the next basic tick,
+    // and which should also allow some energy-saving sleep.
+    if(OTV0P2BASE::CLI::isCLIActive()) {
+        const uint8_t stopBy = nearOverrunThreshold - 1;
+        OTV0P2BASE::ScratchSpace s((uint8_t*)globalWorkSpace.cli, sizeof(globalWorkSpace.cli));
+        pollCLI(stopBy, 0 == TIME_LSD, s);
+    }
+}


Small tweaks. Squash this update.

Small tweaks. Squash this update.

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


added base line ATtiny code for the Thermal Camera sensor

it does not work on the ATtiny, moving it to ATmega328

error: pathspec 'Sensor' did not match any file(s) known to git
error: pathspec 'Modules/ThermalCamera' did not match any file(s) known to git
error: pathspec 'Sensor/ThermalCameraATtiny/ThermalCameraATtiny.ino' did not match any file(s) known to git


Color sensor example.

Color sensor example.

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

--- Arduino/ColorSensor/ColorSensor.ino
+++ Arduino/ColorSensor/ColorSensor.ino
@@ -0,0 +1,39 @@
+#include <Wire.h>
+#include "Adafruit_TCS34725.h"
+
+Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);
+
+void setup() {
+  Serial.begin(9600);
+
+  if (!tcs.begin()) {
+    Serial.println("No TCS34725 found ... check your connections");
+    while (1); // halt!
+  }
+}
+
+void loop() {
+  uint16_t clear, red, green, blue;
+
+  tcs.setInterrupt(false);      // turn on LED
+
+  delay(60);  // takes 50ms to read
+
+  tcs.getRawData(&red, &green, &blue, &clear);
+
+  tcs.setInterrupt(true);  // turn off LED
+
+  Serial.print(red); Serial.print("\t");
+  Serial.print(green); Serial.print("\t");
+  Serial.print(blue); Serial.print("\t");
+  Serial.print(clear); Serial.println();
+
+//  uint32_t sum = clear;
+//  float r, g, b;
+//  r = red; r /= sum;
+//  g = green; g /= sum;
+//  b = blue; b /= sum;
+//  r *= 256; g *= 256; b *= 256;
+//  Serial.print((int)r ); Serial.print("\t"); Serial.print((int)g);Serial.print("\t");  Serial.println((int)b );
+}
+


Reads soil, light, humidity, and temperature.

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

--- growdammit.ino
+++ growdammit.ino
@@ -0,0 +1,82 @@
+#include <SparkFunHTU21D.h>
+#include <Wire.h>
+
+// Soil Moisture Sensor
+//    SIG pin connected to AD0
+//    GND pin connected to GND
+//    VCC pin connected to D2 (GPIO4)
+#define SOIL_ENABLE  4
+//
+// Ambient Light Sensor
+//    SIG pin connected to AD0
+//    GND pin connected to GND
+//    VCC pin connected to D1 (GPIO5)
+#define LIGHT_ENABLE 5
+//
+// Temperature and Humidity Sensor
+//    GND pin connected to GND
+//    3.3V pin connected to 3.3V
+//    DA pin connected to D6 (GPIO12)
+//    CL pin connected to D5 (GPIO14)
+#define WEATHER_DA 12
+#define WEATHER_CL 14
+
+// humidity and temp sensor library
+HTU21D weather;
+
+void setup() {
+  // disables soil and light sensors on boot
+  pinMode(SOIL_ENABLE, OUTPUT);
+  digitalWrite(SOIL_ENABLE, LOW);
+  pinMode(LIGHT_ENABLE, OUTPUT);
+  digitalWrite(LIGHT_ENABLE, LOW);
+
+  Wire.begin(WEATHER_DA, WEATHER_CL);
+  weather.begin();
+
+  pinMode(LED_BUILTIN, OUTPUT);
+  Serial.begin(115200);
+  Serial.println();
+  Serial.println("GROW DAMMIT!");
+  Serial.print("built on ");
+  Serial.print(__DATE__);
+  Serial.print(" at ");
+  Serial.println(__TIME__);
+}
+
+void loop() {
+  int light, soil;
+  float humidity, temperature;
+
+  digitalWrite(LED_BUILTIN, LOW);
+
+  // read light sensor
+  digitalWrite(LIGHT_ENABLE, HIGH);
+  delay(100);
+  light = analogRead(A0);
+  digitalWrite(LIGHT_ENABLE, LOW);
+  delay(100);
+
+  // read soil sensor
+  digitalWrite(SOIL_ENABLE, HIGH);
+  delay(100);
+  soil = analogRead(A0);
+  digitalWrite(SOIL_ENABLE, LOW);
+  delay(100);
+
+  // read humidity and temperature
+  humidity = weather.readHumidity();
+  temperature = weather.readTemperature();
+
+  digitalWrite(LED_BUILTIN, HIGH);
+  Serial.print("Light: ");
+  Serial.print(light);
+  Serial.print("  Soil: ");
+  Serial.print(soil);
+  Serial.print("  Temperature: ");
+  Serial.print(temperature);
+  Serial.print("  Humiditiy: ");
+  Serial.print(humidity);
+  Serial.println();
+  delay(5000);
+}


Added MidiUSB example (wip).

Added MidiUSB example (wip).

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

--- examples/MidiUSB/MidiUSB.ino
+++ examples/MidiUSB/MidiUSB.ino
@@ -0,0 +1,39 @@
+#include <MIDI.h>
+#include <midi_UsbTransport.h>
+
+static const unsigned sUsbTransportBufferSize = 16;
+typedef midi::UsbTransport<sUsbTransportBufferSize> UsbTransport;
+
+UsbTransport sUsbTransport;
+
+MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, MIDI);
+
+// --
+
+void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity)
+{
+    Serial.print("NoteOn  ");
+    Serial.print(inNumber);
+    Serial.print("\tvelocity: ");
+    Serial.println(inVelocity);
+}
+void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity)
+{
+    Serial.print("NoteOff ");
+    Serial.print(inNumber);
+    Serial.print("\tvelocity: ");
+    Serial.println(inVelocity);
+}
+
+void setup() {
+    Serial.begin(115200);
+    while (!Serial);
+    MIDI.begin();
+    MIDI.setHandleNoteOn(handleNoteOn);
+    MIDI.setHandleNoteOff(handleNoteOff);
+    Serial.println("Arduino ready.");
+}
+
+void loop() {
+    MIDI.read();
+}


Testing of two optical encoders working together

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

--- zumo_two_encoder_test/zumo_two_encoder_test.ino
+++ zumo_two_encoder_test/zumo_two_encoder_test.ino
@@ -0,0 +1,98 @@
+/* Read Quadrature Encoder
+* Connect Encoder to Pins encoder0PinA, encoder0PinB, and +5V.
+*
+* Sketch by max wolf / www.meso.net
+* v. 0.1 - very basic functions - mw 20061220
+*
+*/
+#include "Timer.h"
+
+int encoder0PinA = 2;
+int encoder0PinB = 11;
+int encoder0Pos = 0;
+int encoder0PinALast = LOW;
+volatile int n = LOW;
+int last = 0;
+
+int encoder1PinA = 5;
+int encoder1PinB = A2;
+int encoder1Pos = 0;
+int encoder1PinALast = LOW;
+volatile int n2 = LOW;
+int last1 = 0;
+
+Timer t;
+
+void doLeft()
+{
+    n = digitalRead(encoder1PinA);
+    if ((encoder1PinALast == LOW) && (n == HIGH))
+    {
+        if (analogRead(encoder1PinB) < 450)
+        {
+            encoder1Pos--;
+        }
+        else
+        {
+            encoder1Pos++;
+        }
+    }
+    encoder1PinALast = n;
+}
+
+void doRight()
+{
+    n = digitalRead(encoder0PinA);
+    if ((encoder0PinALast == LOW) && (n == HIGH))
+    {
+        if (digitalRead(encoder0PinB) == LOW)
+        {
+            //if (analogRead(encoder0PinB) < 450) {
+            encoder0Pos--;
+        }
+        else
+        {
+            encoder0Pos++;
+        }
+    }
+    encoder0PinALast = n;
+}
+
+void printStuff()
+{
+    if (last != encoder0Pos)
+    {
+        Serial.print ("A");
+        Serial.println (encoder0Pos);
+//        Serial.print ("/");
+        last = encoder0Pos;
+    }
+
+    if (last1 != encoder1Pos)
+    {
+        Serial.print ("B");
+        Serial.println (encoder1Pos);
+//        Serial.print ("/");
+        last1 = encoder1Pos;
+    }
+}
+
+void setup()
+{
+    pinMode (encoder0PinA, INPUT);
+    pinMode (encoder0PinB, INPUT);
+    pinMode (encoder1PinA, INPUT);
+    // pinMode (encoder1PinB, INPUT); // Analog
+
+    attachInterrupt(0, doRight, CHANGE);
+    Serial.begin (9600);
+
+    t.every(1000, printStuff);
+}
+
+void loop()
+{
+    doLeft();
+
+    t.update();
+}


Lightbulb on sketch.

Lightbulb on sketch.

error: pathspec 'clients/examples/arduino/lightbulb_on/lightbulb_on.ino' did not match any file(s) known to git

--- clients/examples/arduino/lightbulb_on/lightbulb_on.ino
+++ clients/examples/arduino/lightbulb_on/lightbulb_on.ino
@@ -0,0 +1,75 @@
+
+#include <Dhcp.h>
+#include <Dns.h>
+#include <Ethernet.h>
+#include <EthernetClient.h>
+#include <util.h>
+#include <SPI.h>
+
+#include <Base64.h>
+#include <global.h>
+#include <MD5.h>
+#include <sha1.h>
+#include <WebSocketClient.h>
+
+char *json = "{\"path\":\"/api/v1/actor/perform/device/lighting\",\"requestID\":\"1\",\"perform\":\"on\",\"parameter\":\"{\\\"brightness\\\":100}\"}";
+
+byte mac[] = { 0x0, 0xA2, 0xDA, 0x0D, 0x90, 0xE2 };
+
+EthernetClient client;
+WebSocketClient webSocketClient;
+
+void setup() {
+  Serial.begin(9600);
+  while(!Serial) {  }
+
+  if (Ethernet.begin(mac) == 0) {
+    Serial.println("Error: Failed to configure Ethernet using DHCP");
+    while(1) {  }
+  }
+
+  Serial.println("Connecting to steward...");
+  if( client.connect("192.168.1.91",8887) ) {
+      Serial.println("Connected");
+      webSocketClient.path = "/manage";
+      webSocketClient.host = "dastardly.local";
+      if (webSocketClient.handshake(client)) {
+        Serial.println("Handshake successful");
+      } else {
+        Serial.println("Handshake failed.");
+        while(1) {
+          // Hang on failure
+        }
+      }
+      webSocketClient.sendData(json);
+
+  }
+}
+
+void loop() {
+  String data;
+
+  if (client.connected()) {
+    data = webSocketClient.getData();
+    if (data.length() > 0) {
+      Serial.print("Received data: ");
+      Serial.println(data);
+    }
+  } else {
+    Serial.println("Client disconnected.");
+    while (1) {
+      // Hang on disconnect.
+    }
+  }
+
+  // wait to fully let the client disconnect
+  delay(3000);
+
+}
+
+const char* ip_to_str(const uint8_t* ipAddr) {
+  static char buf[16];
+  sprintf(buf, "%d.%d.%d.%d\0", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);
+  return buf;
+}
+


Added the ability to send VIN

Added the ability to send VIN

Adapt the example code to include the ability to send VIN. Still needs
to work out the timing issues.

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

--- _07_Send_Component_ID_and_VIN/_07_Send_Component_ID_and_VIN.ino
+++ _07_Send_Component_ID_and_VIN/_07_Send_Component_ID_and_VIN.ino
@@ -0,0 +1,244 @@
+//Reading data from CAN and displaying it on a serial port
+//Include the realtime clock functions inspired by the TimeTeensy3.ino example
+//Add capability to send request messages
+
+
+//Include the CAN libraries for the Teensy microprocessor
+#include <FlexCAN.h>
+#include <TimeLib.h>
+
+char compID[100] = "TULSA*Teensy-Crash-EDR*Box*1";
+char VIN[18] = "THIS IS MY VIN 12";
+
+const uint16_t PGNRequestList[28] = {
+  65261, // Cruise Control/Vehicle Speed Setup
+  65214, // Electronic Engine Controller 4
+  65259, // Component Identification
+  65242, // Software Identification
+  65244, // Idle Operation
+  65260, // Vehicle Identification
+  65255, // Vehicle Hours
+  65253, // Engine Hours, Revolutions
+  65257, // Fuel Consumption (Liquid)
+  65256, // Vehicle Direction/Speed
+  65254, // Time/Date
+  65211, // Trip Fan Information
+  65210, // Trip Distance Information
+  65209, // Trip Fuel Information (Liquid)
+  65207, // Engine Speed/Load Factor Information
+  65206, // Trip Vehicle Speed/Cruise Distance Information
+  65205, // Trip Shutdown Information
+  65204, // Trip Time Information 1
+  65200, // Trip Time Information 2
+  65250, // Transmission Configuration
+  65253, // Engine Hours, Revolutions
+  65203, // Fuel Information (Liquid)
+  65201, // ECU History
+  65168, // Engine Torque History
+  64981, // Electronic Engine Controller 5
+  64978, // ECU Performance
+  64965, // ECU Identification Information
+  65165  // Vehicle Electrical Power #2
+};
+
+uint8_t pgnIndex = 0;
+
+//Declare which pin is connected to the LED
+//Use the Teensy Reference Card and the board schematics to determine the pin number
+const int redLEDpin = 21;
+
+//initiate the CAN library at 250kbps
+FlexCAN CANbus(250000);
+
+//Set up the CAN data structure
+static CAN_message_t rxmsg;
+static CAN_message_t txmsg;
+
+//set up a counter for each received message
+unsigned long int rxCount = 0;
+
+//set up a timer to toggle the LEDs so the delay function isn't needed.
+elapsedMillis LEDtoggleTimer;
+elapsedMillis millisecondsPerSecond;
+elapsedMillis requestTimer;
+elapsedMillis requestSendTimer;
+
+const int millisBetweenRequests = 1000;
+
+//Keep track of the LED state
+boolean ledState = true;
+
+
+void setup() {
+  // put your setup code here, to run once:
+
+  //declare the LED Pin to be the output
+  pinMode(redLEDpin,OUTPUT);
+  digitalWrite(redLEDpin,ledState); //Turn on the LED
+
+  //start the CAN access
+  CANbus.begin();
+  rxmsg.timeout = 0;
+  txmsg.timeout = 0;
+  txmsg.len = 8;
+  txmsg.id = 0x18EA00F6; //Reqest PGN
+  txmsg.ext = 1;
+
+  //try to wait for the Serial bus to come up for 1 second
+  delay(1000);
+  Serial.println(F("Teensy 3.2 CAN Receive Test with Component ID."));
+
+
+  //print a header
+  Serial.print(F("     Count\t    micros\t  CAN ID\tDLC"));
+  for (uint8_t i = 1; i<9;i++){ //label the byte columns according to J1939
+    char byteDigits[4]; //declare a byte display array
+    sprintf(byteDigits,"\tB%i",i);
+    Serial.print(byteDigits);
+  }
+
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+  while(CANbus.read(rxmsg)){
+    rxCount++;
+
+    uint32_t ID = rxmsg.id;
+    uint8_t len = rxmsg.len;
+
+    char timeCountIDandDLCdigits[50];
+    sprintf(timeCountIDandDLCdigits,"%10i\t%10i\t%08X\t%1i",rxCount,micros(),ID,len);
+   // Serial.print(timeCountIDandDLCdigits);
+
+    for (uint8_t i = 0; i<len;i++){
+      char byteDigits[4];
+      sprintf(byteDigits,"\t%02X",rxmsg.buf[i]);
+ //     Serial.print(byteDigits);
+    }
+ //Serial.println();
+
+    if ((ID & 0x00FF0000) == 0x00EA0000){
+        Serial.print("Request Message Received");
+
+        char timeCountIDandDLCdigits[50];
+        sprintf(timeCountIDandDLCdigits,"%10i\t%10i\t%08X\t%1i",rxCount,micros(),ID,len);
+        Serial.print(timeCountIDandDLCdigits);
+
+        for (uint8_t i = 0; i<len;i++){
+          char byteDigits[4];
+          sprintf(byteDigits,"\t%02X",rxmsg.buf[i]);
+          Serial.print(byteDigits);
+        }
+        Serial.println();
+
+        if (rxmsg.buf[0] == 0xEC && rxmsg.buf[1] == 0xFE){
+          requestSendTimer = 0;
+          sendVIN(VIN);
+        }
+//        else if (rxmsg.buf[0] == 0xEB && rxmsg.buf[1] == 0xFE){
+//          requestSendTimer = 0;
+//          sendComponentInfo(compID);
+//        }
+
+    }
+  }
+
+
+
+  if (LEDtoggleTimer >=100){
+    LEDtoggleTimer = 0; //reset the timer
+    ledState = !ledState; // Toggle values
+    digitalWrite(redLEDpin,ledState);
+  }
+
+  if (requestTimer >= millisBetweenRequests) {
+    requestTimer = 0;
+    uint16_t pgnToRequest = PGNRequestList[pgnIndex];
+    pgnIndex++;
+    if (pgnIndex > sizeof(PGNRequestList) ) pgnIndex = 0;
+
+    txmsg.ext = 1;
+    txmsg.id = 0x18EA00F9; //request PGN
+    txmsg.buf[0] = (pgnToRequest & 0x0000FF);
+    txmsg.buf[1] = (pgnToRequest & 0x00FF00) >> 8 ;
+    txmsg.buf[2] = (pgnToRequest & 0xFF0000) >> 16;
+    txmsg.len = 3;
+    CANbus.write(txmsg);
+  }
+
+
+}
+
+void sendComponentInfo(char id[100])
+{
+       txmsg.ext = 1;
+       txmsg.len = 8;
+
+       Serial.print("Received Request for Component ID. Sending  ");
+       for (int i = 0; i < 35;i++) Serial.print(id[i]);
+       Serial.println();
+       byte transport0[8] = {32,35,0,5,0xFF,0xEB,0xFE,0};
+       byte transport1[8] = {1,id[0],id[1],id[2],id[3],id[4],id[5],id[6]};
+       byte transport2[8] = {2,id[7],id[8],id[9],id[10],id[11],id[12],id[13]};
+       byte transport3[8] = {3,id[14],id[15],id[16],id[17],id[18],id[19],id[20]};
+       byte transport4[8] = {4,id[21],id[22],id[23],id[24],id[25],id[26],id[27]};
+       byte transport5[8] = {5,id[28],id[29],id[30],id[31],id[32],id[33],id[34]};
+       txmsg.id = 0x18ECFF18;
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport0[i];
+       CANbus.write(txmsg);
+       delay(3);
+       txmsg.id = 0x18EBFF18;
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport1[i];
+       CANbus.write(txmsg);
+       delay(3);
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport2[i];
+       CANbus.write(txmsg);
+       delay(3);
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport3[i];
+       CANbus.write(txmsg);
+       delay(3);
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport4[i];
+       CANbus.write(txmsg);
+       delay(3);
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport5[i];
+       if (CANbus.write(txmsg)) Serial.print("Sent Last Message");
+
+}
+
+void sendVIN(char id[100])
+{
+       txmsg.ext = 1;
+       txmsg.len = 8;
+
+       Serial.print("Received Request for VIN. Sending  ");
+       for (int i = 0; i < 35;i++) Serial.print(id[i]);
+       Serial.println();
+       byte transport0[8] = {32,17,0,3,0xFF,0xEC,0xFE,0};
+       byte transport1[8] = {1,id[0],id[1],id[2],id[3],id[4],id[5],id[6]};
+       byte transport2[8] = {2,id[7],id[8],id[9],id[10],id[11],id[12],id[13]};
+       byte transport3[8] = {3,id[14],id[15],id[16],0xFF,0xFF,0xFF,255};
+       txmsg.id = 0x18ECFF18;
+       //Priority 0x18 -> 6,
+       //PGN 0x00EC00 Transport Protocol Connection Management (TP.CM),
+       //Destination Address -> 0xFF (Global),
+       //Source address -> 0x18 = 24 (Trip recorder)
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport0[i];
+       CANbus.write(txmsg);
+       delay(3);
+       txmsg.id = 0x18EBFF18;
+       //Priority 0x18 -> 6,
+       //PGN 0x00EB00 Transport Protocol Data Transfer (TP.DT),
+       //Destination Address -> 0xFF (Global),
+       //Source address -> 0x18 = 24 (Trip recorder)
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport1[i];
+       CANbus.write(txmsg);
+       delay(3);
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport2[i];
+       CANbus.write(txmsg);
+       delay(3);
+       for (int i = 0;i<8;i++) txmsg.buf[i]=transport3[i];
+       if (CANbus.write(txmsg)) Serial.print("Sent Last Data Message for VIN");
+
+}


Gpah

Gpah

Small changes

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


inital Arduino example

inital Arduino example

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


Changed to make it work on ESP-01 boards (on ESP-01 pin cannot be checked at setup as its only available pins, gpio0 and gpio2, are shared with the bootloader) - moved triggering of configuration portal into Arduino's loop() function - changed pin from 4 to 0 (and made it  #define-able)

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


Capacitive sensor

Capacitive sensor

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

--- Project_13/Project_13.ino
+++ Project_13/Project_13.ino
@@ -0,0 +1,22 @@
+#include <CapacitiveSensor.h>
+
+CapacitiveSensor capSensor = CapacitiveSensor(4, 2);
+int threshold = 500;
+const int ledPin = 12;
+
+void setup() {
+  Serial.begin(9600);
+  pinMode(ledPin, OUTPUT);
+}
+
+void loop() {
+  long sensorValue = capSensor.capacitiveSensor(30);
+  Serial.println(sensorValue);
+  if (sensorValue > threshold) {
+    digitalWrite(ledPin, HIGH);
+  }
+  else {
+    digitalWrite(ledPin, LOW);
+  }
+  delay(10);
+}


Add Arduino example that communicated to the sensor

error: pathspec 'utils/rs485-sensor-tester/rs485-sensor-tester.ino' did not match any file(s) known to git

--- utils/rs485-sensor-tester/rs485-sensor-tester.ino
+++ utils/rs485-sensor-tester/rs485-sensor-tester.ino
@@ -0,0 +1,140 @@
+#include <Bounce2.h>
+#include <ModbusMaster.h>
+#include <SoftwareSerial.h>
+#include <SdFat.h>
+
+SoftwareSerial softSerial(2, 3); // RX, TX
+Bounce button = Bounce();
+ModbusMaster sensor;
+SdFat sd;
+
+
+#define DRIVER_ENABLE 7
+#define SENSOR_PWR 8
+#define LED1 6
+#define LED2 A1
+
+const int chipSelect = 4;
+
+void preTransmission() {
+  digitalWrite(DRIVER_ENABLE, HIGH);
+}
+
+void postTransmission() {
+  digitalWrite(DRIVER_ENABLE, LOW);
+}
+
+uint16_t data[6];
+
+void setup() {
+  Serial.begin(9600);
+  softSerial.begin(19200);
+
+  pinMode(A0, INPUT_PULLUP);
+  pinMode(LED1, OUTPUT);
+  pinMode(LED2, OUTPUT);
+  pinMode(DRIVER_ENABLE, OUTPUT);
+  pinMode(SENSOR_PWR, OUTPUT);
+  digitalWrite(SENSOR_PWR, HIGH);
+
+  button.attach(A0);
+  button.interval(5);
+
+  digitalWrite(LED2, HIGH);
+  digitalWrite(LED1, HIGH);
+  delay(1000);
+  digitalWrite(LED2, LOW);
+  digitalWrite(LED1, LOW);
+
+  sensor.begin(1, softSerial);
+  sensor.preTransmission(preTransmission);
+  sensor.postTransmission(postTransmission);
+
+  Serial.begin(9600);
+  Serial.println("#Begins...");
+
+  if(sd.begin (chipSelect, SPI_HALF_SPEED)) {
+    Serial.println("SD card ok");
+  }
+}
+
+void loop() {
+  if(button.fell()) {
+
+      digitalWrite(LED1, LOW);
+      digitalWrite(LED2, LOW);
+      delay(100);
+      uint8_t tests = 0;
+      uint16_t moisture=0;
+      int16_t temperature=0;
+      uint16_t ver=0;
+      uint8_t err = 0;
+      uint8_t r;
+
+      while(tests < 10 && err == 0) {
+        r = sensor.readInputRegisters(0, 3);
+
+        if(0 != r) {
+          Serial.print("#Error: ");
+          Serial.println(r);
+
+          digitalWrite(LED1, LOW);
+          digitalWrite(LED2, HIGH);
+          delay(100);
+          err = 1;
+        } else {
+          moisture = sensor.getResponseBuffer(0);
+          temperature = sensor.getResponseBuffer(1);
+          ver = sensor.getResponseBuffer(2);
+
+          tests++;
+          if(moisture < 200 || moisture > 450) {
+//            Serial.println(moisture);
+            Serial.println("#MOISTURE OUT OF BOUNDS!");
+            err = 1;
+          }
+
+          if(temperature < 150 || temperature > 800) {
+//            Serial.println(temperature);
+            Serial.println("#TEMPERATURE OUT OF BOUNDS!");
+            err = 1;
+          }
+
+          if(err) {
+            digitalWrite(LED1, LOW);
+            digitalWrite(LED2, HIGH);
+            delay(200);
+          } else {
+            digitalWrite(LED1, HIGH);
+            digitalWrite(LED2, LOW);
+            delay(50);
+          }
+        }
+      }
+
+      if(0 == r) {
+        Serial.print(moisture);
+        Serial.print(", ");
+        Serial.println(temperature);
+        File results = sd.open("results.txt", FILE_WRITE);
+        results.print(moisture);
+        results.print(",");
+        results.println(temperature);
+        results.close();
+      }
+      while(LOW == button.read()){
+        button.update();
+      }
+      delay(100);
+      digitalWrite(LED2, LOW);
+      digitalWrite(LED1, LOW);
+
+  } else if(button.rose()){
+    digitalWrite(LED2, LOW);
+    digitalWrite(LED1, LOW);
+  }
+
+  button.update();
+
+
+}


ethernet.begin fix for ethernetcommander example

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


add testColor

add testColor

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

--- arduinoTestOutsideLibrary/colorTest/colorTest.ino
+++ arduinoTestOutsideLibrary/colorTest/colorTest.ino
@@ -0,0 +1,87 @@
+int s0=3,s1=4,s2=5,s3=6;
+int out=2;
+int flag=0;
+byte counter=0;
+byte countR=0,countG=0,countB=0;
+void setup()
+ {
+ Serial.begin(115200);
+ pinMode(s0,OUTPUT);
+ pinMode(s1,OUTPUT);
+ pinMode(s2,OUTPUT);
+ pinMode(s3,OUTPUT);
+
+ }
+void TCS()
+ {
+ flag=0;
+ digitalWrite(s1,HIGH);
+ digitalWrite(s0,HIGH);
+ digitalWrite(s2,LOW);
+ digitalWrite(s3,LOW);
+ attachInterrupt(0, ISR_INTO, CHANGE);
+ timer0_init();
+
+ }
+void ISR_INTO()
+ {
+ counter++;
+ }
+ void timer0_init(void)
+ {
+  TCCR2A=0x00;
+  TCCR2B=0x07;   //the clock frequency source 1024 points
+  TCNT2= 100;    //10 ms overflow again
+  TIMSK2 = 0x01; //allow interrupt
+ }
+ int i=0;
+ ISR(TIMER2_OVF_vect)//the timer 2, 10ms interrupt overflow again. Internal overflow interrupt executive function
+{
+    TCNT2=100;
+    flag++;
+ if(flag==1)
+  {
+    countR=counter;
+    /*Serial.print("red=");
+    Serial.println(countR,DEC);*/
+    digitalWrite(s2,HIGH);
+    digitalWrite(s3,HIGH);
+  }
+  else if(flag==2)
+   {
+    countG=counter;
+    /*Serial.print("green=");
+    Serial.println(countG,DEC);*/
+    digitalWrite(s2,LOW);
+    digitalWrite(s3,HIGH);
+   }
+   else if(flag==3)
+    {
+    countB=counter;
+    /*Serial.print("blue=");
+    Serial.println(countB,DEC);
+    Serial.println("\n"); */
+    digitalWrite(s2,LOW);
+    digitalWrite(s3,LOW);
+
+    }
+    else if(flag==4)
+     {
+     flag=0;
+     }
+       counter=0;
+}
+void loop()
+ {
+  TCS();
+  while(1){
+    Serial.print("red=");
+    Serial.println(countR,DEC);
+    Serial.print("green=");
+    Serial.println(countG,DEC);
+    Serial.print("blue=");
+    Serial.println(countB,DEC);
+    Serial.println("\n");
+    delay(2000);
+  }
+ }


Testing

Testing

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


Shows basic information on using a LCD Module using the Arduino library LiquidCrystal

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

--- arduino_src/SimpleLCDExample/SimpleLCDExample.ino
+++ arduino_src/SimpleLCDExample/SimpleLCDExample.ino
@@ -0,0 +1,23 @@
+#include <LiquidCrystal.h>
+
+// Connections:
+// rs (LCD )to Arduino pin 11
+// rw (LCD) to Arduino pin 10
+// enable (LCD ) to Arduino pin 9
+// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2
+LiquidCrystal lcd(11, 10, 9, 5, 4, 3, 2);
+
+void setup()
+{
+  lcd.begin(16,2);              // Use 16,2 for a 16x2 LCD Module
+  lcd.clear();                  // Clear screen
+  lcd.setCursor(0,0);           // Set cursor to column 0, row 0 (the first row)
+  lcd.print("Text Line 0");      // Display text at line 0
+  lcd.setCursor(0,1);           // set cursor to column 0, row 1
+  lcd.print("Text Line 1");     // Display text at line 1
+}
+
+void loop()
+{
+
+}


prototype of banner functionality of snowduino's dot matrices

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


Adds missing arduino code

Adds missing arduino code

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

--- arduino/ledstrip/ledstrip.ino
+++ arduino/ledstrip/ledstrip.ino
@@ -0,0 +1,202 @@
+#include <Adafruit_NeoPixel.h>
+#ifdef __AVR__
+  #include <avr/power.h>
+#endif
+
+#define PIN 6
+#define NUM_LEDS 150
+#define NUM_LEDS_HORIZONTAL 5
+#define NUM_LEDS_VERTICAL 10
+
+// Parameter 1 = number of pixels in strip
+// Parameter 2 = Arduino pin number (most are valid)
+// Parameter 3 = pixel type flags, add together as needed:
+//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
+//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
+//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
+//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
+//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
+Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
+int brightness = 255;
+uint32_t topColor = strip.Color(100, 0, 0);
+uint32_t rightColor = strip.Color(0, 100, 0);
+uint32_t bottomColor = strip.Color(0, 0, 100);
+uint32_t leftColor = strip.Color(100, 100, 100);
+
+void setup() {
+  Serial.begin(9600);
+
+  strip.begin();
+  strip.show();
+  colorWipe();
+}
+
+void loop() {
+  // cmd = cmd:params\n
+  // rgb:r:g:b\n
+  // mode:rainbow\n
+  // brightness:value\n
+  // side:top/right/bottom/left:r:g:b\n
+  if(Serial.available()){
+    String cmd = Serial.readStringUntil(':');
+    if(cmd.equals("brightness")){
+      int brightness = Serial.readStringUntil('\n').toInt();
+      setBrightness(brightness);
+    } else if(cmd.equals("rgb")){
+      setColor(parseRGB());
+    } else if(cmd.equals("mode")){
+      String mode = Serial.readStringUntil(':');
+      int speed = Serial.readStringUntil('\n').toInt();
+      setMode(mode, speed);
+    } else if(cmd.equals("side")){
+      String side = Serial.readStringUntil(':');
+      setSideColor(side, parseRGB());
+    }
+  }
+}
+
+void setSideColor(String side, uint32_t c){
+  if(side == "top"){
+    topColor = c;
+  } else if(side == "right"){
+    rightColor = c;
+  } else if(side == "bottom"){
+    bottomColor = c;
+  } else if(side == "left"){
+    leftColor = c;
+  }
+  colorWipe();
+}
+
+
+uint32_t parseRGB(){
+  int r = Serial.readStringUntil(':').toInt();
+  int g = Serial.readStringUntil(':').toInt();
+  int b = Serial.readStringUntil(':').toInt();
+  return strip.Color(r, g, b);
+}
+
+void setBrightness(int bri){
+  brightness = bri;
+}
+
+void setColor(uint32_t c){
+  topColor = c;
+  rightColor = c;
+  bottomColor = c;
+  leftColor = c;
+  colorWipe();
+}
+
+void setMode(String mode, int speed){
+  if(mode.equals("rainbow")){
+    rainbow(speed);
+  } else if(mode.equals("rainbowCycle")){
+    rainbowCycle(speed);
+  } else if(mode.equals("theaterChaseRainbow")){
+    theaterChaseRainbow(speed);
+  }
+}
+
+void setPixelColor( uint16_t n, uint32_t c) {
+   strip.setPixelColor(n, (brightness*c/255)); //TODO, I think I must multiply each color seperately?
+}
+
+void colorWipe() {
+  uint32_t c;
+  for(uint16_t i=0; i<strip.numPixels(); i++) {
+    if(i >= 0 && i < NUM_LEDS_HORIZONTAL){
+      c = topColor;
+    } else if(i >= NUM_LEDS_HORIZONTAL && i < NUM_LEDS_HORIZONTAL + NUM_LEDS_VERTICAL){
+      c = rightColor;
+    } else if(i >= NUM_LEDS_HORIZONTAL + NUM_LEDS_VERTICAL && i < 2 * NUM_LEDS_HORIZONTAL + NUM_LEDS_VERTICAL){
+      c = bottomColor;
+    } else {
+      c = leftColor;
+    }
+    setPixelColor(i, c);
+    strip.show();
+    delay(5);
+  }
+}
+
+void colorWipe(uint32_t c, uint8_t wait) {
+  for(uint16_t i=0; i<strip.numPixels(); i++) {
+    setPixelColor(i, c);
+    strip.show();
+    delay(wait);
+  }
+}
+
+void rainbow(uint8_t wait) {
+  uint16_t i, j;
+
+  for(j=0; j<256 && !Serial.available(); j++) {
+    for(i=0; i<strip.numPixels(); i++) {
+      setPixelColor(i, Wheel((i+j) & 255));
+      if(Serial.available()) return;
+    }
+    strip.show();
+    delay(wait);
+  }
+}
+
+void rainbowCycle(uint8_t wait) {
+  uint16_t i, j;
+
+  for(j=0; j<256*5 && !Serial.available(); j++) { // 5 cycles of all colors on wheel
+    for(i=0; i< strip.numPixels(); i++) {
+      setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
+    }
+    strip.show();
+    delay(wait);
+  }
+}
+
+void theaterChase(uint32_t c, uint8_t wait) {
+  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
+    for (int q=0; q < 3; q++) {
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        setPixelColor(i+q, c);    //turn every third pixel on
+      }
+      if(Serial.available()) return;
+      strip.show();
+
+      delay(wait);
+
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        setPixelColor(i+q, 0);        //turn every third pixel off
+      }
+    }
+  }
+}
+
+void theaterChaseRainbow(uint8_t wait) {
+  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
+    for (int q=0; q < 3; q++) {
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
+      }
+      strip.show();
+      if(Serial.available()) return;
+      delay(wait);
+
+      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
+        setPixelColor(i+q, 0);        //turn every third pixel off
+      }
+    }
+  }
+}
+
+uint32_t Wheel(byte WheelPos) {
+  WheelPos = 255 - WheelPos;
+  if(WheelPos < 85) {
+    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
+  }
+  if(WheelPos < 170) {
+    WheelPos -= 85;
+    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
+  }
+  WheelPos -= 170;
+  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
+}


Print all virtual data example

Print all virtual data example

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

--- examples/More/PrintAllVirtual/PrintAllVirtual.ino
+++ examples/More/PrintAllVirtual/PrintAllVirtual.ino
@@ -0,0 +1,56 @@
+/**************************************************************
+ * Blynk is a platform with iOS and Android apps to control
+ * Arduino, Raspberry Pi and the likes over the Internet.
+ * You can easily build graphic interfaces for all your
+ * projects by simply dragging and dropping widgets.
+ *
+ *   Downloads, docs, tutorials: http://www.blynk.cc
+ *   Blynk community:            http://community.blynk.cc
+ *   Social networks:            http://www.fb.com/blynkapp
+ *                               http://twitter.com/blynk_app
+ *
+ * Blynk library is licensed under MIT license
+ * This example code is in public domain.
+ *
+ **************************************************************
+ * This sketch prints all virtual pin operations!
+ *
+ **************************************************************/
+
+#define BLYNK_PRINT Serial
+#include <SPI.h>
+#include <Ethernet.h>
+#include <BlynkSimpleEthernet.h>
+
+// You should get Auth Token in the Blynk App.
+// Go to the Project Settings (nut icon).
+char auth[] = "YourAuthToken";
+
+void setup()
+{
+  Serial.begin(9600); // See the connection status in Serial Monitor
+  Blynk.begin(auth);
+}
+
+// This is called for all virtual pins, that don't have BLYNK_WRITE handler
+BLYNK_WRITE_DEFAULT() {
+  BLYNK_LOG("V%d input: ", request.pin);
+  // Print all parameter values
+  for (auto i = param.begin(); i < param.end(); ++i) {
+    BLYNK_LOG("* %s", i.asString());
+  }
+}
+
+// This is called for all virtual pins, that don't have BLYNK_READ handler
+BLYNK_READ_DEFAULT() {
+  // Generate random response
+  int val = random(0, 100);
+  BLYNK_LOG("V%d output: %d", request.pin, val);
+  Blynk.virtualWrite(request.pin, val);
+}
+
+void loop()
+{
+  Blynk.run();
+}
+


Create pixy-rover.ino

Create pixy-rover.ino

error: pathspec 'pixy-rover.ino' did not match any file(s) known to git

--- pixy-rover.ino
+++ pixy-rover.ino
@@ -0,0 +1,259 @@
+// By Jeremy Ellis.
+// Rover should move slowly unless several seconds no response from the pixy then it should stop.
+// should move left or right based on where the object is.
+// should stop if the object gets to big a width ie it is close.
+
+
+
+/* Globals -------------------------------------------------------------------*/
+// Note my pololu big motor driver needs two digital and one analog PWM controls for the drive motor VNH5019
+//   https://www.pololu.com/product/1451
+
+
+SYSTEM_MODE(SEMI_AUTOMATIC);
+
+int myConnect = 0;      // means neighter connected or disconnected yet
+
+
+
+int myForward          = D6;      // set or change these as needed
+int myBackward         = D5;      // set or change these as needed
+int myDriveMotor       = A0;
+
+
+
+// I am using the cheap pololu motor driver that only needs one digital and one analog pins for the turning motor DRV8835
+//    https://www.pololu.com/product/2135    note: this can drive two motors but I needed the bigger driver for the 2.3 amp drivemotor
+
+int myLeftRight        = D0;       // different motor driver does both left and right
+int myTurnMotor        = A1;   //A4;  // this one must be changed since it is used by the Pixy along with A3,A4,A5
+
+int myD7               = D7;      // to test if wifi is working
+int myLast             = Time.now();   // for emergency motor stop after 4 s  no activity from the pixy
+
+
+
+
+
+
+
+// Begin User Defined Defines
+#define FRAME_SKIP 25                // How many frames to skip. Lower numbers cause faster response
+// End User Defined Defines
+
+//      #include "SPI.h"        // got rid of this since it would not compile
+#include "Pixy.h"
+#include "TPixy.h"
+
+
+// Not sure how to flash new code so will not do this yet. Just carry my cll with hotspot turned on.
+//SYSTEM_MODE(MANUAL);                // Set the system mode to manual. We don't need the cloud for this code
+
+
+Pixy pixy;                          // Create our pixy object
+int i = 0;                          // Create an int to count for frame skipping. Frame skipping is used to artificially slow down the pixy loop
+
+// Setup - Runs Once @ Startup
+void setup() {
+//    Spark.function("my-main", myMain);
+    pinMode(myDriveMotor, OUTPUT);
+    pinMode(myForward, OUTPUT);
+    pinMode(myBackward, OUTPUT);
+
+    pinMode(myTurnMotor, OUTPUT);
+    pinMode(myLeftRight, OUTPUT);
+
+    pinMode(myD7, OUTPUT);
+
+   pinMode(D7, OUTPUT);
+   Spark.connect();
+
+   delay(20000);   // wait 20 seconds at startup
+
+
+   if (WiFi.ready()){
+        myConnect = 1;   // means wifi got connected
+    } else {
+        Spark.disconnect();
+        WiFi.off();
+        myConnect =2;   // means no wifi cloud connectivity
+        digitalWrite(D7,HIGH);     // D7 high says no wifi but spark working fine.
+      }
+
+
+
+    //PUT YOUR SETUP CODE HERE. Note: Only three more functions allowed!
+    // test everything using the return int from a function!
+
+
+    //RGB.control(true);
+    //RGB.color(0, 255, 255);  //cyan
+    RGB.brightness(1);    // 1=very low light, 255 = max
+
+
+    Serial.begin(9600);             // Initalize the USB Serial port
+    pixy.init();                    // Initalize the pixy object
+}
+
+// Loop - Runs over and over
+void loop(){
+
+
+
+        RGB.brightness(1);
+    if (Time.now() >= myLast + 4){                // car may be out of control or wifi down or pixy down
+
+         analogWrite(myDriveMotor,  0);           // shut both motors down
+         analogWrite(myTurnMotor,   0);
+         RGB.brightness(100);                     // show that no processing lately
+    }
+
+
+
+    // Variable Creation
+    uint16_t blocks;                // Create an unsigned int to hold the number of found blocks
+    char buf[50];                   // Create a buffer for printing over serial
+    int myX, myWidth;
+
+    blocks = pixy.getBlocks();      // Do the pixy stuff. Grab the numbers of blocks the pixy finds
+
+  // If we have some blocks increment the frame counter (i) and if enough frames have passed print out data about the found blocks to the serial port
+  if (blocks) {
+    myLast = Time.now();           // update time to see if car still in wifi control
+
+
+
+
+
+
+    myX = pixy.blocks[0].x;      //  Deal with all the turning x = 0 - 319
+
+
+
+    if (myX < 20){   //object on the left so car should turn left
+        digitalWrite(myLeftRight, 1);
+        analogWrite(myTurnMotor, 250);     //200 - myX); // turn sharper if more left
+    }
+
+
+    if (myX >= 20 && myX < 100){   //object slightly on the left so car should turn left
+        digitalWrite(myLeftRight, 1);
+        analogWrite(myTurnMotor, 100);     //200 - myX); // turn less sharper if more left
+    }
+
+
+
+
+    if (myX >= 100 && myX < 220 ){   //Stop turning
+        analogWrite(myTurnMotor,  0);
+    }
+
+
+
+    if (myX >= 220 && myX < 300){   //object slightly on the right so car should turn right
+        digitalWrite(myLeftRight, 0);
+        analogWrite(myTurnMotor,  100);   //myX - 120);  // turn less sharper if more right
+    }
+
+
+
+
+    if (myX >= 300){   //object on the right so car should turn right
+        digitalWrite(myLeftRight, 0);
+        analogWrite(myTurnMotor,  250);   //myX - 120);  // turn sharper if more right
+    }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    myWidth = pixy.blocks[0].width;                // deal with the speed of the rover  width = 0 - 320
+
+    if (myWidth < 25 ){   // Object is very far away, small so stop
+        analogWrite(myDriveMotor,  0);
+        analogWrite(myTurnMotor,  0);
+    }
+
+    if (myWidth >= 25 && myWidth < 27){   //Object is  far away so go faster
+        digitalWrite(myForward, 1);
+        digitalWrite(myBackward, 0);
+        analogWrite(myDriveMotor,  255);
+    }
+
+
+    if (myWidth >= 27 && myWidth < 40){   //Object is  far away so go faster
+        digitalWrite(myForward, 1);
+        digitalWrite(myBackward, 0);
+        analogWrite(myDriveMotor,  100);
+    }
+
+    if (myWidth >= 40 && myWidth < 100){   // object a good distance away go a medium speed 0-250
+        digitalWrite(myForward, 1);
+        digitalWrite(myBackward, 0);
+        analogWrite(myDriveMotor,  50);  //slow    //250 - (myWidth * 2));
+    }
+
+    if ( myWidth >= 100){   // Object is very wide, so it is too close, so stop
+        analogWrite(myDriveMotor,  0);
+        analogWrite(myTurnMotor,  0);
+    }
+
+
+
+    delay(2);   // just slow things down a bit
+
+
+  } // end blocks
+
+
+
+      if (myConnect == 1){   // only put code here that needs wifi
+
+        digitalWrite(D7,HIGH);     // fast blink says Wifi connected
+        delay(50);
+        digitalWrite(D7,LOW);
+        delay(50);
+
+        // put your code here that needs wifi Cloud connectivity
+    }
+
+
+    if (myConnect == 2){   // only put code here that is fine with no wifi
+
+      // put your code here that runs without Wifi Cloud connectivity
+      // LED D7 should be HIGH to prove Wifi has been disconnected
+
+    }
+
+
+
+
+
+
+
+}   // end loop
+
+
+
+
+
+
+
+
+
+
+
+
+
+


Adding the sketch for our main gantry.

Adding the sketch for our main gantry.

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

--- arduino/main_gantry/main_gantry.ino
+++ arduino/main_gantry/main_gantry.ino
@@ -0,0 +1,224 @@
+/* Analog Read to LED
+ * ------------------
+ *
+ * turns on and off a light emitting diode(LED) connected to digital
+ * pin 13. The amount of time the LED will be on and off depends on
+ * the value obtained by analogRead(). In the easiest ase we connect
+ * a potentiometer to analog pin 2.
+ *
+ * Created 1 December 2005
+ * copyleft 2005 DojoDave <http://www.0j0.org>
+ * http://arduino.berlios.de
+ *
+ */
+
+#include <Wire.h>
+#include <Adafruit_MotorShield.h>
+#include "utility/Adafruit_PWMServoDriver.h"
+#include <Servo.h>
+
+#define MARKER_SERVO_DOWN       (112)
+#define MARKER_SERVO_UP         (90)
+#define ERASER_LIFT_SERVO_UP    (255)
+#define ERASER_LIFT_SERVO_DOWN  (100)
+#define ERASER_SPIN_SERVO_GO    (0)
+#define ERASER_SPIN_SERVO_STOP  (255)
+
+int potPin = 2;    // select the input pin for the potentiometer
+int ledPin = 13;   // select the pin for the LED
+int val = 0;       // variable to store the value coming from the sensor
+
+
+
+// Create the motor shield object with the default I2C address
+Adafruit_MotorShield AFMS = Adafruit_MotorShield();
+
+// Connect a stepper motor with 200 steps per revolution (1.8 degree)
+// to motor port #2 (M3 and M4)
+Adafruit_StepperMotor *markerMotor = AFMS.getStepper(200, 2);
+Adafruit_StepperMotor *tapeMotor = AFMS.getStepper(200, 1);
+
+// We'll also test out the built in Arduino Servo library
+Servo markerServo;
+Servo eraserLiftServo;
+Servo eraserSpinServo;
+
+void setup() {
+  Serial.begin(9600);
+
+  AFMS.begin();  // create with the default frequency 1.6KHz
+  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz
+
+  // Init our stepper that moves the marker
+  markerMotor->setSpeed(1000);  // 10 rpm
+
+  // Init our stepper that feeds the tape
+  tapeMotor->setSpeed(10);
+
+  // Attach a servo to pin #10
+  markerServo.attach(10);
+  eraserLiftServo.attach(9);
+  eraserSpinServo.attach(11);
+
+  eraserLift();
+  eraserStop();
+  markerLift();
+  //gantryZero();
+}
+
+
+
+void markerLift() {
+  markerServo.write(map(MARKER_SERVO_UP, 0, 255, 0, 180));
+}
+
+void markerLower() {
+  markerServo.write(map(MARKER_SERVO_DOWN, 0, 255, 0, 180));
+}
+
+static uint16_t gantryPosition = 0;
+
+void gantryZero(){
+  gantryPosition = 0;
+  markerMotor->step(3000, BACKWARD, SINGLE);
+  markerMotor->step(1000, FORWARD, DOUBLE);
+}
+
+void gantryOut(){
+  if(gantryPosition < 1600){
+    gantryPosition += 1600;
+    markerMotor->step(1600, FORWARD, DOUBLE);
+  }
+}
+
+void gantryIn(){
+  if(gantryPosition >= 1600){
+    gantryPosition -= 1600;
+    markerMotor->step(1600, BACKWARD, DOUBLE);
+  }
+}
+
+void eraserLower(){
+  eraserLiftServo.write(map(ERASER_LIFT_SERVO_DOWN, 0, 255, 0, 180));
+}
+
+void eraserLift(){
+  eraserLiftServo.write(map(ERASER_LIFT_SERVO_UP, 0, 255, 0, 180));
+}
+
+void eraserSpin(){
+  eraserSpinServo.write(map(ERASER_SPIN_SERVO_GO, 0, 255, 0, 180));
+}
+
+void eraserStop(){
+  eraserSpinServo.write(map(ERASER_SPIN_SERVO_STOP, 0, 255, 0, 180));
+}
+
+void tapeMoveLeft() {
+  tapeMotor->step(60, BACKWARD, DOUBLE);
+}
+
+void tapeMoveRight() {
+  tapeMotor->step(60, FORWARD, DOUBLE);
+}
+void loop() {
+  static uint8_t val = 128;
+  uint8_t c;
+  if(Serial.available()){
+    c = Serial.read();
+
+    Serial.print("Read: ");
+    Serial.println(c);
+    switch(c){
+
+    // Eraser functions
+    case 'E':
+      eraserLift();
+      break;
+    case 'e':
+      eraserLower();
+      break;
+    case 'S':
+      eraserSpin();
+      break;
+    case 's':
+      eraserStop();
+      break;
+    case 'D':
+      tapeMotor->step(125, FORWARD, DOUBLE);
+      delay(100);
+      eraserLower();
+      delay(100);
+      eraserSpin();
+      delay(100);
+      tapeMotor->step(30, BACKWARD, DOUBLE);
+      delay(100);
+      tapeMotor->step(30, FORWARD, DOUBLE);
+      delay(100);
+      tapeMotor->step(30, BACKWARD, DOUBLE);
+      delay(100);
+      tapeMotor->step(30, FORWARD, DOUBLE);
+      delay(100);
+      eraserStop();
+      eraserLift();
+      delay(100);
+      tapeMotor->step(125, BACKWARD, DOUBLE);
+      break;
+    case 'M':
+      markerLift();
+      break;
+    case 'm':
+      markerLower();
+      break;
+    case 'G':
+      gantryOut();
+      break;
+    case 'g':
+      gantryIn();
+      break;
+    case 'z':
+      gantryZero();
+      break;
+    case '1':
+      markerLower();
+      gantryOut();
+      markerLift();
+      gantryIn();
+      break;
+    case '0':
+      markerLower();
+      gantryOut();
+      tapeMotor->step(20, FORWARD, DOUBLE);
+      gantryIn();
+      tapeMotor->step(20, BACKWARD, DOUBLE);
+      markerLift();
+      break;
+    case '+':
+      //markerLift();
+      if(val < 252)
+        val += 4;
+      break;
+    case '-':
+      //markerLower();
+      if(val > 3)
+        val -= 4;
+      break;
+
+
+    // Tape Movements
+    case 'l':
+      tapeMoveLeft();
+      break;
+    case 'r':
+      tapeMoveRight();
+      break;
+
+    }
+
+    // Init
+
+
+//    Serial.println(val);
+//    eraserSpinServo.write(map(val, 0, 255, 0, 180));
+  }
+}


Arduino firmware

Arduino firmware

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

--- arduinoFirmware/arduinoFirmware.ino
+++ arduinoFirmware/arduinoFirmware.ino
@@ -0,0 +1,67 @@
+#include <CapacitiveSensor.h>
+/*
+ * CapitiveSense Library Demo Sketch
+ * Paul Badger 2008
+ * Uses a high value resistor e.g. 10 megohm between send pin and receive pin
+ * Resistor effects sensitivity, experiment with values, 50 kilohm - 50 megohm. Larger resistor values yield larger sensor values.
+ * Receive pin is the sensor pin - try different amounts of foil/metal on this pin
+ * Best results are obtained if sensor foil and wire is covered with an insulator such as paper or plastic sheet
+ */
+
+#define SENSI 200
+#define STEVILO_MERITEV 5
+#define STEVILO_SENZOR_PINOV 3
+
+// 1 - 40 Mohm resistor between pins 4 & x, pin x is sensor pin, and wire, foil
+CapacitiveSensor   cs_4_2 = CapacitiveSensor(4,2);
+CapacitiveSensor   cs_4_5 = CapacitiveSensor(4,5);
+CapacitiveSensor   cs_4_8 = CapacitiveSensor(4,8);
+//CapacitiveSensor   cs_4_10 = CapacitiveSensor(4,10);
+//CapacitiveSensor   cs_4_12 = CapacitiveSensor(4,12);
+meritve = [ STEVILO_MERITEV * STEVILO_SENZOR_PINOV ]
+poprecje = [ STEVILO_SENZOR_PINOV ]
+stevec = 0
+
+void setup()
+{
+
+   cs_4_2.set_CS_AutocaL_Millis(0xFFFFFFFF);     // turn off autocalibrate on channel 1 - just as an example
+   cs_4_5.set_CS_AutocaL_Millis(0xFFFFFFFF);
+   cs_4_8.set_CS_AutocaL_Millis(0xFFFFFFFF);
+   //cs_4_10.set_CS_AutocaL_Millis(0xFFFFFFFF);
+   //cs_4_12.set_CS_AutocaL_Millis(0xFFFFFFFF);
+   Serial.begin(9600);
+}
+
+void loop()
+{
+    long start = millis();
+    long total1 =  cs_4_2.capacitiveSensor(30);
+    meritve[stevec + 0] = total1;
+    long total2 =  cs_4_5.capacitiveSensor(30);
+    meritve[stevec + 1] = total2;
+    long total3 =  cs_4_8.capacitiveSensor(30);
+    meritve[stevec + 2] = total3;
+
+    //if (total1 > SENSI) {total1=1;}else{total1=0;}
+    //if (total2 > SENSI) {total2=1;}else{total2=0;}
+    //if (total3 > SENSI) {total3=1;}else{total3=0;}
+    for (int i=0; i < STEVILO_SENZOR_PINOV; i++){
+      poprecje[i] = 0
+      for (int j=0; j < STEVILO_MERITEV; j++){
+        poprecje[i] += meritve[i+i*j]
+      }
+    }
+
+    Serial.print(millis() - start);        // check on performance in milliseconds
+    Serial.print("\t");                    // tab character for debug windown spacing
+
+    Serial.print(total1);                  // print sensor output 1
+    Serial.print("\t");
+    Serial.print(total2);                  // print sensor output 2
+    Serial.print("\t");
+    Serial.println(total3);                // print sensor output 3
+
+    delay(10);                             // arbitrary delay to limit data to serial port
+    meritve += STEVILO_SENZOR_PINOV
+}


Create wifi-localization.ino

Create wifi-localization.ino

error: pathspec 'The' did not match any file(s) known to git
error: pathspec 'Things' did not match any file(s) known to git
error: pathspec 'Network/localization/wifi-localization.ino' did not match any file(s) known to git