for a project of mine with an esp32, I would like to read a string (in mine case a mac address) from serial monitor and copy that into EEPROM.
once the esp32 reboots or starts up looks for this mac address in EEPROM and stores it in a var. so the esp32 could later send to that mac address.
I'm new to reading from serial and storing it for later retrieval.
where is a great place to start?
i already have experience with espnow so that isn't an issue.
Thank you 6v6gt for that information.
the first thing I have tried is changing example 3 to my liking.
I don't fully understand how the "Preferences" library would work. it's a bit over my head.
so added EEPROM commands for reading and writing to EEPROM.
tried it to compile it but got a:
invalid conversion from 'char*' to 'uint8_t {aka unsigned char}'
I know what it means. i can't get my head around how to convert a char to an int. I
s here where the "Preferences" library comes into play?
// the current address in the EEPROM (i.e. which byte
// we're going to write to next)
#include "EEPROM.h"
int addr = 0;
#define EEPROM_SIZE 32
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
void setup() {
Serial.begin(115200);
if (!EEPROM.begin(EEPROM_SIZE))
{
Serial.println("failed to initialise EEPROM"); delay(1000000);
}
Serial.print("this has been written to EEPROM");
Serial.println(EEPROM.read(0));
Serial.println();
}
void loop() {
recvWithStartEndMarkers();
showNewData();
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void showNewData() {
if (newData == true) {
Serial.print("This just in ... ");
Serial.println(receivedChars);
Serial.print("writing to EEPROM");
EEPROM.write(0, receivedChars);
EEPROM.commit();
delay(100);
Serial.print("this has been written to EEPROM");
Serial.println(EEPROM.read(0));
newData = false;
}
}
so I wanted to do a little update. (also linked the code below.
I have come further with saving the mac address to EEPROM and later retrieving it. This is now successful without checking if the mac address is valid. send a example mac address with ma:cA:dr:es:s1
the next challenge I can't really wrap my head around it.
get this example mac address (B4:E6:2D:FB:29:31) into this array:
i have got it almost working. entered the mac address and got a "loadprohibited" and strtoul() is causing it.
it's probably a wrong address pointer.
the only thing i have added to the code is this void:
char *token;
char *ptr;
void axe() {
int a = 0;//counter for printing what's inside broadcastAddress[]
const char s[2] = ":";
/* Serial.print("before STRTOK: ");
for(int i; i< 6; i++) Serial.print(broadcastAddress[i]);
Serial.println(" before STRTOK");*/
/* get the first token */
token = strtok(read_eeprom, s);
/* walk through other tokens */
while ( token != NULL ) {
Serial.println(token);
token = strtok(NULL, s);
broadcastAddress[a] = strtoul(token, &ptr, 2);// here is where the LoadProhibited is comming from.
Serial.print(a); Serial.print(" ");
Serial.println(broadcastAddress[a]);
a++;
}
/* Serial.print("after STRTOK: ");
for(int i; i< 6; i++) Serial.print(broadcastAddress[i]);
Serial.println(" after STRTOK");*/
Serial.print("Choping it up done"); * /
}
i have found something else than EEPROM, SPIFFS. this could be much easier and its almost the same as eeprom.
this way the esp32 could save the mac address in a text doc.
this option is also there.
Here is a fixed version, tested on a Nano. I think that there were two problems
1)
See reply #8
2)
In the while loop, you immediately called a strtok() again; that should have been done after you did process the initial token (so at the end of the while-loop).
You can harden the code to reject invalid characters in the input and add a warning.
Instead of storing the text in EEPROM, it might be better to store the 6 bytes in EEPROMl saves some space.
#include <EEPROM.h>
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
char read_eeprom[numChars];
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};//B4:E6:2D:FB:29:31 and sending works just fine.
void setup()
{
Serial.begin(57600);
}
void loop()
{
recvWithStartEndMarkers();
showNewData();
}
void recvWithStartEndMarkers()
{
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false)
{
rc = Serial.read();
if (recvInProgress == true)
{
if (rc != endMarker)
{
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars)
{
ndx = numChars - 1;
}
}
else
{
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker)
{
recvInProgress = true;
}
}
}
int Read = 12;
void showNewData()
{
if (newData == true)
{
Serial.print("I recieved this: ");
Serial.println(receivedChars);
Serial.println("writing to EEPROM ");
EEPROM.put(0, receivedChars);
delay(100);
Serial.print("this has been written to EEPROM ");
EEPROM.get(0, read_eeprom);
Serial.println(read_eeprom);
axe();
newData = false;
}
}
void axe()
{
Serial.println(F("Entering axe"));
char *token;
char *ptr;
int a = 0;//counter for printing what's inside broadcastAddress[]
const char s[2] = ":";
Serial.println(read_eeprom);
/* get the first token */
token = strtok(read_eeprom, s);
/* walk through other tokens */
while ( token != NULL )
{
Serial.println(token);
broadcastAddress[a] = strtoul(token, &ptr, 16);// here is probably where the LoadProhibited is comming from.
Serial.print(a); Serial.print(" > ");
Serial.println(broadcastAddress[a], HEX);
a++;
token = strtok(NULL, s);
}
Serial.print("Choping it up done");
}
Output:
I recieved this: B4:E6:2D:FB:29:31
writing to EEPROM
this has been written to EEPROM B4:E6:2D:FB:29:31
Entering axe
B4:E6:2D:FB:29:31
B4
0 > B4
E6
1 > E6
2D
2 > 2D
FB
3 > FB
29
4 > 29
31
5 > 31
Choping it up done
i have found something else than EEPROM, SPIFFS. this could be much easier and its almost the same as eeprom.
In my experience, using eeprom.h with .put() and .get() is more simple.
I tested the code posted by @sterretje on an ESP32, and it works as intended after the necessary changes for the esp32 you previously used in your code-- reserving a size, using that size in .begin() and using .commit() on the write.
Since sterretje has made a post. I have tested and added some commands just like cattledog did and the code works without an issue.
now the next thing on my list is to add a check if receivedChars is a valid MAC address and if it's not the same as stored in EEPROM or read_eeprom. (sorry if I'm a bit too demanding....)
i would like to have a little help with this.
also here below I have posted the code that worked for me.
#include <EEPROM.h>
#define EEPROM_SIZE 32
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
char read_eeprom[numChars];
//< 34:7d:f6:b2:01:1f > here is a test mac address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};//B4:E6:2D:FB:29:31 and sending works just fine.
void setup()
{
Serial.begin(115200);
delay(2000);
Serial.println(" ");
Serial.println("Entering setup");
if (!EEPROM.begin(EEPROM_SIZE)) {
Serial.println("failed to initialise EEPROM");
delay(1000000);
}
EEPROM.get(0, read_eeprom);
Serial.print("this has been Stored EEPROM: ");
Serial.println(read_eeprom);
axe();
Serial.println("Setup done");
}
void loop()
{
recvWithStartEndMarkers();
showNewData();
}
void recvWithStartEndMarkers()
{
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false)
{
rc = Serial.read();
if (recvInProgress == true)
{
if (rc != endMarker)
{
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars)
{
ndx = numChars - 1;
}
}
else
{
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker)
{
recvInProgress = true;
}
}
}
void showNewData()
{
if (newData == true)
{
Serial.print("I recieved this: "); Serial.print(receivedChars);
Serial.print(" And this has been written to EEPROM: ");
EEPROM.get(0, read_eeprom); Serial.println(read_eeprom);
delay(100);
// later add a check if receivedChars and read_eeprom are different.
Serial.print("writing to EEPROM: "); Serial.println(read_eeprom);
EEPROM.writeString(0, receivedChars);
EEPROM.commit();
delay(100);
axe();
Serial.println(" ");
newData = false;
}
}
void axe()
{
Serial.println("Entering axe");
// later add a check if the mac Address is valid
char *token;
char *ptr;
int a = 0;//counter for printing what's inside broadcastAddress[]
const char s[2] = ":";
Serial.println(read_eeprom);
// get the first token
token = strtok(read_eeprom, s);
// walk through other tokens
while ( token != NULL ) {
//Serial.println(token);
broadcastAddress[a] = strtoul(token, &ptr, 16);
Serial.print(a); Serial.print(" > ");
Serial.print(broadcastAddress[a], HEX);
Serial.print(" | ");
a++;
token = strtok(NULL, s);
}
Serial.println("");
Serial.println("Choping it up done");
}
now the next thing on my list is to add a check if receivedChars is a valid MAC address and if it's not the same as stored in EEPROM or read_eeprom. (sorry if I'm a bit too demanding....)
I would like to have a little help with this.
What are the criteria that make receivedChars a valid MAC address?
Both receivedChars and read_eeprom are null terminated character arrays (c-strings) and you can compare them with strcmp() or strncmp()
What is the purpose of what you are trying to do?
One issue with comparing the strings is that you are exposed issues like b4 is not equal to B4, and you are sensitive to extra blank spaces added by error. You may want to do more error checking on length or conditioning on receivedChars[] with toupper or tolower.
You could also test the numerical byte broadcastAddress[] arrays generated by the stored and entered data.
A valid MAC address always has a length of 17 bytes (6x2 plus 5 colons).
2)
A token has a length of two bytes.
3)
After you retrieved a token, you can use isHexadecimalDigit() to check if the characters in the token are valid hex characters.
4)
The (end)ptr in the call to strtoul() can give an indication; based on man strtol (same applies to strtoul).
if (endptr == token)
{
// error, no hex digits
}
if (*endptr != '\0')
{
// warning, there is more data in the token
}
Hi all!
First of all, Thank you all for your help! For the last few days, I didn't have time to work and debug the code. Today I did and didn't get it working. it has some conflicts and eventually gave up.
So wanted to get some help again and saw Sterretjes last post. This is precisely what I wanted!
My only concern is writing to EEPROM too much will damage it. Could it be possible to look at what MAC address is inside EEPROM and compare it to the received MAC address? If there are the same don't go further.
I totally loved this challenge of using new functions I never heard of! (btw thanks for writing down these checks it really helped me!)
sterretje:
There are some checks
A valid MAC address always has a length of 17 bytes (6x2 plus 5 colons).
2)
A token has a length of two bytes.
3)
After you retrieved a token, you can use isHexadecimalDigit() to check if the characters in the token are valid hex characters.
4)
The (end)ptr in the call to strtoul() can give an indication; based on man strtol (same applies to strtoul).
This is what I came up with the help of these 4 testings points:
Don't mind running it. it WON'T compile or run. i have learned myself to code so.
i will use Sterretje genius, organised and working code.
#include <EEPROM.h>
#define EEPROM_SIZE 18
const byte numChars = 30;
char receivedChars[numChars];
boolean newData = false;
char read_eeprom[numChars];
//<34:7d:f6:b2:01:1f> here is a test mac address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};//B4:E6:2D:FB:29:31 and sending works just fine.
void setup()
{
Serial.begin(115200);
delay(2000);
Serial.println(" ");
Serial.println("Entering setup");
if (!EEPROM.begin(EEPROM_SIZE)) {
Serial.println("failed to initialise EEPROM");
delay(1000000);
}
EEPROM.get(0, read_eeprom);
Serial.print("this has been Stored EEPROM: ");
Serial.println(read_eeprom);
axe();
}
void loop()
{
recvWithStartEndMarkers();
showNewData();
}
void recvWithStartEndMarkers()
{
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false)
{
rc = Serial.read();
if (recvInProgress == true)
{
if (rc != endMarker)
{
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars)
{
ndx = numChars - 1;
}
}
else
{
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker)
{
recvInProgress = true;
}
}
}
int Read = 12;
void showNewData()
{
if (newData == true)
{
int counter = 0;
newData = false;
Serial.print("I recieved this: "); Serial.println(receivedChars);
for (byte i = 0; i < sizeof(receivedChars); i++) if (receivedChars[i] != '\0') counter++;
// Serial.println(counter);
if (counter == 17) {
axe();
// later add a check if receivedChars and read_eeprom are different.
Serial.print(" This has been written to EEPROM: ");
EEPROM.get(0, read_eeprom); Serial.println(read_eeprom);
delay(100);
Serial.println(" ");
} else Serial.println("Expected 17 characters length for MAC address");
}
}
void axe()
{
Serial.println("Entering axe");
// later add a check if the mac Address is valid
char *token;
char *ptr;
int a = 0;//counter for printing what's inside broadcastAddress[]
const char s[2] = ":";
Serial.print("I received this: '");
Serial.print(receivedChars);
Serial.println("'");
// get the first token
token = strtok(receivedChars, s);
// walk through other tokens
while ( token != NULL ) {
//Serial.println(token);
if (strlen(token) != 2){
Serial.println("Expected 2 characters for Mac length");
return;
}
Serial.print(" | ");
broadcastAddress[a] = strtoul(token, &ptr, 16);
Serial.print(a); Serial.print(" > ");
Serial.print(broadcastAddress[a], HEX);
if (isHexadecimalDigit(token[a])) {Serial.print(" Yes");} else {Serial.print(" No");}
Serial.print(" | ");
a++;
token = strtok(NULL, s);
}
Serial.println("writing to EEPROM: "); Serial.println(broadcastAddress);
EEPROM.writeString(0, broadcastAddress);
EEPROM.commit();
Serial.println("");
Serial.println("Choping it up done");
}
How often are you planning to update? Flash memory has a life of 10,000 write cycles (AVR, check the spec of the ESP).
The put in my code will not update an eeprom cell if it does not change; that applies to AVR, I don't know if the ESP works in similar fashion.
But yes, you can implement it yourself; you just need an other variable to store the MAC that is read from the eeprom and the MAC that you received and want to store. Use memcmp to compare the two and decide based on that.