[SOLVED] Having problems converting char from array

I’m having a few problems converting characters received from a softwareserial input. I have constructed a circuit to transfer speed and command data from my milling machine to the variable frequency drive over a fibre optic link. Because I’m using low cost led diodes I can only transmit at 2400Baud which has limited the number of characters I can send.
I have converted the speed (an integer from 0 to 7500) into 2 characters first representing the high 8 bits of a 16 bit value and the second being the lower 8 bits (by bit shifting by eight) a third character is a bit weighted BCD representation of spindle direction and suds operation.
Format of transmission is
The string is sent by
XSERIAL.write (‘<’)
XSERIAL.write (Spindle1)
XSERIAL.write (Spindle2)
XSERIAL.write (function)
XSERIAL.write (‘>’)
XSERIAL.write (‘\n’).

Tried to send the values as string but data stream got corrupted and change Baud rates but again data gets corrupted.

So this method of sending the data is the best achievable method and successfully works.

The problem is on the receiving end – again using softwareserial

I have read the character in the third position in the serial input array with atoi and can see the and use the value from the bcd

Trying to convert the characters back to an integer from array locations 0 & 1 is where I’m hitting a wall

I think where I’m getting confused is that the characters are being sent as bytes which are being received into a char array so I should be able to do atoi on sub[0] & sub[1] and bit shift by 8.
I have tried a few examples from the tinterweb but had no success so far.

Any pointers would be gratefully accepted.

Post the code you have. Also, from your description, it's hard to figure out what is sending the serial data and what is receiving it.

Using Robin2's example
showparseddata () is where the magic happens, I have commented out bits that are not working yet that i'm experimenting with.

#include <SoftwareSerial.h>
#define rx 2
#define tx 3

SoftwareSerial XSERIAL =  SoftwareSerial(rx, tx, false);

const byte numChars = 10;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

      // variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;
int FS_SpeedMsg =0;
boolean newData = false;

//============

void setup() {
  
    Serial.begin(9600);
    XSERIAL.begin(2400);

}

//============

