Overcome Arduino memory limitations with multiple sensors - c++

I have an Arduino Nano piggybacked on a ENC28j60 ethernet module. I have eight (8) DHT22 sensors (named A, B, C ... H ) and i want to write their temperature and humidity data to Pushingbox.
The program is working great with ONE sensor. So that's good. However when i un-remark (i.e. take out the //'s) for anything more than one sensor, yes even for ONE other sensor, it won't write anything at all.
The arduino IDE complier says:
Sketch uses 23824 bytes (77%) of program storage space. Maximum is 30720 bytes.
Global variables use 1870 bytes (91%) of dynamic memory, leaving 178 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.
FYI, If i un-remark just one other sensor it goes up +2% to 93% of dynamic memory and stops working. So i'm assuming its a memory problem. i have already removed all floats (and made integers *10 to keep one decimal place of accuracy), i need 8 instances of DHT so considered reducing the library size but the .h seems tiny and pretty lean already (within the .h file it even boasts: Very low memory footprint - Very small code. so i havent modified this or the .cpp.
The overall code i have written is not large, maybe there are some in-efficiencies there, but i can't see that it is going to make anything near the amount of memory saving needed for the next 7 sensors.
The full arduino code (written for all 8 sensors, with 'ghosted' bits for 4 sensors and in use only working for one sensor) is below:
#include "DHTesp.h"
//#include <SPI.h>
#include <UIPEthernet.h>
DHTesp dhtA;
//DHTesp dhtB;
//DHTesp dhtC;
//DHTesp dhtD;
//DHTesp dhtE;
//DHTesp dhtF;
//DHTesp dhtG;
//DHTesp dhtH;
const int ledPin = LED_BUILTIN;
int ledState = LOW;
int interval = 10; // this is the number of seconds between reads (120=2mins)
int numReads = 5; // Number of reads between posting to google docs.
int multFact = 10; // multiplication factor 10 = 1 dec. place 100 = 2 dec places
byte mac[] = {0xBE, 0xEF, 0xDE, 0xAD, 0xDE, 0xAD }; //Ethernet shield MAC. Andy's working {0xBE, 0xEF, 0xDE, 0xAD, 0xDE, 0xAD}
byte ip[] = { 192,168,1,12 }; // Arduino device IP address
char devid [] = "vCE3D036xxxxxxxx"; // Gsheets device ID from Pushingbox ('x's for privacy:)
char server[] = "api.pushingbox.com";
EthernetClient client;
void setup()
{
Serial.begin(9600);
Serial.println ("RESTART");
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Trying to connect...");
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
while(true);
}
else{
Serial.print("OK, connected. Ethernet ready. ");
// print the Ethernet board/shield's IP address:
Serial.print("IP address: ");
Serial.println(Ethernet.localIP());
}
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println();
//Serial.println("A-Stat\t\tA-Temp (C)\tA-humdid%\tA-HeatI (C)\tB-Stat\t\tB-Temp (C)\tA-humdid%\tB-HeatI (C)");
Serial.println("\t\t\t\tt-A\th-A\tt-B\th-B\tt-C\th-C\tt-D\th-D\tt-E\th-E\tt-F\th-F\tt-G\th-G\tt-H\th-H");
//hey dB for some reason the Ethernet sheild uses pin D2 :( and pins 10,11,12,13
// https://arduinodiy.wordpress.com/2013/04/07/connect-an-enc28j60-ethernet-module-to-an-arduino/
// assign data pins
dhtA.setup(3);
//dhtB.setup(4);
//dhtC.setup(5);
//dhtD.setup(6);
//dhtE.setup(7);
//dhtF.setup(8);
//dhtG.setup(9);
//dhtH.setup(10); //watchout! i think Ethernet uses this pin too?
pinMode(ledPin, OUTPUT);
//end of void setup
}
void loop()
{
int Ahumid = 0; int Atemp = 0;
int Bhumid = 0; int Btemp = 0;
int Chumid = 0; int Ctemp = 0;
int Dhumid = 0; int Dtemp = 0;
//int Ehumid = 0; int Etemp = 0;
//int Fhumid = 0; int Ftemp = 0;
//int Ghumid = 0; int Gtemp = 0;
//int Hhumid = 0; int Htemp = 0;
int j=0;
for (j = 1; j <= numReads ; j++ ) {
int p = 0;
for (p=1; p <= interval ; p++) {
delay (1000);
// swap the led state
if (ledState == LOW) { ledState = HIGH; } else { ledState = LOW; }
Serial.print (p); //show the seconds passing
Serial.print (",");
digitalWrite(ledPin, ledState);
}
Serial.print (" Reading");
Atemp += dhtA.getTemperature()*multFact; Ahumid += dhtA.getHumidity()*multFact;
//Btemp += dhtB.getTemperature()*multFact; Bhumid += dhtB.getHumidity()*multFact;
//Ctemp += dhtC.getTemperature()*multFact; Chumid += dhtC.getHumidity()*multFact;
//Dtemp += dhtD.getTemperature()*multFact; Dhumid += dhtD.getHumidity()*multFact;
// print the readings
//Serial.print(dhtA.getStatusString());
Serial.print("\t"); Serial.print(Atemp);
Serial.print("\t"); Serial.print(Ahumid);
Serial.print("\t"); Serial.print(Btemp);
Serial.print("\t"); Serial.print(Bhumid);
Serial.print("\t"); Serial.print(Ctemp);
Serial.print("\t"); Serial.print(Chumid);
Serial.print("\t"); Serial.print(Dtemp);
Serial.print("\t"); Serial.print(Dhumid);
Serial.println();
// and so here endeth 'j', the number of reads
}
Serial.print ("Avg...");
Atemp = Atemp/numReads; Ahumid = Ahumid/numReads;
Btemp = Btemp/numReads; Bhumid = Bhumid/numReads;
Ctemp = Ctemp/numReads; Chumid = Chumid/numReads;
Dtemp = Dtemp/numReads; Dhumid = Dhumid/numReads;
// print the averages so we can see what it is going to send
Serial.print("\t\t\t");
Serial.print("\t"); Serial.print(Atemp); Serial.print("\t"); Serial.print(Ahumid);
Serial.print("\t"); Serial.print(Btemp); Serial.print("\t"); Serial.print(Bhumid);
Serial.print("\t"); Serial.print(Ctemp); Serial.print("\t"); Serial.print(Chumid);
Serial.print("\t"); Serial.print(Dtemp); Serial.print("\t"); Serial.print(Dhumid);
Serial.println();
Serial.print ("Prep for upload... ");
if (client.connect(server, 80))
{
Serial.print("Connected OK ... writing...");
client.print("GET /pushingbox?devid=");
client.print(devid);
client.print("&tempA="); client.print(Atemp);
client.print("&tempB="); client.print(Btemp);
client.print("&tempC="); client.print(Ctemp);
client.print("&tempD="); client.print(Dtemp);
client.print("&tempE=29&tempF=39&tempG=49&tempH=59");
//now humidity too
client.print("&humidA="); client.print(Ahumid);
client.print("&humidB="); client.print(Bhumid);
client.print("&humidC="); client.print(Chumid);
client.print("&humidD="); client.print(Dhumid);
client.print("&humidE=26&humidF=27&humidG=28&humidH=29");
client.print("&submit=Submit");
client.println(" HTTP/1.1");
client.println("Host: api.pushingbox.com");
client.println("Connection: close");
client.println();
Serial.println("written OK. & connection closed.");
Serial.println(); //Serial.println();
delay(1000); // maybe take this out to keep time stable?
client.stop();
}
else {
Serial.println("** NO CONNEX **"); Serial.println();
}
//here endeth void loop
}
Extra info (that i dont think is relevant, but maybe): IDE compiler also reports:
WARNING: library DHT_sensor_library_for_ESP32 claims to run on [esp32] architecture(s) and may be incompatible with your current board which runs on [avr] architecture(s).

