Go Down

Topic: Reading 8 Bytes of data from Serial Port[SOLVED] (Read 378 times) previous topic - next topic

Steveiboy

Apr 20, 2019, 10:18 am Last Edit: Apr 29, 2019, 07:20 pm by Steveiboy
I' working on a project to read Serial data of 8 bytes that comes in once every second from an eLelvel inclinometer to enable me to have a remote screen.


I have wrote some sample code to get the data(may not be the best way so far) I'm getting some data coming in and it changes each time I move the inclinometer.

There a couple of issues I have so far.
1. which is the best method to receive all of the data without missing packets as sometimes I get garbage ?
2. How to convert the incoming data of bytes 1&2 to a human readable format like 1.30 Degrees ?

I've attached PDF file which contains the information of the serial data coming out, I sort of understand it and just after some guidance


Code: [Select]
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <SoftwareSerial.h>

SoftwareSerial mySerial(3, 2); // RX, T
LiquidCrystal_I2C lcd(0x03f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
byte index = 0;
int DMM_ARRAY[8];   // Where to store the Bytes read
byte c;
char SETUP = 0x65;
int n = 0;
#define ECHO_TO_SERIAL 1 //change to zero = no serial output
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};
void printRecord(Print* pr, char sep = ',') {
  pr->print(t);
  pr->print(sep);
  pr->print(DMM_ARRAY[0]); // Print btye 0
  pr->print(sep);
  pr->print(DMM_ARRAY[1]); // Print btye 1
  pr->print(sep);
  pr->print(DMM_ARRAY[2]); // Print btye 2
  pr->print(sep);
  pr->print(DMM_ARRAY[3]); // Print btye 3
  pr->print(sep);
  pr->print(DMM_ARRAY[4]); // Print btye 4
  pr->print(sep);
  pr->print(DMM_ARRAY[5]); // Print btye 5
  pr->print(sep);
  pr->print(DMM_ARRAY[6]); // Print btye 6
  pr->print(sep);
  pr->print(DMM_ARRAY[7]); // Print btye 7
  pr->println();

}
void setup() {
  lcd.begin (20, 4); // for 20 X 4 LCD module
  lcd.setBacklight(HIGH);
  lcd.print("TESTING");
  delay(3000);
  lcd.clear();
  Serial.begin(9600);
  mySerial.begin(9600);
  mySerial.write(SETUP);
  Serial.println(SETUP);
  delay(1000);
}

void loop() {
  Get_data(); //Read the Serial bytes coming in
  // Send debug to serial port if enabled
#if ECHO_TO_SERIAL
  printRecord(&Serial);
#endif ECHO_TO_SERIAL
  // LCD line 1
  lcd.setCursor(0, 0);
  lcd.print(DMM_ARRAY[0]); // Print btye 0
  lcd.print(", ");
  lcd.setCursor(5, 0);
  lcd.print(DMM_ARRAY[1]);// Print btye 1
  lcd.print(", ");
  // LCD line 2
  lcd.setCursor(0, 1);
  lcd.print( DMM_ARRAY[2]);// Print btye 2
  lcd.print(", ");
  lcd.setCursor(5, 1);
  lcd.print( DMM_ARRAY[3]);// Print btye 3
  lcd.print(" ");
  // LCD line 3
  lcd.setCursor(0, 2);
  lcd.print( DMM_ARRAY[4]);// Print btye 4
  lcd.print(", ");
  lcd.setCursor(5, 2);
  lcd.print( DMM_ARRAY[5]);// Print btye 5
  lcd.print(", ");
  // LCD line 4
  lcd.setCursor(0, 3);
  lcd.print( DMM_ARRAY[6]);// Print btye 6
  lcd.print(", ");
  lcd.setCursor(5, 3);
  lcd.print( DMM_ARRAY[7]);// Print btye 7
  lcd.print(" ");

}


void  Get_data() {

  if (mySerial.available()) {
    for (int i = 0; i < 8; i++) {
      DMM_ARRAY[i] = mySerial.read();
    }
  }

}

Robin2