void loop() {
  
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (XSERIAL.available() > 0 && newData == false) {
        rc = XSERIAL.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}



//============

void showParsedData() {

  
//FS_SpeedMsg |= (receivedChars [0],DEC)<< 8 ;
//FS_SpeedMsg |= (receivedChars [1],DEC) & 0xFF ;
    
    //Serial.print (receivedChars[0]);
    //Serial.println (FS_SpeedMsg); 
//Serial.println(int(receivedChars [1]) & 0xFF) ;
//Serial.println 
    
Serial.println (receivedChars[2],DEC);

}

its receiving the values as i can see value from Serial.println (receivedChars[2],DEC); it is the bit weighted byte i'm using to operate the machine functions such as forward, reverse and the coolant/suds pump.
its value is changing.

using SMD serial monitor and looking at non printable characters i can see the ascii characters in receivedChars[0] & receivedChars[1] so i know they are being transmitted.

my previous attempt at this code i sent comma delimited string values and the communication and control of the machine worked but started to get snarled up. in order to reduce the number of characters so that i could add start and end characters i had to convert the speed/rpm into two bytes representing a 16 bit value instead of using 4 characters.

I will send the transmitter code later, its on another machine outside in the workshop.
No there not linked by this fibre fiasco, i was working on a different machine which is off grid. Not interfered with by tinternet and constant updates as its dedicated to the milling machine.

using

FS_SpeedMsg = receivedChars [0];
FS_SpeedMsg = (FS_SpeedMsg << 8) | receivedChars [1];

I have managed to see values outputted to the serial monitor. where the range is from 0 to 7500.
i can only get the high values above 4000 to work correctly, below that i'm getting negative low numbers.
ie -23, -106 etc when adjusting the speed ?

@Op

Can I have answer to the following queries that relate to your post/project:

1. When you say 'character' -- do you want mean ASCII character/8-bit ASCII code?

2. Assume that the speed information is: 4668 (decimal). If I have understood your post correctly, you are transferring this information as : upper 8-bit first which is 12 of 123C (123C is the hex/binary of decimal 4668), and then the lower 8-bit which 3C. Am I correct?

3. Assume 'spindle direction and stud operation' together amounts to decimal 62. Are you sending it as 0110 0010 (BCD representation)? If not, please explain your approach.

4. You are sending the character < as the beginning marker of your 3-byte data frame. While transmitting < over the UART Port, the value 3C (ASCII Code of <) is transmitted. Is this value not going to mingle with the 3C data of Step-2 when the speed is 4668? If there exists such a possibility, then we have to adapt another transmission protocol which does not have this 'serious flaw'. Do you agree?

Hi Golam,
wow and i thought i wasn't far off finishing.
Ok
Question 1
yes im refering to 8 bit ascii char

Question2
i'm converting the value into a 16 bit integer and sending the high 8 bits as a char and low 8 bits as a char
so
4668 will be something like ".D" where the character "." = 46 and "D" = 68 or at least that's what i think i'm doing. Sending Hex requires 2 characters where an ascii character has a decimal value 0 to 255.

Question3
I'm adding the values of the functions to power of 2
Clockwise is either 0 or 1 counterclockwise is either 0 or 2 and suds is either 0 or 4.
this is being transmitted as a character of int value. On the receiving end i will be looking at the binary bit of value in the array. This works really well.

Question4
Im assuming that i am sending < character as a byte (decimal 60 Ascii).
the start < and end > characters are being stripped by the code and the buffer only holds 3 values.
the data will always be transmitted with Buf [0] and Buf [1] holding the spindle speed characters and Buf [2] having a single character representing the functions.

Interesting what you have said regarding the conflict of < start char and < representing 46** Rpm thanks for the heads up If i choose a character with decimal value over 75 as the start character then that should prevent a conflict happening perhaps U (85 decimal).

I do agree with the point of using a different protocol, it was the only tunnel vision and blinkered direction i could see. any more than 7 bytes transmitted over the fibre results in the data just being streamed out in a line of constant gibberish, very much like myself.

I hope this answers your questions and look forward to your response.

JT007:
Using Robin2's example
showparseddata () is where the magic happens, I have commented out bits that are not working yet that i'm experimenting with.

Let's keep things as simple as possible. Before you worry about parsing anything get your program to print what it has received with a simple

Serial.println(receivedChars);

and post that here

In your Original Post you have this piece

Format of transmission is
The string is sent by
XSERIAL.write ('<')
XSERIAL.write (Spindle1)
XSERIAL.write (Spindle2)
XSERIAL.write (function)
XSERIAL.write ('>')
XSERIAL.write ('\n').

If it is an Arduino that is running that code then write() will be sending a binary value rather than a character - unless the variables Spindle1 etc contain a single printable character.

I find it much easier to send data in human readable form using Serial.print()

...R

Hi Robin,

Id love to but some of the characters are in the extended ascii table and unprintable
I have used serial monitor deluxe to capture it and attached a screen grab. on the left is candle the cnc controller s/ware its using software serial to send the speed and command out over a plastic fibre at 2400 baud. another Atmega is receiving the comms over fibre.

The output is displayed on the left of the picture

what can be seen is 3 values the first two are speed and the last is the function.
what the \ characters are i have no idea, I think its something SMD is doing.

I changed the debug to and altered the cnc output to give better readable value.
(some readings come out negative i have no idea why)

Serial.print (receivedChars[0],DEC);
Serial.print (receivedChars[1],DEC);
Serial.println (receivedChars[2],DEC);

value 1 and 2 are speed, the 1 on the end represents the spindle on in the forward direction

The hardware setup

I did try the chr string o/p, i will go back to it, change it and post the results in a screen shot again.
We have found the level of my intellect (or lack of), for some reason i convinced myself write would send a single (byte) character where string goes to a buffer hangs around for a while eats a few mips etc.

OP's pictures:

I still get frustrated trying to get images into my posts, the number of times i've gone for quick reply and found i couldn't attach any just link to URLs. How do you get them in the post just copy and paste ?

The arduino is running GRBL the cnc control firmware, im sniffing the TTL Tx line and parsing it on the small pcb which is then transmitting speed/dir etc over fibre to the control board which will be wired up to a variable frequency drive.

It all worked with a rs485 comms link (Nick Gammons code) but the noise from the inverter used the twisted pair wire as a gigantic aerial and caused loads of data loss issues. Hence the switch to fibre.

I have attached the GRBL Parse/transmitter code

Things i'm currently considering
Setup some basic hardware (2 Arduinos) and get the two handling the comms between each other at 2400 Baud using this current coding method. Will also try using the RS485 comms link without using the 75176 RS485 IC and using the ttl signal used in that setup to drive the fibre, the fibre would then be running Nicks 485 protocol but using fibre as the link ?

parseGRBLFibreRev1.0.ino (12.3 KB)

JT007:
I hope this answers your questions and look forward to your response.

Thank you very much for taking time and patience in answering to my queries.

Now, I am requesting you to provide me sample data of 'speed (0 - 7500)' and 'spindle direction (0 - 255)' for test purposes. I will be designing a 'UART Serial Transmission/Reception Protocol' based on Intel-Hex formatted frame (the one Arduino IDE uses to upload sketch into UNO) where the digits of the data would be sent as 8-bit ASCII codes (restricted within 0x30 - 0x39 and 0x41 - 0x46) and the Frame Marker would be a Control Character (say: 0x3A = :slight_smile: that will never ever conflict/mingle with the data bytes. The protocol has the option of accommodating normal CHECKSUM byte for data validation.

JT007:
Id love to but some of the characters are in the extended ascii table and unprintable

Ideally, if you have control of the program that sends the data then get it to send data in human readable form.

If you can't do that then you can get the receiving program to print the ASCII codes for the received data like this

for (byte n = 0; n < numChars; n++) {
   Serial.print( (byte) receivedChars[n]);
   Serial.print(' '); // a space character
}
Serial.println();

Note that this prints ALL the characters that are received - that is important. Just printing the items you think are relevant can be the cause of all sorts of confusion.

And please don't post pictures of text - just copy and paste the text.

...R
Simple Image Guide

Hi Golam

the best way to get the data is to use a spare arduino uno and run the grbl firmware on it

and control it via Candle

the code i attached above parseGRBLFibreRev1.0.ino will parse the data from grbl

Robin, Im going back to basics, i will keep you updated

Ok im talking pants,
there is no limitation on sending string

I have used this project here

https://www.instructables.com/id/Arduino-Communication/

I don't know what led diodes he is using to get 9600 Baud but i cant get my link over 2400 baud.

I suppose getting more expensive diodes and using glass fibre would speed up the link - so would buying £300 transceivers.

this is what im getting with my link (removed the delays)

Test 1
5mm Green LED
Success :slight_smile:

Test 1
5mm Green LED
Success :slight_smile:

Test 1
5mm Green LED
Success :slight_smile:

I think i steered away from it because i had a strange issue of every other line outputting a zero which obviously killed the spindle and then ramped it up again.

sorry for faces - its a forum thing when i send : & ) together

Ok so the progress so far,
I can send readable string commands without any problem.
when i try to add parts from grbl code transmission it gets corrupted

GRBL_msg4 should be FS:0,7500 for a spindle speed of 7500 (with feedrate 0 - not reqd) but the previous and next string from the array keeps breaking through corrupting the value.

when i send the value out via serial port at 9600 Baud i get a constant stream of FS:0,7500 but when the same is transmitted at 2400 Baud it gets the spurious messages.

So if i can find a way of guaranteeing the parse data to always output FS:0,7500 then the project is almost cracked.

it would appear that the slower baud rate is triggered when the parse isn't complete ?
like there is a timing issue or order of precedence issue.

#include <SoftwareSerial.h>
#define rx 3  
#define tx 2  

SoftwareSerial XSERIAL =  SoftwareSerial(rx, tx);

const byte LED_PIN = 12;

// Parsing GRBL message variables  

const byte numChars =71;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

//Feed and Speed variables
char FSMsgGRBL[12];
int FS_FeedMsg = 0;
int FS_SpeedMsg=0;
char FS_speed1=0;
char FS_speed2=0;


//Overide variables
bool OvMsg = false; //Ov message being transmitted
int CmdCoolant = 0;
int CmdSpindleCW = 0;
int Mach_fcn =0;
// Variables to hold Message blocks

char GRBL_msg1 [numChars] = {0};
char GRBL_msg2 [numChars] = {0};
char GRBL_msg3 [numChars] = {0};
char GRBL_msg4 [numChars] = {0};
char GRBL_msg5 [numChars] = {0};
char GRBL_msg6 [numChars] = {0};


boolean newData = false;



void setup() {
    
    
    Serial.begin(115200);

    XSERIAL.begin(2400);
    
    //rs485.begin (28800);
    //pinMode (ENABLE_PIN, OUTPUT);  // RS485 IC driver output enable
    pinMode (LED_PIN, OUTPUT);  // built-in LED - lights on transmission error
    digitalWrite(LED_PIN, HIGH);
    Serial.println("Ready");
}  // end of setup

//============

void loop() {

//  byte level = (data);
  
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        outputParsedData();
        newData = false;
        
        
    }
}  // End of loop

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}// End of recvWithStartEndMarkers

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

  //Parse into Blocks based on "|" Delimiter
  
        // machine state
            strtokIndx = strtok(tempChars,"|"); // Find first part of string (Machine State Idle, Run, Alarm) 
            strcpy(GRBL_msg1, strtokIndx);      // write characters from string to machine state variable
            
        // machine position   
            strtokIndx = strtok(NULL,"|"); // look for next data block in string  
            strcpy(GRBL_msg2, strtokIndx); // write characters from string to machine position variable 
             
        // BF god knows - alarms ?
            strtokIndx = strtok(NULL,"|"); // look for next data block in string      
            strcpy(GRBL_msg3, strtokIndx); // write characters from string to BF variable  
            
        // feed and spindle speed
            strtokIndx = strtok(NULL,"|"); // look for next data block in string     
            strcpy(GRBL_msg4, strtokIndx); // write characters from string to Feed&Speed variable 
             
        //Ov Overides 1% to 200%
            strtokIndx = strtok(NULL,"|"); // look for next data block in string  
            strcpy(GRBL_msg5, strtokIndx); // write characters from string to Overide variable 
             
        //machine command status
            strtokIndx = strtok(NULL,"|"); // look for next data block in string  
            strcpy(GRBL_msg6, strtokIndx); // write characters from string to machine command status variable 
            
        //anything else
   
} //End of parseData

 
//============

