diagram:
#include <Arduino.h>
//#include <NeoSWSerial.h>
//NeoSWSerial portOne(10, 11);
#include "ImmersionHeaterController.h"
#include <SoftwareSerial.h>
#include "ApplicationMonitor.h"
#include <OneWire.h>
#include <DallasTemperature.h>
Watchdog::CApplicationMonitor ApplicationMonitor;
int g_nIterations = 0;
//enum debugLevel : int {
// off = 0, low = 1, med, high
//};
//enum updateMode : int {
// zero = 0, down = 1, up = 2
//};
debugLevel debug = high;
constexpr int softwareBaudRate = 9600;
int countOfEnergisedRelays = 0;
constexpr int maxNumberOfRelays = 6;
constexpr int relayPin1 = 2;
constexpr int relayPin2 = 3;
constexpr int relayPin3 = 4;
constexpr int relayPin4 = 5;
constexpr int relayPin5 = 6;
constexpr int relayPin6 = 7;
constexpr int relayPinSocket = 8;
constexpr int delayAfterSendingMessage = 50;
unsigned long countOFAllBufferResets = 0;
unsigned long countOfAllMessagesCorrectlyBounded = 0;
unsigned long bufferResetMax = 400000000; //4294967040
unsigned long previousTimeIncPlus = 0;
unsigned long storedTimeIncPlus = 0;
unsigned long previousTimeIncMinus = 0;
unsigned long storedTimeIncMinus = 0;
unsigned long minimumSwitchingTime = 2000;
/**
* variables for Serial Communication
*/
constexpr int RXpin = 10;
constexpr int TXpin = 11;
SoftwareSerial port(RXpin, TXpin);
constexpr byte numBytes = 8;
char latestInstruction[numBytes];
byte numReceived = 0;
boolean newData = false;
boolean newInstruction = false;
constexpr char startMarker = '<'; //<
constexpr char endMarker = '>'; //>
int pauseWhileSending = 50;
/**
* variables for temperature measurement
*/
// temp sensors wire pins
constexpr int ONE_WIRE_LOWERT = 9;
constexpr int ONE_WIRE_UPPERT = 12;
// Setup a oneWire instance to communicate with any OneWire device
OneWire OneWireLowerT(ONE_WIRE_LOWERT);
OneWire OneWireUpperT(ONE_WIRE_UPPERT);
// Pass oneWire reference to DallasTemperature library
DallasTemperature TempSensorLower(&OneWireLowerT);
DallasTemperature TempSensorUpper(&OneWireUpperT);
//callibrations
constexpr float referenceHigh = 99.9;
constexpr float referenceLow = 0;
constexpr float tLowerRawHigh = 98.3;
constexpr float tLowerRawLow = 1.0;
constexpr float tLowerRawRange = tLowerRawHigh - tLowerRawLow;
constexpr float referenceRange = referenceHigh - referenceLow;
float tLowerCorrectedValue;
float tLowerRawValue;
constexpr float tUpperRawHigh = 98.3;
constexpr float tUpperRawLow = 0.7;
constexpr float tUpperRawRange = tUpperRawHigh - tUpperRawLow;
float tUpperCorrectedValue;
float tUpperRawValue;
/**
* TODO this message is not used! Deprecate?
*/
void logError(String error) {
Serial.print("ERROR! ");
Serial.println(error);
//debug = high;
}
/**
* this function is to help with error detection of transmitted messages
*/
void countMessageNumber(boolean messageStarted) {
if (messageStarted) {
countOfAllMessagesCorrectlyBounded++;
// if (debug >= high) {
// Serial.print("countOfAllMessagesCorreclyBounded ");
// Serial.println(countOfAllMessagesCorreclyBounded);
// }
if (countOfAllMessagesCorrectlyBounded > bufferResetMax) {
Serial.println("count1 overflow! ");
Serial.println("Reset both counts to zero");
countOfAllMessagesCorrectlyBounded = 0;
countOFAllBufferResets = 0;
}
}
}
/**
* This requires 1000ms between message sends to avoid buffer filling up
* If buffer does fill, it will get emptied (elsewhere) but ~5/10% messages are void because of buffer overruns
*/
void recvCharsWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char receivedChar[numBytes];
//set receivedChar to un-initialised
memset(receivedChar, 0, sizeof(receivedChar));
char charReadFromPort;
boolean messageStarted = false;
port.listen();
unsigned long timeout = 10000;
unsigned long startTimeWhile = millis();
unsigned long currentTime = millis();
while (port.available() > 0 && newData == false
&& currentTime - startTimeWhile < timeout) {
currentTime = millis();
charReadFromPort = (char) port.read();
if (recvInProgress == true) {
if (charReadFromPort != endMarker) {
receivedChar[ndx] = charReadFromPort;
ndx++;
//it is possible that the message sent is longer than numBytes. if so, the last characters will be discarded
if (ndx >= numBytes - 1) {
ndx = numBytes - 2;
}
} else {
//countMessageNumber(messageStarted);
receivedChar[ndx] = '\0'; // terminate the string
recvInProgress = false;
numReceived = ndx; // save the number for use when printing
ndx = 0;
newData = true;
}
} else if (charReadFromPort == startMarker) {
recvInProgress = true;
messageStarted = true;
}
}
if (newData) {
strncpy(latestInstruction, receivedChar, numBytes);
newInstruction = true;
if (debug >= high) {
Serial.print("latestInstruction ");
Serial.println(latestInstruction);
}
} else {
strncpy(latestInstruction, "nochnge", numBytes);
newInstruction = false;
}
newData = false;
}
/**
* this works for messages less than 8 characters
* a delay is required: delayAfterSendingMessage
* currently the messages invoke no action - this requires implementation on the other side
*/
void wrapCharMessage(char message[]) {
port.print(startMarker);
port.print(message);
port.print(endMarker);
delay(delayAfterSendingMessage);
}
/****
* make sure to check if relay switches on high or low
*/
void adjustLoad(int relayID, boolean on) {
uint8_t cond;
//low switching
// if (on)
// cond = LOW;
// else
// cond = HIGH;
//high switching
if (on)
cond = HIGH;
else
cond = LOW;
switch (relayID) {
case 1:
digitalWrite(relayPin1, cond);
break;
case 2:
digitalWrite(relayPin2, cond);
break;
case 3:
digitalWrite(relayPin3, cond);
break;
case 4:
digitalWrite(relayPin4, cond);
break;
case 5:
digitalWrite(relayPin5, cond);
break;
case 6:
digitalWrite(relayPin6, cond);
break;
default:
break;
//todo this should not happen
}
}
void updateRelayCount(updateMode mode) {
switch (mode) {
case 0:
countOfEnergisedRelays = 0;
break;
case 1:
countOfEnergisedRelays--;
break;
case 2:
countOfEnergisedRelays++;
break;
default:
break;
//send data to emonloadcontrol
}
}
void configureSocket(boolean turnOn) {
if (turnOn)
digitalWrite(relayPinSocket, HIGH);
else
digitalWrite(relayPinSocket, LOW);
}
void configureLoads(int numberOfRelays, boolean turnOn) {
switch (countOfEnergisedRelays) {
case 0:
if (turnOn) {
adjustLoad(1, true);
updateRelayCount(up);
}
break; /* optional */
case 1:
case 2:
case 3:
case 4:
case 5:
if (turnOn) {
adjustLoad(6, true);
updateRelayCount(up);
} else {
//work out if frequent switching is an issue
for (int i = 0; i < numberOfRelays; i++) {
adjustLoad(countOfEnergisedRelays, false);
if (countOfEnergisedRelays != 0)
updateRelayCount(down);
}
}
break;
case 6:
if (turnOn) {
//sending the messages invokes no action on the other side!
//wrapCharMessage("IHfull");
} else {
//wrapCharMessage("IHNF");
for (int i = 0; i < numberOfRelays; i++) {
adjustLoad(countOfEnergisedRelays, false);
if (countOfEnergisedRelays != 0)
updateRelayCount(down);
}
}
break;
default:
//logError("issue with configureRelays");
Serial.print("count ");
Serial.print(countOfEnergisedRelays);
Serial.print(" numberOfRelays ");
Serial.print(numberOfRelays);
Serial.print(" turnOn(1) turnOff(0):");
Serial.println(turnOn);
adjustLoad(1, false);
adjustLoad(2, false);
adjustLoad(3, false);
adjustLoad(4, false);
adjustLoad(5, false);
adjustLoad(6, false);
updateRelayCount(zero);
break;
}
// Serial.print("numberOfRelaysOn after call ");
// Serial.println(numberOfRelaysOn);
}
void checkOverflowAndEmptyBuffer() {
if (port.overflow()) {
Serial.println("overflow!");
int timeout = 0;
//buffer size is 64 bytes, each character can represent a byte
while (port.available() && timeout < 64) {
port.read();
timeout++;
}
}
}
/**
* this is a fudge for buffer errors
*
*/
void updateErrorCount() {
countOFAllBufferResets++;
if (countOFAllBufferResets > bufferResetMax) {
Serial.println("Overflow1 Reset ");
countOFAllBufferResets = 0;
countOfAllMessagesCorrectlyBounded = 0;
}
if (countOfAllMessagesCorrectlyBounded > bufferResetMax) {
Serial.println("overflow2! Reset");
countOfAllMessagesCorrectlyBounded = 0;
}
// Serial.print("bufferErrorCount: ");
// Serial.println(countOFAllBufferResets);
// Serial.print(" countOfAllMessagesCorreclyBounded: ");
// Serial.println(countOfAllMessagesCorreclyBounded);
if (countOFAllBufferResets < bufferResetMax
&& countOfAllMessagesCorrectlyBounded < bufferResetMax) {
Serial.print("% errors ");
if (countOfAllMessagesCorrectlyBounded > 0)
Serial.println(
(countOFAllBufferResets / countOfAllMessagesCorrectlyBounded)
* 100);
}
}
void addressIssues() {
//updateErrorCount();
checkOverflowAndEmptyBuffer();
}
void adjustLoadsAccordingToInstruction() {
if (newInstruction) {
if (strncmp(latestInstruction, "alloff", strlen("alloff")) == 0) {
configureLoads(6, 0);
configureSocket(false);
} else if (strncmp(latestInstruction, "Sct+", strlen("Sct+")) == 0) {
configureSocket(true);
} else if (strncmp(latestInstruction, "SoC-", strlen("SoC-")) == 0) {
configureSocket(false);
} else if (strncmp(latestInstruction, "inc+", strlen("inc+")) == 0) {
if (countOfEnergisedRelays < 6) {
configureLoads(1, 1);
}
} else if (strncmp(latestInstruction, "inc-", strlen("inc-")) == 0) {
// previousTimeIncMinus = storedTimeIncMinus;
// storedTimeIncMinus = millis();
// if (storedTimeIncMinus - storedTimeIncPlus
// > minimumSwitchingTime) {
// }
if (countOfEnergisedRelays > 0) {
configureLoads(1, 0);
}
} else {
if (debug >= med) {
Serial.print("instruction not matched ");
Serial.print("latestInstruction: ");
Serial.println(latestInstruction);
}
addressIssues();
}
}
}
/**
* NOTE: There is a issue with start-up of Arduino programs that control these relays.
* These relays input controls are Active LOW, meaning that setting a pin LOW turns them ON.
* To assure that no relays activate at Reset or Power-On until you want them to,
* the initialization sequence in SETUP should be:
* pinMode(Relay, OUTPUT);
* digitalWrite(Relay, HIGH);
*
*/
void initialiseRelays() {
pinMode(relayPin1, OUTPUT);
pinMode(relayPin2, OUTPUT);
pinMode(relayPin3, OUTPUT);
pinMode(relayPin4, OUTPUT);
pinMode(relayPin5, OUTPUT);
pinMode(relayPin6, OUTPUT);
pinMode(relayPinSocket, OUTPUT);
digitalWrite(relayPin1, LOW);
digitalWrite(relayPin2, LOW);
digitalWrite(relayPin3, LOW);
digitalWrite(relayPin4, LOW);
digitalWrite(relayPin5, LOW);
digitalWrite(relayPin6, LOW);
digitalWrite(relayPinSocket, LOW);
}
void blinkLed(int numBlinks) {
for (int dl = 0; dl < numBlinks; dl++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
}
}
void setup() {
Serial.begin(9600);
Serial.println("setting up");
ApplicationMonitor.Dump(Serial);
ApplicationMonitor.EnableWatchdog(
Watchdog::CApplicationMonitor::Timeout_4s);
//initialise temperature sensors
// TempSensorLower.begin();
// TempSensorUpper.begin();
port.begin(softwareBaudRate);
port.listen();
initialiseRelays();
blinkLed(5);
Serial.println("<Arduino is ready eddy>");
}
void measureTempAndAdjustLoad() {
// Send the command to get temperatures
TempSensorLower.requestTemperatures();
TempSensorUpper.requestTemperatures();
tLowerRawValue = TempSensorLower.getTempCByIndex(0);
tUpperRawValue = TempSensorUpper.getTempCByIndex(0);
if (tLowerRawValue == -127.0 || tUpperRawValue == -127.0) {
//this indicates issue with sensors
Serial.print("temp sensors not functioning correctly ");
//turn off all loads
configureLoads(6, 0);
if (tLowerRawValue == -127.0) {
Serial.print("temp lower issue ");
}
if (tUpperRawValue == -127.0) {
Serial.print("temp lower issue ");
}
}
tLowerCorrectedValue = (((tLowerRawValue - tLowerRawLow) * referenceRange)
/ tLowerRawRange) + referenceLow;
tUpperCorrectedValue = (((tUpperRawValue - tUpperRawLow) * referenceRange)
/ tUpperRawRange) + referenceLow;
// Serial.print("tLowerRawValue: ");
// Serial.println(tLowerRawValue);
// Serial.print("tUpperRawValue: ");
// Serial.println(tUpperRawValue);
// Serial.print("tLowerCorrectedValue: ");
// Serial.println(tLowerCorrectedValue);
// Serial.print("tUpperCorrectedValue: ");
// Serial.println(tUpperCorrectedValue);
if (tUpperCorrectedValue > 90 || tLowerCorrectedValue > 85) {
//turn off all loads
configureLoads(6, 0);
}
}
void testConfigure() {
int shortDelay = 1000;
configureLoads(6, 0);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 1);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(1, 0);
delay(shortDelay);
configureLoads(6, 0);
}
void cycleRelaysOnAndOff() {
int shortDelay = 3000;
int longDelay = 10000;
delay(longDelay);
Serial.println("all off");
digitalWrite(relayPin1, LOW);
digitalWrite(relayPin2, LOW);
digitalWrite(relayPin3, LOW);
digitalWrite(relayPin4, LOW);
digitalWrite(relayPin5, LOW);
digitalWrite(relayPin6, LOW);
delay(shortDelay);
Serial.println("1 on");
digitalWrite(relayPin1, HIGH);
delay(shortDelay);
Serial.println("1 off");
digitalWrite(relayPin1, LOW);
delay(shortDelay);
Serial.println("2 on");
digitalWrite(relayPin2, HIGH);
delay(shortDelay);
Serial.println("2 off");
digitalWrite(relayPin2, LOW);
delay(shortDelay);
Serial.println("3 on");
digitalWrite(relayPin3, HIGH);
delay(shortDelay);
Serial.println("3 off");
digitalWrite(relayPin3, LOW);
delay(shortDelay);
Serial.println("4 on");
digitalWrite(relayPin4, HIGH);
delay(shortDelay);
Serial.println("4 off");
digitalWrite(relayPin4, LOW);
delay(shortDelay);
Serial.println("5 on");
digitalWrite(relayPin5, HIGH);
delay(shortDelay);
Serial.println("5 off");
digitalWrite(relayPin5, LOW);
delay(shortDelay);
Serial.println("6 on");
digitalWrite(relayPin6, HIGH);
delay(shortDelay);
Serial.println("6 off");
digitalWrite(relayPin6, LOW);
delay(shortDelay);
Serial.println("1, 2, 3, 4, 5, 6 on");
digitalWrite(relayPin1, HIGH);
digitalWrite(relayPin2, HIGH);
digitalWrite(relayPin3, HIGH);
digitalWrite(relayPin4, HIGH);
digitalWrite(relayPin5, HIGH);
digitalWrite(relayPin6, HIGH);
delay(shortDelay);
}
/**
* check how often the instructions come from emonloadcontrol and adjust delay accordingly
* otherwise loop goes round too quickly and messages are chopped up
*/
void loop() {
ApplicationMonitor.IAmAlive();
ApplicationMonitor.SetData(g_nIterations++);
const boolean testingSystem = false;
const boolean testConfigureRelays = false;
const boolean cycleRelays = false;
if (!testingSystem) {
delay(200);
recvCharsWithStartEndMarkers();
adjustLoadsAccordingToInstruction();
measureTempAndAdjustLoad();
} else {
if (testConfigureRelays)
testConfigure();
if (cycleRelays)
cycleRelaysOnAndOff();
}
}