#1
Apr 20, 2019, 11:59 am Last Edit: Apr 20, 2019, 12:02 pm by Robin2
First, you need to check if all 8 bytes have arrived with
Code: [Select]
if (mySerial.available() >= 8) {
because the Arduino can clear the serial input buffer much faster than data arrives.

It would also be wise to measure the time between bytes arriving so that you could identify the 1 second interval between data transmissions. Otherwise you run the risk that your 8 bytes could be the last 3 from one message and the first 5 from the next message.

It seems that the value 0xEE is used as a start marker, however it also seems to be used within the message and the end marker seems to be either 0xEE or 0xDE. (Why couldn't they have chosen a simple system ?)


Quote
2. How to convert the incoming data of bytes 1&2 to a human readable format like 1.30 Degrees ?
The PDF document seems to have a formula for that
Quote
Example:
1. If Byte[2] = 02, Byte[1] = 67, means angle is 02.67°.
Calculate method: (02*100+67)/100 = 02.67, so angle value is 02.67°.
...R
Two or three hours spent thinking and reading documentation solves most programming problems.

david_2018

Quote
1. which is the best method to receive all of the data without missing packets as sometimes I get garbage ?
Unfortunately the level isn't sending a unique character to indicate the start or end of data.  The example given in the manual for a windows program is receiving 16 bytes of data, then picking out the valid block of 8 bytes within that, but you won't get an update every second doing it that way. 

You would need to have a non-blocking function to read the serial data into a buffer, and after receiving each character check the buffer for a valid block of 8 bytes of data.  Each time you detect a valid block of data, move it to the DIMM_ARRAY and set a flag to indicate  that new data has been received.

Quote
2. How to convert the incoming data of bytes 1&2 to a human readable format like 1.30 Degrees ?
The data is in BCD (binary coded decimal), so each byte contains two decimal digits, coded as 0 - 9 hexidecimal.
Ascii coding for numbers 0 - 9 is 0x30 to 0x39, take each decimal digit and add 0x30 to get its ascii equivalent.
In the case of your level, "00" is sent as "FF", so that needs to be converted back to "00".

Code: [Select]

char eLevel_temp[6]; //eLevel temperature xx.xx degrees
char eLevel_angle[6]; //eLevel angle xx.xx degrees

void convert_BCD() {
  //"00" is sent as "FF", convert back to "00"
  if (DMM_ARRAY[1] == 0xFF) DMM_ARRAY[1] = 0x00;
  if (DMM_ARRAY[2] == 0xFF) DMM_ARRAY[2] = 0x00;
  if (DMM_ARRAY[5] == 0xFF) DMM_ARRAY[5] = 0x00;
  if (DMM_ARRAY[6] == 0xFF) DMM_ARRAY[6] = 0x00;
  //data is in BCD, convert each digit to ascii by adding 0x30
  eLevel_temp[0] = ((DMM_ARRAY[1] & 0xF0) >> 4) | 0x30;
  eLevel_temp[1] = (DMM_ARRAY[1] & 0x0F) | 0x30;
  eLevel_temp[2] = '.'; //character array terminator
  eLevel_temp[3] = ((DMM_ARRAY[2] & 0xF0) >> 4) | 0x30;
  eLevel_temp[4] = (DMM_ARRAY[2] & 0x0F) | 0x30;
  eLevel_temp[5] = '\0';
  eLevel_angle[0] = ((DMM_ARRAY[5] & 0xF0) >> 4) | 0x30;
  eLevel_angle[1] = (DMM_ARRAY[5] & 0x0F) | 0x30;
  eLevel_angle[2] = '.';
  eLevel_angle[3] = ((DMM_ARRAY[6] & 0xF0) >> 4) | 0x30;
  eLevel_angle[4] = (DMM_ARRAY[6] & 0x0F) | 0x30;
  eLevel_angle[5] = '\0'; //character array terminator
}


david_2018

#3
Apr 20, 2019, 12:26 pm Last Edit: Apr 20, 2019, 12:26 pm by david_2018
I did over complicate the printing, if all you want is to display the data, the print() can display BCD as hexidecimal, so just change your original code to :

Code: [Select]


void printRecord(Print* pr, char sep = ',') {
  pr->print(t);
  pr->print(sep);
  pr->print(DMM_ARRAY[0]); // Print btye 0
  pr->print(sep);
  pr->print(DMM_ARRAY[1], HEX); // Print btye 1
  pr->print(sep);
  pr->print(DMM_ARRAY[2], HEX); // Print btye 2
  pr->print(sep);
  pr->print(DMM_ARRAY[3]); // Print btye 3
  pr->print(sep);
  pr->print(DMM_ARRAY[4]); // Print btye 4
  pr->print(sep);
  pr->print(DMM_ARRAY[5], HEX); // Print btye 5
  pr->print(sep);
  pr->print(DMM_ARRAY[6], HEX); // Print btye 6
  pr->print(sep);
  pr->print(DMM_ARRAY[7]); // Print btye 7
  pr->println();
}


and similar for the appropriate lines in the LCD.print statements.

Steveiboy

Thanks for the input guy's this is helping me understand how it works.

Robin2:
I will try to look into on how to do the timing on the serial data coming in as not sure how I can do that.

david_2018:

I only need to print the decimal value of the Angle on the LCD, The serial is just to hlep debugging, I was only trying the HEX value the readings jsut to see what those values where. The  inclinometer  goes form 0.00 to 90.0 Degrees, I think becasue it's reading the serial to fast it must be missing some data.


Once again guys thanks for what you have inputed so far grat help

david_2018

See how this code works, I took a serial read function that was being used to read input from a scale and modified it a bit to fit the data coming from your level.  The comments should give an idea of how it works, basically it reads in data from the serial port until it finds the correct pattern of data, then sets a flag to let you know there is valid data available.

According to the manual you linked, the numbers are in binary coded decimal, so printing as an integer won't work, and printing as hexidecimal doesn't print leading zero's, so its necessary to either print each digit individually, or convert into ascii and print as characters.  

Code: [Select]

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <SoftwareSerial.h>

SoftwareSerial mySerial(3, 2); // RX, T
LiquidCrystal_I2C lcd(0x03f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
byte index = 0;
int DMM_ARRAY[8];   // Where to store the Bytes read
byte c;
char SETUP = 0x65;
int n = 0;
#define ECHO_TO_SERIAL 1 //change to zero = no serial output
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};

bool newData = false;
char eLevel_temp[6]; //eLevel temperature xx.xx degrees
char eLevel_angle[6]; //eLevel angle xx.xx degrees
int eLevel_temp_; //temperature in hundredths of a degree
int eLevel_angle_; //angle in hundredths of a degree

void printRecord(Print* pr, char sep = ',') {
  pr->print(t);
  pr->print(sep);
  pr->print(DMM_ARRAY[0], HEX); // Print btye 0
  pr->print(sep);
  //  pr->print(DMM_ARRAY[1], HEX); // Print btye 1
  pr->print(DMM_ARRAY[1] >> 4); // Print btye 1
  pr->print(DMM_ARRAY[1] & 0x0F); // Print btye 1
  pr->print(sep);
  //  pr->print(DMM_ARRAY[2], HEX); // Print btye 2
  pr->print(DMM_ARRAY[2] >> 4); // Print btye 2
  pr->print(DMM_ARRAY[2] & 0x0F); // Print btye 2
  pr->print(sep);
  pr->print(DMM_ARRAY[3], HEX); // Print btye 3
  pr->print(sep);
  pr->print(DMM_ARRAY[4], HEX); // Print btye 4
  pr->print(sep);
  //  pr->print(DMM_ARRAY[5], HEX); // Print btye 5
  pr->print(DMM_ARRAY[5] >> 4); // Print btye 5
  pr->print(DMM_ARRAY[5] & 0x0F); // Print btye 5
  pr->print(sep);
  //  pr->print(DMM_ARRAY[6], HEX); // Print btye 6
  pr->print(DMM_ARRAY[6] >> 4); // Print btye 6
  pr->print(DMM_ARRAY[6] & 0x0F); // Print btye 6
  pr->print(sep);
  pr->print(DMM_ARRAY[7], HEX); // Print btye 7
  pr->println();
  pr->print("temperature: ");
  pr->print(eLevel_temp);
  pr->print(" ");
  pr->println(eLevel_temp_);
  pr->print("angle      : ");
  pr->print(eLevel_angle);
  pr->print(" ");
  pr->println(eLevel_angle_);
}

void setup() {
  lcd.begin (20, 4); // for 20 X 4 LCD module
  lcd.setBacklight(HIGH);
  lcd.print("TESTING");
  delay(3000);
  lcd.clear();
  Serial.begin(9600);
  mySerial.begin(9600);
  mySerial.write(SETUP);
  Serial.println(SETUP);
  delay(1000);
}

void loop() {
  Get_data(); //Read the Serial bytes coming in
  if (newData) { //new data received from eLevel
    newData = false;
    convert_BCD(); //convert data from BCD to integer and character array
    // Send debug to serial port if enabled
#if ECHO_TO_SERIAL
    printRecord(&Serial);
#endif //ECHO_TO_SERIAL
    // LCD line 1
    lcd.setCursor(0, 0);
    lcd.print(DMM_ARRAY[0], HEX); // Print btye 0
    lcd.print(", ");
    lcd.setCursor(5, 0);
    //    lcd.print(DMM_ARRAY[1], HEX);// Print btye 1
    lcd.print(DMM_ARRAY[1] >> 4); // Print btye 1
    lcd.print(DMM_ARRAY[1] & 0x0F); // Print btye 1
    lcd.print(", ");
    // LCD line 2
    lcd.setCursor(0, 1);
    //    lcd.print( DMM_ARRAY[2], HEX);// Print btye 2
    lcd.print( DMM_ARRAY[2] >> 4); // Print btye 2
    lcd.print( DMM_ARRAY[2] & 0x0F); // Print btye 2
    lcd.print(", ");
    lcd.setCursor(5, 1);
    lcd.print( DMM_ARRAY[3], HEX);// Print btye 3
    lcd.print(" ");
    // LCD line 3
    lcd.setCursor(0, 2);
    lcd.print( DMM_ARRAY[4], HEX);// Print btye 4
    lcd.print(", ");
    lcd.setCursor(5, 2);
    //    lcd.print( DMM_ARRAY[5], HEX);// Print btye 5
    lcd.print( DMM_ARRAY[5] >> 4);// Print btye 5
    lcd.print( DMM_ARRAY[5] & 0x0F);// Print btye 5
    lcd.print(", ");
    // LCD line 4
    lcd.setCursor(0, 3);
    //    lcd.print( DMM_ARRAY[6], HEX);// Print btye 6
    lcd.print( DMM_ARRAY[6] >> 4); // Print btye 6
    lcd.print( DMM_ARRAY[6] & 0x0F);// Print btye 6
    lcd.print(", ");
    lcd.setCursor(5, 3);
    lcd.print( DMM_ARRAY[7], HEX);// Print btye 7
    lcd.print(" ");
  }
}

void Get_data() {
  /*
    if serialavailable
    read byte into array
    increment array pointer
    if all bytes in array received
      if array fits data pattern of "EE xx xx EE DE xx xx (DD or EE)"
        set data available flag
        reset array pointer
      else
        delete oldest byte in array
        decrement array pointer to allow room for next byte received
  */
  const byte size_DMM_ARRAY = (sizeof(DMM_ARRAY) / sizeof(DMM_ARRAY[0]));
  static byte index_mySerial = 0;
  while (mySerial.available() > 0 && newData == false) {
    DMM_ARRAY[index_mySerial] = mySerial.read();
    index_mySerial++;
    if (index_mySerial == size_DMM_ARRAY) {
      if ((DMM_ARRAY[0] == 0xEE) &&
          (DMM_ARRAY[3] == 0xEE) &&
          (DMM_ARRAY[4] == 0xDE) &&
          ((DMM_ARRAY[7] == 0xEE) || (DMM_ARRAY[7] = 0xDD))) {
        newData = true;
        index_mySerial = 0;
      } else {
        for (byte i = 0; i < size_DMM_ARRAY - 1; i++) {
          DMM_ARRAY[i] = DMM_ARRAY[i + 1];
        }
        index_mySerial = size_DMM_ARRAY - 1;
      }
    }
  }
}


void convert_BCD() {
  //"00" is sent as "FF", convert back to "00"
  if (DMM_ARRAY[1] == 0xFF) DMM_ARRAY[1] = 0x00;
  if (DMM_ARRAY[2] == 0xFF) DMM_ARRAY[2] = 0x00;
  if (DMM_ARRAY[5] == 0xFF) DMM_ARRAY[5] = 0x00;
  if (DMM_ARRAY[6] == 0xFF) DMM_ARRAY[6] = 0x00;
  //data is in BCD, convert each digit to ascii by adding 0x30
  eLevel_temp[0] = (DMM_ARRAY[1] >> 4) | 0x30;
  eLevel_temp[1] = (DMM_ARRAY[1] & 0x0F) | 0x30;
  eLevel_temp[2] = '.'; //character array terminator
  eLevel_temp[3] = (DMM_ARRAY[2] >> 4) | 0x30;
  eLevel_temp[4] = (DMM_ARRAY[2] & 0x0F) | 0x30;
  eLevel_temp[5] = '\0';
  eLevel_angle[0] = (DMM_ARRAY[5] >> 4) | 0x30;
  eLevel_angle[1] = (DMM_ARRAY[5] & 0x0F) | 0x30;
  eLevel_angle[2] = '.';
  eLevel_angle[3] = (DMM_ARRAY[6] >> 4) | 0x30;
  eLevel_angle[4] = (DMM_ARRAY[6] & 0x0F) | 0x30;
  eLevel_angle[5] = '\0'; //character array terminator
  //convert data from BCD to integer, integer is in hundredths of a degree
  eLevel_temp_ = (DMM_ARRAY[1] >> 4) * 1000 + (DMM_ARRAY[1] & 0x0F) * 100 + (DMM_ARRAY[2] >> 4) * 10 + (DMM_ARRAY[2] & 0x0F);
  eLevel_angle_ = (DMM_ARRAY[5] >> 4) * 1000 + (DMM_ARRAY[5] & 0x0F) * 100 + (DMM_ARRAY[6] >> 4) * 10 + (DMM_ARRAY[6] & 0x0F);
}


Something I discovered while trying to test this, apparently its not possible to do a loopback on the software serial port, it doesn't seem to be capable of sending and receiving at the same time.

Robin2

its not possible to do a loopback on the software serial port, it doesn't seem to be capable of sending and receiving at the same time.
Correct. It is very primitive.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Steveiboy

Correct. It is very primitive.

...R
Mean's that this may have some influence on it not receiving the data I've now changed it over to a Mega256 for testing.

 david_2018 :
Thanks for this, It still not giving me the correct readings or I'm not understanding what I should get, I've added/removed PR-print so that is display's in the serial port to make it easier to show what the LCD is printing and easier to paste it here.

Code: [Select]
1,0,4,6,4 // 0.70 degrees
2,0,4,4,11
3,0,4,4,11
4,0,4,4,11
5,0,4,4,11
6,0,4,4,11
7,0,4,4,11
8,0,4,4,11
9,0,4,4,11
10,-4,9,4,11
11,6,4,4,11
12,6,4,-7,8 // 3.8 degrees
13,6,4,-7,8
14,0,4,2,2
15,0,4,4,4
16,0,4,4,4
17,0,4,4,4
18,0,4,4,4
19,0,4,4,4
20,0,4,4,4


New code
Code: [Select]
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
LiquidCrystal_I2C lcd(0x03f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
byte index = 0;
char DMM_ARRAY[8];   // Where to store the Bytes read
byte c;
char SETUP = 0x65;
int n = 0;
int eLevel_temp[6]; //eLevel temperature xx.xx degrees
int eLevel_Math; //eLevel temperature xx.xx degrees
int eLevel_Math1; //eLevel temperature xx.xx degrees
int eLevel_Math2; //eLevel temperature xx.xx degrees
int eLevel_angle[6]; //eLevel angle xx.xx degrees
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)

#define ECHO_TO_SERIAL 1 //change to zero = no serial output
bool newData = false;
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};


char eLevel_temp_; //temperature in hundredths of a degree
char eLevel_angle_; //angle in hundredths of a degree

void printRecord(Print* pr, char sep = ',') {
  pr->print(t);
  pr->print(sep);
  pr->print( DMM_ARRAY[2] >> 4); // Print btye 2
  pr->print(sep);
  pr->print(DMM_ARRAY[2] & 0x0F); // Print btye 1
  pr->print(sep);
  pr->print(DMM_ARRAY[5] >> 4); // Print btye 2
  pr->print(sep);
  pr->print(DMM_ARRAY[5] & 0x0F);// Print btye 5

  pr->println();

}
void setup() {
  lcd.begin (20, 4); // for 20 X 4 LCD module
  lcd.setBacklight(HIGH);
  lcd.print("TESTING");
  delay(3000);
  lcd.clear();
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial1.write(SETUP);
  Serial.println(SETUP);
  delay(1000);
}

void loop() {
  Get_data(); //Read the Serial bytes coming in
  if (newData) { //new data received from eLevel
    newData = false;
    convert_BCD(); //convert data from BCD to integer and character array
    // Send debug to serial port if enabled
  }
  eLevel_Math =  (DMM_ARRAY[5] * 100 + DMM_ARRAY[6]) / 100;
  unsigned long currentMillis = millis();
#if ECHO_TO_SERIAL
  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    t = t + 1;
    printRecord(&Serial);
#endif //ECHO_TO_SERIAL
  }
  // LCD line 1
  lcd.setCursor(0, 0);
  //lcd.print(DMM_ARRAY[0], HEX); // Print btye 0
  // lcd.print(", ");

  //    lcd.print(DMM_ARRAY[1], HEX);// Print btye 1
  lcd.print(DMM_ARRAY[5] >> 4); // Print btye 1
  lcd.print(", ");
  lcd.print(DMM_ARRAY[5] & 0x0F); // Print btye 1

  // LCD line 2
  lcd.setCursor(5, 0);
  //    lcd.print( DMM_ARRAY[2], HEX);// Print btye 2
  lcd.print( DMM_ARRAY[6] >> 4); // Print btye 2
  lcd.print(", ");
  lcd.print( DMM_ARRAY[6] & 0x0F); // Print btye 2
  lcd.setCursor(0, 2);
  lcd.print(  eLevel_Math);// Print btye 5
  lcd.print("   ");// Print btye 5

}
void Get_data() {
  const byte size_DMM_ARRAY = (sizeof(DMM_ARRAY) / sizeof(DMM_ARRAY[0]));
  static byte index_mySerial = 0;
  while (Serial1.available() > 0 && newData == false) {
    DMM_ARRAY[index_mySerial] = Serial1.read();
    index_mySerial++;
    if (index_mySerial == size_DMM_ARRAY) {
      if ((DMM_ARRAY[0] == 0xEE) &&
          (DMM_ARRAY[3] == 0xEE) &&
          (DMM_ARRAY[4] == 0xDE) &&
          ((DMM_ARRAY[7] == 0xEE) || (DMM_ARRAY[7] = 0xDD))) {
        newData = true;
        index_mySerial = 0;
      } else {
        for (byte i = 0; i < size_DMM_ARRAY - 1; i++) {
          DMM_ARRAY[i] = DMM_ARRAY[i + 1];
        }
        index_mySerial = size_DMM_ARRAY - 1;
      }
    }
  }
}


