Reading and writing the EEPROM from the Serial Monitor

First of all, yes, I saw the other topic opened down below by another guy for exactly this same problem, he is probably part of the Coursera course Interfacing with the Arduino, in which, in the assignment for Week 3 we are asked to develop a program that takes two commands: “read argument1” and “write argument1 argument2” from the Serial Monitor as an input from the user, and then uses those commands to read and write the EEPROM.

Now, I want to say that I’m seriously ashamed of that guy, when you enroll in a Coursera course you are asked to agree with the Coursera Honor Code, that among other things says: “My answers to homework, quizzes, and exams will be my own work”. He didn’t even made the attempt of trying, he just wants someone to do all the code for him (which makes me wonder why is he even in the course if he is not interested in learning), and probably he will notice this thread and copy what I’ve got of code up to this point, but I really need some help because I can’t figure out why is not working as intended.

I’ve written some comments in my code to illustrate all it does, but I will explain here shortly what it should do: the program, as stated before, should take one of those two commands. If the command entered is “read”, it should have one argument, that is one numeric value between 0 and 1023, both included. That argument will be passed later as the address for the EEPROM.read function.

If the command entered is “write”, it should have two arguments, both numeric values, first one must be between 0 and 1023, both included, and the second argument must be a number between 0 and 255, again, both included. The first argument will be the address, and the second argument will be the value that I want to write into that address using the EEPROM.write function.

#include <EEPROM.h>

String command;

void setup() {
  Serial.begin(9600);
}

void loop() {

  if (Serial.available() > 0) {
    command = Serial.readStringUntil('\n'); // I use this to take what I type and store it as a String
    String commandRead = command;
    String commandWrite = command;
    String sRead = "read";
    String sWrite = "write";
    commandRead.remove(4);
    commandWrite.remove(5);
    //Here I create a few String just to be able to compare them later, so if it detects
    //the string "read", it deletes everything after that in a new String, made only so I can
    //compare it to the String sRead later in the if, if it's the same, then it enters in the if
    //made for the "read" command, if it detects "write", then it goes to the "write" if instead.

    if (commandRead.equals(sRead)) {
      String sreadArg1 = command.substring(5); //With this I take the only argument from the "read"
                                                                 //command
      int readArg1 = sreadArg1.toInt(); //I turn that argument it into an int

      if (readArg1 > 1023 || readArg1 < 0) {
        Serial.println("Sorry, that value is not valid, please input a number between 1 and 1024, both included.");
        //Here I make sure the number entered it's a valid one
      }

      else { //If it's a valid one then it comes to this else and reads the address from the EEPROM
        int valueEEP = EEPROM.read(readArg1); //For the address it uses the argument from the "read"
        Serial.print("The value in that address is ");
        Serial.println(valueEEP, DEC);
      }
    }

    if (commandWrite.equals(sWrite)) { //Here is the if for the "write" command
      String swriteArgs = command;
      swriteArgs.remove(0, 6); //I remove the "write" part from the string, I no longer need it
      int swriteSpace = swriteArgs.indexOf(' '); //I find out where the space between the two arguments is
      String swriteArg1 = swriteArgs.substring(0, swriteSpace); //I use this to get the first argument
      int writeArg1 = swriteArg1.toInt(); //And then I turn it into an int
      String swriteArg2 = swriteArgs.substring(swriteSpace + 1); //This is to get the second argument
      int writeArg2 = swriteArg2.toInt(); //And to turn it into an int too

      if (writeArg1 < 0 || writeArg1 > 1023 || writeArg2 < 0 || writeArg2 > 255) {
        Serial.println("Sorry, but your arguments aren't valid. The first argument should be a number between 0 and 1023, both included. And the second argument should be a number between 0 and 255, both included.");
        //This tests if both the two arguments are valid numbers
      }

      else { //If they are it comes to this else that writes into the EEPROM
        byte byte1Arg2 = writeArg2 & 0XFF;
        byte byte2Arg2 = (writeArg2 >> 8) & 0XFF;
        EEPROM.write(writeArg1, byte1Arg2);
        EEPROM.write(writeArg1 + 1, byte2Arg2);
        Serial.println("You wrote the value to that address successfully.");
        //What I do here is divide the int (that int is the second argument from the "write" command),
        //that is 2 bytes long, to two separated bytes so I can write them on the EEPROM.
        //I used a little Endian ordering for this, so the second half of the int is written in the address
        //(with that address being the first argument), and then the first half of the int written into the
        //next address.
      }
    }
  }
}

