Go Down

Topic: Python To Arduino Issues - Tokenizing (Read 231 times) previous topic - next topic

phoenix442

Hello All, I am fairly new to Arduino and am using it for a class project where my friends and I are developing a robotic arm. We decided to use Python as an interface and were able to send and verify two values with LEDs however we would need to hard code each received character to some place in the Arduino code. It would be preferable to tokenize this information so that we can send numeric variables or other important information. As I said, we did get the code to work when we only sent two values but sending a string has been more difficult. Here is the Python Code:

Code: [Select]
import serial
from serial import *
import time
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
#from PIL import ImageTk, Image


def GUI():

    window = Tk()
    window.title("The Egg Bot")
    window.geometry("400x250")


    #img = ImageTk.PhotoImage(Image.open("Unit_Circle.png"))
    #imglabel = Label(window, image=img).place(x=500, y=100)


    s = serial.Serial('COM5', 9600)

    title = Label(window,
                text="Testing Python to Arduino Inputs",
                font="Times 32",
                anchor = N)
    title.pack()

    Header = Label(window,
                text="Begin by entering the Parameters",
                font = "Times 14",
                anchor = N)
    Header.pack()



    def confirmExit():
        if messagebox.askokcancel("Quit", "Are you sure you want to quit?"):
            window.destroy()

    Button(window, text="Quit", command=confirmExit).place(x=5,y=350)

    #def Power_Robot_On

    #trnon = Button(window, text="Turn On", command=Power_Robot_On)
    #trnon.place(x=210, y=298)

    #def Power_Robot_Off():

    #stp = Button(window, text="Turn Off", command=Power_Robot_Off)
    #stp.place(x=200, y=298)

    lbl2 = Label(window, text="Position of Egg in X")
    lbl2.place(x=5, y=200)
    txt_1 = Entry(window,width=10)
    txt_1.place(x=120, y=200)

    def Button_1():
        res1 = "Please Wait... "
        lbl2.configure(text = res1)

        if str(txt_1['state']) == 'normal':
                txt_1.configure(state = 'disable')

    btn1 = Button(window, text="OK", command=Button_1)
    btn1.place(x=185, y=198)



    lbl3 = Label(window, text="Position of Egg in Y")
    lbl3.place(x=5, y=250)
    txt_2 = Entry(window,width=10)
    txt_2.place(x=120, y=250)

    def Button_2():
        res2 = "Please Wait... "
        lbl3.configure(text = res2)

        if str(txt_2['state']) == 'normal':
                txt_2.configure(state = 'disable')

    btn2 = Button(window, text ="OK", command=Button_2)
    btn2.place(x=185, y=248)


    #lbl4 = Label(window, text="Height")
    #lbl4.place(x=5, y=300)
    #txt_3 = Entry(window,width=10)
    #txt_3.place(x=120, y=300)


    #def Button_3():
        #res3 = "Please Wait... "
        #lbl4.configure(text = res3)

        #if str(txt_3['state']) == 'normal':
                #txt_3.configure(state = 'disable')


    #btn3 = Button(window, text="OK", command=Button_3)
    #btn3.place(x=185, y=298)


    def Reset_Button():
        lbl2.configure(text = "Position of Egg in X")
        lbl3.configure(text = "Position of Egg in Y")
        #lbl4.configure(text = "Velocity")
        txt_1.configure(state = 'normal')
        txt_2.configure(state = 'normal')
        #txt_3.configure(state = 'normal')
        txt_1.delete(0,END)
        txt_2.delete(0,END)
        #txt_3.delete(0,END)

    rst = Button(window, text = "Reset", command=Reset_Button)
    rst.place(x=40, y=350)

    def Execute_Button():
        X = txt_1.get()
        Y = txt_2.get()
        #Z = txt_3.get()

        SX = str(X)
        SY = str(Y)
        SZ = '*#' + SX + '#' + SY + '#*$'

        # s.write(SX.encode())
        # s.write(SY.encode())
        s.write(SZ.encode())
        time.sleep(5)
        print(SZ)

    Exbtn = Button(window, text = "Execute", command=Execute_Button)
    Exbtn.place(x=155, y=348)

    window.mainloop()

GUI()


They Python code will take the variables, regardless of size, and store them in SX and SY which has special characters that will be dealt with in Arduino.

Code: [Select]
#include <string.h>
#include <stdio.h>

char i;
char j[10];
int arry[10];
char *strings[10];
String readString;
int d = 0;
int e = 0;
int x;
int y;
int z = 0;
int Length = 0;
bool flag = true;
char *fix = NULL;
byte index = 0;

char arrayt[] = "*#22#33#*$";