void convert_BCD() {
  //"00" is sent as "FF", convert back to "00"
  if (DMM_ARRAY[1] == 0xFF) DMM_ARRAY[1] = 0x00;
  if (DMM_ARRAY[2] == 0xFF) DMM_ARRAY[2] = 0x00;
  if (DMM_ARRAY[5] == 0xFF) DMM_ARRAY[5] = 0x00;
  if (DMM_ARRAY[6] == 0xFF) DMM_ARRAY[6] = 0x00;
  //data is in BCD, convert each digit to ascii by adding 0x30
  eLevel_temp[0] = (DMM_ARRAY[1] >> 4) | 0x30;
  eLevel_temp[1] = (DMM_ARRAY[1] & 0x0F) | 0x30;
  eLevel_temp[2] = '.'; //character array terminator
  eLevel_temp[3] = (DMM_ARRAY[2] >> 4) | 0x30;
  eLevel_temp[4] = (DMM_ARRAY[2] & 0x0F) | 0x30;
  eLevel_temp[5] = '\0';
  eLevel_angle[0] = (DMM_ARRAY[5] >> 4) | 0x30;
  eLevel_angle[1] = (DMM_ARRAY[5] & 0x0F) | 0x30;
  eLevel_angle[2] = '.';
  eLevel_angle[3] = (DMM_ARRAY[6] >> 4) | 0x30;
  eLevel_angle[4] = (DMM_ARRAY[6] & 0x0F) | 0x30;
  eLevel_angle[5] = '\0'; //character array terminator
  //convert data from BCD to integer, integer is in hundredths of a degree
  eLevel_temp_ = (DMM_ARRAY[1] >> 4) * 1000 + (DMM_ARRAY[1] & 0x0F) * 100 + (DMM_ARRAY[2] >> 4) * 10 + (DMM_ARRAY[2] & 0x0F);
  eLevel_angle_ = (DMM_ARRAY[5] >> 4) * 1000 + (DMM_ARRAY[5] & 0x0F) * 100 + (DMM_ARRAY[6] >> 4) * 10 + (DMM_ARRAY[6] & 0x0F);
}


