Hello everyone, I've posted this in 'programming' as I suspect it's a quirk of the language I'm not understanding that is causing my problem - but it may ultimately belong in 'sensors', as a Dallas DS18b20 being pulled out while the problem is in full swing seems to stop it.
I have a small network of microcontollers:
- An UNO (strictly a bare Atmel 328) that acts as the central controller for my heating system.
- An ESP01 (ESP8266 based) hard-wired to the UNO to send and receive data over software serial, and wirelessly connected to the below for sending and receiving over the ESP-NOW protocol.
- A standalone Wemos D1 mini (ESP8266 based) acting as a remote thermostatic control, wirelessly connected to the ESP01 sending/receiving over ESP-NOW protocol.
The UNO and ESP-01 are talking to each other perfectly, sending, receiving, & validating, with the UNO sending out the status of the central heating boiler and the current time, and the ESP-01 forwarding the sensed temperature, the desired temperature, the zone ref. and whether there is a heat demand.
The Wemos is transmitting to the ESP-01 flawlessly, allowing the ESP to forward the above data to the UNO.
The problem I have is with the Wemos receiving data. Sort of. The data comes in good, as confirmed in the serial monitor.
Data that comes in over ESP-NOW fires off a callback OnDataRecv(), which uses memcpy() to fill a struct that is set up identically to the one that sent the data.
Printing to serial inside the callback proves that the data came in just fine, so it isn't the transmission that is the problem.
Printing the data that has been memcpy()'d to the struct over serial in the loop, however, shows that it has been corrupted. The data also changes to seemingly random byte values... at the same frequency as the DS18b20 is requesting temperatures.
And pulling the DS18b20 out while this is happening stops the variables from changing.
Very odd. However, leaving it disconnected and rebooting doesn't result in the correct data going to the variables, just stops the random updates every time the temperature sensor is polled.
(worth noting that the sensor is performing perfectly).
Every time new data is received, the memcpy() works, ans the serial monitor shows several loops with the correct values, but they quickly stray away from that without any apparent cause.
There isn't anything in the sketch that writes to the boilerStatus struct except for the OnDataRecv() callback, and it only runs on receipt of new data, so I'm clueless as to how the members of the struct can be changing in between calls of the callback function?
My hacky way around the problem is that inside the OnDataRecv() callback I copy the member variables of the struct to separate global vars. But I would like to know why it happens, as I believe I should be doing as little as possible in an interrupt function - setting the value of four variables isn't much, but it's something.
(I'm also not sure why this works, as I had previously tried creating a separate global struct, and inside the OnDataRecv() callback, I assigned the incoming struct members to the new struct members. It didn't fix it.)
One more thing which might prove useful - each time the DS18b20 polls, the LED display I am using flickers, suggesting a power issue. Tried decoupling on the sensor, decoupling on the 595s driving the display, all sorts of caps and values in lots of locations, and none if it helped.
The full sketch is below:
/*
Sketch for WEMOS D1 (board "Lolin(Wemos) D1 Mini (Clone)")
DS18b20, encoder control for set temp up & down. A display showing the current temp and set temp.
Sends the sensed temp, set temp, heat demand status and zone reference to an ESP01 over ESP-NOW
Reveives the boiler status & override status from the ESP01 over ESP-NOW
CMcK 2022
*/
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <EEPROM.h>
#include <ESPRotary.h>
#include <CMcK_Led.h>
#include <CMcK_Button.h>
// *REDACTED for privacy, sketch includes correct MAC*
uint8_t kitchenEspMacAddress[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// for Dallas DS18b20 temp sensor
#include <SPI.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS D1 // temp sensor Data wire is plugged into this port on the WEMOS D1
/*
/// PARASITE POWER
#define PARASITE_POWER_PIN D2 // wemos:D2 if sensor is DS18b20PAR, connect this pin to the data line as well, it will be used to provide power during conversion
bool parasite_power = false; // set this false if normal DS18b20 with Vcc, or true if parasite power type (PAR) NOTE!!! you never got this working well on ESP01, maybe power wasn't great, seems better on WEMOS so far
*/
// pins for the 595 shift register
#define LATCH_PIN D2 // wemos:D2
#define CLOCK_PIN D3 // wemos:D3
#define DATA_PIN D7 // wemos:D7
#define SEG_9_PIN D0 // wemos:D0
#define NUM_DISPLAY_SEGMENTS 9
const int screenSleepDelay = 30000;
byte Segment[NUM_DISPLAY_SEGMENTS]; // array of the characters to display each loop
byte displaySegmentCount = 0; // to track which segment to display each loop
const byte Number[10] = {
// numbers 0-9 LCD format, a,b,c,d,e,f,g,dp
0b11111100, // 0
0b01100000, // 1
0b11011010, // 2
0b11110010, // 3
0b01100110, // 4
0b10110110, // 5
0b10111110, // 6
0b11100000, // 7
0b11111110, // 8
0b11110110 // 9
};
const byte decimalPoint = 0b00000001;
const byte Letter[30] = {
0b00000000, // 0 <space>
0b11101110, // 1 A
0b00111110, // 2 b
0b10011100, // 3 C
0b01111010, // 4 d
0b10011110, // 5 E
0b10001110, // 6 F
0b10111100, // 7 G
0b01101110, // 8 H
0b00001100, // 9 I
0b01110000, // 10 J
0b00000000, // 11* k?
0b00011100, // 12L
0b1110110, // 13* m?
0b00101010, // 14 n
0b00111010, // 15 o
0b11001110, // 16 P
0b11100110, // 17 q
0b00001010, // 18 r
0b10110110, // 19 S
0b00011110, // 20 t
0b00111000, // 21 u
0b01010100, // 22* v?
0b01111100, // 23* w?
0b00101000, // 24* x?
0b01110110, // 25 y
0b10010000, // 26* z?
0b11000110, // 27 degree symbol
0b00000010, // 28 "-"
0b00010010 // 29 "="
};
OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance
DallasTemperature sensors(&oneWire);
unsigned long lastSensorCheck = 0;
//const int sensorCheckDelay = 750; // not needed when using dallas library method to ask if the temperature conversion is complete instead of timing between reads
float senseTemp = 30;
float setTemp = 18.5;
byte minSetTemp = 10;
byte maxSetTemp = 30;
float hysteresis = 0.3; // this value is the +- from setTemp at which to switch
byte zone = 1;
unsigned long lastTransmit = 0;
const int transmitDelay = 1000;
#define ENC_1 D6
#define ENC_2 D5
#define CLICKS_PER_STEP 4
ESPRotary encoder;
#define ENCODER_BUTTON D4
Button button1(ENCODER_BUTTON);
unsigned long lastInputMillis;
unsigned long saveStatusTimer;
const int saveStatusDelay = 10000;
unsigned long storeDialogLastChange;
bool storeDialogFlag = true;
const int storeDialogFlashDelay = 2000;
//#define BOILER_LED D4
byte modeState = 1;
byte prevModeState;
// Structure to send data
// Must match the receiver structure
typedef struct temps_floats_message {
float senseTemp;
float setTemp;
byte zone;
bool heatDemand;
};
typedef struct boiler_status_message {
byte boilerIsOn;
byte overrideIsOn;
byte hour;
byte minute;
//byte crcNum;
};
byte boilerIsOn;
byte overrideIsOn;
byte hour;
byte minute;
// Create struct instances
temps_floats_message zoneData;
boiler_status_message boilerStatus;
boiler_status_message boilerStatusIncoming;
// struct for a function that converts temps from "##.#" to "#, #, #"
typedef struct temperatureBytes {
byte tens;
byte units;
byte tenths;
};
// function to take temp as float andreturn a struct with three bytes
temperatureBytes convertFloatToBytes(float tempAsFloat) {
// ds18b20 accurate to 0.5degC, so not worried about tenths changing at 5 hundredths
tempAsFloat *= 10; // bring tenths up to units
int tempAsInt = int(tempAsFloat); // cast to int to discard remaining decimal (hundredths)
temperatureBytes tempInBytes; // initialize struct
tempInBytes.tenths = tempAsInt % 10;
tempAsInt /= 10;
tempInBytes.units = tempAsInt % 10;
tempInBytes.tens = tempAsInt / 10;
return tempInBytes;
}
// Smoothing global vars
bool firstRun = true;
byte avgCount = 0;
const byte numAvgCounts = 25;
float avgArr[numAvgCounts];
float avgTVal;
// Callback when data is sent (registered in setup)
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
Serial.print("Last Packet Send Status: ");
if (sendStatus == 0){
Serial.println("Delivery success");
}
else{
Serial.println("Delivery fail");
}
}
// Callback when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
Serial.print("Bytes received: ");
Serial.println(len);
memcpy(&boilerStatus, incomingData, sizeof(boilerStatus));
boilerIsOn = boilerStatus.boilerIsOn;
overrideIsOn = boilerStatus.overrideIsOn;
hour = boilerStatus.hour;
minute = boilerStatus.minute;
}
void bootEEPROMCheck() {
// look for existing set temp in EEPROM at location 0, if between bounds store it in setTemp, else save current setTemp in EEPROM and commit change
float setTempCheck;
EEPROM.get(0, setTempCheck);
if (setTempCheck < maxSetTemp && setTempCheck > minSetTemp) {
setTemp = setTempCheck;
Serial.println();
Serial.print("Existing Set Temp found in EEPROM: ");
Serial.println(setTempCheck);
} else {
EEPROM.put(0, setTemp);
if (EEPROM.commit()) {
Serial.println();
Serial.println("EEPROM successfully committed");
} else {
Serial.println();
Serial.println("ERROR! EEPROM commit failed");
}
}
}
// callback func to reset the timer since last input
void encoderChangedCallbackFunction(ESPRotary& encoder) {
lastInputMillis = millis();
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
sensors.begin();// Start up the dallas sensors library
sensors.setWaitForConversion(false); //*** for non-blocking
sensors.requestTemperatures(); //*** request first temp now to give time to convert before asking what it is in the main sketch
EEPROM.begin(256); // required on ESP8266 to allow EEPROM function, with size in bytes
delay(1000); //give Serial a little time
Serial.print("MAC: ");
Serial.println(WiFi.macAddress());
bootEEPROMCheck();
// setup the encoder, ESPRotary library format
encoder.begin(ENC_1, ENC_2, CLICKS_PER_STEP);
encoder.setChangedHandler(encoderChangedCallbackFunction);
//encoder.setLeftRotationHandler(showDirection);
//encoder.setRightRotationHandler(showDirection);
digitalWrite(SEG_9_PIN, HIGH);
pinMode(SEG_9_PIN, OUTPUT);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
WiFi.disconnect();
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// set role as COMBO to allow send and receive
esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
// register callback func for when data is sent
esp_now_register_send_cb(OnDataSent);
// Register a callback function that will be called when data is received
esp_now_register_recv_cb(OnDataRecv);
// Register peers
esp_now_add_peer(kitchenEspMacAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
// set 595 pins
pinMode(LATCH_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(DATA_PIN, OUTPUT);
}
void loop() {
// Serial.print("boil: "); Serial.print(boilerStatus.boilerIsOn);
// Serial.print(" override "); Serial.println(boilerStatus.overrideIsOn);
Serial.println(hour);
encoder.loop(); // reads the encoder, ESPRotary library format
if(millis() - lastInputMillis > screenSleepDelay) modeState = 0; // sleep the screen
displayUpdate();
senseTemp = avgTemp( checkTemp() ); // set the senseTemp to the averaged array of the instantaneous reads
transmitZonedata();
// depending on which modeState, run a function to diplay that mode
// mode 0 sleep,
// mode 1 diplay sense temp -> "XX.X*C off" "XX.X*C on"
// mode 3 display save dialog -> "Pr.= StorE"
// under modes, use:
/*
if(modeState != prevModeState) {
encoder.setIncrement(int);
encoder.setUpperBound(int);
encoder.setLowerBound(int);
encoder.resetPosition(int);
prevModeState = modeState;
}
varToChange = encoder.getPosition();
*/
switch (modeState) {
case 0: {
// display sleep
sleepMenu();
break;
}
case 1: {
// sense temp & output
senseTempMenu();
break;
}
case 2: {
// set temp screen
setTempMenu();
break;
}
case 20: {
// save to EEPROM dialog
setTempSaveScreen();
break;
}
case 21: {
// eeprom confirmation
saveStatusScreen();
break;
}
case 3: {
//clock
clockScreen();
break;
}
default: {
modeState = 1;
break;
}
}
/*
// button check needs moved into mode functions
switch(button1.check()) {
case 1:
// single press
// change screen mode
Serial.println("sing");
break;
case 2:
// double press
Serial.println("doub");
break;
case 3:
// hold
break;
case 4:
// long hold
break;
default:
break;
}
*/
}
// modestate 0
void sleepMenu() {
if(modeState != prevModeState) {
prevModeState = modeState;
encoder.setIncrement(1);
encoder.setUpperBound(1);
encoder.setLowerBound(-1);
encoder.resetPosition(0);
// fill Segment array with " ", i.e. blank
for(byte seg = 0; seg < NUM_DISPLAY_SEGMENTS; seg++) {
Segment[seg] = Letter[0];
}
}
// if button pressed or encoder moved
if(button1.check() != 0 || encoder.getPosition() != 0 ) {
modeState = 1;
lastInputMillis = millis();
}
}
// modestate 1
void senseTempMenu() {
if(modeState != prevModeState) {
prevModeState = modeState;
}
// struct containing setTemp converted to three bytes
temperatureBytes senseTempBytes = convertFloatToBytes(senseTemp);
// "XX.X*C off" "XX.X*C on"
Segment[0] = Number[senseTempBytes.tens]; // #
Segment[1] = Number[senseTempBytes.units] | decimalPoint; // #.
Segment[2] = Number[senseTempBytes.tenths]; // #
Segment[3] = Letter[27]; // degree symbol
Segment[4] = Letter[3] | decimalPoint; // C
Segment[5] = Letter[0]; // " "
if(boilerIsOn == 1) { // boiler is on
Segment[6] = Letter[0]; // " "
Segment[7] = Letter[15]; // o
Segment[8] = Letter[14]; // n
} else { // boiler is off
Segment[6] = Letter[15]; // o
Segment[7] = Letter[6]; // F
Segment[8] = Letter[6]; // F
}
if(button1.check() == 1) {
lastInputMillis = millis();
modeState = 2; // single press goes to set temp screen
}
}
/*
void updateBoilerStatusOnScreen() {
if(boilerStatus.boilerIsOn == 1) { // boiler is on
Segment[6] = Letter[0]; // " "
Segment[7] = Letter[15]; // o
Segment[8] = Letter[14]; // n
} else { // boiler is off
Segment[6] = Letter[15]; // o
Segment[7] = Letter[6]; // F
Segment[8] = Letter[6]; // F
}
}
*/
// modestate 2
void setTempMenu() {
// struct containing setTemp converted to three bytes
temperatureBytes setTempBytes = convertFloatToBytes(setTemp);
if(modeState != prevModeState) {
prevModeState = modeState;
// if just changed to this screen set encoder parameters
encoder.setIncrement(1);
encoder.setUpperBound(280);
encoder.setLowerBound(120);
int t = int(setTemp * 10);
encoder.resetPosition(t);
}
// load the right characters into the display array
// "SEt. XX.X*C"
Segment[0] = Letter[19]; // S
Segment[1] = Letter[5]; // E
Segment[2] = Letter[20] | decimalPoint; // t.
Segment[3] = Letter[0]; // " "
Segment[4] = Number[setTempBytes.tens]; // #
Segment[5] = Number[setTempBytes.units] | decimalPoint; // #.
Segment[6] = Number[setTempBytes.tenths]; // #
Segment[7] = Letter[27]; // degree symbol
Segment[8] = Letter[3]; // C
setTemp = float(encoder.getPosition()) / 10; // takes the encoder position, makes it a float, divides by ten, to give temp in degrees to 1 decimal place
switch (button1.check()) {
case 1:
lastInputMillis = millis();
modeState = 3; // single press goes to clock
break;
case 3:
lastInputMillis = millis();
modeState = 20; // press & hold to go to setTemp save screeen
break;
default:
break;
}
}
// modestate 20
void setTempSaveScreen() {
if(modeState != prevModeState) {
prevModeState = modeState;
}
// struct containing setTemp converted to three bytes
temperatureBytes setTempBytes = convertFloatToBytes(setTemp);
/*
// "L.Pr.=StorE"
Segment[0] = Letter[12] | decimalPoint; // L.
Segment[1] = Letter[16]; // P
Segment[2] = Letter[18] | decimalPoint; // r.
Segment[3] = Letter[29]; // =
Segment[4] = Letter[19]; // S
Segment[5] = Letter[20]; // t
Segment[6] = Letter[15]; // o
Segment[7] = Letter[18]; // r
Segment[8] = Letter[5]; // E
*/
// toggle a boolean every period to decide what to display
if(millis() - storeDialogLastChange > storeDialogFlashDelay) {
storeDialogFlag = !storeDialogFlag;
storeDialogLastChange = millis();
}
// first display...
if(storeDialogFlag) {
// "LonG.PrESS"
Segment[0] = Letter[12]; // L
Segment[1] = Letter[15]; // o
Segment[2] = Letter[14]; // n
Segment[3] = Letter[7]| decimalPoint; // G.
Segment[4] = Letter[16]; // P
Segment[5] = Letter[18]; // r
Segment[6] = Letter[5]; // E
Segment[7] = Letter[19]; // S
Segment[8] = Letter[19]; // S
// ...second display
} else {
// "2.StorE.##.#"
Segment[0] = Number[2] | decimalPoint; // 2.
Segment[1] = Letter[19]; // S
Segment[2] = Letter[20]; // t
Segment[3] = Letter[15]; // o
Segment[4] = Letter[18]; // r
Segment[5] = Letter[5] | decimalPoint; // E.
Segment[6] = Number[setTempBytes.tens]; // #
Segment[7] = Number[setTempBytes.units] | decimalPoint; // #.
Segment[8] = Number[setTempBytes.tenths]; // #
}
switch (button1.check()) {
case 1:
lastInputMillis = millis();
modeState = 2; // single press goes back to set temp
break;
case 3:
// move to saveStatus screen & run EEPROM save function
lastInputMillis = millis();
modeState = 21; // "stored"/"failed" screen
break;
default:
break;
}
}
// modestate 21
void saveStatusScreen() {
if(modeState != prevModeState) {
prevModeState = modeState;
saveStatusTimer = millis();
if(setTempToEEPROM(setTemp)) {
//"Store.Good"
Segment[0] = Letter[19]; // S
Segment[1] = Letter[20]; // t
Segment[2] = Letter[15]; // o
Segment[3] = Letter[18]; // r
Segment[4] = Letter[5] | decimalPoint; // E.
Segment[5] = Letter[7]; // G
Segment[6] = Letter[15]; // o
Segment[7] = Letter[15]; // o
Segment[8] = Letter[4]; // d
} else {
//"Store.FaIL"
Segment[0] = Letter[19]; // S
Segment[1] = Letter[20]; // t
Segment[2] = Letter[15]; // o
Segment[3] = Letter[18]; // r
Segment[4] = Letter[5] | decimalPoint; // E.
Segment[5] = Letter[6]; // F
Segment[6] = Letter[1]; // A
Segment[7] = Letter[9]; // I
Segment[8] = Letter[12]; // L
}
}
if(millis() - saveStatusTimer > saveStatusDelay) modeState = 1;
if(button1.check()) modeState = 2;
}
// modestate 3
void clockScreen() {
if(modeState != prevModeState) {
prevModeState = modeState;
//"Chron.##.##" but initialized with spaces for the numbers to clear the previous screen's data
Segment[0] = Letter[3]; // C
Segment[1] = Letter[8]; // h
Segment[2] = Letter[18]; // r
Segment[3] = Letter[15]; // o
Segment[4] = Letter[14] | decimalPoint; // n.
}
byte hTens = hour / 10;
byte hUnits = hour % 10;
byte mTens = minute / 10;
byte mUnits = minute %10;
Segment[5] = Number[hTens]; // #
Segment[6] = Number[hUnits] | decimalPoint; // #.
Segment[7] = Number[mTens]; // #
Segment[8] = Number[mUnits]; // #
if(button1.check()) modeState = 1;
}
/*
void updateClockMinutesOnScreen(byte h, byte m) {
byte hTens = h / 10;
byte hUnits = h % 10;
byte mTens = m / 10;
byte mUnits = m%10;
Segment[5] = Number[hTens]; // #
Segment[6] = Number[hUnits] | decimalPoint; // #.
Segment[7] = Number[mTens]; // #
Segment[8] = Number[mUnits]; // #
}
*/
//*** this is not yet implemented - need another set of variables passed to the kitchen ESP, forwarded over serial to the kitchen arduino, and handled by the arduino to enact the override
/*
// modestate 4
const int overrideScreenSpashDelay = 1500;
unsigned long overrideScreenStarted;
void overrideScreen() {
if(modeState != prevModeState) {
prevModeState = modeState;
// "booSt oFF" | "booSt on ", initially with spaces to clear the previous screen
// the "oFF" or "on " added by function called by onDataRecv callback
Segment[0] = Letter[18]; // b
Segment[1] = Letter[18]; // o
Segment[2] = Letter[18]; // o
Segment[3] = Letter[19]; // S
Segment[4] = Letter[20]; // t
}
if(overrideIsOn) {
Segment[6] = Letter[15]; // o
Segment[7] = Letter[14]; // n
Segment[8] = Letter[0]; // " "
}
else {
Segment[6] = Letter[15]; // o
Segment[7] = Letter[6]; // F
Segment[8] = Letter[6]; // F
}
if(button1.check()) modeState = 1;
}
*/
void displayUpdate() {
// code here to refresh the screen, each loop is a different segment in sequence
// takes the counter value and sends out the byte for the required character ( Segment[segmentCounter] ) and the byte for displaySegmentCount
byte character = Segment[displaySegmentCount];
if (displaySegmentCount < 8) { // the hardware segment will be selected by 8 bit shift reg
byte position = ~(0b00000001<<displaySegmentCount);
shift2Bytes(character, position);
digitalWrite(SEG_9_PIN, HIGH); // segment 9 has a direct connection to the microcontroller, not enough pins on the 595 shift register
} else {
byte position = ~0b00000000;
shift2Bytes(character, position);
digitalWrite(SEG_9_PIN, LOW); // segment 9 has a direct connection to the microcontroller, not enough pins on the 595 shift register
}
// increment segment counter
displaySegmentCount++;
if (displaySegmentCount > NUM_DISPLAY_SEGMENTS) displaySegmentCount = 0;
}
void displayClear() {
shift2Bytes(0b00000000, 0b11111111);
digitalWrite(SEG_9_PIN, HIGH);
}
void shift2Bytes(byte data1, byte data2) {
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, data1);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, data2);
digitalWrite(LATCH_PIN, HIGH);
}
// saves the passed float parameter to the EEPROM, if different to the current value
bool setTempToEEPROM(float tPassedIn) {
float setTempInEEPROM;
bool saveSuccess;
EEPROM.get(0, setTempInEEPROM);
if (setTempInEEPROM != tPassedIn) {
EEPROM.put(0, tPassedIn);
if (EEPROM.commit()) {
saveSuccess = true;
//Serial.println();
//Serial.println("EEPROM successfully committed");
} else {
saveSuccess = false;
//Serial.println();
//Serial.println("ERROR! EEPROM commit failed");
}
} else saveSuccess = true;
return saveSuccess;
}
float checkTemp() {
//if (millis() - lastSensorCheck > sensorCheckDelay) {
if (sensors.isConversionComplete()) { // this is much neater than a timed delay
/*
if(parasite_power) {
pinMode(PARASITE_POWER_PIN, INPUT); //*** PARASITE this tri-states the pin before asking the sensor for data
}
*/
displayClear(); // clear it before the getTempByIndex call to reduce LED display flicker
float t = sensors.getTempCByIndex(0);
displayUpdate();
sensors.requestTemperatures(); // re-request to give time to convert before next request
//Serial.println(t);
/*
if(parasite_power) {
pinMode(PARASITE_POWER_PIN, OUTPUT); //*** PARASITE this sets the pin as output...
digitalWrite(PARASITE_POWER_PIN, HIGH); //*** PARASITE ... and sets HIGH to power the DS18b20 during conversion
}
*/
lastSensorCheck = millis();
//Serial.print("Non-smoothed T\tn"); Serial.println(t);
return t;
}
return senseTemp; // if delay not met, return the existing value
}
void checkHeatDemand() {
if (zoneData.senseTemp + hysteresis <= zoneData.setTemp) zoneData.heatDemand = true;
else if (zoneData.senseTemp - hysteresis >= zoneData.setTemp) zoneData.heatDemand = false;
}
float avgTemp(float tReading) {
if (firstRun) { // if it's the first run through, array needs filled with the current read
for (int arrayIndex = 0; arrayIndex < numAvgCounts; arrayIndex++) {
avgArr[arrayIndex] = tReading;
}
firstRun = false;
}
else {
avgArr[avgCount] = tReading; // if not first run, set array value at the current count location to the current value
}
// increment counter, if at end loop back to zero & set max/min to the avg
avgCount++;
if (avgCount > numAvgCounts) avgCount = 0;
// take average
float tSum = 0; // this will become the sum total of all values in the array
for (int arrayIndex = 0; arrayIndex < numAvgCounts; arrayIndex++) {
tSum += avgArr[arrayIndex];
}
avgTVal = tSum / numAvgCounts;
return avgTVal;
}
void transmitZonedata() {
if (millis() - lastTransmit > transmitDelay) {
// Set zoneData values to send
zoneData.zone = zone;
zoneData.senseTemp = senseTemp;
zoneData.setTemp = setTemp;
checkHeatDemand(); // sets zoneData.heatDemand
// sends the saved stuct_message zoneData
esp_now_send(kitchenEspMacAddress, (uint8_t *) &zoneData, sizeof(zoneData));
lastTransmit = millis();
}
}
My suspicion is that this might have something to do with the fact that an interrupt - from the incoming ESP-NOW data - is corrupting the variables, and so I tried making the struct volatile. The compiler didn't like this as the memcpy() I use to put the data into the struct is trying to cast void to volatile void. It wasn't happy, I didn't understand, and I don't know if further effort here would bear any fruit. (it's a shame a Raspberry Pi pun doesn't work here...)
I tried making the struct members volatile, this didn't help the problem.
Maybe I'm just using memcpy wrong? Though what I've done seems to fit the examples I'd read.
Is it possible that the wire library or the DallasTemperature library could be interfering in some way?
A quirk of the Wemos D1 board's D1 pin (that the DS18b20/onewire bus is on)?
If anyone can shed any light on how a struct can be (potentially) modified by a sensor that doesn't relate to it, (or more likely how I'm using memcpy() completely wrong...) I'm all ears!
For now, I'll proceed with my hacky fix, as I've been banging my head for too long...