Sending numbers serially

Hi guys,

Using SoftwareSerial to manipulate some wireless transceivers (SX1276) and can't quite grasp what I'm doing wrong. I'm sending a payload that's usually at or around 40 characters once a second at 9600bps. I'm having trouble figuring out how to send numbers (in my case, always integers). They are always received as "", "?", or not at all, unless I send it as a string (like trx.write("1023")) but I'm not sure how that helps in the context of sending integer variables.

Transmitter code:

#include <SoftwareSerial.h>
#include <SPI.h>
#include <Adafruit_ssd1306syp.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
Adafruit_BME280 bme(8, 9, 10, 11);  // BME280 || VIN = 5V, GND = GND, 3Vo = N/C, CS = D8, SDI = D9, SDO = D10, SCK = D11
SoftwareSerial trx(4,5);            // SX1276 || BLU/RXD = D5, YEL/TXD = D4, BLK/GND = GND, RED/VCC = 5V
Adafruit_ssd1306syp display(7,6);   // OLED DISPLAY || VCC = 3.3V, GND = GND, SDA = D7, SCL = D6
                                    // TRANSDUCER || RED = 5V, BLK = GND, WHT = A2

const char alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char protocol[5] = "AAAC", channel[5];
unsigned short freqID, netID;

void setup() {
  Serial.begin(9600);
  bme.begin();
  trx.begin(9600);
  trx.flush();

  setChannelID();
  display.initialize();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,0);
  display.println("TRX-1276");
  display.update();
}

void loop() {
    txData();
    delay(1000);
}

void txData() {
  short pressure = (short)analogRead(A2);
  short temperature = (short)(bme.readTemperature() + .5);
  short humidity = (short)(bme.readHumidity() + .5);
  short barometric = (short)(((bme.readPressure() * 0.000145037738) * 100) + .5); 
  short voltage = (short)1023;
  long runtime = (long)(millis()/1000);
  
  trx.write('I');                 // COMMUNICATION ID
  Serial.print('I');
  for(short i = 0; i < 4; i++){
    trx.write(channel[i]);
    Serial.print(channel[i]);
  }
  trx.write(freqID);
  Serial.print(freqID);
  trx.write(netID);
  Serial.print(netID);
  trx.write('K');                 // KP VERSION NAME
  Serial.print('K');
  for(short i = 0; i < 4; i++){
    trx.write(protocol[i]);
    Serial.print(protocol[i]);
  }
  trx.write('P');                 // ADC-READ PRESSURE
  Serial.print('P');
  if(pressure <= 0){
    trx.write('0'); 
    Serial.print('0');
  } else if(pressure >= 1023){
    trx.write("1023");
    Serial.print("1023");
  } else {
    trx.write(pressure);
    Serial.print(pressure);
  }
  trx.write('T');                 // DEGREES CENTIGRADE
  Serial.print('T');
  if(temperature <= -99){
    trx.write("-99");
    Serial.print("-99");
  } else if(temperature >= 99){
    trx.write("99");
    Serial.print("99");
  } else {
    trx.write(temperature);
    Serial.print(temperature);
  }
  trx.write('H');                 // RELATIVE HUMIDITY
  Serial.print('H');
  if(humidity <= 0){
    trx.write('0');
    Serial.print('0');
  } else if(humidity >= 99){
    trx.write("99");
    Serial.print("99");
  } else {
    trx.write(humidity);
    Serial.print(humidity);
  }
  trx.write('B');                 // BAROMETRIC PSI
  Serial.print('B');
  if(barometric <= 0){
    trx.write('0');
    Serial.print('0');
  } else if(barometric >= 9999){
    trx.write("9999");
    Serial.print("9999");
  } else {
    trx.write(barometric);
    Serial.print(barometric);
  }
  trx.write('V');                 // BATTERY VOLTAGE
  Serial.print('V');
  if(voltage <= 0){
    trx.write('0');
    Serial.print('0');
  } else if(voltage >= 1023){
    trx.write("1023");
    Serial.print("1023");
  } else {
    trx.write(voltage);
    Serial.print(voltage);
  }
  trx.write('R');                 // TOTAL RUNTIME
  Serial.print('R');
  if(runtime <= 0){
    trx.write('0');
    Serial.print('0');
  } else if(runtime >= 86401){
    trx.write("86401");
    Serial.print("86401");
  } else {
    trx.write(runtime);
    Serial.print(runtime);
  }
  trx.write('E');                 // END TRANSMISSION
  Serial.println('E');
  return;
}