To give a little more information, I’m trying this on the 123d.circuits.io simulator. And for the simulation I use an Arduino UNO Rev3. What is wrong about my code is that if I use only “read”, all is fine, it goes and reads that value, and I can repeat it as many times as I want to. However, if I use the “write” command, supposedly it will write it (because I get the print message after it’s done writing), but then it won’t let me use any other commands at all again, neither “read” or “write”. And since I can’t use “read” I can’t check if it really wrote the value I sent it to the EEPROM either. So any idea of why it gets like stuck after I use the “write” command?

By the way, in case someone understands it better, I took a screenshot of what the code looks like in the Arduino IDE, with all the syntax highlight, that could be helpful to see the code clearer.

Click here to have a full view of it

Thanks in advance to everyone who takes the time to read the whole post and answer to it.

This is one of those exercises where it seems like using capital-S Strings is okay because it's such a small program you couldn't possibly run out of SRAM. Then you go putting long constant strings into SRAM and it is looking seriously big for the little Arduino's memory. Two copies of the incoming command created every time something comes in?

I particularly like the error message that says "this might be wrong or that might be wrong." User interfacing requires extremely verbose outputs. Test each condition separately. If it is less than zero, then tell the user they should enter a number greater than zero. If it is greater then the max (which should be a constant at the top of the program) then tell them that.

readStringUntil() is a really poor function to use. If you test this with the Arduino serial monitor, it will disguise the worst problems with that function but I would recommend against using it for anything.

Your specification seems to require bytes to be stored, so why did you put in a full two-byte integer write? It looks like it was copy-pasted from a different source. Your read code only reads one byte, so the integer will be mangled. You will get negative results if your stored value is greater than 127.

I believe the issue is with how you enter the function arguments for “read” and “write” and what you use for delimiters and spacing. I have set my monitor to and all send with ‘\n’ newline.

For this explanation call address A and value V

If you enter “write A V” and “read A” with single spaces i.e. write(space)A(space)V and read(space)A your program works to correctly to write and read.

The program will handle other delimiters for read because it throws the first away and ignores the second as it take the integer. “read(A)” works and “read” works because you are taking the integer from the remaining A) and A> Any additional space between the “read” and the first delimiter results in a failure to read.

I don’t usually work with strings, and since its homework I leave you to figure out how to clean up the “write” if you really need to enter “write”. “write” works.

Certainly a prompt to the user about how to enter the data would be helpful.

Two copies of the incoming command created every time something comes in?

Those two would be the two in command, commandRead, and commandWrite? Even with one arm tied behind my back I count THREE copies of the command.

The whole exercise can be done with C style string (NULL terminated arrays of chars) and standard string functions.

Yes, I know, it could be done with C-style arrays, I could avoid creating a lot of variables, I could add more Serial.prints to exactly tell the user what he should do, but that’s not the point of it.

MorganS:
This is one of those exercises where it seems like using capital-S Strings is okay because it’s such a small program you couldn’t possibly run out of SRAM. Then you go putting long constant strings into SRAM and it is looking seriously big for the little Arduino’s memory. Two copies of the incoming command created every time something comes in?

I particularly like the error message that says “this might be wrong or that might be wrong.” User interfacing requires extremely verbose outputs. Test each condition separately. If it is less than zero, then tell the user they should enter a number greater than zero. If it is greater then the max (which should be a constant at the top of the program) then tell them that.

readStringUntil() is a really poor function to use. If you test this with the Arduino serial monitor, it will disguise the worst problems with that function but I would recommend against using it for anything.

I’m still new with Arduino and C programming in general, but if I run out of memory shouldn’t I get an error? Because if that’s the case I’m not getting any error whenever I verify and upload the code using the Arduino IDE. I know that my code could be done way more simple, avoiding a lot of stuff, but I first want to figure out why it doesn’t work as intended and then clean it. There is no point in cleaning a code that doesn’t work (because I doubt that will fix my problem). For the user interfacing output, that is not really needed, my code is going to be used only by me, and I know that I need to write values between 0 and 1023 and 0 and 255. So that print was simply to add something in case I ever mistype the whole number when I write it, that would show some output of why the “write” command wasn’t written. Again, if I manage to fix the problem, don’t worry, I will clean the code, removing parts that aren’t really necessary and adding more verbose outputs.