void outputParsedData() 
     {
  
          ParseFS (); // Parse feed and speed values from FS Block
          ParseOv_A (); //Reads first character in buffer looking for "O" then knows commands follow "A:" (A:SF Buffer) 

//XSERIAL.println = ((receivedChars));
  //String stringOne = ("<");
  //String stringTwo = stringOne + String(FS_SpeedMsg);
 // XSERIAL.print(stringTwo); 
  //String stringThree = stringOne + stringTwo;
  //String stringFour = (",");
  //String stringFive = stringFour + Mach_fcn;
  //String stringSix = stringFive + (">");
 // XSERIAL.println(stringSix); 

XSERIAL.print(GRBL_msg4); 
 XSERIAL.print("-");         

          XSERIAL.println(String(Mach_fcn));
        
    }//end of outputParsedData


//Feeds and speeds  FS Message GRBL /////////////////////////////////////////////////////////

void ParseFS ()
{
  
//GRBL_msg4 array containing Feed & Speed message characters
   
    char * partOfString; // this is used by strtok_r() as an index
    //FS_SpeedMsg = 0;
   // FS_speed1 = 0;
    //FS_speed2 = 0;
    
    partOfString = strtok (GRBL_msg3,":"); // get the first part - the string
    strcpy(FSMsgGRBL, partOfString);     // copy it to inputCsvString
    
    partOfString = strtok (NULL, ","); // this continues where the previous call left off
    FS_FeedMsg = atoi(partOfString);     // convert this part to an integer
    
    partOfString = strtok (NULL, ","); // this continues where the previous call left off
    FS_SpeedMsg = atoi(partOfString);   // convert this part to a float// actually an int

    
        //FS_speed1 = (FS_SpeedMsg>>8);
        //FS_speed2 = (FS_SpeedMsg & 0xFF);
 
}//End of ParseFS