So if the numbers are sent in binary coded decimal, How could I convert those to real values so when the  inclinometer reads 3.8 degrees I can get the serial port/lcd disply the same rahter than the numbers above /

thanks
Steve


david_2018

I see you changed DIM_ARRAY to a char array, that is conflicting with the hex literal values I was using in the IF statements, the array needs to be either int or byte, or recast the hex values as (char).

Also, in the loop there is an " if (newData) " , you will only have valid input data when newData is true, but you are executing most of the code in the loop regardless of whether it is true or not.

Do you have a sample of the output from the exact code that I posted earlier?  It would help to actually see what you are getting as input from the level, manuals aren't always accurate.

Steveiboy

#9
Apr 20, 2019, 11:45 pm Last Edit: Apr 20, 2019, 11:46 pm by Steveiboy
I see you changed DIM_ARRAY to a char array, that is conflicting with the hex literal values I was using in the IF statements, the array needs to be either int or byte, or recast the hex values as (char).

Also, in the loop there is an " if (newData) " , you will only have valid input data when newData is true, but you are executing most of the code in the loop regardless of whether it is true or not.

Do you have a sample of the output from the exact code that I posted earlier?  It would help to actually see what you are getting as input from the level, manuals aren't always accurate.

I've have tried several ways, I've reloaded your code tried with both software serial and now Serial1 on the mega.