Use the F macro for double-quoted string literal prints. Instead of this:
Serial.println ("RESTART");
or
client.print("GET /pushingbox?devid=");
... do this:
Serial.println ( F("RESTART") );
or
client.print( F("GET /pushingbox?devid=") );
This will easily save a bunch o' RAM.
I would also suggest using the single quoted literal for single characters, not the double quote:
Serial.print( '\t' );

Related

Lidar Sensors not working properly - How to work with two lidar Sensors over I2C on arduino

I'm currently working on a project with some friends about lidar measuraments based on ARDUINO and GARMIN Lidar v3HP and we are getting some reading that are questionable from the sensors. They seem to work but the measurements are not correct.
We have issues with the data and also with the address, we setup the sensors with two different addresses 0x42 and 0x43, but one of the sensors keeps on the default address.
#include <Arduino.h>
#include <Wire.h>
#include <stdint.h>
#include <LIDARLite_v3HP.h>
#include <I2CFunctions.h>
#define POWER_ENABLE_S1 12
#define POWER_ENABLE_S2 11
#define DEFAULT_ADDRESS 98
#define FAST_I2C
#define NUMERO_LIDARS 2
LIDARLite_v3HP Sensor1;
LIDARLite_v3HP Sensor2;
int detectedAddreses[NUMERO_LIDARS];
int currentAdd;
int deviceCount = 0;
int i = 0;
void scanI2C()
{
int nDevices = 0;
while (i < NUMERO_LIDARS)
{
for (byte addr = 1; addr < 127; ++addr)
{
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0)
{
Serial.print("Se encontro un dispositivo en ");
Serial.print(addr);
Serial.println(" !");
++nDevices;
detectedAddreses[i] = addr;
if (addr == DEFAULT_ADDRESS)
{
configSensors(i, 66 + deviceCount, addr);
detectedAddreses[i] = addr;
i++;
}else{
detectedAddreses[i] = addr;
i++;
}
}
else if (error == 4)
{
Serial.print("Error desconocido en ");
Serial.println(addr);
}
}
if (nDevices == 0)
{
Serial.println("No se encontraron dispositivos\n");
}
else
{
Serial.println("Terminado\n");
}
}
}
void configSensors(int sensor, uint8_t new_address, uint8_t old_address)
{
switch (sensor)
{
case 0:
Sensor1.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S1, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor1.configure(0,new_address);
break;
case 1:
Sensor2.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S2, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor2.configure(0,new_address);
i = 999;
break;
case 2:
break;
}
}
void setup()
{
Serial.begin(115200);
#ifdef FAST_I2C
#if ARDUINO >= 157
Wire.setClock(400000UL); // Set I2C frequency to 400kHz (for Arduino Due)
#else
TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
#endif
#endif
pinMode(POWER_ENABLE_S1, OUTPUT);
pinMode(POWER_ENABLE_S2, OUTPUT);
digitalWrite(POWER_ENABLE_S1, HIGH);
digitalWrite(POWER_ENABLE_S2, HIGH);
Wire.begin();
scanI2C();
digitalWrite(POWER_ENABLE_S1,HIGH);
digitalWrite(POWER_ENABLE_S2,HIGH);
Sensor1.configure(3,detectedAddreses[0]);
Sensor2.configure(3,detectedAddreses[1]);
}
void measure(){
float s1;
float s2;
Sensor1.waitForBusy();
Sensor1.takeRange();
Sensor1.waitForBusy();
s1 = Sensor1.readDistance(detectedAddreses[0]);
Sensor2.waitForBusy();
Sensor2.takeRange();
Sensor2.waitForBusy();
s2 = Sensor2.readDistance(detectedAddreses[1]);
Serial.println("Sensor 1: " + String(s1) + "; Sensor 2: " + String(s2));
}
void loop()
{
/*Serial.println(detectedAddreses[0]);
Serial.println(detectedAddreses[1]);*/
measure();
}
Based on your top comment, there may be an issue with configuring both lidars at the same time.
From factory default, they will both respond to the default I2C address 0x62. So, when you try to reconfigure one at a time, they will both respond [and there may be a race condition] and will both get programmed to the new I2C address.
If [and this is a big if] the lidar can save the configuration to non-volatile storage on the unit, you can connect one at a time [physically/manually] and give them different addresses. The unit saves the address. And, next time, will only respond to the "new" address.
Then, after both units have been reconfigured, you can then connect both simultaneously and they will respond individually [as desired].
I looked at the .pdf and the wiring diagram. You may be able to connect the lidar's power pin [or enable pin] to an Arduino GPIO port pin (instead of +5V). Then, you can control the power up of each unit individually. Then, you can reconfigure both as above. That is, assert power to one, reconfigure it, power it down [with the saved config]. Do this for the other unit. Then, you can power up both units [at this point, they are responding to different I2C addresses].
Don't know if Garmin starts up the lasers immediately or whether you have to give it a "start" command. Being able to control power individually may be a good thing if there is no separate start command.
I'm not familiar with Garmin's lidars, but I've written S/W to control Velodyne lidars and we had to apply power in a staggered manner because the power surge when they both started up would "brown out" the system. With Garmin, YMMV.
If all else fails, you may have to put each unit on a separate/different physical I2C bus [because you can't reconfigure them separately].
Here's the working code,
The sensors are hocked up in the same I2C bus, power enable pins to each sensor and ground conected to arduino. Power to the sensors is supplied by a 11.1V battery with a power regulator to 5V
#include <Arduino.h>
#include <Wire.h>
#include <stdint.h>
#include <LIDARLite_v3HP.h>
#include <I2CFunctions.h>
#define POWER_ENABLE_S1 12
#define POWER_ENABLE_S2 11
#define DEFAULT_ADDRESS 98
#define FAST_I2C
#define NUMERO_LIDARS 2
LIDARLite_v3HP Sensor1;
LIDARLite_v3HP Sensor2;
int detectedAddreses[NUMERO_LIDARS];
int currentAdd;
int deviceCount = 0;
int i = 0;
void scanI2C()
{
int nDevices = 0;
while (i < NUMERO_LIDARS)
{
for (byte addr = 1; addr < 127; ++addr)
{
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0)
{
Serial.print("Se encontro un dispositivo en ");
Serial.print(addr);
Serial.println(" !");
++nDevices;
detectedAddreses[i] = addr;
if (addr == DEFAULT_ADDRESS)
{
configSensors(i, 66 + deviceCount, addr);
detectedAddreses[i] = addr;
i++;
}else{
detectedAddreses[i] = addr;
i++;
}
}
else if (error == 4)
{
Serial.print("Error desconocido en ");
Serial.println(addr);
}
}
if (nDevices == 0)
{
Serial.println("No se encontraron dispositivos\n");
}
else
{
Serial.println("Terminado\n");
}
}
}
void configSensors(int sensor, uint8_t new_address, uint8_t old_address)
{
switch (sensor)
{
case 0:
Sensor1.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S1, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor1.configure(0,new_address);
break;
case 1:
Sensor2.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S2, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor2.configure(0,new_address);
i = 999;
break;
case 2:
break;
}
}
void setup()
{
Serial.begin(115200);
#ifdef FAST_I2C
#if ARDUINO >= 157
Wire.setClock(400000UL); // Set I2C frequency to 400kHz (for Arduino Due)
#else
TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
#endif
#endif
pinMode(POWER_ENABLE_S1, OUTPUT);
pinMode(POWER_ENABLE_S2, OUTPUT);
digitalWrite(POWER_ENABLE_S1, HIGH);
digitalWrite(POWER_ENABLE_S2, HIGH);
Wire.begin();
scanI2C();
digitalWrite(POWER_ENABLE_S1,HIGH);
digitalWrite(POWER_ENABLE_S2,HIGH);
Sensor1.configure(3,detectedAddreses[0]);
Sensor2.configure(3,detectedAddreses[1]);
}
void measure(){
float s1;
float s2;
digitalWrite(POWER_ENABLE_S1,HIGH);
digitalWrite(POWER_ENABLE_S2,LOW);
delay(25);
Sensor1.waitForBusy();
Sensor1.takeRange();
Sensor1.waitForBusy();
s1 = Sensor1.readDistance(detectedAddreses[0]);
digitalWrite(POWER_ENABLE_S1,LOW);
digitalWrite(POWER_ENABLE_S2,HIGH);
delay(25);
Sensor2.waitForBusy();
Sensor2.takeRange();
Sensor2.waitForBusy();
s2 = Sensor2.readDistance(detectedAddreses[1]);
Serial.println("Sensor 1: " + String(s1) + "; Sensor 2: " + String(s2));
}
void loop()
{
/*Serial.println(detectedAddreses[0]);
Serial.println(detectedAddreses[1]);*/
measure();
}

