Update chorus-rf-laptimer.ino

Update chorus-rf-laptimer.ino

--- Arduino/chorus-rf-laptimer/chorus-rf-laptimer.ino
+++ Arduino/chorus-rf-laptimer/chorus-rf-laptimer.ino
@@ -1,908 +1,908 @@
-/**
- * DIY RF Laptimer by Andrey Voroshkov (bshep)
- * SPI driver based on fs_skyrf_58g-main.c by Simon Chambers
- * fast ADC reading code is by "jmknapp" from Arduino forum
- * fast port I/O code from http://masteringarduino.blogspot.com.by/2013/10/fastest-and-smallest-digitalread-and.html
-
-The MIT License (MIT)
-
-Copyright (c) 2016 by Andrey Voroshkov (bshep)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-/*
-TODO: Resolve problem:
-    when CONTROL_DATA_REQUEST is being processed, i.e. all data items are sent in sequence,
-    and either external or external logic decides to send an individual item,
-    then the latter just breaks execution of CONTROL_DATA_REQUEST and data is not sent completely
-
-TODO: when implement send queue, there's possible optimization:
-    remove already existing items from queue
-*/
-
-// #define DEBUG
-
-#ifdef DEBUG
-    #define DEBUG_CODE(x) do { x } while(0)
-#else
-    #define DEBUG_CODE(x) do { } while(0)
-#endif
-
-uint8_t MODULE_ID = 0;
-uint8_t MODULE_ID_HEX = '0';
-
-#define SERIAL_DATA_DELIMITER '\n'
-
-#include <avr/pgmspace.h>
-
-#define BAUDRATE 9600
-
-const uint16_t musicNotes[] PROGMEM = { 523, 587, 659, 698, 784, 880, 988, 1046 };
-
-// number of analog rssi reads to average for the current check.
-// single analog read with FASTADC defined (see below) takes ~20us on 16MHz arduino
-// so e.g. 10 reads will take 200 ms, which gives resolution of 5 RSSI reads per ms,
-// this means that we can theoretically have 1ms timing accuracy :)
-#define RSSI_READS 5 // 5 should give about 10 000 readings per second
-
-// input control byte constants
-#define CONTROL_START_RACE      'R'
-#define CONTROL_END_RACE        'r'
-#define CONTROL_DEC_MIN_LAP     'm'
-#define CONTROL_INC_MIN_LAP     'M'
-#define CONTROL_DEC_CHANNEL     'c' //keeping because of compatiblity
-#define CONTROL_INC_CHANNEL     'C' //keeping because of compatiblity
-#define CONTROL_DEC_THRESHOLD   't'
-#define CONTROL_INC_THRESHOLD   'T'
-#define CONTROL_SET_THRESHOLD   'S' //it either triggers or sets an uint16
-#define CONTROL_SET_SOUND       'D'
-#define CONTROL_DATA_REQUEST    'A'
-#define CONTROL_INC_BAND        'B' //keeping because of compatiblity
-#define CONTROL_DEC_BAND        'b' //keeping because of compatiblity
-#define CONTROL_START_CALIBRATE 'I'
-#define CONTROL_END_CALIBRATE   'i'
-#define CONTROL_MONITOR_ON      'V'
-#define CONTROL_MONITOR_OFF     'v'
-#define CONTROL_SET_SKIP_LAP0   'F'
-#define CONTROL_GET_VOLTAGE     'Y'
-// input control byte constants for uint16 "set value" commands
-#define CONTROL_SET_FREQUENCY   'O'
-// input control byte constants for long "set value" commands
-#define CONTROL_SET_MIN_LAP     'L'
-#define CONTROL_SET_CHANNEL     'H'
-#define CONTROL_SET_BAND        'N'
-
-// output id byte constants
-#define RESPONSE_CHANNEL        'C'
-#define RESPONSE_RACE_STATE     'R'
-#define RESPONSE_MIN_LAP_TIME   'M'
-#define RESPONSE_THRESHOLD      'T'
-#define RESPONSE_CURRENT_RSSI   'S'
-#define RESPONSE_LAPTIME        'L'
-#define RESPONSE_SOUND_STATE    'D'
-#define RESPONSE_BAND           'B'
-#define RESPONSE_CALIBR_TIME    'I'
-#define RESPONSE_CALIBR_STATE   'i'
-#define RESPONSE_MONITOR_STATE  'V'
-#define RESPONSE_LAP0_STATE     'F'
-#define RESPONSE_END_SEQUENCE   'X'
-#define RESPONSE_IS_CONFIGURED  'P'
-#define RESPONSE_VOLTAGE        'Y'
-#define RESPONSE_FREQUENCY      'O'
-
-// send item byte constants
-// Must correspond to sequence of numbers used in "send data" switch statement
-// Subsequent items starting from 0 participate in "send all data" response
-#define SEND_CHANNEL        0
-#define SEND_RACE_STATE     1
-#define SEND_MIN_LAP_TIME   2
-#define SEND_THRESHOLD      3
-#define SEND_ALL_LAPTIMES   4
-#define SEND_SOUND_STATE    5
-#define SEND_BAND           6
-#define SEND_CALIBR_STATE   7
-#define SEND_MONITOR_STATE  8
-#define SEND_LAP0_STATE     9
-#define SEND_IS_CONFIGURED  10
-#define SEND_FREQUENCY      11
-#define SEND_END_SEQUENCE   12
-// following items don't participate in "send all items" response
-#define SEND_LAST_LAPTIMES  100
-#define SEND_CALIBR_TIME    101
-#define SEND_CURRENT_RSSI   102
-#define SEND_VOLTAGE        103
-// special item that sends all subsequent items from 0 (see above)
-#define SEND_ALL_DEVICE_STATE 255
-
-//----- RSSI --------------------------------------
-#define FILTER_ITERATIONS 5
-uint16_t rssiArr[FILTER_ITERATIONS + 1];
-uint16_t rssiThreshold = 190;
-uint16_t rssi;
-
-#define RSSI_MAX 1024
-#define RSSI_MIN 0
-#define MAGIC_THRESHOLD_REDUCE_CONSTANT 2
-#define THRESHOLD_ARRAY_SIZE  100
-uint16_t rssiThresholdArray[THRESHOLD_ARRAY_SIZE];
-
-#define MAX_RSSI_MONITOR_DELAY_CYCLES 1000 //each 100ms, if cycle takes 100us
-
-//----- Voltage monitoring -------------------------
-#define VOLTAGE_READS 3 //get average of VOLTAGE_READS readings
-
-// analog readings less than VOLTAGE_ZERO_THRESHOLD value won't be sent.
-// This way entire chorus device will send voltages only from devices that are attached to LiPo
-// So if single Solo device has LiPo attached, then broadcast voltage request to
-// entire Chorus device will produce a single voltage response.
-#define VOLTAGE_ZERO_THRESHOLD 100
-
-uint16_t voltage = 0;
-
-//----- Send Queue ---------------------------------
-#define SEND_QUEUE_MAXLEN 20
-uint8_t sendQueue[SEND_QUEUE_MAXLEN];
-uint8_t sendQueueHead = 0;
-uint8_t sendQueueTail = 0;
-uint8_t isSendQueueFull = 0;
-
-//----- Lap timings--------------------------------
-uint32_t lastMilliseconds = 0;
-#define MIN_MIN_LAP_TIME 1 //seconds
-#define MAX_MIN_LAP_TIME 60 //seconds
-uint8_t minLapTime = 5; //seconds
-#define MAX_LAPS 100
-uint32_t lapTimes[MAX_LAPS];
-
-//----- Calibration -------------------------------
-uint8_t isCalibrated = 0;
-uint32_t calibrationMilliseconds = 0;
-
-// Usage of signed int calibration constant:
-// calibratedMs = readMs + readMs/timeCalibrationConst
-int32_t timeCalibrationConst = 0;
-
-//----- other globals------------------------------
-uint8_t allowEdgeGeneration = 0;
-uint8_t channelIndex = 0;
-uint8_t bandIndex = 0;
-uint8_t isRaceStarted = 0;
-uint8_t isSoundEnabled = 1;
-uint8_t isConfigured = 0; //changes to 1 if any input changes the state of the device. it will mean that externally stored preferences should not be applied
-uint8_t rssiMonitor = 0;
-uint8_t newLapIndex = 0;
-uint8_t shouldSkipFirstLap = 1; //start table is before the laptimer, so first lap is not a full-fledged lap (i.e. don't respect min-lap-time for the very first lap)
-uint8_t isSendingData = 0;
-uint8_t sendStage = 0;
-uint8_t sendLapTimesIndex = 0;
-uint8_t sendLastLapIndex = 0;
-uint8_t shouldSendSingleItem = 0;
-uint8_t lastLapsNotSent = 0;
-uint16_t rssiMonitorDelayExpiration = 0;
-uint16_t frequency = 0;
-
-//----- read/write bufs ---------------------------
-#define READ_BUFFER_SIZE 20
-uint8_t readBuf[READ_BUFFER_SIZE];
-uint8_t proxyBuf[READ_BUFFER_SIZE];
-uint8_t readBufFilledBytes = 0;
-uint8_t proxyBufDataSize = 0;
-
-// ----------------------------------------------------------------------------
-#include "fastReadWrite.h"
-#include "fastADC.h"
-#include "pinAssignments.h"
-#include "channels.h"
-#include "sendSerialHex.h"
-#include "rx5808spi.h"
-#include "sounds.h"
-
-// ----------------------------------------------------------------------------
-void setup() {
-    // initialize digital pin 13 LED as an output.
-    pinMode(led, OUTPUT);
-    digitalHigh(led);
-
-    // init buzzer pin
-    pinMode(buzzerPin, OUTPUT);
-
-    //init raspberrypi interrupt generator pin
-    pinMode(pinRaspiInt, OUTPUT);
-    digitalLow(pinRaspiInt);
-
-    // SPI pins for RX control
-    setupSPIpins();
-
-    // set the channel as soon as we can
-    // faster boot up times :)
-    setChannelModule(channelIndex, bandIndex, 0);
-    Serial.begin(BAUDRATE);
-
-    initFastADC();
-
-    // Setup Done - Turn Status LED off.
-    digitalLow(led);
-
-    DEBUG_CODE(
-        pinMode(serialTimerPin, OUTPUT);
-        pinMode(loopTimerPin, OUTPUT);
-    );
-}
-// ----------------------------------------------------------------------------
-void loop() {
-    DEBUG_CODE(
-        digitalToggle(loopTimerPin);
-    );
-
-    rssi = getFilteredRSSI();
-
-    // check rssi threshold to identify when drone finishes the lap
-    if (rssiThreshold > 0) { // threshold = 0 means that we don't check rssi values
-        if(rssi > rssiThreshold) { // rssi above the threshold - drone is near
-            if (allowEdgeGeneration) {  // we haven't fired event for this drone proximity case yet
-                allowEdgeGeneration = 0;
-                gen_rising_edge(pinRaspiInt);  //generate interrupt for EasyRaceLapTimer software
-
-                uint32_t now = millis();
-                uint32_t diff = now - lastMilliseconds;
-                if (timeCalibrationConst) {
-                    diff = diff + (int32_t)diff/timeCalibrationConst;
-                }
-                if (isRaceStarted) { // if we're within the race, then log lap time
-                    if (diff > minLapTime*1000 || (shouldSkipFirstLap && newLapIndex == 0)) { // if minLapTime haven't passed since last lap, then it's probably false alarm
-                        digitalLow(led);
-                        if (newLapIndex < MAX_LAPS-1) { // log time only if there are slots available
-                            lapTimes[newLapIndex] = diff;
-                            newLapIndex++;
-                            lastLapsNotSent++;
-                            addToSendQueue(SEND_LAST_LAPTIMES);
-                        }
-                        lastMilliseconds = now;
-                        playLapTones(); // during the race play tone sequence even if no more laps can be logged
-                    }
-                }
-                else {
-                    playLapTones(); // if not within the race, then play once per case
-                }
-            }
-        }
-        else  {
-            allowEdgeGeneration = 1; // we're below the threshold, be ready to catch another case
-            digitalHigh(led);
-        }
-    }
-
-    readSerialDataChunk();
-
-    sendProxyDataChunk();
-
-    // send data chunk through Serial
-
-    if (isSendingData) {
-        switch (sendStage) {
-            case 0: // SEND_CHANNEL
-                if (send4BitsToSerial(RESPONSE_CHANNEL, channelIndex)) {
-                    onItemSent();
-                }
-                break;
-            case 1: // SEND_RACE_STATE
-                if (send4BitsToSerial(RESPONSE_RACE_STATE, isRaceStarted)) {
-                    DEBUG_CODE(
-                        digitalLow(serialTimerPin);
-                    );
-                    onItemSent();
-                }
-                break;
-            case 2: // SEND_MIN_LAP_TIME
-                if (sendByteToSerial(RESPONSE_MIN_LAP_TIME, minLapTime)) {
-                    onItemSent();
-                }
-                break;
-            case 3: // SEND_THRESHOLD
-                if (sendIntToSerial(RESPONSE_THRESHOLD, rssiThreshold)) {
-                    onItemSent();
-                }
-                break;
-            case 4: // SEND_ALL_LAPTIMES
-                if (sendLapTimesIndex < newLapIndex) {
-                    if (sendLaptimeToSerial(RESPONSE_LAPTIME, sendLapTimesIndex, lapTimes[sendLapTimesIndex])) {
-                        sendLapTimesIndex++;
-                    }
-                }
-                else {
-                    onItemSent();
-                }
-                break;
-            case 5: // SEND_SOUND_STATE
-                if (send4BitsToSerial(RESPONSE_SOUND_STATE, isSoundEnabled)) {
-                    onItemSent();
-                }
-                break;
-            case 6: // SEND_BAND
-                if (send4BitsToSerial(RESPONSE_BAND, bandIndex)) {
-                    onItemSent();
-                }
-                break;
-            case 7: // SEND_CALIBR_STATE
-                if (send4BitsToSerial(RESPONSE_CALIBR_STATE, isCalibrated)) {
-                    onItemSent();
-                }
-                break;
-            case 8: // SEND_MONITOR_STATE
-                if (send4BitsToSerial(RESPONSE_MONITOR_STATE, rssiMonitor)) {
-                    onItemSent();
-                }
-                break;
-            case 9: // SEND_LAP0_STATE
-                if (send4BitsToSerial(RESPONSE_LAP0_STATE, shouldSkipFirstLap)) {
-                    onItemSent();
-                }
-                break;
-            case 10: // SEND_IS_CONFIGURED
-                if (send4BitsToSerial(RESPONSE_IS_CONFIGURED, isConfigured)) {
-                    onItemSent();
-                }
-                break;
-            case 11: // SEND_FREQUENCY
-                if (sendIntToSerial(RESPONSE_FREQUENCY, frequency)) {
-                    onItemSent();
-                }
-                break;
-            // Below is a termination case, to notify that data for CONTROL_DATA_REQUEST is over.
-            // Must be the last item in the sequence!
-            case 12: // SEND_END_SEQUENCE
-                if (send4BitsToSerial(RESPONSE_END_SEQUENCE, 1)) {
-                    onItemSent();
-                    isSendingData = 0;
-                    shouldSendSingleItem = 1;
-                }
-                break;
-
-            //--------------------------------------------------------------------------------------
-            // Here is the gap in sequence between items that are sent as response to
-            // "CONTROL_DATA_REQUEST" and items that are only sent individually
-            //--------------------------------------------------------------------------------------
-
-            case 100: // SEND_LAST_LAPTIMES
-                uint8_t idx;
-                idx = newLapIndex - lastLapsNotSent;
-                if (lastLapsNotSent == 0) {
-                    break;
-                }
-                if (sendLaptimeToSerial(RESPONSE_LAPTIME, idx, lapTimes[idx])) {
-                    lastLapsNotSent--;
-                    onItemSent();
-                }
-                break;
-            case 101: // SEND_CALIBR_TIME
-                if (sendLongToSerial(RESPONSE_CALIBR_TIME, calibrationMilliseconds)) {
-                    onItemSent();
-                }
-                break;
-            case 102: // SEND_CURRENT_RSSI
-                if (sendIntToSerial(RESPONSE_CURRENT_RSSI, rssi)) {
-                    onItemSent();
-                }
-                break;
-            case 103: // SEND_VOLTAGE
-                if (voltage > VOLTAGE_ZERO_THRESHOLD) {
-                    if (sendIntToSerial(RESPONSE_VOLTAGE, voltage)) {
-                        onItemSent();
-                    }
-                } else {
-                    onItemSent();
-                }
-                break;
-            default:
-                isSendingData = 0;
-                DEBUG_CODE(
-                    digitalLow(serialTimerPin);
-                );
-        }
-    }
-
-    if (!isSendingData && !isQueueEmpty()) {
-        uint8_t item = getFromSendQueue();
-        if (item == SEND_ALL_DEVICE_STATE) {
-            setupToSendAllItems();
-        } else {
-            setupToSendSingleItem(item);
-        }
-    }
-
-    if (rssiMonitor) {
-        rssiMonitorDelayExpiration++;
-        if (rssiMonitorDelayExpiration > MAX_RSSI_MONITOR_DELAY_CYCLES) {
-            addToSendQueue(SEND_CURRENT_RSSI);
-            rssiMonitorDelayExpiration = 0;
-        }
-    }
-
-    if (isSoundEnabled && playSound) {
-        if (playStartTime == 0) {
-            tone(buzzerPin,curToneSeq[curToneIndex]);
-            playStartTime = millis();
-        }
-        uint32_t dur = millis() - playStartTime;
-        if (dur >= curToneSeq[curDurIndex]) {
-            if (curDurIndex >= lastToneSeqIndex) {
-                noTone(buzzerPin);
-                playSound = 0;
-            } else {
-                curToneIndex += 2;
-                curDurIndex += 2;
-                tone(buzzerPin, curToneSeq[curToneIndex]);
-                playStartTime = millis();
-            }
-        }
-    }
-}
-// ----------------------------------------------------------------------------
-uint8_t addToSendQueue(uint8_t item) {
-    if (isSendQueueFull) {
-        return 0; // couldn't add
-    }
-    sendQueue[sendQueueTail] = item;
-    sendQueueTail++;
-    if (sendQueueTail >= SEND_QUEUE_MAXLEN) {
-        sendQueueTail = 0;
-    }
-    if (sendQueueTail == sendQueueHead) {
-        isSendQueueFull = 1;
-    }
-    return 1; // successfully added
-}
-// ----------------------------------------------------------------------------
-uint8_t getFromSendQueue() {
-    // don't check for Q emptiness because it's expected to be done before this function call
-
-    // if (isQueueEmpty()) {
-    //     return 0; // couldn't read - queue is empty
-    // }
-    uint8_t result = sendQueue[sendQueueHead];
-    sendQueueHead++;
-    if (sendQueueHead >= SEND_QUEUE_MAXLEN) {
-        sendQueueHead = 0;
-    }
-    isSendQueueFull = 0;
-
-    return result;
-}
-// ----------------------------------------------------------------------------
-uint8_t isQueueEmpty() {
-    return (sendQueueHead == sendQueueTail && !isSendQueueFull);
-}
-// ----------------------------------------------------------------------------
-void onItemSent() {
-    if (shouldSendSingleItem) {
-        isSendingData = 0;
-    } else {
-        sendStage++;
-    }
-}
-// ----------------------------------------------------------------------------
-void setupToSendAllItems() {
-    isSendingData = 1;
-    sendLapTimesIndex = 0;
-    sendStage = 0;
-    shouldSendSingleItem = 0;
-}
-// ----------------------------------------------------------------------------
-void setupToSendSingleItem(uint8_t itemId) {
-    isSendingData = 1;
-    sendLapTimesIndex = 0;
-    sendStage = itemId;
-    shouldSendSingleItem = 1;
-}
-// ----------------------------------------------------------------------------
-void handleSerialControlInput(uint8_t *controlData, uint8_t length) {
-    uint8_t controlByte = controlData[0];
-    uint8_t valueToSet;
-
-    if (length > 3) {
-        switch(controlByte) {
-            case CONTROL_SET_CHANNEL:
-                valueToSet = TO_BYTE(controlData[1]);
-                setChannel(valueToSet);
-                playClickTones();
-                addToSendQueue(SEND_CHANNEL);
-                addToSendQueue(SEND_FREQUENCY);
-                isConfigured = 1;
-                break;
-            case CONTROL_SET_BAND:
-                valueToSet = TO_BYTE(controlData[1]);
-                setBand(valueToSet);
-                playClickTones();
-                addToSendQueue(SEND_BAND);
-                addToSendQueue(SEND_FREQUENCY);
-                isConfigured = 1;
-                break;
-            case CONTROL_SET_MIN_LAP:
-                valueToSet = HEX_TO_BYTE(controlData[1], controlData[2]);
-                setMinLap(valueToSet);
-                playClickTones();
-                addToSendQueue(SEND_MIN_LAP_TIME);
-                isConfigured = 1;
-                break;
-            case CONTROL_SET_THRESHOLD: // set threshold
-                setThreshold(HEX_TO_UINT16(&controlData[1]));
-                addToSendQueue(SEND_THRESHOLD);
-                isConfigured = 1;
-                break;
-            case CONTROL_SET_FREQUENCY: // set frequency
-                setChannelModule(0, 0, HEX_TO_UINT16(&controlData[1]));
-                addToSendQueue(SEND_FREQUENCY);
-                isConfigured = 1;
-                break;
-        }
-    } else {
-        switch (controlByte) {
-            case CONTROL_START_RACE: // start race
-                lastMilliseconds = millis();
-                DEBUG_CODE(
-                    digitalHigh(serialTimerPin);
-                );
-                newLapIndex = 0;
-                isRaceStarted = 1;
-                allowEdgeGeneration = 0;
-                playStartRaceTones();
-                addToSendQueue(SEND_RACE_STATE);
-                isConfigured = 1;
-                break;
-            case CONTROL_END_CALIBRATE: // end calibration
-                calibrationMilliseconds = millis() - calibrationMilliseconds;
-                addToSendQueue(SEND_CALIBR_TIME);
-                break;
-            case CONTROL_START_CALIBRATE: // start calibration
-                calibrationMilliseconds = millis();
-                break;
-            case CONTROL_END_RACE: // end race
-                isRaceStarted = 0;
-                newLapIndex = 0;
-                playEndRaceTones();
-                addToSendQueue(SEND_RACE_STATE);
-                isConfigured = 1;
-                break;
-            case CONTROL_DEC_MIN_LAP: // decrease minLapTime
-                decMinLap();
-                playClickTones();
-                addToSendQueue(SEND_MIN_LAP_TIME);
-                isConfigured = 1;
-                break;
-            case CONTROL_INC_MIN_LAP: // increase minLapTime
-                incMinLap();
-                playClickTones();
-                addToSendQueue(SEND_MIN_LAP_TIME);
-                isConfigured = 1;
-                break;
-            case CONTROL_DEC_CHANNEL: // decrease channel
-                decChannel();
-                playClickTones();
-                addToSendQueue(SEND_CHANNEL);
-                addToSendQueue(SEND_FREQUENCY);
-                isConfigured = 1;
-                break;
-            case CONTROL_INC_CHANNEL: // increase channel
-                incChannel();
-                playClickTones();
-                addToSendQueue(SEND_CHANNEL);
-                addToSendQueue(SEND_FREQUENCY);
-                isConfigured = 1;
-                break;
-            case CONTROL_DEC_BAND: // decrease band
-                decBand();
-                playClickTones();
-                addToSendQueue(SEND_BAND);
-                addToSendQueue(SEND_FREQUENCY);
-                isConfigured = 1;
-                break;
-            case CONTROL_INC_BAND: // increase channel
-                incBand();
-                playClickTones();
-                addToSendQueue(SEND_BAND);
-                addToSendQueue(SEND_FREQUENCY);
-                isConfigured = 1;
-                break;
-            case CONTROL_DEC_THRESHOLD: // decrease threshold
-                decThreshold();
-                playClickTones();
-                addToSendQueue(SEND_THRESHOLD);
-                isConfigured = 1;
-                break;
-            case CONTROL_INC_THRESHOLD: // increase threshold
-                incThreshold();
-                playClickTones();
-                addToSendQueue(SEND_THRESHOLD);
-                isConfigured = 1;
-                break;
-            case CONTROL_SET_THRESHOLD: // set threshold
-                setThreshold(0);
-                addToSendQueue(SEND_THRESHOLD);
-                isConfigured = 1;
-                break;
-            case CONTROL_SET_SOUND: // set sound
-                isSoundEnabled = !isSoundEnabled;
-                if (!isSoundEnabled) {
-                    noTone(buzzerPin);
-                }
-                addToSendQueue(SEND_SOUND_STATE);
-                playClickTones();
-                isConfigured = 1;
-                break;
-            case CONTROL_MONITOR_ON: // start RSSI monitor
-                rssiMonitor = 1;
-                rssiMonitorDelayExpiration = 0;
-                addToSendQueue(SEND_MONITOR_STATE);
-                // don't play tones here because it suppresses race tone when used simultaneously
-                // playClickTones();
-                break;
-            case CONTROL_MONITOR_OFF: // stop RSSI monitor
-                rssiMonitor = 0;
-                addToSendQueue(SEND_MONITOR_STATE);
-                // don't play tones here because it suppresses race tone when used simultaneously
-                // playClickTones();
-                break;
-            case CONTROL_SET_SKIP_LAP0: // set valid first lap
-                shouldSkipFirstLap = !shouldSkipFirstLap;
-                addToSendQueue(SEND_LAP0_STATE);
-                playClickTones();
-                isConfigured = 1;
-                break;
-            case CONTROL_GET_VOLTAGE: //get battery voltage
-                voltage = readVoltage();
-                addToSendQueue(SEND_VOLTAGE);
-                break;
-            case CONTROL_DATA_REQUEST: // request all data
-                addToSendQueue(SEND_ALL_DEVICE_STATE);
-                break;
-        }
-    }
-}
-// ----------------------------------------------------------------------------
-void readSerialDataChunk () {
-    uint8_t availBytes = Serial.available();
-    if (availBytes && proxyBufDataSize == 0) {
-        uint8_t freeBufBytes = READ_BUFFER_SIZE - readBufFilledBytes;
-
-        //reset buffer if we couldn't find delimiter in its contents in prev step
-        if (freeBufBytes == 0) {
-            readBufFilledBytes = 0;
-            freeBufBytes = READ_BUFFER_SIZE;
-        }
-
-        //read minimum of "available to read" and "free place in buffer"
-        uint8_t canGetBytes = availBytes > freeBufBytes ? freeBufBytes : availBytes;
-        Serial.readBytes(&readBuf[readBufFilledBytes], canGetBytes);
-        readBufFilledBytes += canGetBytes;
-
-        //try finding a delimiter
-        uint8_t foundIdx = 255;
-        for (uint8_t i = 0; i < readBufFilledBytes; i++) {
-            if (readBuf[i] == SERIAL_DATA_DELIMITER) {
-                foundIdx = i;
-                break;
-            }
-        }
-
-        uint8_t shouldPassMsgFurther = 1;
-        //if delimiter found
-        if (foundIdx < READ_BUFFER_SIZE) {
-            switch (readBuf[0]) {
-                case 'R': //read data from module
-                    if (readBuf[1] == MODULE_ID_HEX) {
-                        //process input targeted for this device
-                        handleSerialControlInput(&readBuf[2], foundIdx);
-                        shouldPassMsgFurther = 0;
-                    }
-                    else if (readBuf[1] == '*') {
-                        //broadcast message. process in this module and pass further
-                        handleSerialControlInput(&readBuf[2], foundIdx);
-                    }
-                    break;
-                case 'C': //read calibration data for current module
-                    if (readBuf[1] == MODULE_ID_HEX) {
-                        timeCalibrationConst = HEX_TO_SIGNED_LONG(&readBuf[2]);
-                        isCalibrated = 1;
-                        shouldPassMsgFurther = 0;
-                        addToSendQueue(SEND_CALIBR_STATE);
-                        isConfigured = 1;
-                    }
-                    break;
-                case 'N':  //enumerate modules
-                    //process auto-enumeration request (save current number and pass next number further)
-                    MODULE_ID_HEX = readBuf[1];
-                    MODULE_ID = TO_BYTE(MODULE_ID_HEX);
-                    readBuf[1] = TO_HEX(MODULE_ID + 1);
-                    break;
-            }
-            if (shouldPassMsgFurther) {
-                memmove(proxyBuf, readBuf, foundIdx);
-                proxyBufDataSize = foundIdx;
-            }
-            //remove processed portion of data
-            memmove(readBuf, &readBuf[foundIdx+1], readBufFilledBytes - foundIdx+1);
-            readBufFilledBytes -= foundIdx+1;
-        }
-    }
-}
-// ----------------------------------------------------------------------------
-void sendProxyDataChunk () {
-    if (proxyBufDataSize && Serial.availableForWrite() > proxyBufDataSize) {
-        Serial.write(proxyBuf, proxyBufDataSize);
-        Serial.write(SERIAL_DATA_DELIMITER);
-        proxyBufDataSize = 0;
-    }
-}
-// ----------------------------------------------------------------------------
-void decMinLap() {
-    if (minLapTime > MIN_MIN_LAP_TIME) {
-        minLapTime--;
-    }
-}
-// ----------------------------------------------------------------------------
-void incMinLap() {
-    if (minLapTime < MAX_MIN_LAP_TIME) {
-        minLapTime++;
-    }
-}
-// ----------------------------------------------------------------------------
-void setMinLap(uint8_t mlt) {
-    if (mlt >= MIN_MIN_LAP_TIME && mlt <= MAX_MIN_LAP_TIME) {
-        minLapTime = mlt;
-    }
-}
-// ----------------------------------------------------------------------------
-void incChannel() {
-    if (channelIndex < 7) {
-        channelIndex++;
-    }
-    setChannelModule(channelIndex, bandIndex, 0);
-}
-// ----------------------------------------------------------------------------
-void decChannel() {
-    if (channelIndex > 0) {
-        channelIndex--;
-    }
-    setChannelModule(channelIndex, bandIndex, 0);
-}
-// ----------------------------------------------------------------------------
-void setChannel(uint8_t channel) {
-    if (channel >= 0 && channel <= 7) {
-        channelIndex = channel;
-        setChannelModule(channelIndex, bandIndex, 0);
-    }
-}
-// ----------------------------------------------------------------------------
-void incBand() {
-    if (bandIndex < MAX_BAND) {
-        bandIndex++;
-    }
-    setChannelModule(channelIndex, bandIndex, 0);
-}
-// ----------------------------------------------------------------------------
-void decBand() {
-    if (bandIndex > 0) {
-        bandIndex--;
-    }
-    setChannelModule(channelIndex, bandIndex, 0);
-}
-// ----------------------------------------------------------------------------
-void setBand(uint8_t band) {
-    if (band >= 0 && band <= MAX_BAND) {
-        bandIndex = band;
-        setChannelModule(channelIndex, bandIndex, 0);
-    }
-}
-// ----------------------------------------------------------------------------
-void incThreshold() {
-    if (rssiThreshold < RSSI_MAX) {
-        rssiThreshold++;
-    }
-}
-// ----------------------------------------------------------------------------
-void decThreshold() {
-    if (rssiThreshold > RSSI_MIN) {
-        rssiThreshold--;
-    }
-}
-// ----------------------------------------------------------------------------
-void setThreshold(uint16_t threshold) {
-    if (threshold != 0)
-    {
-      rssiThreshold = threshold;
-      playSetThresholdTones();
-    }
-    else {
-      if (rssiThreshold == 0) {
-          uint16_t median;
-          for(uint8_t i=0; i < THRESHOLD_ARRAY_SIZE; i++) {
-              rssiThresholdArray[i] = getFilteredRSSI();
-          }
-          sortArray(rssiThresholdArray, THRESHOLD_ARRAY_SIZE);
-          median = getMedian(rssiThresholdArray, THRESHOLD_ARRAY_SIZE);
-          if (median > MAGIC_THRESHOLD_REDUCE_CONSTANT) {
-              rssiThreshold = median - MAGIC_THRESHOLD_REDUCE_CONSTANT;
-              playSetThresholdTones();
-          }
-      }
-      else {
-          rssiThreshold = 0;
-          playClearThresholdTones();
-      }
-    }
-}
-
-// ----------------------------------------------------------------------------
-uint16_t getFilteredRSSI() {
-    rssiArr[0] = readRSSI();
-
-    // several-pass filter (need several passes because of integer artithmetics)
-    // it reduces possible max value by 1 with each iteration.
-    // e.g. if max rssi is 300, then after 5 filter stages it won't be greater than 295
-    for(uint8_t i=1; i<=FILTER_ITERATIONS; i++) {
-        rssiArr[i] = (rssiArr[i-1] + rssiArr[i]) >> 1;
-    }
-
-    return rssiArr[FILTER_ITERATIONS];
-}
-// ----------------------------------------------------------------------------
-void sortArray(uint16_t a[], uint16_t size) {
-    for(uint16_t i=0; i<(size-1); i++) {
-        for(uint16_t j=0; j<(size-(i+1)); j++) {
-                if(a[j] > a[j+1]) {
-                    uint16_t t = a[j];
-                    a[j] = a[j+1];
-                    a[j+1] = t;
-                }
-        }
-    }
-}
-// ----------------------------------------------------------------------------
-uint16_t getMedian(uint16_t a[], uint16_t size) {
-    return a[size/2];
-}
-// ----------------------------------------------------------------------------
-void gen_rising_edge(int pin) {
-    digitalHigh(pin); //this will open mosfet and pull the RasPi pin to GND
-    delayMicroseconds(10);
-    digitalLow(pin); // this will close mosfet and pull the RasPi pin to 3v3 -> Rising Edge
-}
-// ----------------------------------------------------------------------------
-uint16_t readRSSI() {
-    int rssiA = 0;
-
-    for (uint8_t i = 0; i < RSSI_READS; i++) {
-        rssiA += analogRead(rssiPinA);
-    }
-
-    rssiA = rssiA/RSSI_READS; // average of RSSI_READS readings
-    return rssiA;
-}
-// ----------------------------------------------------------------------------
-uint16_t readVoltage() {
-    int voltageA = 0;
-
-    for (uint8_t i = 0; i < VOLTAGE_READS; i++) {
-        voltageA += analogRead(voltagePinA);
-    }
-
-    voltageA = voltageA/VOLTAGE_READS; // average of RSSI_READS readings
-    return voltageA;
-}
+/**
+ * DIY RF Laptimer by Andrey Voroshkov (bshep)
+ * SPI driver based on fs_skyrf_58g-main.c by Simon Chambers
+ * fast ADC reading code is by "jmknapp" from Arduino forum
+ * fast port I/O code from http://masteringarduino.blogspot.com.by/2013/10/fastest-and-smallest-digitalread-and.html
+
+The MIT License (MIT)
+
+Copyright (c) 2016 by Andrey Voroshkov (bshep)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/*
+TODO: Resolve problem:
+    when CONTROL_DATA_REQUEST is being processed, i.e. all data items are sent in sequence,
+    and either external or external logic decides to send an individual item,
+    then the latter just breaks execution of CONTROL_DATA_REQUEST and data is not sent completely
+
+TODO: when implement send queue, there's possible optimization:
+    remove already existing items from queue
+*/
+
+// #define DEBUG
+
+#ifdef DEBUG
+    #define DEBUG_CODE(x) do { x } while(0)
+#else
+    #define DEBUG_CODE(x) do { } while(0)
+#endif
+
+uint8_t MODULE_ID = 0;
+uint8_t MODULE_ID_HEX = '0';
+
+#define SERIAL_DATA_DELIMITER '\n'
+
+#include <avr/pgmspace.h>
+
+#define BAUDRATE 115200
+
+const uint16_t musicNotes[] PROGMEM = { 523, 587, 659, 698, 784, 880, 988, 1046 };
+
+// number of analog rssi reads to average for the current check.
+// single analog read with FASTADC defined (see below) takes ~20us on 16MHz arduino
+// so e.g. 10 reads will take 200 ms, which gives resolution of 5 RSSI reads per ms,
+// this means that we can theoretically have 1ms timing accuracy :)
+#define RSSI_READS 5 // 5 should give about 10 000 readings per second
+
+// input control byte constants
+#define CONTROL_START_RACE      'R'
+#define CONTROL_END_RACE        'r'
+#define CONTROL_DEC_MIN_LAP     'm'
+#define CONTROL_INC_MIN_LAP     'M'
+#define CONTROL_DEC_CHANNEL     'c' //keeping because of compatiblity
+#define CONTROL_INC_CHANNEL     'C' //keeping because of compatiblity
+#define CONTROL_DEC_THRESHOLD   't'
+#define CONTROL_INC_THRESHOLD   'T'
+#define CONTROL_SET_THRESHOLD   'S' //it either triggers or sets an uint16
+#define CONTROL_SET_SOUND       'D'
+#define CONTROL_DATA_REQUEST    'A'
+#define CONTROL_INC_BAND        'B' //keeping because of compatiblity
+#define CONTROL_DEC_BAND        'b' //keeping because of compatiblity
+#define CONTROL_START_CALIBRATE 'I'
+#define CONTROL_END_CALIBRATE   'i'
+#define CONTROL_MONITOR_ON      'V'
+#define CONTROL_MONITOR_OFF     'v'
+#define CONTROL_SET_SKIP_LAP0   'F'
+#define CONTROL_GET_VOLTAGE     'Y'
+// input control byte constants for uint16 "set value" commands
+#define CONTROL_SET_FREQUENCY   'O'
+// input control byte constants for long "set value" commands
+#define CONTROL_SET_MIN_LAP     'L'
+#define CONTROL_SET_CHANNEL     'H'
+#define CONTROL_SET_BAND        'N'
+
+// output id byte constants
+#define RESPONSE_CHANNEL        'C'
+#define RESPONSE_RACE_STATE     'R'
+#define RESPONSE_MIN_LAP_TIME   'M'
+#define RESPONSE_THRESHOLD      'T'
+#define RESPONSE_CURRENT_RSSI   'S'
+#define RESPONSE_LAPTIME        'L'
+#define RESPONSE_SOUND_STATE    'D'
+#define RESPONSE_BAND           'B'
+#define RESPONSE_CALIBR_TIME    'I'
+#define RESPONSE_CALIBR_STATE   'i'
+#define RESPONSE_MONITOR_STATE  'V'
+#define RESPONSE_LAP0_STATE     'F'
+#define RESPONSE_END_SEQUENCE   'X'
+#define RESPONSE_IS_CONFIGURED  'P'
+#define RESPONSE_VOLTAGE        'Y'
+#define RESPONSE_FREQUENCY      'O'
+
+// send item byte constants
+// Must correspond to sequence of numbers used in "send data" switch statement
+// Subsequent items starting from 0 participate in "send all data" response
+#define SEND_CHANNEL        0
+#define SEND_RACE_STATE     1
+#define SEND_MIN_LAP_TIME   2
+#define SEND_THRESHOLD      3
+#define SEND_ALL_LAPTIMES   4
+#define SEND_SOUND_STATE    5
+#define SEND_BAND           6
+#define SEND_CALIBR_STATE   7
+#define SEND_MONITOR_STATE  8
+#define SEND_LAP0_STATE     9
+#define SEND_IS_CONFIGURED  10
+#define SEND_FREQUENCY      11
+#define SEND_END_SEQUENCE   12
+// following items don't participate in "send all items" response
+#define SEND_LAST_LAPTIMES  100
+#define SEND_CALIBR_TIME    101
+#define SEND_CURRENT_RSSI   102
+#define SEND_VOLTAGE        103
+// special item that sends all subsequent items from 0 (see above)
+#define SEND_ALL_DEVICE_STATE 255
+
+//----- RSSI --------------------------------------
+#define FILTER_ITERATIONS 5
+uint16_t rssiArr[FILTER_ITERATIONS + 1];
+uint16_t rssiThreshold = 190;
+uint16_t rssi;
+
+#define RSSI_MAX 1024
+#define RSSI_MIN 0
+#define MAGIC_THRESHOLD_REDUCE_CONSTANT 2
+#define THRESHOLD_ARRAY_SIZE  100
+uint16_t rssiThresholdArray[THRESHOLD_ARRAY_SIZE];
+
+#define MAX_RSSI_MONITOR_DELAY_CYCLES 1000 //each 100ms, if cycle takes 100us
+
+//----- Voltage monitoring -------------------------
+#define VOLTAGE_READS 3 //get average of VOLTAGE_READS readings
+
+// analog readings less than VOLTAGE_ZERO_THRESHOLD value won't be sent.
+// This way entire chorus device will send voltages only from devices that are attached to LiPo
+// So if single Solo device has LiPo attached, then broadcast voltage request to
+// entire Chorus device will produce a single voltage response.
+#define VOLTAGE_ZERO_THRESHOLD 100
+
+uint16_t voltage = 0;
+
+//----- Send Queue ---------------------------------
+#define SEND_QUEUE_MAXLEN 20
+uint8_t sendQueue[SEND_QUEUE_MAXLEN];
+uint8_t sendQueueHead = 0;
+uint8_t sendQueueTail = 0;
+uint8_t isSendQueueFull = 0;
+
+//----- Lap timings--------------------------------
+uint32_t lastMilliseconds = 0;
+#define MIN_MIN_LAP_TIME 1 //seconds
+#define MAX_MIN_LAP_TIME 60 //seconds
+uint8_t minLapTime = 5; //seconds
+#define MAX_LAPS 100
+uint32_t lapTimes[MAX_LAPS];
+
+//----- Calibration -------------------------------
+uint8_t isCalibrated = 0;
+uint32_t calibrationMilliseconds = 0;
+
+// Usage of signed int calibration constant:
+// calibratedMs = readMs + readMs/timeCalibrationConst
+int32_t timeCalibrationConst = 0;
+
+//----- other globals------------------------------
+uint8_t allowEdgeGeneration = 0;
+uint8_t channelIndex = 0;
+uint8_t bandIndex = 0;
+uint8_t isRaceStarted = 0;
+uint8_t isSoundEnabled = 1;
+uint8_t isConfigured = 0; //changes to 1 if any input changes the state of the device. it will mean that externally stored preferences should not be applied
+uint8_t rssiMonitor = 0;
+uint8_t newLapIndex = 0;
+uint8_t shouldSkipFirstLap = 1; //start table is before the laptimer, so first lap is not a full-fledged lap (i.e. don't respect min-lap-time for the very first lap)
+uint8_t isSendingData = 0;
+uint8_t sendStage = 0;
+uint8_t sendLapTimesIndex = 0;
+uint8_t sendLastLapIndex = 0;
+uint8_t shouldSendSingleItem = 0;
+uint8_t lastLapsNotSent = 0;
+uint16_t rssiMonitorDelayExpiration = 0;
+uint16_t frequency = 0;
+
+//----- read/write bufs ---------------------------
+#define READ_BUFFER_SIZE 20
+uint8_t readBuf[READ_BUFFER_SIZE];
+uint8_t proxyBuf[READ_BUFFER_SIZE];
+uint8_t readBufFilledBytes = 0;
+uint8_t proxyBufDataSize = 0;
+
+// ----------------------------------------------------------------------------
+#include "fastReadWrite.h"
+#include "fastADC.h"
+#include "pinAssignments.h"
+#include "channels.h"
+#include "sendSerialHex.h"
+#include "rx5808spi.h"
+#include "sounds.h"
+
+// ----------------------------------------------------------------------------
+void setup() {
+    // initialize digital pin 13 LED as an output.
+    pinMode(led, OUTPUT);
+    digitalHigh(led);
+
+    // init buzzer pin
+    pinMode(buzzerPin, OUTPUT);
+
+    //init raspberrypi interrupt generator pin
+    pinMode(pinRaspiInt, OUTPUT);
+    digitalLow(pinRaspiInt);
+
+    // SPI pins for RX control
+    setupSPIpins();
+
+    // set the channel as soon as we can
+    // faster boot up times :)
+    setChannelModule(channelIndex, bandIndex, 0);
+    Serial.begin(BAUDRATE);
+
+    initFastADC();
+
+    // Setup Done - Turn Status LED off.
+    digitalLow(led);
+
+    DEBUG_CODE(
+        pinMode(serialTimerPin, OUTPUT);
+        pinMode(loopTimerPin, OUTPUT);
+    );
+}
+// ----------------------------------------------------------------------------
+void loop() {
+    DEBUG_CODE(
+        digitalToggle(loopTimerPin);
+    );
+
+    rssi = getFilteredRSSI();
+
+    // check rssi threshold to identify when drone finishes the lap
+    if (rssiThreshold > 0) { // threshold = 0 means that we don't check rssi values
+        if(rssi > rssiThreshold) { // rssi above the threshold - drone is near
+            if (allowEdgeGeneration) {  // we haven't fired event for this drone proximity case yet
+                allowEdgeGeneration = 0;
+                gen_rising_edge(pinRaspiInt);  //generate interrupt for EasyRaceLapTimer software
+
+                uint32_t now = millis();
+                uint32_t diff = now - lastMilliseconds;
+                if (timeCalibrationConst) {
+                    diff = diff + (int32_t)diff/timeCalibrationConst;
+                }
+                if (isRaceStarted) { // if we're within the race, then log lap time
+                    if (diff > minLapTime*1000 || (shouldSkipFirstLap && newLapIndex == 0)) { // if minLapTime haven't passed since last lap, then it's probably false alarm
+                        digitalLow(led);
+                        if (newLapIndex < MAX_LAPS-1) { // log time only if there are slots available
+                            lapTimes[newLapIndex] = diff;
+                            newLapIndex++;
+                            lastLapsNotSent++;
+                            addToSendQueue(SEND_LAST_LAPTIMES);
+                        }
+                        lastMilliseconds = now;
+                        playLapTones(); // during the race play tone sequence even if no more laps can be logged
+                    }
+                }
+                else {
+                    playLapTones(); // if not within the race, then play once per case
+                }
+            }
+        }
+        else  {
+            allowEdgeGeneration = 1; // we're below the threshold, be ready to catch another case
+            digitalHigh(led);
+        }
+    }
+
+    readSerialDataChunk();
+
+    sendProxyDataChunk();
+
+    // send data chunk through Serial
+
+    if (isSendingData) {
+        switch (sendStage) {
+            case 0: // SEND_CHANNEL
+                if (send4BitsToSerial(RESPONSE_CHANNEL, channelIndex)) {
+                    onItemSent();
+                }
+                break;
+            case 1: // SEND_RACE_STATE
+                if (send4BitsToSerial(RESPONSE_RACE_STATE, isRaceStarted)) {
+                    DEBUG_CODE(
+                        digitalLow(serialTimerPin);
+                    );
+                    onItemSent();
+                }
+                break;
+            case 2: // SEND_MIN_LAP_TIME
+                if (sendByteToSerial(RESPONSE_MIN_LAP_TIME, minLapTime)) {
+                    onItemSent();
+                }
+                break;
+            case 3: // SEND_THRESHOLD
+                if (sendIntToSerial(RESPONSE_THRESHOLD, rssiThreshold)) {
+                    onItemSent();
+                }
+                break;
+            case 4: // SEND_ALL_LAPTIMES
+                if (sendLapTimesIndex < newLapIndex) {
+                    if (sendLaptimeToSerial(RESPONSE_LAPTIME, sendLapTimesIndex, lapTimes[sendLapTimesIndex])) {
+                        sendLapTimesIndex++;
+                    }
+                }
+                else {
+                    onItemSent();
+                }
+                break;
+            case 5: // SEND_SOUND_STATE
+                if (send4BitsToSerial(RESPONSE_SOUND_STATE, isSoundEnabled)) {
+                    onItemSent();
+                }
+                break;
+            case 6: // SEND_BAND
+                if (send4BitsToSerial(RESPONSE_BAND, bandIndex)) {
+                    onItemSent();
+                }
+                break;
+            case 7: // SEND_CALIBR_STATE
+                if (send4BitsToSerial(RESPONSE_CALIBR_STATE, isCalibrated)) {
+                    onItemSent();
+                }
+                break;
+            case 8: // SEND_MONITOR_STATE
+                if (send4BitsToSerial(RESPONSE_MONITOR_STATE, rssiMonitor)) {
+                    onItemSent();
+                }
+                break;
+            case 9: // SEND_LAP0_STATE
+                if (send4BitsToSerial(RESPONSE_LAP0_STATE, shouldSkipFirstLap)) {
+                    onItemSent();
+                }
+                break;
+            case 10: // SEND_IS_CONFIGURED
+                if (send4BitsToSerial(RESPONSE_IS_CONFIGURED, isConfigured)) {
+                    onItemSent();
+                }
+                break;
+            case 11: // SEND_FREQUENCY
+                if (sendIntToSerial(RESPONSE_FREQUENCY, frequency)) {
+                    onItemSent();
+                }
+                break;
+            // Below is a termination case, to notify that data for CONTROL_DATA_REQUEST is over.
+            // Must be the last item in the sequence!
+            case 12: // SEND_END_SEQUENCE
+                if (send4BitsToSerial(RESPONSE_END_SEQUENCE, 1)) {
+                    onItemSent();
+                    isSendingData = 0;
+                    shouldSendSingleItem = 1;
+                }
+                break;
+
+            //--------------------------------------------------------------------------------------
+            // Here is the gap in sequence between items that are sent as response to
+            // "CONTROL_DATA_REQUEST" and items that are only sent individually
+            //--------------------------------------------------------------------------------------
+
+            case 100: // SEND_LAST_LAPTIMES
+                uint8_t idx;
+                idx = newLapIndex - lastLapsNotSent;
+                if (lastLapsNotSent == 0) {
+                    break;
+                }
+                if (sendLaptimeToSerial(RESPONSE_LAPTIME, idx, lapTimes[idx])) {
+                    lastLapsNotSent--;
+                    onItemSent();
+                }
+                break;
+            case 101: // SEND_CALIBR_TIME
+                if (sendLongToSerial(RESPONSE_CALIBR_TIME, calibrationMilliseconds)) {
+                    onItemSent();
+                }
+                break;
+            case 102: // SEND_CURRENT_RSSI
+                if (sendIntToSerial(RESPONSE_CURRENT_RSSI, rssi)) {
+                    onItemSent();
+                }
+                break;
+            case 103: // SEND_VOLTAGE
+                if (voltage > VOLTAGE_ZERO_THRESHOLD) {
+                    if (sendIntToSerial(RESPONSE_VOLTAGE, voltage)) {
+                        onItemSent();
+                    }
+                } else {
+                    onItemSent();
+                }
+                break;
+            default:
+                isSendingData = 0;
+                DEBUG_CODE(
+                    digitalLow(serialTimerPin);
+                );
+        }
+    }
+
+    if (!isSendingData && !isQueueEmpty()) {
+        uint8_t item = getFromSendQueue();
+        if (item == SEND_ALL_DEVICE_STATE) {
+            setupToSendAllItems();
+        } else {
+            setupToSendSingleItem(item);
+        }
+    }
+
+    if (rssiMonitor) {
+        rssiMonitorDelayExpiration++;
+        if (rssiMonitorDelayExpiration > MAX_RSSI_MONITOR_DELAY_CYCLES) {
+            addToSendQueue(SEND_CURRENT_RSSI);
+            rssiMonitorDelayExpiration = 0;
+        }
+    }
+
+    if (isSoundEnabled && playSound) {
+        if (playStartTime == 0) {
+            tone(buzzerPin,curToneSeq[curToneIndex]);
+            playStartTime = millis();
+        }
+        uint32_t dur = millis() - playStartTime;
+        if (dur >= curToneSeq[curDurIndex]) {
+            if (curDurIndex >= lastToneSeqIndex) {
+                noTone(buzzerPin);
+                playSound = 0;
+            } else {
+                curToneIndex += 2;
+                curDurIndex += 2;
+                tone(buzzerPin, curToneSeq[curToneIndex]);
+                playStartTime = millis();
+            }
+        }
+    }
+}
+// ----------------------------------------------------------------------------
+uint8_t addToSendQueue(uint8_t item) {
+    if (isSendQueueFull) {
+        return 0; // couldn't add
+    }
+    sendQueue[sendQueueTail] = item;
+    sendQueueTail++;
+    if (sendQueueTail >= SEND_QUEUE_MAXLEN) {
+        sendQueueTail = 0;
+    }
+    if (sendQueueTail == sendQueueHead) {
+        isSendQueueFull = 1;
+    }
+    return 1; // successfully added
+}
+// ----------------------------------------------------------------------------
+uint8_t getFromSendQueue() {
+    // don't check for Q emptiness because it's expected to be done before this function call
+
+    // if (isQueueEmpty()) {
+    //     return 0; // couldn't read - queue is empty
+    // }
+    uint8_t result = sendQueue[sendQueueHead];
+    sendQueueHead++;
+    if (sendQueueHead >= SEND_QUEUE_MAXLEN) {
+        sendQueueHead = 0;
+    }
+    isSendQueueFull = 0;
+
+    return result;
+}
+// ----------------------------------------------------------------------------
+uint8_t isQueueEmpty() {
+    return (sendQueueHead == sendQueueTail && !isSendQueueFull);
+}
+// ----------------------------------------------------------------------------
+void onItemSent() {
+    if (shouldSendSingleItem) {
+        isSendingData = 0;
+    } else {
+        sendStage++;
+    }
+}
+// ----------------------------------------------------------------------------
+void setupToSendAllItems() {
+    isSendingData = 1;
+    sendLapTimesIndex = 0;
+    sendStage = 0;
+    shouldSendSingleItem = 0;
+}
+// ----------------------------------------------------------------------------
+void setupToSendSingleItem(uint8_t itemId) {
+    isSendingData = 1;
+    sendLapTimesIndex = 0;
+    sendStage = itemId;
+    shouldSendSingleItem = 1;
+}
+// ----------------------------------------------------------------------------
+void handleSerialControlInput(uint8_t *controlData, uint8_t length) {
+    uint8_t controlByte = controlData[0];
+    uint8_t valueToSet;
+
+    if (length > 3) {
+        switch(controlByte) {
+            case CONTROL_SET_CHANNEL:
+                valueToSet = TO_BYTE(controlData[1]);
+                setChannel(valueToSet);
+                playClickTones();
+                addToSendQueue(SEND_CHANNEL);
+                addToSendQueue(SEND_FREQUENCY);
+                isConfigured = 1;
+                break;
+            case CONTROL_SET_BAND:
+                valueToSet = TO_BYTE(controlData[1]);
+                setBand(valueToSet);
+                playClickTones();
+                addToSendQueue(SEND_BAND);
+                addToSendQueue(SEND_FREQUENCY);
+                isConfigured = 1;
+                break;
+            case CONTROL_SET_MIN_LAP:
+                valueToSet = HEX_TO_BYTE(controlData[1], controlData[2]);
+                setMinLap(valueToSet);
+                playClickTones();
+                addToSendQueue(SEND_MIN_LAP_TIME);
+                isConfigured = 1;
+                break;
+            case CONTROL_SET_THRESHOLD: // set threshold
+                setThreshold(HEX_TO_UINT16(&controlData[1]));
+                addToSendQueue(SEND_THRESHOLD);
+                isConfigured = 1;
+                break;
+            case CONTROL_SET_FREQUENCY: // set frequency
+                setChannelModule(0, 0, HEX_TO_UINT16(&controlData[1]));
+                addToSendQueue(SEND_FREQUENCY);
+                isConfigured = 1;
+                break;
+        }
+    } else {
+        switch (controlByte) {
+            case CONTROL_START_RACE: // start race
+                lastMilliseconds = millis();
+                DEBUG_CODE(
+                    digitalHigh(serialTimerPin);
+                );
+                newLapIndex = 0;
+                isRaceStarted = 1;
+                allowEdgeGeneration = 0;
+                playStartRaceTones();
+                addToSendQueue(SEND_RACE_STATE);
+                isConfigured = 1;
+                break;
+            case CONTROL_END_CALIBRATE: // end calibration
+                calibrationMilliseconds = millis() - calibrationMilliseconds;
+                addToSendQueue(SEND_CALIBR_TIME);
+                break;
+            case CONTROL_START_CALIBRATE: // start calibration
+                calibrationMilliseconds = millis();
+                break;
+            case CONTROL_END_RACE: // end race
+                isRaceStarted = 0;
+                newLapIndex = 0;
+                playEndRaceTones();
+                addToSendQueue(SEND_RACE_STATE);
+                isConfigured = 1;
+                break;
+            case CONTROL_DEC_MIN_LAP: // decrease minLapTime
+                decMinLap();
+                playClickTones();
+                addToSendQueue(SEND_MIN_LAP_TIME);
+                isConfigured = 1;
+                break;
+            case CONTROL_INC_MIN_LAP: // increase minLapTime
+                incMinLap();
+                playClickTones();
+                addToSendQueue(SEND_MIN_LAP_TIME);
+                isConfigured = 1;
+                break;
+            case CONTROL_DEC_CHANNEL: // decrease channel
+                decChannel();
+                playClickTones();
+                addToSendQueue(SEND_CHANNEL);
+                addToSendQueue(SEND_FREQUENCY);
+                isConfigured = 1;
+                break;
+            case CONTROL_INC_CHANNEL: // increase channel
+                incChannel();
+                playClickTones();
+                addToSendQueue(SEND_CHANNEL);
+                addToSendQueue(SEND_FREQUENCY);
+                isConfigured = 1;
+                break;
+            case CONTROL_DEC_BAND: // decrease band
+                decBand();
+                playClickTones();
+                addToSendQueue(SEND_BAND);
+                addToSendQueue(SEND_FREQUENCY);
+                isConfigured = 1;
+                break;
+            case CONTROL_INC_BAND: // increase channel
+                incBand();
+                playClickTones();
+                addToSendQueue(SEND_BAND);
+                addToSendQueue(SEND_FREQUENCY);
+                isConfigured = 1;
+                break;
+            case CONTROL_DEC_THRESHOLD: // decrease threshold
+                decThreshold();
+                playClickTones();
+                addToSendQueue(SEND_THRESHOLD);
+                isConfigured = 1;
+                break;
+            case CONTROL_INC_THRESHOLD: // increase threshold
+                incThreshold();
+                playClickTones();
+                addToSendQueue(SEND_THRESHOLD);
+                isConfigured = 1;
+                break;
+            case CONTROL_SET_THRESHOLD: // set threshold
+                setThreshold(0);
+                addToSendQueue(SEND_THRESHOLD);
+                isConfigured = 1;
+                break;
+            case CONTROL_SET_SOUND: // set sound
+                isSoundEnabled = !isSoundEnabled;
+                if (!isSoundEnabled) {
+                    noTone(buzzerPin);
+                }
+                addToSendQueue(SEND_SOUND_STATE);
+                playClickTones();
+                isConfigured = 1;
+                break;
+            case CONTROL_MONITOR_ON: // start RSSI monitor
+                rssiMonitor = 1;
+                rssiMonitorDelayExpiration = 0;
+                addToSendQueue(SEND_MONITOR_STATE);
+                // don't play tones here because it suppresses race tone when used simultaneously
+                // playClickTones();
+                break;
+            case CONTROL_MONITOR_OFF: // stop RSSI monitor
+                rssiMonitor = 0;
+                addToSendQueue(SEND_MONITOR_STATE);
+                // don't play tones here because it suppresses race tone when used simultaneously
+                // playClickTones();
+                break;
+            case CONTROL_SET_SKIP_LAP0: // set valid first lap
+                shouldSkipFirstLap = !shouldSkipFirstLap;
+                addToSendQueue(SEND_LAP0_STATE);
+                playClickTones();
+                isConfigured = 1;
+                break;
+            case CONTROL_GET_VOLTAGE: //get battery voltage
+                voltage = readVoltage();
+                addToSendQueue(SEND_VOLTAGE);
+                break;
+            case CONTROL_DATA_REQUEST: // request all data
+                addToSendQueue(SEND_ALL_DEVICE_STATE);
+                break;
+        }
+    }
+}
+// ----------------------------------------------------------------------------
+void readSerialDataChunk () {
+    uint8_t availBytes = Serial.available();
+    if (availBytes && proxyBufDataSize == 0) {
+        uint8_t freeBufBytes = READ_BUFFER_SIZE - readBufFilledBytes;
+
+        //reset buffer if we couldn't find delimiter in its contents in prev step
+        if (freeBufBytes == 0) {
+            readBufFilledBytes = 0;
+            freeBufBytes = READ_BUFFER_SIZE;
+        }
+
+        //read minimum of "available to read" and "free place in buffer"
+        uint8_t canGetBytes = availBytes > freeBufBytes ? freeBufBytes : availBytes;
+        Serial.readBytes(&readBuf[readBufFilledBytes], canGetBytes);
+        readBufFilledBytes += canGetBytes;
+
+        //try finding a delimiter
+        uint8_t foundIdx = 255;
+        for (uint8_t i = 0; i < readBufFilledBytes; i++) {
+            if (readBuf[i] == SERIAL_DATA_DELIMITER) {
+                foundIdx = i;
+                break;
+            }
+        }
+
+        uint8_t shouldPassMsgFurther = 1;
+        //if delimiter found
+        if (foundIdx < READ_BUFFER_SIZE) {
+            switch (readBuf[0]) {
+                case 'R': //read data from module
+                    if (readBuf[1] == MODULE_ID_HEX) {
+                        //process input targeted for this device
+                        handleSerialControlInput(&readBuf[2], foundIdx);
+                        shouldPassMsgFurther = 0;
+                    }
+                    else if (readBuf[1] == '*') {
+                        //broadcast message. process in this module and pass further
+                        handleSerialControlInput(&readBuf[2], foundIdx);
+                    }
+                    break;
+                case 'C': //read calibration data for current module
+                    if (readBuf[1] == MODULE_ID_HEX) {
+                        timeCalibrationConst = HEX_TO_SIGNED_LONG(&readBuf[2]);
+                        isCalibrated = 1;
+                        shouldPassMsgFurther = 0;
+                        addToSendQueue(SEND_CALIBR_STATE);
+                        isConfigured = 1;
+                    }
+                    break;
+                case 'N':  //enumerate modules
+                    //process auto-enumeration request (save current number and pass next number further)
+                    MODULE_ID_HEX = readBuf[1];
+                    MODULE_ID = TO_BYTE(MODULE_ID_HEX);
+                    readBuf[1] = TO_HEX(MODULE_ID + 1);
+                    break;
+            }
+            if (shouldPassMsgFurther) {
+                memmove(proxyBuf, readBuf, foundIdx);
+                proxyBufDataSize = foundIdx;
+            }
+            //remove processed portion of data
+            memmove(readBuf, &readBuf[foundIdx+1], readBufFilledBytes - foundIdx+1);
+            readBufFilledBytes -= foundIdx+1;
+        }
+    }
+}
+// ----------------------------------------------------------------------------
+void sendProxyDataChunk () {
+    if (proxyBufDataSize && Serial.availableForWrite() > proxyBufDataSize) {
+        Serial.write(proxyBuf, proxyBufDataSize);
+        Serial.write(SERIAL_DATA_DELIMITER);
+        proxyBufDataSize = 0;
+    }
+}
+// ----------------------------------------------------------------------------
+void decMinLap() {
+    if (minLapTime > MIN_MIN_LAP_TIME) {
+        minLapTime--;
+    }
+}
+// ----------------------------------------------------------------------------
+void incMinLap() {
+    if (minLapTime < MAX_MIN_LAP_TIME) {
+        minLapTime++;
+    }
+}
+// ----------------------------------------------------------------------------
+void setMinLap(uint8_t mlt) {
+    if (mlt >= MIN_MIN_LAP_TIME && mlt <= MAX_MIN_LAP_TIME) {
+        minLapTime = mlt;
+    }
+}
+// ----------------------------------------------------------------------------
+void incChannel() {
+    if (channelIndex < 7) {
+        channelIndex++;
+    }
+    setChannelModule(channelIndex, bandIndex, 0);
+}
+// ----------------------------------------------------------------------------
+void decChannel() {
+    if (channelIndex > 0) {
+        channelIndex--;
+    }
+    setChannelModule(channelIndex, bandIndex, 0);
+}
+// ----------------------------------------------------------------------------
+void setChannel(uint8_t channel) {
+    if (channel >= 0 && channel <= 7) {
+        channelIndex = channel;
+        setChannelModule(channelIndex, bandIndex, 0);
+    }
+}
+// ----------------------------------------------------------------------------
+void incBand() {
+    if (bandIndex < MAX_BAND) {
+        bandIndex++;
+    }
+    setChannelModule(channelIndex, bandIndex, 0);
+}
+// ----------------------------------------------------------------------------
+void decBand() {
+    if (bandIndex > 0) {
+        bandIndex--;
+    }
+    setChannelModule(channelIndex, bandIndex, 0);
+}
+// ----------------------------------------------------------------------------
+void setBand(uint8_t band) {
+    if (band >= 0 && band <= MAX_BAND) {
+        bandIndex = band;
+        setChannelModule(channelIndex, bandIndex, 0);
+    }
+}
+// ----------------------------------------------------------------------------
+void incThreshold() {
+    if (rssiThreshold < RSSI_MAX) {
+        rssiThreshold++;
+    }
+}
+// ----------------------------------------------------------------------------
+void decThreshold() {
+    if (rssiThreshold > RSSI_MIN) {
+        rssiThreshold--;
+    }
+}
+// ----------------------------------------------------------------------------
+void setThreshold(uint16_t threshold) {
+    if (threshold != 0)
+    {
+      rssiThreshold = threshold;
+      playSetThresholdTones();
+    }
+    else {
+      if (rssiThreshold == 0) {
+          uint16_t median;
+          for(uint8_t i=0; i < THRESHOLD_ARRAY_SIZE; i++) {
+              rssiThresholdArray[i] = getFilteredRSSI();
+          }
+          sortArray(rssiThresholdArray, THRESHOLD_ARRAY_SIZE);
+          median = getMedian(rssiThresholdArray, THRESHOLD_ARRAY_SIZE);
+          if (median > MAGIC_THRESHOLD_REDUCE_CONSTANT) {
+              rssiThreshold = median - MAGIC_THRESHOLD_REDUCE_CONSTANT;
+              playSetThresholdTones();
+          }
+      }
+      else {
+          rssiThreshold = 0;
+          playClearThresholdTones();
+      }
+    }
+}
+
+// ----------------------------------------------------------------------------
+uint16_t getFilteredRSSI() {
+    rssiArr[0] = readRSSI();
+
+    // several-pass filter (need several passes because of integer artithmetics)
+    // it reduces possible max value by 1 with each iteration.
+    // e.g. if max rssi is 300, then after 5 filter stages it won't be greater than 295
+    for(uint8_t i=1; i<=FILTER_ITERATIONS; i++) {
+        rssiArr[i] = (rssiArr[i-1] + rssiArr[i]) >> 1;
+    }
+
+    return rssiArr[FILTER_ITERATIONS];
+}
+// ----------------------------------------------------------------------------
+void sortArray(uint16_t a[], uint16_t size) {
+    for(uint16_t i=0; i<(size-1); i++) {
+        for(uint16_t j=0; j<(size-(i+1)); j++) {
+                if(a[j] > a[j+1]) {
+                    uint16_t t = a[j];
+                    a[j] = a[j+1];
+                    a[j+1] = t;
+                }
+        }
+    }
+}
+// ----------------------------------------------------------------------------
+uint16_t getMedian(uint16_t a[], uint16_t size) {
+    return a[size/2];
+}
+// ----------------------------------------------------------------------------
+void gen_rising_edge(int pin) {
+    digitalHigh(pin); //this will open mosfet and pull the RasPi pin to GND
+    delayMicroseconds(10);
+    digitalLow(pin); // this will close mosfet and pull the RasPi pin to 3v3 -> Rising Edge
+}
+// ----------------------------------------------------------------------------
+uint16_t readRSSI() {
+    int rssiA = 0;
+
+    for (uint8_t i = 0; i < RSSI_READS; i++) {
+        rssiA += analogRead(rssiPinA);
+    }
+
+    rssiA = rssiA/RSSI_READS; // average of RSSI_READS readings
+    return rssiA;
+}
+// ----------------------------------------------------------------------------
+uint16_t readVoltage() {
+    int voltageA = 0;
+
+    for (uint8_t i = 0; i < VOLTAGE_READS; i++) {
+        voltageA += analogRead(voltagePinA);
+    }
+
+    voltageA = voltageA/VOLTAGE_READS; // average of RSSI_READS readings
+    return voltageA;
+}