This is a copy of your code as you posted the only thing I've changed is myseerail to Serial1.

AS your code stands now the e get's printed on the Serial port and after that nothing get's displayed on the LCD or Serial1 port as if it's locked or receiving no data I take it

Your code with change of serial port
Code: [Select]

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
LiquidCrystal_I2C lcd(0x03f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
byte index = 0;
int DMM_ARRAY[8];   // Where to store the Bytes read
byte c;
char SETUP = 0x65;
int n = 0;
#define ECHO_TO_SERIAL 1 //change to zero = no serial output
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};

bool newData = false;
char eLevel_temp[6]; //eLevel temperature xx.xx degrees
char eLevel_angle[6]; //eLevel angle xx.xx degrees
int eLevel_temp_; //temperature in hundredths of a degree
int eLevel_angle_; //angle in hundredths of a degree

void printRecord(Print* pr, char sep = ',') {
  pr->print(t);
  pr->print(sep);
  pr->print(DMM_ARRAY[0], HEX); // Print btye 0
  pr->print(sep);
  //  pr->print(DMM_ARRAY[1], HEX); // Print btye 1
  pr->print(DMM_ARRAY[1] >> 4); // Print btye 1
  pr->print(DMM_ARRAY[1] & 0x0F); // Print btye 1
  pr->print(sep);
  //  pr->print(DMM_ARRAY[2], HEX); // Print btye 2
  pr->print(DMM_ARRAY[2] >> 4); // Print btye 2
  pr->print(DMM_ARRAY[2] & 0x0F); // Print btye 2
  pr->print(sep);
  pr->print(DMM_ARRAY[3], HEX); // Print btye 3
  pr->print(sep);
  pr->print(DMM_ARRAY[4], HEX); // Print btye 4
  pr->print(sep);
  //  pr->print(DMM_ARRAY[5], HEX); // Print btye 5
  pr->print(DMM_ARRAY[5] >> 4); // Print btye 5
  pr->print(DMM_ARRAY[5] & 0x0F); // Print btye 5
  pr->print(sep);
  //  pr->print(DMM_ARRAY[6], HEX); // Print btye 6
  pr->print(DMM_ARRAY[6] >> 4); // Print btye 6
  pr->print(DMM_ARRAY[6] & 0x0F); // Print btye 6
  pr->print(sep);
  pr->print(DMM_ARRAY[7], HEX); // Print btye 7
  pr->println();
  pr->print("temperature: ");
  pr->print(eLevel_temp);
  pr->print(" ");
  pr->println(eLevel_temp_);
  pr->print("angle      : ");
  pr->print(eLevel_angle);
  pr->print(" ");
  pr->println(eLevel_angle_);
}

