Convert incoming serial data to ASCII

Hi,

I would just like to know why my code isnt working. I am sending a command to my transceiver (read device temperature), and the microcontroller is responding appropriately with 2 bytes of data (checked on scope). Code:

#include <SoftwareSerial.h>

uint8_t tx =8;
uint8_t rx =7;
int dataLength;
uint8_t temp[10];

char theData[100];
uint8_t temperature[6] = {0xCC,0xA4,0,0,0,0};
uint8_t readEeprom[2] = {0xCC, 0xC0};

SoftwareSerial nss (rx,tx);

void setup() {
  nss.begin(9600);
  Serial.begin(9600);
  dataLength = 2;     // The length of the returned data
}

void loop() {
  // Send command
  for (int i=0; i<6; i++) { 
    nss.print(temperature[i]);
  }
  // Read data (temperature)
  for (int i=0; i<dataLength; i++){
    temp[i] = nss.read();    
  } 
  // Print data
  for (int i=0; i<dataLength; i++){
    Serial.print(temp[i], BYTE);
  }
  Serial.println("");  
  delay(100);
}

I seem to be getting back meaningless characters on the serial monitor (little squares). I guess this means that the data is not within the scope of ascii.... or its an 'undisplayable' ascii char... not too sure...

Thanks for any help,

Rich

I seem to be getting back meaningless characters on the serial monitor (little squares). I guess this means that the data is not within the scope of ascii.... or its an 'undisplayable' ascii char... not too sure...

Did you remember to set the baud rate on the Arduino serial monitor pull down menu at 9600 baud?

Lefty

Hmm didnt think of that - I'll give it a look when I get back to the lab tomorrow.

Software serial ports don't have a read buffer. If you're not calling nss.read() before the bytes are being sent, then you're going to "miss" them. It's probable that you're missing the responses, and the read() function is returning garbage or something else outside of the printable range.

If you're getting the data OK, then it's possible that the temperature sensor is returning a non-ascii value. I'd try Serial.print(data, DEC). That will convert any number you receive into an ASCII sequence of numbers.

do you think this will work?

void loop() {
  // Send command
  for (int i=0; i<6; i++) {
    nss.print(temperature[i]);
  }

  while (digitalRead(rx));
      
  // Read data (temperature)
  for (int i=0; i<dataLength; i++){
    while (digitalRead(rx));
    temp[i] = nss.read();    
  }
  // Print data
  for (int i=0; i<dataLength; i++){
    Serial.print(temp[i], BYTE);
  }
  Serial.println("");  
  delay(100);
}

I'll give it a shot when I get to the lab tomorrow.

Ok, so I got it to read in some data - The thing is, the data isnt totally correct.

The array used to store the incoming data (fyi):
uint8_t temp[10];

Here's the important code:

void loop() {
  
  delay(3000);
  
  for (int i=0; i<6; i++) {
    nss.print(temperature[i]);
  }
  
  while(digitalRead(rx));
  
  for (int i=0; i<dataLength; i++){
    temp[i] = nss.read();      
  } 
   
  delay(1000);
  Serial.print("printed");
  Serial.print(" ");
  
  for (int i=0; i<dataLength; i++){
    Serial.print(temp[i], BIN);
  }
  
  Serial.println("");  
  delay(100);
  //count ++;
}

So I ended up getting the following bit stream back:
1100110011011
This is only 13 bits! Odd...

And I expected to recieve:
00110011 11011000 (0xCC, 0x0B)

Does anybody know whats going on?

Ok, so I've confirmed that the data is meaningful. The data that is being read by the arduino is temperature data from a transceiver. So, I blew on it to heat it up, and got some results:

printed 110011 0011001 = 25 degrees

printed 110011 0011100 = 28 degrees

printed 110011 00100000 = 32 degrees

the first byte is 0xCC, and the second is the temp data - so it seems that the 0xCC is missing the insignificant zeros at the higher order bits - that explains the 'less than 16' bit printout.

I think that has to do with the Serial.print() command, it might be ignoring the insignificant zeros in the first byte. You can always try changing the print parameter from BIN to HEX, or just try putting a space between the printouts

for (int i=0; i<dataLength; i++){
    Serial.print(temp[i], HEX);
    Serial.print(' ');
  }