// Parse Overide Ov Message GRBL ////////////////////////////////////////////////////////////

void ParseOv_A ()
{
  //GRBL_msg5 array containing Overide message characters  
   
      if (GRBL_msg4[0] == 'O')// Ov statement when true check machine command instructions
      
      {
              if (GRBL_msg5[0] == 'A')
               {
                
              //Serial.print("Got A ");
              //Serial.println(GRBL_msg5);
              
                      
              if (GRBL_msg5[2] == 'S')
                    {
                    CmdSpindleCW =1;
                 
                    }
              if(GRBL_msg5[2] == 'C')
                    {
                    CmdSpindleCW = 2;
                            
                    }
                    
         
                if (GRBL_msg5[2] == 'F'|(GRBL_msg5[3] == 'F'))
                    {
                    CmdCoolant = 4;           
                    }                                                    
                else
                   {
                   (CmdCoolant = 0);      
                                          
                   }
                                                         
              }//End - seeking "A" in GRBL_msg6
              
              if(strlen(GRBL_msg5) == 0) // makes sure spindle direction and coolant conditions are off woth no signal present
              {
              CmdSpindleCW = 0;  
              CmdCoolant = 0;  
              }                                
      }

      Mach_fcn = CmdSpindleCW + CmdCoolant;
     
} // end of ParseOv_A

This is the parsed messages

                   Serial.println("1");
                   Serial.println(GRBL_msg1);
                   Serial.println("2");
                   Serial.println(GRBL_msg2);
                   Serial.println("3");
                   Serial.println(GRBL_msg3);
                   Serial.println("4");
                   Serial.println(GRBL_msg4);
                   Serial.println("5");
                   Serial.println(GRBL_msg5);
                   Serial.println("6");
                   Serial.println(GRBL_msg6);

and output attached - this is bomb proof not a single glitch or loss of data

Output:-
1
Idle
2
WPos:0.000,0.000,0.000
3
Bf
4
FS:0,5300
5
Ov:100,100,100
6
A:SF

message 5 and 6 systematically change.

I was going to respond to Reply #17, but your Reply #18 has confused me - does it mean that you have solved the problem?

...R