void setChannelID() {
  randomSeed(analogRead(0));
  channel[0] = alpha[random(1,26)]; // Soft ID 1
  channel[1] = alpha[random(0,26)]; // Soft ID 2
  channel[2] = alpha[random(0,26)]; // Soft ID 3
  channel[3] = alpha[random(0,26)]; // Soft ID 4
  freqID = char(random(1,10)); // Freq ID
  netID = char(random(1,10)); // Net ID
  return;
}

Receiver code:

#include <SoftwareSerial.h>
SoftwareSerial trx(4,5);

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

void loop() {
  if(trx.available() > 0){
    Serial.print(char(trx.read())); // Convert from ASCII ID to char and print
    delay(1); // For stability
  }
}

Typical receiver output:

INBGYKAAACPhTHB⸮V1023RE

What's strange is that I can print those number values into the Serial console just fine, not "write()" them. Any guidance is appreciated.

TY&BR,
Chris

It sounds as though you are trying to send integer values directly via serial but interpreting the data at the other end as readable text?

If so then consider the following.

When you transmit readable text then you are transmitting integer codes from the ASCII table: http://www.asciichars.com/_site_media/ascii/ascii-chars-landscape.jpg

So when you transmit an 'A' over serial then you in effect transmitting the integer value 65.

Now if you happen to be sending an integer value that is between 0 and 31 or greater than 176, and then trying to interpret these as readable text then it won't work because these values are non-printable control characters from the ASCII table.

For example ASCII 10 and 13 are represent the line feed and carriage return on the old fashioned type writers. On computers they apply to the system carret, or the position on the screen where the next ASCII character will be displayed.

If you want to send 10 and 13 as readable numbers at the other end then you need to explicitly convert them to strings before transmitting via serial.

You could write yourself a convenient function like the one below. This is from my custom CString class so it won't compile for you. But I assume you have enough knowledge to modify my code to make it compile for yourself.

It allows you to do this:

int n = 10;
CBuff<10> buff;
CString str(buff);
str.format("%d", n); // str = "10" ASCII 48 and ASCII 49

You are welcome to use my custom CString class if you want.....in stead of modifying the function below.

 void CString::format(const __FlashStringHelper *str, ...)
  {
    const uint16_t nSize = strlen_P((PGM_P)str) + 1;
    char *strTemp = new char[nSize];
    memset(strTemp, 0, nSize);
    strcpy_P(strTemp, (PGM_P)str);
    
    va_list argptr;  
    va_start(argptr, str); 
    int nRet = vsnprintf(m_pBuff->m_strBuff, m_pBuff->capacity() - 1, strTemp, argptr);
    if (nRet == 0)
      debug.logRuntimeError(F("CString.cpp"), __LINE__);
    delete strTemp;
  }

CString.cpp (25 KB)

CString.h (8.78 KB)

Thanks for the suggestions, @boylesg

At this point, this is what I understand: letters are transmitting fine; I can't String cast numbers within serial write commands; when I transmit them (regardless of cast) they are interpreted as ASCII codes.

While I appreciate the idea of a function that can convert them to Strings, it doesn't show in the code I posted because I chopped out a lot of irrelevant functions but I'm running a bit tight on program space so I'd like to avoid implementing another library if possible.

I'll be able to test this later tonight or tomorrow morning but couldn't I conceivably break each number into single integer (ex 123 -> 1, 2, 3), add 48 to each, then transmit them? That way their ASCII value would be correctly sent. Or even, since I know when to expect an int vs a letter and I don't use any ASCII characters 0-9, interpret ASCII values received under 10 as digits to a number until the next ASCII value over 9?

Let me know if that logic isn't sound because it seems like while it isn't the most orthodox/professional way, it would be by far more efficient in terms of program size. Once I get a chance to test it I'll share my findings.

Thanks, Chris.

There is a difference between the write() and print() functions so this is why things look differently on your serial monitor. You are using Serial.print() but trx.write(). print() turns numbers into strings but write() does not.

If you do Serial.print(123); you would see the string "123" but if you do Serial.write(123) you are writing one byte with ASCII code 123 which happens to be the opening curly brace '{'. If you are write() ing short integer values, they are 2 bytes so you would always be transmitting 2 bytes and your receiving code would have to always be reading 2 bytes.

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

...R

Thanks for the help, guys @Robin2 @blh64. It seems that from your help and from that of boylesg I can figure out an efficient and nearly-foolproof way to communicate integers. I'll test to confirm tomorrow and update if anything doesn't work out

Build_and_Break:
Thanks for the help, guys @Robin2 @blh64. It seems that from your help and from that of boylesg I can figure out an efficient and nearly-foolproof way to communicate integers. I'll test to confirm tomorrow and update if anything doesn't work out

Consider using an Arduino mega clone from ebay. $10 to $15, a heap more flash memory and SD RAM and additional hardware serial ports. Instead of an Uno that is.