Root page should be of type text/html (#6776)

In order to be displayed properly by a browser the HTML should be returned as text/html.

--- libraries/ESP8266WebServer/examples/PostServer/PostServer.ino
+++ libraries/ESP8266WebServer/examples/PostServer/PostServer.ino
@@ -38,7 +38,7 @@

 void handleRoot() {
   digitalWrite(led, 1);
-  server.send(200, "text/plain", postForms);
+  server.send(200, "text/html", postForms);
   digitalWrite(led, 0);
 }


led the fuck outtta her

led the fuck outtta her

--- board/board.ino
+++ board/board.ino
@@ -30,26 +30,39 @@
 long currentBitOffCount;
 int bits;

-int ledPin = 0;
+int ledR = 9;
+int ledG = 10;
+int ledB = 11;
+
+int rCycle = 128;
+int gCycle = 255;
+int bCycle = 128;

 void setup() {
   // put your setup code here, to run once:
   Serial.begin(9600);
   Serial.println("ok");
-
-  pinMode(ledPin, OUTPUT);
-  digitalWrite(ledPin, HIGH);
+
+  pinMode(ledR,   OUTPUT);
+  pinMode(ledG, OUTPUT);
+  pinMode(ledB,  OUTPUT);
+

   AFMS.begin();  // create with the default frequency 1.6KHz
-
-  analogWrite(2, 255);
-  analogWrite(3, 255);
-  analogWrite(4, 255);
 }

 void loop() {
   // put your main code here, to run repeatedly:
   readInput();
+  analogWrite(ledR, rCycle);
+  analogWrite(ledG, gCycle);
+  analogWrite(ledB, bCycle);
+
+  rCycle = (rCycle + 2) % 256;
+  gCycle = (gCycle + 3) % 256;
+  bCycle = (bCycle + 1) % 256;
+
+  delay(10);
 }


including lines to GPS

including lines to GPS

--- ctd/ctd.ino
+++ ctd/ctd.ino
@@ -19,13 +19,26 @@
 void setup(){
   pinMode(rLed, OUTPUT);
   pinMode(gLed, OUTPUT);
+  pinMode(CS_PIN, OUTPUT);
   Serial.begin(9600);

   if (!SD.begin(CS_PIN)) {
     Serial.println(F("begin failed"));
     digitalWrite(rLed, HIGH);
+    delay(2000);
+    digitalWrite(rLed, LOW);
     return;
   }
+
+  // Insert here condition to GPS data
+  // if(!GPS.begin(GPS_PIN){
+  //   delay(1000);
+  //   digitalWrite(rLed, HIGH);
+  //   digitalWrite(gLed, HIGH);
+  //   delay(2000);
+  //   return;
+  // }
+
   while (SD.exists(fileName)) {
     if (fileName[BASE_NAME_SIZE + 1] != '9') {
       fileName[BASE_NAME_SIZE + 1]++;


Added floor name to topic

Added floor name to topic

--- sensor/ZoneControlSensor.ino
+++ sensor/ZoneControlSensor.ino
@@ -25,6 +25,7 @@

 //MQTT variables
 MQTT::Client<IPStack, Countdown> mqttClient = MQTT::Client<IPStack, Countdown>(ipstack, timeout);
+char floorName[21] = "ground"; //floor of room within building
 char room[21] = "roman"; //name of room within building

 //Room State
@@ -68,6 +69,8 @@
 		//dynamically set the room's state for will message topic
 		char willTopicBuffer[100];
 		strcpy(willTopicBuffer, mainTopic);
+		strcat(willTopicBuffer, "/");
+		strcat(willTopicBuffer, floorName);
 		strcat(willTopicBuffer, "/");
 		strcat(willTopicBuffer, room);
 		strcat(willTopicBuffer, "/state");
@@ -121,6 +124,8 @@

 	//store copy of input arrays to buffers
 	strcpy(topicBuffer, mainTopic);
+	strcat(topicBuffer, "/");
+	strcat(topicBuffer, floorName);
 	strcat(topicBuffer, "/");
 	strcat(topicBuffer, room);
 	strcat(topicBuffer, "/");


clear scores in serial bufffer at reset (untested) #1

--- range/arduino/control_module/control_module.ino
+++ range/arduino/control_module/control_module.ino
@@ -124,6 +124,18 @@
   SCOREBD.println(F("D0"));
   TIMER.println(F("R"));
   TARGET.println(F("X"));
+  // clear the serial buffer until we get zeros
+  // (max. 10 tries)
+  TARGET.println(F("D"));
+  delay(UPDATE_DELAY);
+  long ret_score = TARGET.parseInt();
+  int i = 0;
+  if (i < 10 && ret_score != 0) {
+    TARGET.println(F("D"));
+    delay(UPDATE_DELAY);
+    ret_score = TARGET.parseInt();
+    i++;
+  }
 }

 void sendPlayerID() {


update controller

update controller

--- document/arduino/motor_control/motor_control.ino
+++ document/arduino/motor_control/motor_control.ino
@@ -1,6 +1,7 @@
 /*
 Copyright 2017 ADLINK Technology Inc.
-Developer: Chester, Tseng
+Developer: Chester, Tseng
+           HaoChih, LIN
 Email: chester.tseng@adlinktech.com
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,21 +15,30 @@
 */
 #include <MsTimer2.h>

+// Pin define
 const int out1_pin = 5;
 const int out2_pin = 6;
 const int out3_pin = 9;
 const int out4_pin = 10;
-const int decoder_pin_1 = 2;
-const int decoder_pin_2 = 3;
+const int decoder_pin_1 = 3;
+const int decoder_pin_2 = 2;
+
+// Timer internal
+const int timer_period = 50; // ms
+const float timer_hz = 1000.0/( (float)timer_period ); // hz
+
+// encoder
 volatile int decoder_pin_1_counter = 0;
 volatile int decoder_pin_2_counter = 0;
-
 volatile int decoder_pin_1_delay_counter = 0;
 volatile int decoder_pin_2_delay_counter = 0;
-
-// Report internal;
-const int report_period = 100;
-
+volatile int encoder_res = 18; // 20 pulse in a circle --> 360/20 = 18(deg/pulse)
+
+// controller
+volatile float WL_ref = 0.0; //reference speed for left wheel (deg/sec)
+volatile float WR_ref = 0.0;
+volatile float Kp_L = 0.1;
+volatile float Kp_R = 0.1;

 void setup() {
   // Set 4 pwm channel pin to output
@@ -55,34 +65,29 @@
   attachInterrupt(digitalPinToInterrupt(decoder_pin_1), decoder_1_isr, FALLING);
   attachInterrupt(digitalPinToInterrupt(decoder_pin_2), decoder_2_isr, FALLING);

-  // Start timer for reporting wheel speed
-  MsTimer2::set(report_period, report_decoder_result_isr); // 100ms period
+  // Start timer for motor control & reporting wheel speed
+  MsTimer2::set(timer_period, controller_repoter_isr);
   MsTimer2::start();
 }

 void loop() {
     if (Serial.available()) {
       String commad_string = Serial.readString();
-      String out_1 = getValue(commad_string, ',', 0);
+      String out_1 = getValue(commad_string, ',', 0); //getValue func should be removed (TBD)
       String out_2 = getValue(commad_string, ',', 1);
       String out_3 = getValue(commad_string, ',', 2);
       String out_4 = getValue(commad_string, ',', 3);
-      if (out_1 != NULL) {
-        int out1_value = out_1.toFloat();
-        analogWrite(out1_pin, convert_adc(out1_value));
-      }
-      if (out_2 != NULL) {
-        int out2_value = out_2.toFloat();
-        analogWrite(out2_pin, convert_adc(out2_value));
-      }
-      if (out_3 != NULL) {
-        int out3_value = out_3.toFloat();
-        analogWrite(out3_pin, convert_adc(out3_value));
-      }
-      if (out_4 != NULL) {
-        int out4_value = out_4.toFloat();
-        analogWrite(out4_pin, convert_adc(out4_value));
-      }
+      if (out_1 != NULL)
+        WL_ref =  out_1.toFloat(); // deg/sec
+
+      if (out_2 != NULL)
+        WL_ref = -out_2.toFloat();
+
+      if (out_3 != NULL)
+        WR_ref =  out_3.toFloat();
+
+      if (out_4 != NULL)
+        WR_ref = -out_4.toFloat();
     }
 }

@@ -94,22 +99,97 @@
   decoder_pin_2_counter += 1;
 }

-void report_decoder_result_isr() {
+// convert required deg/s to pwm command (based on statistical)
+float feedforward(float value) {
+  return 0.1335 * value + 7.07;
+}
+
+void controller_repoter_isr() {
+  // controller (feedforward + feedback)
+  float cmd = 0.0;
+  float error = 0.0;
+  if(WL_ref >= 0.0)
+  {
+    error = WL_ref - (float)(decoder_pin_1_counter*encoder_res)*timer_hz; // deg/sec
+    cmd = feedforward(WL_ref) + error*Kp_L;
+    if(cmd>=0.0)
+    {
+      analogWrite(out1_pin, (int)cmd);
+      analogWrite(out2_pin, 0);
+    }
+    else
+    {
+      analogWrite(out1_pin, 0);
+      analogWrite(out2_pin, -(int)cmd);
+    }
+  }
+  else // the encoder has no capability of distinguishing the cw/ccw rotation
+  {
+    error = -WL_ref - (float)(decoder_pin_1_counter*encoder_res)*timer_hz; // deg/sec
+    cmd = feedforward(WL_ref) + error*Kp_L;
+    if(cmd>=0.0)
+    {
+      analogWrite(out2_pin, (int)cmd);
+      analogWrite(out1_pin, 0);
+    }
+    else
+    {
+      analogWrite(out2_pin, 0);
+      analogWrite(out1_pin, -(int)cmd);
+    }
+  }
+
+  if(WR_ref >= 0.0)
+  {
+    error = WR_ref - (float)(decoder_pin_2_counter*encoder_res)*timer_hz; // deg/sec
+    cmd = feedforward(WR_ref) + error*Kp_R;
+    if(cmd>=0.0)
+    {
+      analogWrite(out3_pin, (int)cmd);
+      analogWrite(out4_pin, 0);
+    }
+    else
+    {
+      analogWrite(out3_pin, 0);
+      analogWrite(out4_pin, -(int)cmd);
+    }
+  }
+  else // the encoder has no capability of distinguishing the cw/ccw rotation
+  {
+    error = -WR_ref - (float)(decoder_pin_2_counter*encoder_res)*timer_hz; // deg/sec
+    cmd = feedforward(WR_ref) + error*Kp_R;
+    if(cmd>=0.0)
+    {
+      analogWrite(out4_pin, (int)cmd);
+      analogWrite(out3_pin, 0);
+    }
+    else
+    {
+      analogWrite(out4_pin, 0);
+      analogWrite(out3_pin, -(int)cmd);
+    }
+  }
+
+
   // This is a workaround to prevent from motor idle when speed  = 0;
-  if (decoder_pin_1_delay_counter > 50) {
+  if (decoder_pin_1_delay_counter > (100 + WR_ref/encoder_res/timer_hz) ) {
+    WR_ref = 0.0;
     analogWrite(out3_pin, 0);
     analogWrite(out4_pin, 0);
     decoder_pin_1_delay_counter = 0;
   }

-  if (decoder_pin_2_delay_counter > 50) {
+  if (decoder_pin_2_delay_counter > (100 + WL_ref/encoder_res/timer_hz) ) {
+    WL_ref = 0.0;
     analogWrite(out1_pin, 0);
     analogWrite(out2_pin, 0);
     decoder_pin_2_delay_counter = 0;
   }
-
+
+  // report encoder info (deg/sec)
   char str[16];
-  sprintf(str, "%d,%d\r\n", decoder_pin_1_counter*18, decoder_pin_2_counter*18); // --> 360deg/20pic = 18
+  sprintf(str, "%d,%d\r\n", decoder_pin_1_counter*encoder_res
+                          , decoder_pin_2_counter*encoder_res ); // deg/sec
   Serial.print(str);

   if (decoder_pin_1_counter == 0)
@@ -138,10 +218,4 @@
   return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
 }

-// convert required deg/s to pwm command (feedforward)
-float convert_adc(int value) {
-  return 0.1335 * value + 7.07;
-}
-
-
-
+


Invert buzzer logic so it stops beeping all the time

--- src/rx5808-pro-diversity/rx5808-pro-diversity.ino
+++ src/rx5808-pro-diversity/rx5808-pro-diversity.ino
@@ -52,7 +52,7 @@

     // Enable buzzer and LED for duration of setup process.
     digitalWrite(PIN_LED, HIGH);
-    digitalWrite(PIN_BUZZER, HIGH);
+    digitalWrite(PIN_BUZZER, LOW);

     setupSettings();

@@ -71,7 +71,7 @@

     // Setup complete.
     digitalWrite(PIN_LED, LOW);
-    digitalWrite(PIN_BUZZER, LOW);
+    digitalWrite(PIN_BUZZER, HIGH);

     // Switch to initial state.
     StateMachine::switchState(StateMachine::State::MENU);


Make web call handlers re-entrant.

Make web call handlers re-entrant.

--- webservertask.ino
+++ webservertask.ino
@@ -130,7 +130,7 @@

   server.on( "/api/getdevice", HTTP_GET, []( AsyncWebServerRequest * request)
   {
-    AsyncResponseStream *response;
+    //AsyncResponseStream *response;
     if ( request->hasArg( "boottime" ) )
     {
       char response[25];
@@ -141,7 +141,7 @@

     else if ( request->hasArg( "channelcolors" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
       {
         response->printf( "%s\n", channel[channelNumber].color );
@@ -151,7 +151,7 @@

     else if ( request->hasArg( "channelnames" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
       {
         response->printf( "%s\n", channel[channelNumber].name );
@@ -161,7 +161,7 @@

     else if ( request->hasArg( "diskspace" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%lu", FFat.freeBytes() );
       return request->send( response );
     }
@@ -182,7 +182,7 @@
       {
         return request->send( 404 );
       }
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       while ( file )
       {
         if ( !file.isDirectory() )
@@ -201,7 +201,7 @@

     else if ( request->hasArg( "minimumlevels" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
       {
         response->printf( "%.2f\n", channel[channelNumber].minimumLevel );
@@ -215,7 +215,7 @@
       {
         return request->send( 501, HEADER_HTML, NOT_PRESENT_ERROR_501 );
       }
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%i\n%.4f\n", moonData.angle, moonData.percentLit );
       return request->send( response );
     }
@@ -226,7 +226,7 @@
       {
         return request->send( 501, HEADER_HTML, NOT_PRESENT_ERROR_501 );
       }
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%i", oledContrast );
       return request->send( response );
     }
@@ -242,14 +242,14 @@

     else if ( request->hasArg( "pwmdepth" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%i", ledcNumberOfBits );
       return request->send( response );
     }

     else if ( request->hasArg( "pwmfrequency" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%.0f", ledcActualFrequency );
       return request->send( response );
     }
@@ -260,7 +260,7 @@
       if ( !request->hasArg( "number" ) ) return request->send( 400, HEADER_HTML, "No sensornumber" );
       uint8_t num = request->arg( "number" ).toInt();
       if ( num >= sensor.count() ) return request->send( 400, HEADER_HTML, "Invalid sensornumber" );
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       sensorName_t name;
       sensorId_t id;
       response->printf( "%s\n%.3f\n%s\n", sensor.getName( num, name ), sensor.temp( num ), sensor.getId( num, id ) );
@@ -272,7 +272,7 @@
       if ( !sensor.count() ) return request->send( 501, HEADER_HTML, NOT_PRESENT_ERROR_501 );
       sensorName_t name;
       sensorId_t id;
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       for ( uint8_t num = 0; num < sensor.count(); num++ )
         response->printf( "%s,%.3f,%s\n", sensor.getName( num, name ), sensor.temp( num ), sensor.getId( num, id ) );

@@ -303,7 +303,7 @@

     else if ( request->hasArg( "status" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       for ( uint8_t channelNumber = 0; channelNumber < NUMBER_OF_CHANNELS; channelNumber++ )
       {
         char content[8];
@@ -333,7 +333,7 @@
       {
         return request->send( 501, HEADER_HTML, NOT_PRESENT_ERROR_501 );
       }
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%.2f", tftBrightness );
       return request->send( response );
     }
@@ -349,7 +349,7 @@

     else if ( request->hasArg( "timezone" ) )
     {
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%s", getenv( "TZ" ) );
       return request->send( response );
     }
@@ -450,7 +450,6 @@

   server.on( "/api/setdevice", HTTP_POST, []( AsyncWebServerRequest * request )
   {
-    AsyncResponseStream *response;
     if ( !request->authenticate( WWW_USERNAME, preferences.getString( PASSWD_KEY_NVS, WWW_DEFAULT_PASSWD ).c_str() ) )
     {
       return request->requestAuthentication();
@@ -516,7 +515,7 @@
       oledContrast = contrast;
       OLED.setContrast( contrast << 4 );
       preferences.putUInt( "oledcontrast", oledContrast );
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%i", contrast );
       return request->send( response );
     }
@@ -567,7 +566,7 @@
         setupDimmerPWMfrequency( LEDC_MAXIMUM_FREQ, newPWMDepth );
         preferences.putUInt( "pwmdepth" , ledcNumberOfBits );
       }
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%i", ledcNumberOfBits );
       return request->send( response );
     }
@@ -584,7 +583,7 @@
         setupDimmerPWMfrequency( tempPWMfrequency, ledcNumberOfBits );
         preferences.putDouble( "pwmfrequency", ledcActualFrequency );
       }
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%.0f", ledcActualFrequency );
       return request->send( response );
     }
@@ -669,7 +668,7 @@
       tft.setRotation( tftOrientation );
       tftClearScreen = true;
       preferences.putString( "tftorientation", ( tftOrientation == TFT_ORIENTATION_NORMAL ) ? "normal" : "upsidedown" );
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%s", preferences.getString( "tftorientation", "" ).c_str() );
       return request->send( response );
     }
@@ -683,7 +682,7 @@
       }
       tftBrightness = brightness;
       preferences.putFloat( "tftbrightness", brightness );
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%.2f", tftBrightness );
       return request->send( response );
     }
@@ -698,7 +697,7 @@
       {
         return request->send( 400, HEADER_HTML, "Error setting timezone." );
       }
-      response = request->beginResponseStream( HEADER_HTML );
+      AsyncResponseStream *response = request->beginResponseStream( HEADER_HTML );
       response->printf( "%s", getenv( "TZ" ) );
       return request->send( response );
     }


temperature warning check at 70C. If exceeded, buzz 5 times every 2 min interval

--- short-circuit-limiter/short-circuit-limiter.ino
+++ short-circuit-limiter/short-circuit-limiter.ino
@@ -3,6 +3,9 @@

 #define CURRENT_ENABLE_THRESHOLD 0.23
 #define CURRENT_MCB_CUT 16
+
+#define TEMP_WARNING 70
+#define TEMP_WARNING_RATE 120000 //2 x 60 x 1000ms

 #define PIN_LED 26
 #define PIN_CT A0
@@ -232,6 +235,8 @@

        u8g2.sendBuffer();
+
+       temperatureWarningCheck(temperature);
     }
   }

@@ -452,3 +457,18 @@

 }

+
+void temperatureWarningCheck(float temperature){
+  if(temperature > TEMP_WARNING){
+    static unsigned long lastTempAlert = 0;
+
+    unsigned long currentTime = millis();
+
+    if((currentTime - lastTempAlert) > TEMP_WARNING_RATE){
+      lastTempAlert = currentTime;
+      shortBeepXTimesNoDelay(5);
+    }
+
+  }
+}
+


Added sim900 radio

Added sim900 radio

--- Arduino/hardware/REV10/REV10HardwareTest/REV10HardwareTest.ino
+++ Arduino/hardware/REV10/REV10HardwareTest/REV10HardwareTest.ino
@@ -68,14 +68,14 @@
 // OBJECTS & VARIABLES
 /**
  * Peripherals present on REV7
- * - PHT        IO_POWER_UP LOW
- * - LED        Set pin HIGH?
- * - TMP112     read() once.
- * - RFM23B     ??
- * - SIM900     ??
- * - Boiler     ??
+ * - PHT        read from in loop
+ * - LED        Toggle
+ * - TMP112     read from in loop.
+ * - RFM23B     read from in loop
+ * - SIM900     TODO
+ * - Boiler     TODO
  * - XTAL       Setup and leave running?
- * - UART       Disable
+ * - UART       print sensor values
  */

 /**
@@ -92,7 +92,7 @@
 OTV0P2BASE::SupplyVoltageCentiVolts Supply_cV;

 /*
- * Radio instance
+ * RFM23B instance
  */
 static constexpr bool RFM23B_allowRX = true;

@@ -105,6 +105,26 @@
 static const OTRadioLink::OTRadioChannelConfig RFM23BConfigs[nPrimaryRadioChannels] = {
     // GFSK channel 0 full config, RX/TX, not in itself secure.
     OTRadioLink::OTRadioChannelConfig(OTRFM23BLink::StandardRegSettingsGFSK57600, true), };
+
+
+/**
+ * SIM900 instance
+ */
+// SIM card configs
+static const char SIM900_PIN[5] PROGMEM       = "1111";
+static const char SIM900_APN[] PROGMEM      = "\"mobiledata\""; // GeoSIM
+// UDP Configs - Edit SIM900_UDP_ADDR for relevant server. NOTE: The server IP address should never be committed to GitHub.
+static const char SIM900_UDP_ADDR[16] PROGMEM = ""; // Of form "1.2.3.4".
+static const char SIM900_UDP_PORT[5] PROGMEM = "9999";             // Standard port for OpenTRV servers
+const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(
+                                                false,
+                                                SIM900_PIN,
+                                                SIM900_APN,
+                                                SIM900_UDP_ADDR,
+                                                SIM900_UDP_PORT);
+OTSIM900Link::OTSIM900Link<8, 5, RADIO_POWER_PIN, OTV0P2BASE::getSecondsLT> SecondaryRadio; // (REGULATOR_POWERUP, RADIO_POWER_PIN);
+
+

 /*
  * TMP112 instance
@@ -188,6 +208,16 @@
     // Check that the radio is correctly connected; panic if not...
     if(!PrimaryRadio.configure(nPrimaryRadioChannels, RFM23BConfigs) || !PrimaryRadio.begin()) { panic(F("r1")); }

+  // Turn power on for SIM900 with PFET for secondary power control.
+    fastDigitalWrite(A3, 0);
+    pinMode(A3, OUTPUT);
+  // Initialise the radio, if configured, ASAP because it can suck a lot of power until properly initialised.
+    SecondaryRadio.preinit(NULL);
+//  DEBUG_SERIAL_PRINTLN_FLASHSTRING("R2");
+  // Check that the radio is correctly connected; panic if not...
+    if(!SecondaryRadio.configure(1, &SecondaryRadioConfig) || !SecondaryRadio.begin()) { panic(F("r2")); }
+  // Assume no RX nor filtering on secondary radio.
+
     // Collect full set of environmental values before entering loop() in normal mode.
     // This should also help ensure that sensors are properly initialised.

@@ -212,6 +242,7 @@

     // Do OpenTRV-specific (late) setup.
     PrimaryRadio.listen(true);
+    SecondaryRadio.listen(false);
 }

@@ -256,7 +287,7 @@
       DEBUG_SERIAL_PRINTLN();
       PrimaryRadio.removeRXMsg();
       }
-
+    SecondaryRadio.poll();

     // =====
     // To sleep, perchance to dream...


Serial debugging

Serial debugging

--- resilient-network.ino
+++ resilient-network.ino
@@ -9,6 +9,8 @@
 #define leds2pin 3
 #define button1pin 2 // not used
 #define button2pin 4
+
+#define SERIAL_DEBUG true

 #define ENERGY 0
 #define DROPPING_CONNECTOR 254
@@ -26,6 +28,7 @@
 int alertTimer;

 void sendEnergy() {
+  if(SERIAL_DEBUG) Serial.println("send energy");
   char c = ENERGY;
   for (int i=0; i<3; i++) bus[i]->write(c);
   addTimer(100, sendEnergy);
@@ -115,11 +118,17 @@

   // start sending energy if we're a source
   if (isSource) addTimer(100, sendEnergy);
+
+  if(SERIAL_DEBUG) {
+    Serial.begin(9600);
+    Serial.println("setup");
+  }
 }

 void loop() {
   if (isSource) {
     if (!digitalRead(button1pin)) {
+      if(SERIAL_DEBUG) Serial.println("alert");
       char c = ALERT;
       for (int i=0; i<3; i++) bus[i]->write(c);
     }
@@ -128,6 +137,7 @@
   // check all lines for incoming data
   for (int i=0;i<3;i++) if(bus[i]->available()) {
     char c = bus[i]->read();
+    if(SERIAL_DEBUG) Serial.println("received: "+c);
     switch(c) {
       case ALERT: // button is pressed
         if (!isSource && !alertState) {


Change default sounds

Change default sounds

--- MagicMushroom.ino
+++ MagicMushroom.ino
@@ -28,14 +28,12 @@

 void setup() {

-  WiFi.hostname("MagicMushroom");
+  WiFi.hostname("ESP-MagicMushroom");
   WiFi.mode(WIFI_STA);

-  sounds.push_back("kranplaetze.mp3");
-  sounds.push_back("badumtss.mp3");
-  sounds.push_back("sad_trumpet.mp3");
-  sounds.push_back("lost_life.mp3");
-  sounds.push_back("i-like-turtles.ogg");
+  sounds.push_back("funes-nein.ogg");
+  sounds.push_back("funes-doch.ogg");
+  sounds.push_back("funes-oh.ogg");

   pinMode(BUTTON_PIN, INPUT_PULLUP);


Eliminated a few delays

Eliminated a few delays

--- Merged/Merged.ino
+++ Merged/Merged.ino
@@ -66,7 +66,7 @@
 	TCCR1A = 0;// set entire TCCR1A register to 0
 	TCCR1B = 0;// same for TCCR1B
 	TCNT1 = 0;//initialize counter value to 0
-	// set compare match register for 1hz increments
+	// set compare match register for 4 seconds increments
 	OCR1A = 62499;// must be <65536, see worklog for calculation
 	// turn on CTC mode
 	TCCR1B |= (1 << WGM12);
@@ -96,11 +96,11 @@
 	digitalWrite(LED_POWER, LOW); // power on the LED
 	delay(delayTime);
 	dustVal = analogRead(DUST_PIN); // read the dust value via pin 5 on the sensor
-	delay(delayTime2);
+	// delay(delayTime2); // commented out for now
 	digitalWrite(LED_POWER, HIGH); // turn the LED off
-	delay(offTime);
-
-	delay(200);
+	//delay(offTime); //  commented out for now
+
+	// delay(200);  // interval between readings, not needed in merged code
 	Serial.print("Dust value: ");
 	Serial.println(dustVal);

@@ -128,7 +128,7 @@
 	}

 	readGasLevel();
-	delay(GAS_LEVEL_READING_PERIOD_MILLIS);
+	// delay(GAS_LEVEL_READING_PERIOD_MILLIS); // interval between readings no longer needed

 	// **************** FOR TEMP & HUMIDITY SENSOR ****************

@@ -174,7 +174,7 @@

 	// **************** FOR TEMP & HUMIDITY SENSOR ****************
 	// Wait a few seconds between measurements.
-	delay(500);
+	// delay(500); // interval between readings no longer needed
 }


Code update ...

Code update ...

--- i2c_cmd_slave_attiny85/i2c_cmd_slave_attiny85.ino
+++ i2c_cmd_slave_attiny85/i2c_cmd_slave_attiny85.ino
@@ -238,98 +238,3 @@
 	digitalWrite(LED_PIN, LOW);    // turn the LED off by making the voltage LOW
 	delay(500);                    // wait for a second
 }
-
-/*
-
-// Gets called when the ATtiny receives an i2c read slave request
-void requestEvent() {
-
-//int i;
-
-// send the data buffer back to the master
-//for (i = 0; i < commandLength; i++)
-//  TinyWireS.send(~command[i]); // ######## GC COMMAND ACKNOWLEDGE !!! ########
-
-int i;
-byte opCodeAck = ~command[0]; // Command Operation Code acknowledge => Command Bitwise "Not".
-switch (command[0]) {
-case STDPB1_1: {
-byte ackLng = 1;
-byte acknowledge[1] = {0};
-acknowledge[0] = opCodeAck;
-digitalWrite(LED_PIN, HIGH);   // turn the LED on (HIGH is the voltage level)
-for (i = 0; i < ackLng; i++) {
-TinyWireS.send(acknowledge[i]);
-}
-break;
-}
-case STDPB1_0: {
-byte ackLng = 1;
-byte acknowledge[1] = {0};
-acknowledge[0] = opCodeAck;
-digitalWrite(LED_PIN, LOW);    // turn the LED off by making the voltage LOW
-for (i = 0; i < ackLng; i++) {
-TinyWireS.send(acknowledge[i]);
-}
-break;
-}
-case STANAPB3: {
-byte ackLng = 2;
-byte acknowledge[2] = {0};
-acknowledge[0] = opCodeAck;
-acknowledge[1] = ~command[1];
-//acknowledge[1] = 0x33;
-for (i = 0; i < ackLng; i++) {
-TinyWireS.send(acknowledge[i]);
-}
-break;
-}
-case READADC2: {
-byte ackLng = 4, analogMSB = 0, analogLSB = 0;
-//word analogRead = 0;
-word analogRead = rand() % (1023 + 1 - 0) + 0;
-//if (analogVal < 1024) {
-//    analogRead = ++analogVal;
-//} else {
-//    analogVal = 0;
-//}
-analogMSB = ((analogRead >> 8) & 0x03);
-analogLSB = (analogRead & 0x0FF);
-word analogValue = ((analogMSB << 8) + analogLSB);
-byte acknowledge[4] = {0};
-acknowledge[0] = opCodeAck;
-acknowledge[1] = analogMSB;
-acknowledge[2] = analogLSB;
-acknowledge[3] = CalculateCRC(acknowledge, ackLng - 1); // Prepare CRC for Reply
-for (i = 0; i < ackLng; i++) {
-TinyWireS.send(acknowledge[i]);
-}
-break;
-}
-default: {
-byte acknowledge[1] = {0};
-acknowledge[0] = opCodeAck;
-break;
-}
-}
-}
-
-// Function Heartbit
-void heartbit() {
-digitalWrite(LED_PIN, HIGH);   // turn the LED on (HIGH is the voltage level)
-delay(500);                    // wait for a second
-digitalWrite(LED_PIN, LOW);    // turn the LED off by making the voltage LOW
-delay(500);                    // wait for a second
-}
-
-// Function CalculateCRC (CRC-8)
-byte CalculateCRC(byte* block, size_t blockLength) {
-int i;
-byte crc = 0, data = 0;
-for (i = 0; i < blockLength; i++) {
-data = (byte) (block[i] ^ crc); // XOR-in next input byte
-crc = (byte) (crcTable[data]); // Get current CRC value = remainder
-}
-return crc;
-}
-*/


Bug fix

Bug fix

--- FH_Tbot.ino
+++ FH_Tbot.ino
@@ -703,7 +703,7 @@
 			//motors.startCommandSet(data);
 		}
 	}
-
+}
 /**
  * Updates websocket client with distance/temperature information.
  */


added internal pullup on motor switch

added internal pullup on motor switch

--- src/analog_sensors_board.ino
+++ src/analog_sensors_board.ino
@@ -59,7 +59,7 @@
 #define WATER_AFT 11

 #define BAT_LOW 26.5f
-#define TH_MOTOR_HIGH 10
+#define TH_MOTOR_HIGH 1
 #define TH_MOTOR_LOW -100
 #define TH_WATER 10

@@ -168,7 +168,7 @@
     pinMode(LED_GREEN, OUTPUT);
     pinMode(LED_YELLOW, OUTPUT);
     pinMode(LED_RED, OUTPUT);
-    pinMode(SW_MOTOR, INPUT);
+    pinMode(SW_MOTOR, INPUT_PULLUP);
     pinMode(WATER_FWD, INPUT);
     pinMode(WATER_AFT, INPUT);


eeprom fix

eeprom fix

--- eeprom8.ino
+++ eeprom8.ino
@@ -65,7 +65,7 @@
 }
 */

-uint8_t EEPROM8_getValue(uint8_t variable) {
+int EEPROM8_getValue(uint8_t variable) {
   if (invalid)
     return -1;


Added 1ms watchdog

Added 1ms watchdog

--- chillduino.ino
+++ chillduino.ino
@@ -34,6 +34,7 @@
 #define DOOR_SWITCH      13
 #define MODE_SWITCH      SCK
 #define THERMISTOR       A0
+#define RELAY_WATCHDOG   A2
 #define LEDS             A4
 #define LED_COUNT        10
 #define LED_OFF_R        0
@@ -56,9 +57,13 @@

 Chillduino chillduino;
 Adafruit_NeoPixel pixels(LED_COUNT, LEDS, NEO_GRB + NEO_KHZ800);
+int watchdog = 0;

 SIGNAL(TIMER0_COMPA_vect) {
   chillduino.tick();
+
+  digitalWrite(RELAY_WATCHDOG, watchdog);
+  watchdog ^= 1;
 }

 void setInterrupt(void) {