///// Flash LED /////
void FlashLED(){
  digitalWrite(13,HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
}




///// Seperating Strings Test /////
void test(){
  Serial.println("Begin Test: ");
  Serial.println(arrayt);
  while (flag == true){
  j[z] = arrayt[z];
  readString += j[z];
  delay(400);
  //arry[z] = (int)(j[z]-'0');
      //if (d < z){
        //FlashLED();
        //d++;
        //}
      Serial.print("Current Value of z: ");
      Serial.println(z);     
      // Length += 1;
      if (j[z] == '$' or z == 9){  // 9 is length of string (last char is '$' yet it will continue to run regardless, it should stop but doesn't)
       flag = false;  // End of data, stop recieving
      }
      else{
      z++;  // (more data, continue recieving)
      }
  }
    Serial.print("readString: ");
    Serial.println(readString);
    Serial.print("What's in 'j'?: ");
    Serial.println(j);
   
  for (z = 0; z < 10; z++){
    Serial.print("Value of z: ");
    Serial.print(z);
    Serial.print(", Item at location: ");
    Serial.println(j[z]);
    //Serial.print(" and at arry: ");
    //Serial.println(arry[z]);   
  }
       
  fix = strtok(j, "#$");
    while (fix != NULL){
      strings[index] = fix;
      index++;
      fix = strtok(NULL, "#$");
    }
    for (int n = 0; n < index; n++){
      Serial.print("n: ");
      Serial.print(n);
      Serial.print(", string: ");
      Serial.println(strings[n]);
    }
    x = atoi(strings[1]);
    y = atoi(strings[2]);
    Serial.print("Val of x: ");
    Serial.println(x);
    Serial.print("Val of y: ");
    Serial.println(y);
}

void read_string(){

  if (Serial.available()){
    digitalWrite(12, HIGH);
    delay(2000);
    digitalWrite(12, LOW);
    delay(2000);
  while (flag == true){
    j[z] = Serial.read(); // read first character bit, loops to read next character
    delay(400);
    //arry[z] = (int)(j[z]-'0');   // turn first character bit into int, then store in a second array, does same for second character
    if (j[z] == '$' or z == 9){
      flag = false;
      //Serial.print("Final Value of z: ");
      //Serial.println(z);
      }
    else{
      z++;
      }
    }
  //char array[z];
  fix = strtok(j, "#$");
  while (fix != NULL){
    strings[index] = fix;
    index++;
    fix = strtok(NULL, "#$");
    }
  x = atoi(strings[1]);
  y = atoi(strings[2]);
  }
}
   
void setup() {
  // put your setup code here, to run once:
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
  //pinMode(10, OUTPUT);
  Serial.begin(9600);
}
 

void loop() {
  test();           // cannot have both test and read_string
  //read_string();  // to run with Python, comment 'test', uncomment 'read_string'
  if (j[0] = '*'){
    FlashLED();
    delay(2000);
  }
  if (x == 22){
    digitalWrite(12,HIGH); //check if inputs are correct
    }
  if(y == 33){
    digitalWrite(11,HIGH);
    }
  //Serial.print("Value of j: ");
  //Serial.println(j);
  //Serial.print("Value of 'arry': ");
  //Serial.println(readString);
  while(1){}
}


Ultimately we are trying to send over numeric variables and later, probably some sort of flags or boolean values to cause some sort of event to happen. The 'test' section is good example of what I am trying to accomplish, separating everything between the delimiters as a string and then converting the necessary strings into integers to be used for operations. Any advice or direction is greatly appreciated, thank you.

PaulS

Quote
They Python code will take the variables, regardless of size, and store them in SX and SY which has special characters that will be dealt with in Arduino.
Not true. Only SZ has special characters. Only SZ is sent.

You are mixing strings and Strings on the Arduino. Get rid of AL of the Strings.

Get rid of ALL of the delays.

Read this: Serial Input Basics - updated. It is the only intelligent way to read serial data.

Use names that make sense. j as the name of the array holding serial data makes no sense.

Quote
Any advice or direction is greatly appreciated, thank you.
The code you posted does something that you failed to describe.
You expect the code to do something that you failed to describe.

It's hard to tell you what to do to get from point A to point B without knowing where either point is.
The art of getting good answers lies in asking good questions.

Robin2

It would also help if you provide a few examples of the messages you want to send to the Arduino and an explanation of what the Arduino is to do with each message.

...R
Python - Arduino demo
Two or three hours spent thinking and reading documentation solves most programming problems.

phoenix442

#3
Feb 11, 2019, 12:24 am Last Edit: Feb 11, 2019, 12:51 am by phoenix442
Quote
Not true. Only SZ has special characters. Only SZ is sent.
You're right. Allow me to correct myself, X and Y are two user inputs taken from our created Python window/interface. The Python code will then convert X and Y numbers to strings called SX and SY to allow them to be combined with other strings / characters, forming one string. From here, we created SZ which combines the selected special characters with SX and SY. From Python, when I enter '22' and '33' into the text boxes, press ok on both boxes and then execute, the data sent should be
Code: [Select]
*#22#33#*$
 
I did this so that when it is sent to the Arduino, we can use the strtok function which can separate the received data into strings that I have already determined. I can then take the strings that are comprised of numerical characters and convert them to integers so I can use them for operations. I would like to use some special characters, '*', to be the first and last strings while I use other characters as a delimiter, '#' and '$'. I use '#' to separate the characters and tried to use '$' to tell the code that this is the last character being sent.

Code: [Select]
void read_string(){
  if (Serial.available()){
  while (flag == true){
    j[z] = Serial.read(); // read first character bit, loops to read next character
    if (j[z] == '$' or z == 9){
      flag = false;
    else{
      z++;
      }
    }
}

 

I use a while statement to continue reading the characters that are in the buffer from Python. It was suppose to stop reading when '$' is reached however it would just reset to 0 and start counting again so I set it to stop after 9 counts which will allow all 10 characters to be received. Once the characters have been read, the next section should separate the 10 characters into 4 strings I will show below. When I comment out the loop that does this, read_string(), and replace it with a test function that already has the same string of characters I'm trying to send from Python, I can separate the data I wish to separate. This is what I'm trying to do, separate the 10 python characters into strings using some deliminator to note where the string starts and stops. To see what I am trying to do, in the arduino loop section, please comment read_string() so it does not run and uncomment test(). In the serial window, I'm concerned with the section that returns, n: 'number', string: '...'. It is a result of the section below. I just cannot get this to work when actually doing the serial read and I'm not sure why. I believe the strtok section works fine based off what I see from the test() section but I don't know if the serial read section is right.
Code: [Select]
fix = strtok(j, "#$");
    while (fix != NULL){
      strings[index] = fix;
      index++;
      fix = strtok(NULL, "#$");
    }
    for (int n = 0; n < index; n++){
      Serial.print("n: ");
      Serial.print(n);
      Serial.print(", string: ");
      Serial.println(strings[n]);
    }

From this, I can use a string/char to integer converter to take, strings[1], which contains 22 and make it of the type int. This next segment shows what the prior segment will return. I'm not sure why the n:4 returns garbage but it should stop at n:3. Maybe because it thinks there should be another string/variable after the last * but the deliminator gets rid of # and $ so it returns empty. I'm not concerned about that and it shouldn't influence what I am doing.
Code: [Select]
n: 0, string: *
n: 1, string: 22
n: 2, string: 33
n: 3, string: *
n: 4, string: a⸮

Quote
You are mixing strings and Strings on the Arduino. Get rid of AL of the Strings.
I only used one String called readString, in my test code to see if when I actually receive code, if I can use j[z] as a temp to take the current character and then continuously add it to a String hence readString. I cannot use readString in place of j as the first parameter of strtok must be of type char. I will adjust the name of j to be a better variable such as serIn.
I apologize if I wasn't clear before. The Python code creates a window that takes two inputs and sends them through a serial connection. The code makes the inputs of the type string and combines them with special characters that act as filters so I can pick them apart in arduino. The arduino code should be able to receive the serial characters and separate them into the four variables (string[n]) shown in the resulting code above, ie: *, 22, 33, *. I will take a look at the serial reference you provided. The purpose of this code is just so I can learn how to receive from Python and pick a apart the needed variables. I plan on applying this concept in other code I've written so it shouldn't do anything in this code other than light an LED if the value is right. I cannot see anything in the serial window of arduino if Python is using the serial port so LEDs were the next best option I thought of. Let me know what else needs further clarification, thanks again.

PaulS

Quote
I'm not sure why the n:4 returns garbage but it should stop at n:3.
What do you do after you store a value in the strings array? You increment index to point to the next location THAT DOES NOT YET HAVE DATA.

When you discover that there are no more tokens to process, you do not decrement index, so, of course, it points to a location that contains garbage.
The art of getting good answers lies in asking good questions.

Robin2

#5
Feb 11, 2019, 10:18 am Last Edit: Feb 11, 2019, 10:19 am by Robin2
when I enter '22' and '33' into the text boxes, press ok on both boxes and then execute, the data sent should be
Code: [Select]
*#22#33#*$
 
I did this so that when it is sent to the Arduino, we can use the strtok function which can separate the received data into strings that I have already determined.
The parse example in Serial Input Basics works like that. In the example the comma is used as the delimiter but you could easily change that if you prefer the # character.


Speaking from recent experience using the # character is a bit of a PITA with Python because it is also used as the comment character so there are #s all over my Python code which makes it very hard to find the one I want to send to the Arduino.

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

Go Up