void setup() {
  lcd.begin (20, 4); // for 20 X 4 LCD module
  lcd.setBacklight(HIGH);
  lcd.print("TESTING");
  delay(3000);
  lcd.clear();
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial1.write(SETUP);
  Serial.println(SETUP);
  delay(1000);
}

void loop() {
  Get_data(); //Read the Serial bytes coming in
  if (newData) { //new data received from eLevel
    newData = false;
    convert_BCD(); //convert data from BCD to integer and character array
    // Send debug to serial port if enabled
#if ECHO_TO_SERIAL
    printRecord(&Serial);
#endif //ECHO_TO_SERIAL
    // LCD line 1
    lcd.setCursor(0, 0);
    lcd.print(DMM_ARRAY[0], HEX); // Print btye 0
    lcd.print(", ");
    lcd.setCursor(5, 0);
    //    lcd.print(DMM_ARRAY[1], HEX);// Print btye 1
    lcd.print(DMM_ARRAY[1] >> 4); // Print btye 1
    lcd.print(DMM_ARRAY[1] & 0x0F); // Print btye 1
    lcd.print(", ");
    // LCD line 2
    lcd.setCursor(0, 1);
    //    lcd.print( DMM_ARRAY[2], HEX);// Print btye 2
    lcd.print( DMM_ARRAY[2] >> 4); // Print btye 2
    lcd.print( DMM_ARRAY[2] & 0x0F); // Print btye 2
    lcd.print(", ");
    lcd.setCursor(5, 1);
    lcd.print( DMM_ARRAY[3], HEX);// Print btye 3
    lcd.print(" ");
    // LCD line 3
    lcd.setCursor(0, 2);
    lcd.print( DMM_ARRAY[4], HEX);// Print btye 4
    lcd.print(", ");
    lcd.setCursor(5, 2);
    //    lcd.print( DMM_ARRAY[5], HEX);// Print btye 5
    lcd.print( DMM_ARRAY[5] >> 4);// Print btye 5
    lcd.print( DMM_ARRAY[5] & 0x0F);// Print btye 5
    lcd.print(", ");
    // LCD line 4
    lcd.setCursor(0, 3);
    //    lcd.print( DMM_ARRAY[6], HEX);// Print btye 6
    lcd.print( DMM_ARRAY[6] >> 4); // Print btye 6
    lcd.print( DMM_ARRAY[6] & 0x0F);// Print btye 6
    lcd.print(", ");
    lcd.setCursor(5, 3);
    lcd.print( DMM_ARRAY[7], HEX);// Print btye 7
    lcd.print(" ");
  }
}

void Get_data() {
  /*
    if serialavailable
    read byte into array
    increment array pointer
    if all bytes in array received
      if array fits data pattern of "EE xx xx EE DE xx xx (DD or EE)"
        set data available flag
        reset array pointer
      else
        delete oldest byte in array
        decrement array pointer to allow room for next byte received
  */
  const byte size_DMM_ARRAY = (sizeof(DMM_ARRAY) / sizeof(DMM_ARRAY[0]));
  static byte index_mySerial = 0;
  while (Serial1.available() > 0 && newData == false) {
    DMM_ARRAY[index_mySerial] = Serial1.read();
    index_mySerial++;
    if (index_mySerial == size_DMM_ARRAY) {
      if ((DMM_ARRAY[0] == 0xEE) &&
          (DMM_ARRAY[3] == 0xEE) &&
          (DMM_ARRAY[4] == 0xDE) &&
          ((DMM_ARRAY[7] == 0xEE) || (DMM_ARRAY[7] = 0xDD))) {
        newData = true;
        index_mySerial = 0;
      } else {
        for (byte i = 0; i < size_DMM_ARRAY - 1; i++) {
          DMM_ARRAY[i] = DMM_ARRAY[i + 1];
        }
        index_mySerial = size_DMM_ARRAY - 1;
      }
    }
  }
}


void convert_BCD() {
  //"00" is sent as "FF", convert back to "00"
  if (DMM_ARRAY[1] == 0xFF) DMM_ARRAY[1] = 0x00;
  if (DMM_ARRAY[2] == 0xFF) DMM_ARRAY[2] = 0x00;
  if (DMM_ARRAY[5] == 0xFF) DMM_ARRAY[5] = 0x00;
  if (DMM_ARRAY[6] == 0xFF) DMM_ARRAY[6] = 0x00;
  //data is in BCD, convert each digit to ascii by adding 0x30
  eLevel_temp[0] = (DMM_ARRAY[1] >> 4) | 0x30;
  eLevel_temp[1] = (DMM_ARRAY[1] & 0x0F) | 0x30;
  eLevel_temp[2] = '.'; //character array terminator
  eLevel_temp[3] = (DMM_ARRAY[2] >> 4) | 0x30;
  eLevel_temp[4] = (DMM_ARRAY[2] & 0x0F) | 0x30;
  eLevel_temp[5] = '\0';
  eLevel_angle[0] = (DMM_ARRAY[5] >> 4) | 0x30;
  eLevel_angle[1] = (DMM_ARRAY[5] & 0x0F) | 0x30;
  eLevel_angle[2] = '.';
  eLevel_angle[3] = (DMM_ARRAY[6] >> 4) | 0x30;
  eLevel_angle[4] = (DMM_ARRAY[6] & 0x0F) | 0x30;
  eLevel_angle[5] = '\0'; //character array terminator
  //convert data from BCD to integer, integer is in hundredths of a degree
  eLevel_temp_ = (DMM_ARRAY[1] >> 4) * 1000 + (DMM_ARRAY[1] & 0x0F) * 100 + (DMM_ARRAY[2] >> 4) * 10 + (DMM_ARRAY[2] & 0x0F);
  eLevel_angle_ = (DMM_ARRAY[5] >> 4) * 1000 + (DMM_ARRAY[5] & 0x0F) * 100 + (DMM_ARRAY[6] >> 4) * 10 + (DMM_ARRAY[6] & 0x0F);
}