Rotary encoder strange behaviour

I have problem with results (on serial monitor) of rotary encoder.
I am using Arduino UNO and RotaryEncoder library.
When I am running example code serial monitor show proper values when rotating with any speed.
I want to use encoder to change volume in Df-player.
Problem starts when I want to use this code together with more complicated one - Mp3 player.
It actually works only when I am rotating encoder very very slowly
#include <SPI.h>
#include <MFRC522.h>
#include <Arduino.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <RotaryEncoder.h>
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
#define PIN_IN1 2
#define PIN_IN2 3
#define ROTARYSTEPS 1
#define ROTARYMIN 0
#define ROTARYMAX 30
const int playPauseButton = 4;
const int shuffleButton = 5;
boolean isPlaying = false;
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
SoftwareSerial mySoftwareSerial(5, 6); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
// Setup a RotaryEncoder with 2 steps per latch for the 2 signal input pins:
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
// Last known rotary position.
int lastPos = -1;
//*****************************************************************************************//
void setup() {
Serial.begin(9600); // Initialize serial communications with the PC, COMMENT OUT IF IT FAILS TO PLAY WHEN DISCONNECTED FROM PC
mySoftwareSerial.begin(9600);
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
while (! Serial);
encoder.setPosition(5 / ROTARYSTEPS); // start with the value of 5.
pinMode(playPauseButton, INPUT_PULLUP);
pinMode(shuffleButton, INPUT_PULLUP);
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3.
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
}
Serial.println(F("DFPlayer Mini online. Place card on reader to play a spesific song"));
//myDFPlayer.volume(15); //Set volume value. From 0 to 30
//volumeLevel = map(analogRead(volumePot), 0, 1023, 0, 30); //scale the pot value and volume level
myDFPlayer.volume(5);
//prevVolume = volumeLevel;
//----Set different EQ----
myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
// myDFPlayer.EQ(DFPLAYER_EQ_POP);
// myDFPlayer.EQ(DFPLAYER_EQ_ROCK);
// myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);
// myDFPlayer.EQ(DFPLAYER_EQ_CLASSIC);
// myDFPlayer.EQ(DFPLAYER_EQ_BASS);
}
//*****************************************************************************************//
void loop() {
encoder.tick();
// get the current physical position and calc the logical position
int newPos = encoder.getPosition() * ROTARYSTEPS;
if (newPos < ROTARYMIN) {
encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
newPos = ROTARYMIN;
} else if (newPos > ROTARYMAX) {
encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
newPos = ROTARYMAX;
} // if
if (lastPos != newPos) {
Serial.println(newPos);
myDFPlayer.volume(newPos);
lastPos = newPos;
} // if
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
//some variables we need
byte block;
byte len;
MFRC522::StatusCode status;
if (digitalRead(playPauseButton) == LOW) {
if (isPlaying) {
myDFPlayer.pause();
isPlaying = false;
Serial.println("Paused..");
}
else {
isPlaying = true;
myDFPlayer.start();
Serial.println("Playing..");
}
delay(500);
}
if (digitalRead(shuffleButton) == LOW) {
myDFPlayer.randomAll();
Serial.println("Shuffle Play");
isPlaying = true;
delay(1000);
}
//-------------------------------------------
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( mfrc522.PICC_IsNewCardPresent()) {
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.println(F("**Card Detected:**"));
//-------------------------------------------
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); //uncomment this to see all blocks in hex
//-------------------------------------------
Serial.print(F("Number: "));
//---------------------------------------- GET NUMBER AND PLAY THE SONG
byte buffer2[18];
block = 1;
len = 18;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid)); //line 834
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer2, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT NUMBER
String number = "";
for (uint8_t i = 0; i < 16; i++)
{
number += (char)buffer2[i];
}
number.trim();
Serial.print(number);
//PLAY SONG
myDFPlayer.play(number.toInt());
isPlaying = true;
//----------------------------------------
Serial.println(F("\n**End Reading**\n"));
delay(1000); //change value if you want to read cards faster
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
}
Any ideas what is wrong?
You have a delay(1000) in your main loop, and since your RotaryEncoder object seems to need a tick() function, i am assuming that it is not interrupt driven. This means that it will check only once per second if it has moved to the next step.
If a rotary encoder is stepped twice, and the middle step is missed by the MCU, the latter has no way of knowing which way round the encoder has turned.
So in this case you can only turn it one step per second.
What you need is, either:
a free running main loop, which goes round at least 100 times per second. (less nice)
a rotary encoder driver that is interrupt driven. (very nice)
I don't know if such a library exists, because i tend not to use arduino libs, but it is a very good exercise to write your own using GPIO interrupts.