Ya, I just decided to print in hex - much much easier that way. Thanks!

HI,

So there seems to be errors when my micro is reading data in from the serial line. Ive confirmed that the input voltage levels are good, and the slew rate is plenty fast, so perhaps there's a timing issue.

Heres the micro's recording of the aerocomm transceivers version number, as read over the serial line. This was recorded on two seperate occasions. (in hex):

A1A2A9A7A1A7A6A690526ACAD930F45E5C9D5CDD0E8406460606C1AFF56A093969A969886A0C8FF
A0A2A9A7A1A7A6A690526ACAC930F65E5C9E5CDD1E8406460606C1AACFF20B39E9C979886A0C8FF

I highlighted two of the errors, but there are more in there.

Is this to be expected? Checking with the scope, I see very little noise on the line so pretty much 0 probability of type 1 or 2 error. Again, timing issue?

Here is the important code :

uint8_t tx = 2;
uint8_t rx = 3;
uint8_t currentCmd;
int count;
uint8_t temp[200];

char     theData[100];
char*    names[29] = {"PRODUCT ID", "STOP BIT DELAY", "CHANNEL NUMBER", "BAUD RATE LOW", "BAUD H", "CTRL 0", "TRANSMIT RETRIES", "BX ATTEMPTS", "STALE COUNT RELOAD", "CTRL 1", "INTERFACE TIMEOUT", "RF PACKET SIZE", "CTS ON", "CTS OFF", "MAX PWR", "PARITY", "DEST ID", "SYS ID", "RS-485", "MAC", "ORIG MAX PWR", "PROD ID", "API CTRL", "PROTOCOL STATUS", "SESSION COUNT REFRESH", "RANDOM BACKOFF", "SENSE ADJUST", "PROBE REPORT", "DES KEY"};
uint8_t  addresses[29] = {0x00, 0x3F, 0x40, 0x42, 0x43, 0x45, 0x4C, 0x4D, 0x4F, 0x56, 0x58, 0x5B, 0x5C, 0x5D, 0x63, 0x6F, 0x70, 0x76, 0x7F, 0x80, 0x8E, 0x90, 0xC1, 0xC2, 0XC4, 0XC3, 0XC8, 0XC9, 0XD0};
int      addressLengths[29] = {40,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,6,1,1,6,1,0x0F,1,1,1,1,1,1,7};
uint8_t  readEeprom[4] = {0xCC, 0xC0, 0, 0}; // {XX, XX, address, length
uint8_t  writeEeprom[4] = {0xCC, 0xC1, 0, 0}; // {XX, XX, address, length

SoftwareSerial nss (rx,tx);


//----------// Main //----------// 


void setup() {
  nss.begin(9600);
  Serial.begin(9600);
  count = 0;
}

void loop() {
  
 for (int n=0; n<29; n++){
   
  Serial.print(names[n]); 
  Serial.print(" ");
  
  readEeprom[2] = addresses[n]; 
  readEeprom[3] = addressLengths[n];

  for (int i=0; i<4; i++) {
    nss.print(readEeprom[i]);
  }
  
  while(digitalRead(rx));
  for (int i=0; i<addressLengths[n]+3; i++){
    temp[i] = nss.read();      
  } 
  
  delay(100);
  Serial.print(" ");
  
  for (int i=0; i<addressLengths[n]; i++){
    Serial.print(temp[i+3], HEX);
  }
  
  Serial.println("");  
  
  if (n==28) {while(1);};
  
  }
}

If anybody could indicate to me what the source of the problem is and how to approach the solution, it would be greatly appreciated.

Regards, :sunglasses:

Rich

Anyone?

You don't give people much to go on. You say there are errors, what should the highlighted numbers actually be?

Timing will not be an issue if the baud rate is right.

Well, both of those hex messages should be identical. Since they are not identical, there are errors. That hex message is the device's hardware version, and does not change, ever. If you look carefully, the two messages are identical, except for at the highligted places, and perhaps a few others.

What clock frequency are you using? 16MHz?

One minor problem with the Arduino is the choice of clock frequency as 16MHz. This does not divide down nicely to give you standard baud rates with 0% error margin.