Using this code:
Code: [Select]
int ByteIndex = 1;      // used to count number of bytes, DMM message comprise of 14 bytes

void setup() {
  Serial.begin(9600);   // set the baudrate for the Serial port
  Serial1.begin(9600);// set the baudrate for the DMMSerial port
}

// Loop collecting data and printing to Monitor
void loop() {

  while (Serial1.available()) {
    // get the new byte:
    byte inChar = Serial1.read();
    Serial.print(inChar, HEX);
    Serial.print("\t");
    ++ByteIndex;                   // Increment number of bytes counter

    if (ByteIndex == 8) {
      Serial.println();       // Print a line
      ByteIndex = 1;           // A full message is received, so reset counter
    }

  }


}

I get this on the Monitor:

Code: [Select]
4 1B 5E 4 8 0 47 4
4 1B 97 4 8 0 47 4
4 1B 5E 4 8 0 47 4
4 1B 5F 4 8 0 47 4
4 1B 5F 4 8 0 47 4
4 1B 5F 4 8 0 47 4
4 1B 5E 4 8 0 47 4
4 1B 5E 4 8 0 47 4
4 1B 5D 4 8 0 47 4




david_2018

#10
Apr 20, 2019, 11:52 pm Last Edit: Apr 20, 2019, 11:53 pm by david_2018
Just to verify, you are using an RS232 converter between the arduino and the level, and the grounds of the level and arduino are tied together?


Steveiboy

Just to verify, you are using an RS232 converter between the arduino and the level, and the grounds of the level and arduino are tied together?


Hi David


Yes I've got an RS232 in between the eLevel, I've just double checked and found there was no ground connection at the eLevel which was very strange the wire had broken in the heat shrink  :o .

I've now corrected this, Your code still gives no output on either but using the second code this is what I now get which is looking more like what the data sheet say apart from I get DD at the end

Code: [Select]
EE 25 83 EE DE 1 4 DD
EE 25 83 EE DE 1 4 DD
EE 25 84 EE DE 1 4 DD
EE 25 85 EE DE 1 4 DD
EE 25 84 EE DE 1 4 DD
EE 25 84 EE DE 1 4 DD
EE 25 84 EE DE 1 4 DD
EE 25 84 EE DE 1 4 DD
EE 25 85 EE DE 1 4 DD
EE 25 87 EE DE 1 4 DD
EE 25 88 EE DE 1 4 DD
EE 25 89 EE DE 1 4 DD
EE 25 90 EE DE 1 5 DD
EE 25 90 EE DE 1 5 DD
EE 25 89 EE DE 1 5 DD
EE 25 89 EE DE FF 9 DD

david_2018

#12
Apr 21, 2019, 12:32 am Last Edit: Apr 21, 2019, 12:38 am by david_2018
Well, my code would still be trying to use software serial, and you've gone to hardware on the mega, so it wouldn't work.

Put this at the start of my code:

#define mySerial Serial1

EDIT:  and comment out the following line:

SoftwareSerial mySerial(3, 2); // RX, T

The DD at the end is correct, the manual says that can be either EE or DD.
The bad ground was the majority of your problem, none of the data would be correct with that.

Steveiboy

OopS!, Sorry I was getting all myself into a mess with that many windows open,

Now I've added #define mySerial Serial1 it's displaying the angle and temperature, There  is a slight issue which I shall look into.

I've now added the angle to the LCD which displays 00.68 and on the eLevel it displays 0.70 degrees. There is some maths on the data sheet which I can see about adding and see if that corrects the issue.


I can't thank you enough for all the hard work and help you have given me, I would of never done it with out your help. Plus the broken wire added to the problem.


Thanks

Steve

Steveiboy

#14
Apr 21, 2019, 11:18 pm Last Edit: Apr 21, 2019, 11:56 pm by Steveiboy
Hi David sorry to trouble you again.
Your code has been working great and I have been studying it.

I've added some code so that I can send the data over a pair of NRF24l01's but when I comment in SendDATA() there is nothing on the TX LCD as if it's frozen. If comment it out it all come back working.

I have the Mega set up as the TX and a nano as the RX, I know that all the connections are good and communication is working as I've loaded the test code that Robin2's tutorial  and can see it count up to 9, I've aslo loaded up some other code that I've used in the past and that works.


Could be be the way the incoming serial data comes in ?

Here is the lastest code that I'm trying at the momment sendDATA() is commented out and tried it in a few places and differnet types of data to send out


Code: [Select]
#include <SPI.h>   // Comes with Arduino IDE
#include "RF24.h"  // Download and Install (See above)
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
//#include <SoftwareSerial.h>
//SoftwareSerial mySerial(6,7); // RX, T 2,3
LiquidCrystal_I2C lcd(0x03f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
#define mySerial Serial1
int DMM_ARRAY[8];   // Where to store the Bytes read
char SETUP = 0x65;
int n = 0;
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};
unsigned long previousMillis = 0;        // will store last time LED was updated
// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)
bool newData = false;
char eLevel_temp[6]; //eLevel temperature xx.xx degrees
char eLevel_angle[6]; //eLevel angle xx.xx degrees
char inclinometer_send[4];
int eLevel_temp_; //temperature in hundredths of a degree
int eLevel_angle_; //angle in hundredths of a degree
RF24 myRadio(9, 10); // "myRadio" is the identifier you will use in following methods
byte addresses[][6] = { "1Node" }; // Create address for 1 pipe.
//#######################################################
//# Send out data to Serial monitor, only for debugging #
//#######################################################