Atmega328P+ENC28J60 freezes after 12 hours operation

I am quite new to the ethernet world. So please bear with me if I do ask stupid questions.
I designed a PCB using Atmega328P+ENC28J60 chip (schematic attached in the images below - sch1, sch2). The function of this board is basically sending GET requests to the server and retrieve a set of json data to turn on output pins, so based on my understanding my board acts as a client only right? The code is attached below:
#include <EEPROM.h>
#include <ArduinoJson.h>
#include <EthernetENC.h>
#define OUT0 2
#define OUT1 A3
#define OUT2 A2
#define OUT3 A1
#define OUT4 A0
#define OUT5 9
#define OUT6 8
#define OUT7 7
#define OUT8 6
#define OUT9 5
#define CS 10
// mac: 46 57 5a 6b 48 51
#define HOSTNAME "autolighting.afa-sports.com"
#define ID_SIZE 6
static byte mac[ID_SIZE];
static char macBuffer[ID_SIZE*2+1];
const byte output[] PROGMEM = {OUT0, OUT1, OUT2, OUT3, OUT4, OUT5, OUT6, OUT7, OUT8, OUT9};
EthernetClient client;
void clientRead() {
StaticJsonDocument<40> filter;
StaticJsonDocument<120> doc;
filter.clear();
doc.clear();
filter["data"]["relay_actions"] = true;
client.find("\r\n\r\n");
deserializeJson(doc, client, DeserializationOption::Filter(filter));
client.flush();
delay(50);
if (!doc["data"]["relay_actions"].isNull()) {
for (byte i = 0; i < 10; i++) {
// Serial.print(doc["data"]["relay_actions"][i].as<bool>());
digitalWrite(pgm_read_byte_near(&output[i]), doc["data"]["relay_actions"][i].as<bool>());
}
// Serial.println();
}
filter.clear();
doc.clear();
}
void sendReq() {
client.println(F("GET /api/iot/master-controller/get-command HTTP/1.1"));
client.println(F("Host: autolighting.afa-sports.com"));
// client.println(F("DEVICE-ID: 46575a6b4851"));
client.print(F("DEVICE-ID: "));
client.println(macBuffer);
client.println(F("Connection: close"));
client.println();
}
void setup() {
Serial.begin(115200);
for (byte i = 0; i < 10; i++) {
pinMode(pgm_read_byte_near(&output[i]), OUTPUT);
digitalWrite(pgm_read_byte_near(&output[i]), LOW);
}
for (uint8_t i = 0; i < ID_SIZE; i++) {
byte charByte = EEPROM.read(i);
if (charByte != 0) {
char temp[2];
mac[i] = charByte;
itoa(mac[i], temp, 16);
strcat(macBuffer, strlwr(temp)); // REMOVE strlwr IN RELEASE VERSION
free(temp);
delay(10);
}
}
strcat(macBuffer, '\0');
Ethernet.init(CS);
while (!Ethernet.begin(mac));
client.setTimeout(5000);
delay(1000);
}
void loop() {
while (!Ethernet.begin(mac)); // init fail
delay(1000);
if (client.connect(HOSTNAME, 80)) {
sendReq();
clientRead();
client.stop();
}
delay(3000);
}
Due to the high SRAM consumption and I might have other things (not sure what yet) to add in to the board in the future, I tried to minimize the dynamic memory by changing this (in the uipethernet-conf.h file):
#define UIP_SOCKET_NUMPACKETS 5
#define UIP_CONF_MAX_CONNECTIONS 4
#define UIP_CONF_UDP_CONNS 4
to this:
#define UIP_SOCKET_NUMPACKETS 2
#define UIP_CONF_MAX_CONNECTIONS 2
#define UIP_CONF_UDP_CONNS 1
I'm wondering will this affect the system performance? Btw, I've also set the timeout to 5s
After 13 hours of smooth operation, the board freezes and only became normal when i hard-reset the board. Currently, I'm connecting my board to a wireless extender, because I am not sitting right next to the wifi router. It looks like a memory leakage issue to me, but does memory leakage issue still exists in the latest ArduinoJson and EthernetENC/UIPEthernet library?
P/S: I was using UIPEthernet.h previously, but someone guided me to try out the EthernetENC library, the memory consumption definitely gone down a little bit, but the freezing problem still persist
Feel free to point out any mistakes I make, still in the learning adventure. =)
Your help is greatly appreciated. Thank you very much.
Library version:
ArduinoJson 6.17.2
EthernetENC 2.0.0
UIPEthernet 2.0.9
Schematics:
don't have these hardware, only analyses based on your code and Arduino references.
from your code
while (!Ethernet.begin(mac)); // init fail
which "may" cause freeze if :
Ethernet.begin always return false
is Ethernet.begin "ok" to call multiple time ?
These are some reference which may help :
Arduino DHCP failed to configure
https://electronics.stackexchange.com/questions/67045/ethernet-begin-only-works-with-sd-card-removed-why
according to the wiki :
https://github.com/jandrassy/EthernetENC/wiki/Examples
You should use :
Ethernet.maintain();
client.available();
write your own delay function :
void mDelay(unsigned long milliseconds) {
const unsigned d = 1;
while (milliseconds > d) {
Ethernet.maintain();
delay(d);
milliseconds -= d;
}
Ethernet.maintain();
delay(milliseconds);
}
note: for arduino or electronics question, may be
https://electronics.stackexchange.com/
is a more suitable site.
not sure about if the hardware can handle, you may try to "burst test" :
void loop() {
unsigned long currentMillis = millis();
Serial.print("Time: ");
Serial.println(currentMillis);
// burst test
if (client.connect(HOSTNAME, 80)) {
sendReq();
Serial.print(", After sendReq(); ");
clientRead();
Serial.print(", After client.clientRead(); ");
client.stop();
Serial.print(", After client.stop(); ");
}
mdelay( 500 );
Serial.println( ', mdelay(500); ' );
// mdelay will call Ethernet.maintain();
// Serial.print("After Ethernet.maintain(); ");
// use Serial.println to check where it freeze ?
}
based on your code, may be these can help to reduce memory problem :
move these two outside of void clientRead() as these 2 used every 4 seconds :
4 sec * 60 = 240 times / minutes
240 * 24 = 5760 times / day
StaticJsonDocument<40> filter;
StaticJsonDocument<120> doc;
void clientRead() {
...
note: all the code are illustration, not tested. you may need to modify to make it work.
note: please check with your hardware if it can handle, or will it burn when run such test ?
lots of thanks to #ocrdu and #Flash Ang for the suggestions, the code below is the working code and my PCB has been running continuously for 58 hours till now:
#include <EEPROM.h>
#include <ArduinoJson.h>
//#include <UIPEthernet.h>
#include <EthernetENC.h>
#define OUT0 2
#define OUT1 A3
#define OUT2 A2
#define OUT3 A1
#define OUT4 A0
#define OUT5 9
#define OUT6 8
#define OUT7 7
#define OUT8 6
#define OUT9 5
#define CS 10
// host name: "autolighting.afa-sports.com
// resource: "/api/iot/master-controller/get-command"
// mac: 46 57 5a 6b 48 51
// mac (char): FWZkHQ
#define HOSTNAME "autolighting.afa-sports.com"
#define ID_SIZE 6
static byte mac[ID_SIZE];
static char macBuffer[ID_SIZE*2+1];
const byte output[] PROGMEM = {OUT0, OUT1, OUT2, OUT3, OUT4, OUT5, OUT6, OUT7, OUT8, OUT9};
EthernetClient client;
void clientRead() {
StaticJsonDocument<40> filter;
StaticJsonDocument<120> doc;
filter.clear();
doc.clear();
filter["data"]["relay_actions"] = true;
client.find("\r\n\r\n");
deserializeJson(doc, client, DeserializationOption::Filter(filter));
client.flush();
delay(50);
if (!doc["data"]["relay_actions"].isNull()) {
for (byte i = 0; i < 10; i++) {
// Serial.print(doc["data"]["relay_actions"][i].as<bool>());
digitalWrite(pgm_read_byte_near(&output[i]), doc["data"]["relay_actions"][i].as<bool>());
}
// Serial.println();
}
filter.clear();
doc.clear();
}
void sendReq() {
client.println(F("GET /api/iot/master-controller/get-command HTTP/1.1"));
client.println(F("Host: autolighting.afa-sports.com"));
// client.println(F("DEVICE-ID: 46575a6b4851"));
client.print(F("DEVICE-ID: "));
client.println(macBuffer);
client.println(F("Connection: close"));
client.println();
}
void setup() {
for (byte i = 0; i < 10; i++) {
pinMode(pgm_read_byte_near(&output[i]), OUTPUT);
digitalWrite(pgm_read_byte_near(&output[i]), LOW);
}
for (uint8_t i = 0; i < ID_SIZE; i++) {
byte charByte = EEPROM.read(i);
if (charByte != 0) {
char temp[2];
mac[i] = charByte;
itoa(mac[i], temp, 16);
strcat(macBuffer, strlwr(temp)); // REMOVE strlwr IN RELEASE VERSION
free(temp);
delay(10);
}
}
strcat(macBuffer, '\0');
Ethernet.init(CS);
while (!Ethernet.begin(mac));
client.setTimeout(5000);
delay(1000);
}
void loop() {
Ethernet.maintain();
while (!Ethernet.begin(mac)) //Serial.println(F("IF")); // init fail
// Serial.println(F("IS")); // init success
delay(1000);
if (client.connect(HOSTNAME, 80)) {
sendReq();
clientRead();
client.stop();
}
Ethernet.maintain();
delay(3000);
}
//int freeRam () { // check remaining RAM space
// extern int __heap_start, *__brkval;
// int v;
// return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
//}
This time to work smart, I used the default ip address to my router and monitor whether my PCB is still connected to the router by looking at the DHCP client list, and remove all the serial commands in the code. I'm afraid for my past experiences on the disconnection issues, it might be just a short temporary disconnect from the router, instead of a permanent disconnection.
I'm sorry if there is any lack of testing in this program. Thank you for the time spent to provide me all the useful suggestions =)

Trying to use DHT11 with a PxMatrix display on ESP32 board

I'm trying to display the readings from a DHT11 onto an LED Matrix. I can get the basic display to work, the issue is when I also put the time on the display. I started with the Morphing Clock as a base for the time then used the Adafruit Sensor code to read the DHT11. The issue seems to be with"
timerAlarmWrite(timer, 2000, true);
Which is setup to call:
void IRAM_ATTR display_updater(){
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
display.display(10);
portEXIT_CRITICAL_ISR(&timerMux);
}
If I slow the timer down I can get readings from the DHT11 but the morphing time display doesn't update enough to look fluid. I'm new to coding for these devices so I'm not sure where I should be looking to move these things out of each others way. Here is the full app if the timer is set to something above 25000 you will get temp results most of the time, but the less are dimmer and the colons flash (they shouldn't).
#define double_buffer
#include <PxMatrix.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include "Digit.h"
#include <Adafruit_Sensor.h>
#include <DHT.h>
const char* ssid = "Gallifrey";
const char* password = "ThisIsAGoodPlaceToPutAPassword!";
// ESP32 Pins for LED MATRIX
#define P_LAT 22
#define P_A 19
#define P_B 23
#define P_C 18
#define P_D 5
#define P_E 15 // NOT USED for 1/16 scan
#define P_OE 2
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D,P_E);
void IRAM_ATTR display_updater(){
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
display.display(10);
portEXIT_CRITICAL_ISR(&timerMux);
}
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;
unsigned long prevEpoch;
byte prevhh;
byte prevmm;
byte prevss;
//====== Digits =======
Digit digit0(&display, 0, 63 - 1 - 9*1, 17, display.color565(0, 250, 0));
Digit digit1(&display, 0, 63 - 1 - 9*2, 17, display.color565(0, 250, 0));
Digit digit2(&display, 0, 63 - 4 - 9*3, 17, display.color565(0, 250, 0));
Digit digit3(&display, 0, 63 - 4 - 9*4, 17, display.color565(0, 250, 0));
Digit digit4(&display, 0, 63 - 7 - 9*5, 17, display.color565(0, 250, 0));
Digit digit5(&display, 0, 63 - 7 - 9*6, 17, display.color565(0, 250, 0));
#define DHTPIN 27
#define DHTTYPE DHT11
//DHT_Unified dht(DHTPIN, DHTTYPE);
DHT dht(DHTPIN, DHTTYPE);
//DHT dht;
const uint32_t delayMS = 6000;
uint32_t lastRead;
void setup() {
display.begin(16); // 1/16 scan
display.setFastUpdate(true);
// Initialize Serial Monitor
Serial.begin(115200);
pinMode(DHTPIN, INPUT_PULLUP);
dht.begin();
// // Set delay between sensor readings based on sensor details.
lastRead = 0;
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &display_updater, true);
timerAlarmWrite(timer, 1500, true); /// The Problem is Here!!!???!!!!?
timerAlarmEnable(timer);
display.fillScreen(display.color565(0, 0, 0));
digit1.DrawColon(display.color565(100, 175, 0));
digit3.DrawColon(display.color565(100, 175, 0));
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Initialize a NTPClient to get time
timeClient.begin();
timeClient.setTimeOffset(-28800);
}
void loop() {
while(!timeClient.update()) {
timeClient.forceUpdate();
}
formattedDate = timeClient.getFormattedDate();
// Extract date
int splitT = formattedDate.indexOf("T");
dayStamp = formattedDate.substring(0, splitT);
// Extract time
timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
displayLocalTemp();
updateTimeDisplay();
}
String readDHTTemperature() {
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
// Read temperature as Celsius (the default)
float t = dht.readTemperature(true);
// Read temperature as Fahrenheit (isFahrenheit = true)
//float t = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again).
if (isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return "--";
}
else {
Serial.println(t);
return String(t);
}
}
String readDHTHumidity() {
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
if (isnan(h)) {
Serial.println("Failed to read from DHT sensor!");
return "--";
}
else {
Serial.println(h);
return String(h);
}
}
void displayLocalTemp() {
uint32_t currentTime = millis();
uint32_t waited = currentTime - lastRead;
static String lastTemp;
static String lastHumid;
if (waited > delayMS) {
lastRead = currentTime;
String temp = readDHTTemperature();
String humidity = readDHTHumidity();
String preTemp = "T:";
String preHumidity = "H:";
String tempDisplay = preTemp + temp;
String humidDisplay = preHumidity + humidity;
Serial.print("temp: ");
Serial.print(temp);
Serial.print(" -- humidity: ");
Serial.println(humidity);
display.setTextColor(display.color565(0,0,0));
display.setCursor(20,16);
display.print(lastTemp);
display.setCursor(20,25);
display.print(lastHumid);
display.setTextColor(display.color565(0,255,0));
display.setCursor(20,16);
display.print(tempDisplay);
display.setCursor(20,25);
display.print(humidDisplay);
lastTemp = tempDisplay;
lastHumid = humidDisplay;
}
}
void updateTimeDisplay() {
unsigned long epoch = timeClient.getEpochTime();
if (epoch != prevEpoch) {
int hh = timeClient.getHours();
int mm = timeClient.getMinutes();
int ss = timeClient.getSeconds();
if (hh > 12) hh = hh % 12;
if (prevEpoch == 0) { // If we didn't have a previous time. Just draw it without morphing.
digit0.Draw(ss % 10);
digit1.Draw(ss / 10);
digit2.Draw(mm % 10);
digit3.Draw(mm / 10);
digit4.Draw(hh % 10);
digit5.Draw(hh / 10);
}
else
{
// epoch changes every miliseconds, we only want to draw when digits actually change.
if (ss!=prevss) {
int s0 = ss % 10;
int s1 = ss / 10;
if (s0!=digit0.Value()) digit0.Morph(s0);
if (s1!=digit1.Value()) digit1.Morph(s1);
//ntpClient.PrintTime();
prevss = ss;
}
if (mm!=prevmm) {
int m0 = mm % 10;
int m1 = mm / 10;
if (m0!=digit2.Value()) digit2.Morph(m0);
if (m1!=digit3.Value()) digit3.Morph(m1);
prevmm = mm;
}
if (hh!=prevhh) {
int h0 = hh % 10;
int h1 = hh / 10;
if (h0!=digit4.Value()) digit4.Morph(h0);
if (h1!=digit5.Value()) digit5.Morph(h1);
prevhh = hh;
}
}
prevEpoch = epoch;
}
}
You could try to assign tasks explicitly to a core.
When you start playing with ESP32 multi core code execution be aware of the following issues:
Both the setup and the main loop functions execute with a priority of 1.
Arduino main loop runs on core 1.
The execution is pinned, so it’s not expected that the core will change during execution of the program
On FreeRTOS (the underlying OS), tasks have an assigned priority which the scheduler uses to decide which task will run.
High priority tasks ready to run will have preference over lower priority tasks, which means that as long as a higher priority task can run, a lower priority task will not have the CPU.
CAUTION shared resources like Serial might be potential issues. Due to two core tasks accessing uncoordinated the same hardware may lead to deadlocks and crashes
For implementation purposes, you need to take in consideration that FreeRTOS priorities are assigned from 0 to N, where lower numbers correspond to lower priorities. So, the lowest priority is 0.
First of all, declare a global variable that will contain the number of the core where the FreeRTOS task to launch will be pinned
static int taskCore = 0; // The core the task should run on
now create the assignment of a task to the core in Setup()
xTaskCreatePinnedToCore(
myCoreTask, /* Function to implement the task */
"myCoreTask", /* Name of the task */
10000, /* Stack size in words */
NULL, /* Task input parameter */
0, /* Priority of the task */
NULL, /* Task handle. */
taskCore); /* Core where the task should run */
Here is a test function which you call in loop()
void myCoreTask( void * pvParameters ){
while(true){
Serial.println("Task running on core ");
Serial.print(xPortGetCoreID());
// This is here to show that other tasks run
// NEVER use in production
delay(1000);
}
}
Hope this gives you an idea how to tackle your problem, read more here RTOS and here ESP32-IDF