Depending on your clock frequency and the baud rate you have chosen to use you could be experiencing as much as +/- 3% baud rate error. Combine this with a potential baud rate error % at the receiver (ie your PC) then you can get the occassional error.

The datasheet for the ATmega168 goes into this in a fair amount of detail.

I have seen this error with the Arduino when selecting too high a baud rate (eg 115k), especially when sending long continuous streams of data because the error accumulates with each character.

because the error accumulates with each character.

Sorry that is not true. Asynchronous protocol (serial data) synchronises the timing every character so they will not build up with long strings. What can happen is that long strings can choke it input buffers of terminals especially on windose.

It would help to know which one is wrong
0 = 0000
1 = 0001
4 = 0100
6 = 0110
It is odd that sometimes these values get through OK and other times they are wrong, if it were a baud miss match issue you would expect it to be consistent over every occurrence of the same character.

Is it possible to try a slower baud rate?

because the error accumulates with each character.

Sorry that is not true. Asynchronous protocol (serial data) synchronises the timing every character so they will not build up with long strings. What can happen is that long strings can choke it input buffers of terminals especially on windose.

I do agree with you about the problem of overflowing input buffers, but that normally results in total loss of bytes not bit errors, however you are wrong about baud rate errors not accumulating, in certain circumstances the error will accumulate and result in bit errors.

Within a byte the baud rate error for each bit transmitted adds up. A baud rate error causes each bit to be either wider or narrower than it should be and the error will be consistent for every bit. This variance from the ideal bit width adds up and hence the bit position within each byte will not be in the ideal position.

If you have a UART pumping out multiple serial bytes, back to back in a tight loop with a baud rate error and 1 or more integer multiples of stop bits between bytes (which is how the Arduino implements it's serial routines) then the error must accumulate over multiple bytes. There is no way of avoiding this. A frequency error is a frequency error.

If you send out enough bytes back to back in a stream then eventually bits will occur in the wrong position, eg the start bit of the next byte can occur during the stop bit of the previous byte. There is no way the receiving UART can handle this and errors will occur as the first '0' in the following byte is received and is interpreted as a start bit. Everything from then on will be out of sync until there is a period when the line is idle (eg idle for a byte period), allowing the receiving UART to flush out junk data and resynchronise.

It doesn't matter much about the number of stop bits, if the accumulated error puts the next start bit in the wrong place, ie during the previous expected stop bit, then a bit error will occur.

I am talking about the situation where you have continuous, long streams of data transmitted back to back in a tight loop. Bursty serial data or short streams, shorter than the length where the accumulated baud rate error causes start bits to be missed, will not experience this problem as there is not enough time for the error to add up.

It is the idle periods between bytes or short bursts of bytes that allows the receiving UART to resynchronise to the incoming data.

No, what Mike said is true.
Errors may accumulate within a received character (the ability of the receiver to tolerate error is called "margin"), but characters are always, as Mike stated, retimed, so timing errors over a multi-character message will not accumulate.

Hardware asynchronous receivers usually use something like a 16x sampling clock. First, the edge of the start bit is detected, and then the receiver waits for half the bit period to detect the centre of the start bit. Successive bit samples are then timed from this point, until the stop bit, when the sampling goes back to 16x.

I used to know the formulae for margin, and the implication is that odd sampling rates offer better margin then evens, but for digital circuits, evens are easier to generate.

If you look at the software serial libraries, the sampling looking for start bits is effectively free-running, and since they don't actively look for stop bits, the free-running starts immediately the last (MSB) data bit has been received (or at least as soon as the code gets back to calling "read").

try removing the while(digitalRead(rx)) to wait for incoming data. the read() function in software serial is supposed to sit and wait until it encounters a byte, so you're not going to miss anything as long as you're constantly calling nss.read().

The documentation for the software serial library says there's a lot of timing issues, and that 9600 is the maximum speed it can operate at. There's been efforts to make a better software serial library though, you might want to try this one NewSoftSerial | Arduiniana

Ah - yeah, you're quite right - in my haste to post I forgot about the sub-bit sampling at the receiver.... oops! Each byte will resynchronise even if the accumulated error within a byte causes bit errors at the end.

But I do contend that what I said is still true in one specific case - when you have totally mis-matched baud rates! :wink:

(Come on - gimme something I'm clutching at straws to re-establish my credibility here! ;D )