About the use of readStringUntil(), what is exactly wrong about it? As I said I’m new to Arduino and C, and I saw that function in the Reference and thought it could help me with what I wanted to do.

PaulS:
Those two would be the two in command, commandRead, and commandWrite? Even with one arm tied behind my back I count THREE copies of the command.

The whole exercise can be done with C style string (NULL terminated arrays of chars) and standard string functions.

Again, as I told MorganS, I know my code could be a lot cleaner, avoid a lot of useless variables, have a more verbose output, etc. But still, that doesn’t help me at all with the problem I’m having, and I doubt cleaning my code before it even works would help me to fix my problem. And if I don’t use C-style strings it’s because I’m new with C, coming from Python, and the strings in C are still out of my league, I need to study and practice more with them before I’m able to do stuff with it because they are way more complex than strings in Python or using this String method.

MorganS:
Your specification seems to require bytes to be stored, so why did you put in a full two-byte integer write? It looks like it was copy-pasted from a different source. Your read code only reads one byte, so the integer will be mangled. You will get negative results if your stored value is greater than 127.

Well, that’s because I’m not really sure of how it works. Isn’t an int always two-bytes long? That is why, since for the “write” command I store the argument2 as an int, and as an int is two-bytes long, I thought I would need to divide it into two bytes and then write it. But if I understand what you mean I guess that is not needed? And yes, thought about the read part yesterday after posting this and going to bed, I realised that if I write two bytes, then my read function would read only one half of the int, but in the assignment it doesn’t say anything of reading two addresses, it simply says that if I type “read 10”, it should read the value stored in the address 10, that’s it. That is why I didn’t add a second read for the next address when I was making the code. But I could add it if needed. About the negative results thing… I don’t understand that part very well either, is that connected with the thing you said before about that it will read only byte? Is 127 supposed to be 2 bytes so if I try to store it and later read it I will get negative results because I will only read half of the number or? It would be helpful if you could explain that part to me a bit clearer, thanks.

cattledog:
I believe the issue is with how you enter the function arguments for “read” and “write” and what you use for delimiters and spacing. I have set my monitor to and all send with ‘\n’ newline.

For this explanation call address A and value V

If you enter “write A V” and “read A” with single spaces i.e. write(space)A(space)V and read(space)A your program works to correctly to write and read.

The program will handle other delimiters for read because it throws the first away and ignores the second as it take the integer. “read(A)” works and “read” works because you are taking the integer from the remaining A) and A> Any additional space between the “read” and the first delimiter results in a failure to read.

I don’t usually work with strings, and since its homework I leave you to figure out how to clean up the “write” if you really need to enter “write”. “write” works.

Certainly a prompt to the user about how to enter the data would be helpful.

Hello, sorry, I think you misunderstood, and it’s actually my fault, the commands will always look like “read argument1” and “write argument1 argument2”, only with spaces between them, I thought it would be clearer if I typed the arguments between <> for the explanation, but I’m guessing that only caused confusion, I’m sorry, I will edit the main post. I don’t need to enter it in any other way like between <> or “”, etc, just spaces, and since I know for a fact that I only need to write spaces, I didn’t bother adding more code for other delimiters (because I’m never going to use other delimiters and it would take more space too). About the prompt, I could do that, that is not hard, just a Serial.println at the start of the loop to let the user know how to enter the data. But first I want to figure out why my code doesn’t work as intended after I use the “write” command. Thanks for helping and trying to figure out why it doesn’t work as intended though.

Again, thanks everybody for your help, if I sound rude I promise I’m not trying to be rude at all, simply that I can’t figure out why it doesn’t work as it should and that annoys me a bit, since the code, even if not clean, supposedly is right.

but if I run out of memory shouldn't I get an error?

When and where? Running out of memory at compile time is one thing. Running out of memory at run time is an entirely different matter. Both are possible, especially if you use Strings and uselessly piss away SRAM (both of which you are doing).