Arduino uint8_t variables

I'm having trouble with some arduino code. im using an Ethernet tutorial code i found and some IR emitter and receiver code i found, and im trying to combine them.
http://www.ladyada.net/learn/sensors/ir.html
http://g33k.blogspot.com/2010/09/arduino-data-webserver-sample-web.html
Both codes work fine by themselves.
The code compiles but when i call the following void IRDetector() , it doesn't work. I have debugged it and so far ive found when i use the variable uint8_t or uint16_t (ive tried replacing them with ints and longs). Do i have to import and libraries to use uint8_t ? Any thoughts?
Any help would be appreciated.
uint16_t pulses[100][2]; // pair is high and low pulse
uint8_t currentpulse = 0; // index for pulses we're storing
uint8_t highpulse, lowpulse; // temporary storage timing
void IRDetectCode(void)
{
while(true){
highpulse = lowpulse = 0; // start out with no pulse length
while (IRpin_PIN & (1 << IRpin)) {
// pin is still HIGH
// count off another few microseconds
highpulse++;
delayMicroseconds(RESOLUTION);
// If the pulse is too long, we 'timed out' - either nothing
// was received or the code is finished, so print what
// we've grabbed so far, and then reset
if ((highpulse >= MAXPULSE) && (currentpulse != 0)) {
Serial.print(" usec, ");
// printpulses();
//currentpulse=0;
return;
}
}
// we didn't time out so lets stash the reading
pulses[currentpulse][0] = highpulse;
// same as above
while (! (IRpin_PIN & _BV(IRpin))) {
// pin is still LOW
Serial.print(" usec, ");
lowpulse++;
delayMicroseconds(RESOLUTION);
if ((lowpulse >= MAXPULSE) && (currentpulse != 0)) {
// printpulses();
// currentpulse=0;
return;
}
}
//pulses[currentpulse][1] = lowpulse;
// we read one high-low pulse successfully, continue!
currentpulse++;
}
}
void printpulses(void) {
Serial.println("\n\r\n\rReceived: \n\rOFF \tON");
for (uint8_t i = 0; i < currentpulse; i++) {
Serial.print(pulses[i][0] * RESOLUTION, DEC);
Serial.print(" usec, ");
Serial.print(pulses[i][1] * RESOLUTION, DEC);
Serial.println(" usec");
}
// print it in a 'array' format
Serial.println("int IRsignal[] = {");
Serial.println("// ON, OFF (in 10's of microseconds)");
for (uint8_t i = 0; i < currentpulse-1; i++) {
Serial.print("\t"); // tab
Serial.print(pulses[i][1] * RESOLUTION / 10, DEC);
Serial.print(", ");
Serial.print(pulses[i+1][0] * RESOLUTION / 10, DEC);
Serial.println(",");
}
Serial.print("\t"); // tab
Serial.print(pulses[currentpulse-1][1] * RESOLUTION / 10, DEC);
Serial.print(", 0};");
}
The uint8_t is a unsigned integer on 8 bits. In Arduino, it's called a "byte", so you can use it like that:
for (byte i = 0; i < currentpulse; i++) {....
It's far better than using the Arduino's "int" type (== int16_t) or "unsigned int" (== uint16_t) because the ATmega328 is 8-bit. So handling an 8-bit var is faster (a lot).
I hope it can help.