How to extract data from Char array

Hi Community,

I need assistance with trying to extract data from serial data sent by a Python3 script that will control motors attached to my arduino.

C++ is not my strong point and i have been racking my brains trying to solve this solution. I have spent weeks scouring the net for a solution, have learnt lots but obviously not enough. Hence me reaching out to the community. I did have code working but it was really laggy. i have been trying to implement the endmarkers solution but i can't work out how to extract the the data in the arduino to control motors; motor direction and speed (PWM).

I am sending the following value format from python3 script:
('b', 157)

The letter is the motor direction and the integer is the pwm value (motor speed).

I would appreciate if someone could offer assistance with code that is responsive and can control my motors.

My Python3 code that generates random values to simulate motor direction and speed:

import random
import string
import serial
import time

startMarker = '<'
endMarker = '>'
dataStarted = False
dataBuf = ""
messageComplete = False

#========================
#========================
# the functions

def setupSerial(baudRate, serialPortName):

global  serialPort

serialPort = serial.Serial(port= serialPortName, baudrate = baudRate, timeout=0, rtscts=True)

print("Serial port " + serialPortName + " opened  Baudrate " + str(baudRate))

waitForArduino()

#========================

def sendToArduino(stringToSend):

# this adds the start- and end-markers before sending
global startMarker, endMarker, serialPort

stringWithMarkers = (startMarker)
stringWithMarkers += stringToSend
stringWithMarkers += (endMarker)

serialPort.write(stringWithMarkers.encode('utf-8')) # encode needed for Python3


#==================

def recvLikeArduino():

global startMarker, endMarker, serialPort, dataStarted, dataBuf, messageComplete

if serialPort.inWaiting() > 0 and messageComplete == False:
    x = serialPort.read().decode("utf-8") # decode needed for Python3
    
    if dataStarted == True:
        if x != endMarker:
            dataBuf = dataBuf + x
        else:
            dataStarted = False
            messageComplete = True
    elif x == startMarker:
        dataBuf = ''
        dataStarted = True

if (messageComplete == True):
    messageComplete = False
    return dataBuf
else:
    return "XXX"

#==================

def waitForArduino():

# wait until the Arduino sends 'Arduino is ready' - allows time for Arduino reset
# it also ensures that any bytes left over from a previous message are discarded

print("Waiting for Arduino to reset")

msg = ""
while msg.find("Arduino is ready") == -1:
    msg = recvLikeArduino()
    if not (msg == 'XXX'):
        print(msg)


#====================
#====================
# the program


setupSerial(115200, "/dev/ttyUSB0")
count = 0
prevTime = time.time()


while True:
        # check for a reply
arduinoReply = recvLikeArduino()
if not (arduinoReply == 'XXX'):
    print ("Time %s  Reply %s" %(time.time(), arduinoReply))


num = random.randint(0,254)
letters = random.choice(string.ascii_lowercase)

command = (letters,num) 
commandstr = str(command)       



#     # send a message at intervals
# if time.time() - prevTime > 1.0:
#     sendToArduino("this is a test " + str(count))
#     prevTime = time.time()
#     count += 1

print (command)
sendToArduino(commandstr)
time.sleep(.2) 



#======================================
# End of Python3 code

My arduino code that receives the data is below. I have only included a portion of the code i am trying to get working. The full code is attached as a file You will see portions of the code // out because i tried to get certain portions working before i move on.

void setup()
{
  Serial.begin(115200);
//  delay(300);

  Serial.println("<Arduino is ready>");

  IO_init();
}

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

void loop() {
//  UART_Control();

    recvWithStartEndMarkers();
//    replyToPython();
}

//==========================
// Receiving data from Python

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;
        }
    }

}

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

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

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

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

}

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

void showParsedData() {
    Serial.print("Message ");
    Serial.println(messageFromPC);
    Serial.print("Integer ");
    Serial.println(integerFromPC);
   
}





//=========================================
// Below is leftover from old code but you can see what i am trying to do
//        Serial.print(receivedChars);
//        Direction = receivedChars.substring(0,1);
//        Direction = receivedChars.substring(2,1);
//        Serial.print("Direction: " + Direction + "\n");
//        string_receivedChars = str(receivedChars);
          
       