I know that my code could be done way more simple

You know that it should be done better, too.

but I first want to figure out why it doesn't work as intended

The first step would be to explain what it does, and how that differs from what you want.
The first step would be to explain what it does, and how that differs from what you want.
The first step would be to explain what it does, and how that differs from what you want.

(Notice the three copies...)

There is no point in cleaning a code that doesn't work

Right. Unless, just maybe, the reason that it doesn't work is because it pisses away memory like it had 2 terabytes to waste.

So that print was simply to add something in case I ever mistype the whole number when I write it, that would show some output of why the "write" command wasn't written.

You could save memory, by using the F() macro:

Serial.print("This string gets copied to SRAM");
Serial.print(F("This one does not."));

About the use of readStringUntil(), what is exactly wrong about it?

Two things. First, it is a blocking function. Nothing will happen until the "until" character arrives or the timeout happens, where the function says "well, I guess that the "until" character isn't going to show up".

Second, it hides what you need to do to successfully read serial data. Now, I'm perfectly willing to let you use readStringUntil() under two conditions. First, that the blocking nature doesn't affect the rest of the program. Second, you should be able to write your own version of readStringUntil() before you use something that someone else wrote, just because it is more convenient.

And if I don't use C-style strings it's because I'm new with C, coming from Python, and the strings in C are still out of my league

But they, and arrays in general, and pointers, should be mastered before you use "easier to use" methods, so that you understand the tradeoffs.

I need to study and practice more with them before I'm able to do stuff with it because they are way more complex than strings in Python or using this String method.

But they form the foundation for what strings in Python and the String class do. So, you should study and practice and understand them.

Isn't an int always two-bytes long?

No. On a Due, for instance, an int is 4 btyes.

If you are reading and writing bytes, which is what EEPROM can deal with, you should deal with just bytes, and the values that a byte can hold, unless the assignment specifically refers to being able to read and write larger values. In that case, you need to decide whether to read and write bytes and ints, just ints, just bytes, etc. The Arduino can't tell you whether a particular address holds a byte, the high byte of an int, the low byte of an int, or the third byte of a float. Your storage scheme needs to manage that somehow.

Hey, PaulS I read the sticky post in the Project Guidance section about how to correctly use this forum, and how I should post my topic, the part where it says how it should work and what it does instead is in the main post, just under the code block.

Sighery:
To give a little more information, I'm trying this on the 123d.circuits.io simulator. And for the simulation I use an Arduino UNO Rev3. What is wrong about my code is that if I use only "read", all is fine, it goes and reads that value, and I can repeat it as many times as I want to. However, if I use the "write" command, supposedly it will write it (because I get the print message after it's done writing), but then it won't let me use any other commands at all again, neither "read" or "write". And since I can't use "read" I can't check if it really wrote the value I sent it to the EEPROM either. So any idea of why it gets like stuck after I use the "write" command?

But you made a valid point, I will clean my code trying to save as much memory as possible, in case that's the problem. About the F() macro, I don't know what that is, or how to use it, but I will Google it for information, thanks for the suggestion. And yes, I'm still learning C, and learning how to use C-style strings and pointers, but I saw the String class while I was checking the References section and they were way easier to user than the C-style strings, but I'm not running away of them anyway, I'm still trying to learn C and C-style strings.

And alright, my next project after I figure out why this one doesn't work and fix it will be to write my own version of readStringUntil().

And I'm using an Arduino UNO Rev3 for the simulation, for the value being written to the EEPROM, it's always going to be a number between 0 and 255, both included. If you could give me any links for info about ints, how many bytes long they are, etc, would be helpful, so I exactly know how many bytes long are the numbers I'm going to type, that would help me in creating a scheme that manages that. Thanks.

If you could give me any links for info about ints, how many bytes long they are, etc, would be helpful, so I exactly know how many bytes long are the numbers I'm going to type, that would help me in creating a scheme that manages that.

All that information is available on the main Reference page, where you got the information about the String class.

It's no fun digging through the reference manual by clicking every link and see what the given function does; although you can learn from it.

So let me give you some pointers. First of all, for any standard C/C++ function you can find information on the web by searching for man functionname; but yeah, you need to know the functionname. If you develop on a Linux machine (Mac probably as well, but no experience with it), you can simply type the above command in a terminal.

