Interpreting Serial Data; NaN and Hex Values

I am trying to write a program that takes incoming data and interprets it (that couldn’t be more obvious xD). I expect either one, two, or four bytes of data coming in from the serial monitor. If the first incoming byte it is an “S” or “R” (including their lowercase forms), the data is a command and is handled accordingly (anything after that is discarded to keep the program less complex). If the first byte is a “1” or “2” or so on through “9,” or “A” or “B” or so on through “F,” it is an actual data byte.

If the incoming data is only one byte (except commands), it should be treated as two, with the first byte being 0x00. If the data is three bytes long, it is to be treated as four, again, with the first byte being set to 0x00.

What happens when the UNO isn’t reading data isn’t too important. Actually, 97% or so of its run time will be spent waiting for user input. The rest of that time is to be spent actually doing stuff with the data. For this reason, I suspect ReadBytesUntil() will be a good function to use.

count = serial.ReadBytesUntil('\n', inData[], 4);

In the code, all variables are of the byte type. ‘count’ should tell me how many bytes were received and ‘inData’ should contain the actual bytes. From here, it shouldn’t be too difficult to manipulate the array to match the length requirements if needed. But here is about where I get…foggy.

I need to check if the very first character is a decimal number (0-9). Sounds simple enough, yes? I am thinking something like this:

if(inData[x] == "R" || "r"){
// When the "R" command is sent
}

if(inData[x] == "S" || "s"){
// When the "S" command is sent
}

if(inData[x] == "A" || "B" || "C" || "D" || "E" || "F"){
inData[x] - 0x37;
}
elseif(inData[x] == "a" || "b" || "c" || "d" || "e" || "f"){
inData[x] - 0x45; //actual value subtracted unknown
}
elseif(!nan(inData[x])){
inData[x] - 0x30;
}
else{
// Invalid data
}

In this code, ‘x’ will be passed down through a function, but will always start at 0.

But I don’t know if ascii “1” will be treated as decimal 1 in that test. If it will, I am set (subtract 0x30 to convert from ASCII to HEX). Else, I’ll need some assistance here. Uppercase “A - F” gets a subtraction of 0x37. IDK about lowercase “a - f” yet.

Also, later in the code, I will need to read a binary value and print it to the serial monitor as a hex value. I think there is a function that can do that as a single step(?), but it’s been a while since I’ve done anything like that.

// Receive a byte of data (code not written yet)
while(static byte i < 0x00){
serial.Print(data, hex); serial.Print(" ");
}
serial.Println(); i = 0x00

This chunk of pseudo code reads a byte of data and sends it to serial monitor, followed with a space ( ). After 16 bytes are sent, it drops to a new line then repeats as necessary.

Sorry if any of this is very confusing. It’s 2:22 AM lol. Thanks for taking the time to read this and any help you can provide.

if(inData[x] == "A" || "B" || "C" || "D" || "E" || "F")

This line of code jumped out at me as wrong.

To test a variable for multiple values you must use the following format

if(inData[x] == "A" || inData[x] == "B" || inData[x] == "C" etc)

Note too that you should be testing against chars like ‘A’ rather than Strings like “A”

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

...R

UKHeliBob:

if(inData[x] == "A" || "B" || "C" || "D" || "E" || "F")

This line of code jumped out at me as wrong.

To test a variable for multiple values you must use the following format

if(inData[x] == "A" || inData[x] == "B" || inData[x] == "C" etc)

Note too that you should be testing against chars like 'A' rather than Strings like "A"

I have successfully used similar code to mine in the past. Admittedly, one case was a completely different platform/language. I can't really test this right now to verify if it will work, but I will do so before I try it in this project.

Robin2:
Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

...R

I have checked that topic in the past (and even had it open in a tab as I created this thread). It assumes my controller has more important things to do than wait on data to arrive. This project, my controller has nothing to do until data arrives. Setting flags and incrementing variables and forcing string terminators all seem a bit overkill for my needs. And the parse example provided works if I send a decimal value, but I'm working strictly with hex. This is a personally set limitation, but if I wanted to work in decimal, I would have to sit down with a pen and paper and convert a lot of numbers (up to 4,096 of them) from hex to decimal before I even boot up the UNO and serial monitor.

This is just a quick reply. I can get a little more detailed later, after I get more actual code written. As of this very moment, this thread contains more code than the actual project file itself.

I have successfully used similar code to mine in the past.

But not in C or C++

I’m working strictly with hex.

In fact you are working with binary data. “Hex” is one of several human readable representations of binary data.

I would have to sit down with a pen and paper and convert a lot of numbers (up to 4,096 of them) from hex to decimal

I cannot begin to imagine why you would think that, but the statement does suggest a certain level of confusion on your part. “Decimal” is another human readable representation of a binary number.

You will probably find these built in character functions interesting, as they allow you to effortlessly check for digits 0-9, alphanumeric, change character case, etc.

PS: the following is not valid and won’t compile. “static byte” is used to declare a new variable.