//        pwmstr = receivedChars.substring(4,3);
//        Serial.print("pwmstr: " + pwmstr + "\n");

//        pwmint = (int) pwmstr.toInt();
//        Motor_PWM = pwmint;
//        Serial.print("pwmstr_x: ");
//        Serial.print(pwmint);


//       if (Direction == "a") {
//         ADVANCE();
//       }
//       else if (Direction == "z") {
//         STOP();
//       }
//       else if (Direction == "b") {
//         RIGHT_1();
//       }
//       else if (Direction == "c") {
//         RIGHT_2();
//       }
//       else if (Direction == "d") {
//         RIGHT_3();
//       }
//       else if (Direction == "e") {
//         BACK();
//       }
//       else if (Direction == "f") {
//         LEFT_3();
//       }
//       else if (Direction == "g") {
//         LEFT_2();
//       }
//       else if (Direction == "h") {
//         LEFT_1();
//       }
//       else if (Direction == "i") {
//         RIGHT_4();
//       }
//       else if (Direction == "j") {
//         LEFT_4();
//       }
//    
//       else {
//         Serial.write("invald");
//    }
//  }

ArduinoSerialPWMBenV3.ino (8 KB)

It would be a good idea to post your Arduino code in your Post - perhaps your next one, so people don't have to download it.

When posting code please use the code button </>
codeButton.png

so your code 
looks like this

and is easy to copy to a text editor See How to use the forum

Have a look at the parse example in Serial Input Basics

...R

You can interface Python with Arduinos via USB serial easily using the compatible libraries: pySerialTransfer and SerialTransfer.h.

pySerialTransfer is pip-installable and cross-platform compatible. SerialTransfer.h runs on the Arduino platform and can be installed through the Arduino IDE's Libraries Manager.

Both of these libraries have highly efficient and robust packetizing/parsing algorithms with easy to use APIs.

Example Python Script:

from time import sleep
from pySerialTransfer import pySerialTransfer as txfer

if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM13')
        
        link.open()
        sleep(2) # allow some time for the Arduino to completely reset
    
        link.txBuff[0] = 'h'
        link.txBuff[1] = 'i'
        link.txBuff[2] = '\n'
        
        link.send(3)
        
        while not link.available():
            if link.status < 0:
                print('ERROR: {}'.format(link.status))
            
        print('Response received:')
        
        response = ''
        for index in range(link.bytesRead):
            response += chr(link.rxBuff[index])
        
        print(response)
        link.close()
        
    except KeyboardInterrupt:
        link.close()

Example Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  myTransfer.txBuff[0] = 'h';
  myTransfer.txBuff[1] = 'i';
  myTransfer.txBuff[2] = '\n';
  
  myTransfer.sendData(3);
  delay(100);

  if(myTransfer.available())
  {
    Serial.println("New Data");
    for(byte i = 0; i < myTransfer.bytesRead; i++)
      Serial.write(myTransfer.rxBuff[i]);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");
    Serial.println(myTransfer.status);
  }
}

@Robin2 - Thank you! I will adjust my post and ensure i follow thoise practices with future posts.

@Power_Broker - Thank you i will look into those libraries. I really appreciate your recommendation.

Hey @green_jellyfish I was doing almost the exact same thing here:

https://forum.arduino.cc/index.php?topic=669671.0

I sent my data as 1:90, which was servo number : servo angle.

Look at the end of the post and you'll see how stupid easy it was to convert it. lol It took me 3 days to figure this out.

1:180 is servo1 with a 180 degree angle

// Split the command in two values
    char* separator = strchr(receivedChars, ':');
    if (separator != 0)
    {
      // Actually split the string in 2: replace ':' with 0
      *separator = 0;
      int servoId = atoi(receivedChars);
      ++separator;
      int position = atoi(separator);

      // Do something with servoId and position
      DoCommand(servoId, position);
    }

DoCommand is where I send the 2 values to the servos.

Hope this helps.