To get the size of a variable or type, there is the function (actually an operator) with the surprising (;)) name sizeof().
To parse a null-terminated character string you can look at strtok() or strchr(). strtok() is destructive in the sense that your original string will be changed. An approach with strchr() does not necessarily have to be destructive. For your scenario, there is no problem if the original character string (command, address and value) is corrupted.
If you want to keep your original intact, you can make a copy of it using strcpy() or preferably strncpy().

One comment on your code: you're storing two bytes in eeprom for the integer, a waste of one byte as it can only be between 0 and 255 and hence will fit in a byte.

I think the observed behavior means it is running out of memory. There is a freememory function you can add to your code that will tell you precisely. Look in the Arduino playground area or Nick Gammon’s site for an example you can copy.

A simpler test is to wrap all the constant strings with F() and see if the program behavior changes.

Try this. Learn how it works, and modify it to your needs.

This code read from the specified EEPROM address and print the byte at that address to serial monitor as well as write a byte to EEPROM address specified.

#include <EEPROM.h>
byte value;
char val3;

void eepromr()//reads the specified EEPROM address and prints result to serial monitor
{
  byte addr = 50;// EEPROM address "50"--can be any address you wish within the limits of your board.
  Serial.println("Reading from EEPROM");
  value = EEPROM.read(addr);
  Serial.print(addr);
  Serial.print("\t");
  Serial.print(value);
  Serial.println();
  delay(100);
}


void eepromwr() //write parsed byte from serial to EEPROM address and prints the wrttien value to serial monitor
{    
  byte addr = 50;
  Serial.println("Reading Serial In");
  delay(100);
  while(Serial.available() > 0)
  {
    Serial.println("Writing to EEPROM");
    delay(100);
    byte val = Serial.parseInt();
    delay(100);
    EEPROM.write(addr, val);

    break;
  }
}

void eeprom1()//allows entrence and exit of either function read or write
{
  while (Serial.available() > 0) {
    char inByte = Serial.read();

    while(inByte == 'r')//if character "r" is sent from pc to arduino enter read function
    {
      eepromr();
      break;
    }
    while(inByte == 'w')//if character w is sent from pc to arduino enter write function
    {
      eepromwr();
      break;
    }
  }
}
void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);

}

void loop()
{ 
  eeprom1();
}

Bill

I may have over simplified, but I used fewer lines of code and it seems to work:

#include<EEPROM.h>

void setup() {
// turn on monitor
Serial.begin(9600);

}

void loop() {

// Look for Serial input:

while (Serial.available() > 0)
{
int Val1=Serial.read();
int Rd=Serial.parseInt(); //captures first number
int Wr1=Serial.parseInt(); //captures second number, if it exists

if(Rd>1024)
{
Serial.println(“EEPROM Location must be between 0 and 1023”); //verifies location is less than 1024
break;
}

if(Wr1>255)
{
Serial.println(“Stored value must be less than or equal to 255”); //verifies data value is less than 256
break;
}

if (Val1==‘r’)
{
Serial.print("Value read from location ");
Serial.println(Rd);
Serial.println(EEPROM.read(Rd), DEC);
}

if(Val1==‘w’)
{
EEPROM.write(Rd, Wr1);
Serial.print("Writing EEPROM Location = ");
Serial.println(Rd);
Serial.print("Writing Value ");
Serial.println(Wr1);
}
}
}

Do you mind if I use this code Ellisd62 and post to another forum within a project?

I'm working on a really simple project to light a model ship with LEDs

At the moment the user has to enter a value 0 - 255 in the sketch to set individual brightness values for each LED then upload the revised sketch

This code would be an ideal starting place to allow them to use the serial monitor to enter the values instead. Been reading up as is this is my first look at reading and writing to EEPROM, but you have already the functionality I want.

Ellisd62:

  if(Rd>1024)

That should actually be > 1023, not 1024. Also, the Mega has 4 kB of EEPROM. There’s a handy macro that defines the EEPROM size, E2END. If you write it like this:

 if(Rd>E2END)
  {
    Serial.print("EEPROM Location must be between 0 and ");
    Serial.println(E2END);

Then the code automatically adapts.