while(static byte i < 0x00){

For this reason, I suspect ReadBytesUntil() will be a good function to use.

Code: [Select]
count = serial.ReadBytesUntil('\n', inData, 4);

Case matters

free-bee:
I have checked that topic in the past (and even had it open in a tab as I created this thread). It assumes my controller has more important things to do than wait on data to arrive. This project, my controller has nothing to do until data arrives.

That does not mean that my code won't work for you.

Setting flags and incrementing variables and forcing string terminators all seem a bit overkill for my needs.

But if it gets the job done reliably without you needing to reinvent the wheel ?

And the parse example provided works if I send a decimal value, but I'm working strictly with hex.

Be fair. How could I anticipate the needs of everyone. I'm sure a small adaptation of the code will your need.

...R

Now that I’m not building a hecking deck (complete with steps), I was finally able to get some actual code written. There is still a fair amount of code missing and revisions to be made throughout, but hopefully what I have will better explain my intent. I actually meant to explain it yesterday, but I was interrupted to go work more on building a deck (complete with steps).

I am building a parallel EEPROM programmer. I intend to connect the data pins directly to the UNO (so I can both read from and write to the EEPROM. The address pins will be driven via shift register(s).

/*
 * |-----SHIFT REGISTER-----|
 * SER - Data Input to Shift Register
 * SRCLK - Data Clock to Shift Register
 * /SRCLR - Clear Shift Register
 * RCLK - Send the Stored DATA to the OUTPUTS
 * /OE - Output Enable
 *
 * Hold /SRCLR HIGH. No need for this function
 * Tie /OE to EEPROM /CE for power saving. Otherwise, hold LOW
 *
 * Data Moves from Qa to Qh; If Qa - A0, MSB First; Else if Qh - A0, LSB First
 *
 * DATA transfers on the DATA bus are bi-directional; connect directly to IO of the UNO
 * DATA transfers on the ADD bus are unidirectional to the EEPROM; shift registers are used to lower required pin count from the microcontroller
 * EEPROM control pins are also unidirectional but need to be directly manipulated by the UNO; instead of an additional Shift Register, connect directly to the UNO's IO;
 * Adding a second Shift Register can increase the readable/writable address space from 0x00FF to 0xFFFF
 *
 * |-----EEPROM-----|
 * /OE - Output Enable; Used for READ operations; Bring HIGH during a Write
 * /WE - Write Enable; Used for WRITE operations; Bring LOW ONLY when performing a WRITE
 * /CE - Chip Enable; Puts the EEPROM in low-power mode and disables all functions when HIGH; can be held LOW; giving the UNO access to this pin will save power when idle
 *
 * |-----Serial DATA Structure-----|
 * "R" or "r" will perform a full readout of the EEPROM
 * "S" or "s" will set the writing address; a second data transfer shall contain the address
 * "xx" will write HEX value xx to current writing address. Writing address will auto-increment by 0x01 for sequential write operations
 * All transmissions TO the UNO have a terminating character '\n'
 * Values read back from the UNO will contain 16 values per line separated by a space ( )
 * Checking the second byte in the (incoming) data array can determine if command or data
 * All data should be 2 or 4 bytes. If one, treat as two with the leading byte set to 0x00. If three bytes, treat as four with the leading byte = 0x00. If more than 4, only first four are read and the rest discarded
 */

// Pin Numbers for Shift Register and EEPROM controls
const char term = '\n';
const int ser = A0; // Serial DATA to Shift Register
const int srclk = A1; // DATA CLOCK to Shift Register
const int rclk = A2; // Bring HIGH to update Shift Register outputs with new data
const int ce = A3; // EEPROM Chip Enable
const int oe = A4; // EEPROM Output Enable
const int we = A5; // EEPROM Write Enable

// Limits
const word maxAdd = 0x00FF; // Highest EEPROM address accessable w/ your circuit

// Variables
byte inData[5] = {0x00, 0x00, 0x00, 0x00}; // Where we store the data coming FROM the serial monitor; expected size is 4 bytes max (16-bit-wide address bus; 1 byte per hex digit)
word address = 0x0000; // EEPROM address location for read/write operations
byte data = 0x00; // Data being read from EEPROM
byte count = 0x00; // How many bytes were read

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

}

void loop() {
  count = serial.ReadBytesUntil('\n', inData[], 4);
  if (inData[0] == 'R' || inData[0] == 'r') { // The Read command was sent
    readOut();
  }

  else if (inData[0] == 'S' || inData[0] == 's') { // The command for "Set Address"
    setAdd();
  }

  else structureBytes();
}


void readOut() {
  // A full readout of the EEPROM
  for (byte i = 2; i < 10; i++) { // D0 connects to digital pin 2 because serial communication
    pinMode(i, INPUT);
  }

  digitalWrite(ce, 0); // Enable the EEPROM and the shift register if you went that rout
  digitalWrite(oe, 0); // Enable the EEPROM outputs
  delay(1); // Give the EEPROM time to settle

  for (word i = 0x0000; i <= maxAdd; i++) { // For the entire address range

    for (byte dat = 0; dat < 8; dat++) { // For the entire byte
      bitWrite(data, dat, digitalRead(dat + 2)) // Set each bit = pin state (EEPROM data)
    }

    Serial.print(data, HEX); // Print the byte

    if (i != 0 && i % F == 0) { // These IF statements tests if we hit the max count for each line printed. If yes, drop to a new line. If no, separate with a space
      Serial.println();
    }

    else {
      Serial.print(" ");
    }
  }

  digitalWrite(ce, 1); // Disable the EEPROM
  digitalWrite(oe, 1); // Disable the EEPROM outputs
  delay(1); // Give the EEPROM time to settle

  for (byte i = 2; i < 10; i++) { // Default to WRITE mode as it will be the most used feature
    pinMode(i, OUTPUT);
  }
}


void setAdd() {
  // Does nothing more than change a variable based on user input
  Serial.println("Changing writing address. Enter a two or four digit HEX value.");

}

void writeDat(byte y) {
  //Writes data to the EEPROM
  for (byte i = 0x00; i < 0x08; i++) {
    digitalWrite(i + 2, bitRead(y, i))
  }

  digitalWrite(ce, 0); // Enable the EEPROM and the shift register if you went that rout
  digitalWrite(we, 0); // Write to EEPROM
  delay(1); // Give EEPROM time to settle

  digitalWrite(ce, 1);
  digitalWrite(we, 1);
  delay(1);

  address++; // Increment our writing address
}

void structureBytes() {
  // Format the inbound data
  // This chunk only corrects the size of the incoming data
  if (count == 1){
  inData[1] = inData[0]; inData[0] = 0x00; count = 2; // Shift numbers around and make one byte of data span two bytes
  }
  else if (count == 3){
    inData[3] = inData[2]; inData[2] = inData[1]; inData[1] = inData[0]; inData[0] = 0x00; count = 4; // Shift numbers around and make three bytes of data span four bytes
  }

  else {
    // Correct length; no action required
  }
}

Robin2:
That does not mean that my code won’t work for you.

But if it gets the job done reliably without you needing to reinvent the wheel ?

Both are fair points. I just want to avoid using 12 steps for a three-step process (pulling random numbers here).

Be fair. How could I anticipate the needs of everyone. I’m sure a small adaptation of the code will your need.

Nothing against you or your code. I was just saying. I need “12,” for example, to be treated as HEX 12 instead of DEC 12. I looked into the function you used and skimmed one or two more (ran out of time; had to go work on building a deck) and didn’t immediately see anything useful.

jremington:
In fact you are working with binary data. “Hex” is one of several human readable representations of binary data.
I cannot begin to imagine why you would think that, but the statement does suggest a certain level of confusion on your part. “Decimal” is another human readable representation of a binary number.

You will probably find these built in character functions interesting, as they allow you to effortlessly check for digits 0-9, alphanumeric, change character case, etc.

No confusion. The numbers are being presented to me in HEX. I’m just copying the numbers from a program into serial monitor, so the easy method for me would to be work only in HEX. Also, that link will probably be valuable to me for this project (and future ones).

UKHeliBob:
But not in C or C++

Actually, yes. It was another UNO project. I don’t recall anything about it; just that I was told this wouldn’t work:

if (x == "a" | "b" | "c")

and that this would:

if (x == "a" || "b" || "c")

It did work, but it could have just been coincidental (that happens to me a lot).

and that this would:
if (x == "a" || "b" || "c")

It did work

Just for laughs, try this program (but first, guess what will be printed):

void setup() {
Serial.begin(9600);
Serial.println("a" || "b" || "c");
}
void loop() {}

Hint: what is printed is indeed the correct value.

jremington:
Just for laughs, try this program (but first, guess what will be printed):

void setup() {

Serial.begin(9600);
Serial.println("a" || "b" || "c");
}
void loop() {}



Hint: what is printed is indeed the correct value.

I'll have to dig out a USB cable to be able to test. But here is my guess:

First, it will take be binary values for ASCII "a", "b", and "c". Next, it will perform an OR operation on the bits. The result will be printed out. Representation, I don't know, but I am going to say....decimal.

"||" = logical OR, not the same as "|" = bitwise OR.

The default for Serial.print is decimal.

free-bee:
Both are fair points. I just want to avoid using 12 steps for a three-step process (pulling random numbers here).

Nothing against you or your code. I was just saying. I need "12," for example, to be treated as HEX 12 instead of DEC 12. I looked into the function you used and skimmed one or two more (ran out of time; had to go work on building a deck) and didn't immediately see anything useful.

First, please understand that I won't be offended if you choose to ignore my advice. However I can't help feeling you are missing the purpose of my examples.

Copying my code into your program is, essentially, a one-step process with all the debugging done for you - apart from any detail changes you may make.

If you are really receiving a message containing numbers in HEX text then you can specify the base for conversion using the strtol() function.

If the string contains the HEX text in the form 0x12 then I suspect atoi() will convert it correctly - but I'm not certain.

However many user manuals describe the data being sent using HEX notation when in fact the data is being sent in binary form. For example it might say 0x12 when the actual value being sent in 0b0010010 or the decimal value 18

...R