void setup() {
  lcd.begin (20, 4); // for 20 X 4 LCD module
  lcd.setBacklight(HIGH);
  lcd.clear();
  Serial.begin(9600);
  Serial.println("TEST");
  mySerial.begin(9600);
  myRadio.begin();  // Start up the physical nRF24L01 Radio
  myRadio.setChannel(108);  // Above most Wifi Channels
  myRadio.setPALevel(RF24_PA_MAX);  // Uncomment for more power
  myRadio.setDataRate(RF24_250KBPS); // Fast enough.. Better range
  myRadio.openWritingPipe(addresses[0]); // Use the first entry in array 'addresses' (Only 1 right now)
}

void loop() {
  Get_data(); //Read the Serial bytes coming in
  if (newData) { //new data received from eLevel
    newData = false;
    convert_BCD(); //convert data from BCD to integer and character array
    // Send debug to serial port if enabled



    //Display the ttemperature
    lcd.setCursor(0, 0);
    lcd.print(DMM_ARRAY[1] >> 4); // Print btye 1
    lcd.print(DMM_ARRAY[1] & 0x0F); // Print btye 1
    lcd.print(".");
    lcd.setCursor(3, 0);
    lcd.print( DMM_ARRAY[2] >> 4); // Print btye 2
    lcd.print( DMM_ARRAY[2] & 0x0F); // Print btye 2
    lcd.print("  ");
    //Display angle
    lcd.setCursor(0, 2);
    lcd.print( DMM_ARRAY[5] >> 4);// Print btye 5
    lcd.print( DMM_ARRAY[5] & 0x0F);// Print btye 5
    lcd.print(".");
    lcd.setCursor(3, 2);
    lcd.print( DMM_ARRAY[6] >> 4); // Print btye 6
    lcd.print( DMM_ARRAY[6] & 0x0F);// Print btye 6
    lcd.print("  ");
    // left in for debuging
    lcd.setCursor(10, 3);
    lcd.print(eLevel_angle);
    lcd.print("   ");
    lcd.setCursor(10, 0);
    lcd.print(eLevel_temp);
    lcd.print("   ");
    // Not yet tested as it freezes on sending data
   // inclinometer_send[0] = DMM_ARRAY[5] >> 4;// send upper btye
   // inclinometer_send[1] = DMM_ARRAY[5] & 0x0F;// send lower byte
   // inclinometer_send[2] = DMM_ARRAY[6] >> 4; //Send upper byte
   // inclinometer_send[3] = DMM_ARRAY[6] & 0x0F;// Send lower byte
    // sendDATA();


  }

  //  sendDATA();
}
void sendDATA() {
  myRadio.write(&eLevel_angle_, sizeof(eLevel_angle_)); //  Transmit the data
  //  myRadio.write(&inclinometer_send_, sizeof(inclinometer_send)); //  Transmit the data
}
void convert_BCD() {
  //"00" is sent as "FF", convert back to "00"
  if (DMM_ARRAY[1] == 0xFF) DMM_ARRAY[1] = 0x00;
  if (DMM_ARRAY[2] == 0xFF) DMM_ARRAY[2] = 0x00;
  if (DMM_ARRAY[5] == 0xFF) DMM_ARRAY[5] = 0x00;
  if (DMM_ARRAY[6] == 0xFF) DMM_ARRAY[6] = 0x00;
  //data is in BCD, convert each digit to ascii by adding 0x30
  eLevel_temp[0] = (DMM_ARRAY[1] >> 4) | 0x30;
  eLevel_temp[1] = (DMM_ARRAY[1] & 0x0F) | 0x30;
  eLevel_temp[2] = '.'; //character array terminator
  eLevel_temp[3] = (DMM_ARRAY[2] >> 4) | 0x30;
  eLevel_temp[4] = (DMM_ARRAY[2] & 0x0F) | 0x30;
  eLevel_temp[5] = '\0';
  eLevel_angle[0] = (DMM_ARRAY[5] >> 4) | 0x30;
  eLevel_angle[1] = (DMM_ARRAY[5] & 0x0F) | 0x30;
  eLevel_angle[2] = '.';
  eLevel_angle[3] = (DMM_ARRAY[6] >> 4) | 0x30;
  eLevel_angle[4] = (DMM_ARRAY[6] & 0x0F) | 0x30;
  eLevel_angle[5] = '\0'; //character array terminator
  //convert data from BCD to integer, integer is in hundredths of a degree
  eLevel_temp_ = (DMM_ARRAY[1] >> 4) * 1000 + (DMM_ARRAY[1] & 0x0F) * 100 + (DMM_ARRAY[2] >> 4) * 10 + (DMM_ARRAY[2] & 0x0F);
  eLevel_angle_ = (DMM_ARRAY[5] >> 4) * 1000 + (DMM_ARRAY[5] & 0x0F) * 100 + (DMM_ARRAY[6] >> 4) * 10 + (DMM_ARRAY[6] & 0x0F);
void Get_data() {
  /*
    if serialavailable
    read byte into array
    increment array pointer
    if all bytes in array received
      if array fits data pattern of "EE xx xx EE DE xx xx (DD or EE)"
        set data available flag
        reset array pointer
      else
        delete oldest byte in array
        decrement array pointer to allow room for next byte received
  */
  const byte size_DMM_ARRAY = (sizeof(DMM_ARRAY) / sizeof(DMM_ARRAY[0]));
  static byte index_mySerial = 0;
  while (mySerial.available() > 0 && newData == false) {
    DMM_ARRAY[index_mySerial] = mySerial.read();
    index_mySerial++;
    if (index_mySerial == size_DMM_ARRAY) {
      if ((DMM_ARRAY[0] == 0xEE) &&
          (DMM_ARRAY[3] == 0xEE) &&
          (DMM_ARRAY[4] == 0xDE) &&
          ((DMM_ARRAY[7] == 0xEE) || (DMM_ARRAY[7] = 0xDD))) {
        newData = true;
        index_mySerial = 0;
      } else {
        for (byte i = 0; i < size_DMM_ARRAY - 1; i++) {
          DMM_ARRAY[i] = DMM_ARRAY[i + 1];
        }
        index_mySerial = size_DMM_ARRAY - 1;
      }
    }
  }
}


}

Go Up