Shop Learn
How to wirelessly control an ActivityBot from Python on a PC? — Parallax Forums

How to wirelessly control an ActivityBot from Python on a PC?

I'd like to wirelessly control an ActivityBot from Python on a PC.
I have a XBee S6B and also a WX ESP8266 WiFi Module + of course an ActivityBot. Haven't used these beauties for years now, and believe I only programmed a PAB through WiFi, but not controlled it.
Anyone knows how to connect from Python to one of these WiFi modules, and control it?

Of course I'd need to have a receiving program at the PAB side (C or Spin), but that should not be a problem once I know how to get the connection going. I found some material at https://learn.parallax.com/tutorials/language/propeller-c/parallax-wx-wi-fi-module-prop-c, but this is mainly about using web pages to control a robot, and only partially applicable to my use case.

In the end, I believe the hardest part will be for me to find the components in my storage boxes :smile:

Comments

  • banjobanjo Posts: 426

    FWIW, I quickly found the XBee S6B and WX ESP8266 modules I have, and decided to use the S6B. Main reason for that is that I used it 8 years ago to control an ActivityBot from iPad and I had already some C-code on the P1 side, basically a collectively developed program, as part of this open project: https://forums.parallax.com/discussion/153214/open-propeller-project-1-ipad-to-activitybot/p1
    I found the below Python skeleton to use on the PC side.
    Now I'm almost able to control the ActivityBot through a consumer grade EEG-headband. If I get this to work more consistently, I'll post a figure-8 video of it :smile:
    Due to the many components involved (my brain => EEG-headband => BlueTooth => phone/phone app => WiFi => PC/Python+TensorFlow => WiFi => ActivityBot), there are some lags, am working on measuring and trying to minimize them. Error handling would also be good to have...

    """
    https://github.com/Serpent999/Xbee-S6B-Wifi-Communication-using-python/blob/master/Xbee_wifi.py
    Project :Xbee Wifi Connection 
    Engineer : Nikhil.P.Lokhande
    email:nikhil.l.1@aol.com
    """
    import socket     # Wifi Xbee is using raw feed, hence a raw socket protocol
    
    ip='255.255.255.255'               #Enter IP of your Xbee
    p=9750                             #Enter the port number for your Xbee
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #Initialize socket object
    s.connect((ip,p))                         #Connect to the Xbee
    
    while(True):                      #Keep looping, receive then print the data
        data=s.recv(1024)
        print(data)    
    
  • GenetixGenetix Posts: 1,669

    banjo,

    That seemed like a worthwhile project that you linked to but too bad it died around the time I started using the Propeller.

    I am not familiar with Python but I like that it's similar to BASIC.
    I grew up with BASIC but PASCAL taught me how to write a proper program; I loved PASCAL but it's not used anymore. :(

  • banjobanjo Posts: 426

    Yeah, Python is a bit similar to BASIC, Spin and partially also Pascal as it it kind of verbose. What I love with Python is that you don't need any curly braces or similar as in C, just like in Spin it's all about indentation. Actually I started to use Python in practice just last month as I started to teach a univesity course in it. Best way to learn a subject is to have to teach it :smile:
    Related to the brain-controlled bot, I've made some serious progress the last hours, if all goes well I'll upload a video after a few days or so.

  • The nice thing about Python it's good on a Mac or a Windows PC. The while loop needs its own thread or it will make the UI unresponsive, I don't know how far you have got with python over the last month or so but this is a real basic example of the beginnings of a GUI. I don't know if I can help much but if I think I have something to offer the conversation I will chime in.

    The example receives a string from the Prop and responds by replying with "Hello World", this example uses ASCII both directions

    import socket     # Wifi Xbee is using raw feed, hence a raw socket protocol
    from tkinter import *
    from threading import *
    
    root=Tk()
    
    root.geometry("300x200")
    root.title("TCP Propeller")
    
    def start_thread():
    
            t1=Thread(target=worker_thread,daemon=True)
            t1.start()
    
    def stop_thread():
        global worker_stop
        worker_stop=True
    
    def worker_thread():
        global worker_stop
        worker_stop=False
        ip='192.168.1.199'               #Enter IP of your Xbee
        p=9750                             #Enter the port number for your Xbee
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #Initialize socket object
        s.connect((ip,p))                         #Connect to the Xbee
        send_string="Hello World\n"
        print("Online")
        while(True):  #Keep looping, receive then print the data
            data=s.recv(1024)
            print(data.decode("utf-8"))
            if data:
                s.sendall(send_string.encode())  
            if worker_stop:
                print("Offline")
                break  
    
    
    frame1=LabelFrame(root,text="Port Control")
    frame1.grid(row=0,column=0)
    btn1=Button(frame1,text="Connect",command=lambda:start_thread())
    btn1.grid(row=0,column=0,padx=5,pady=5)
    btn2=Button(frame1,text="Disconnect",command=lambda:stop_thread())
    btn2.grid(row=1,column=0,padx=5,pady=5)
    
    root.mainloop()
    
  • banjobanjo Posts: 426

    @Unsoundcode Thx a lot! I was going to try to implement the Prop messaging back to Python, but you now saved me at least a few hours of time!

  • I am a little new with python also, the example in the last post is a little crude, I know we can do much better but it gives us a few things to think about if we wanted to create a GUI.

    A couple of things to think about with regards to the socket, this set up is dedicated to just one wifi device defined by the IP (XCTU MY) and port # of the XBee, to point at a different wifi device means changing IP (XCTU MY) and perhaps the port # in your python script

    With a slightly modified script you can make the PC a server that would be able to accept connections from any one of several devices with a destination address that matched you PC (XCTU DL) and matching port number.

    The IP and port # can also be changed over serial with a Propeller script if you wanted.

    Lastly the python script you are currently using will block at the receiving line of code if there is no data this causes the program to hang.

    data=s.recv(1024)

    That can be avoided with a timeout or removing blocking altogether

    s.settimeout(10)
    s.setblocking(False)
    
  • UnsoundcodeUnsoundcode Posts: 1,452
    edited 2022-02-22 15:36

    Modifying the first example we have something that functions pretty well now. This example works on Mac and Windows and sends/receives ASCII char or string of ASCII chars, for now the transmit side from the PC appends a newline but that can easily be removed. The next step would be to decide which commands you wanted to send to the Activity Bot and start creating functions associated to buttons. Maybe add an entry for XBee IP and port selection also.

    import socket 
    import threading   
    from tkinter import *
    from threading import *
    
    
    root=Tk()
    
    root.geometry("320x160")
    root.title("TCP Propeller")
    my_var=["",""]
    
    e=threading.Event()
    
    def start_thread():
            e.clear()
            t1=Thread(target=worker_thread,args=(my_var,),daemon=True)
            t1.start()
    
    def stop_thread():
       e.set()
    
    def worker_thread(my_var):
    
        my_var=my_var
        worker_stop=False
        ip='192.168.1.199'              
        p=9750                             
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 
        s.connect((ip,p)) 
        s.setblocking(False)
    
        lbl2.configure(text="Status: Online waiting")
        while(True):  
            data=0
            data_string=""
            try:
                data=s.recv(1024)
                my_var[1]=data.decode("utf-8")
                lbl2.configure(text=f"Status: {my_var[1]}")
    
            except:
                pass 
    
            if not my_var[0]=="":
                s.sendall(my_var[0].encode())  
                my_var[0]=""
            if e.isSet():
                lbl2.configure(text="Status: Offline")
                break  
    
    def change_var(new_string):
        global my_var
        my_var[0]=new_string + "\n"
    
    def shut_down():
        root.destroy()
    
    frame1=LabelFrame(root,text="Port Control")
    frame1.grid(row=0,column=0,padx=10,pady=10)
    btn1=Button(frame1,text="Connect",width=10,command=lambda:start_thread())
    btn1.grid(row=0,column=0,padx=5,pady=5)
    btn2=Button(frame1,text="Disconnect",width=10,command=lambda:stop_thread())
    btn2.grid(row=1,column=0,padx=5,pady=5)
    btn3=Button(frame1,text="Send",width=10,command=lambda:change_var(e1.get()))
    btn3.grid(row=2,column=0,padx=5,pady=5)
    btn4=Button(frame1,text="Exit",width=10,command=lambda:shut_down())
    btn4.grid(row=0,column=2,padx=5,pady=5)
    lbl1=Label(frame1,text="Data to send")
    lbl1.grid(row=2,column=1)
    lbl2=Label(frame1,text="Status:",relief="sunken")
    lbl2.grid(row=3,column=0,columnspan=3,sticky="we")
    lbl2.configure(anchor="w")
    e1=Entry(frame1)
    e1.grid(row=2,column=2,padx=5)
    
    root.mainloop()
    

    EDIT : there should be the s.close() instruction just before the break

  • dgatelydgately Posts: 1,509

    On the Prop side, what code would this Python script communicate with? Is it C code from the Learn (SimpleIDE) library or .spin code? Is there a simple example?

    Thanks,
    dgately

  • banjobanjo Posts: 426
    edited 2022-02-22 18:14

    @dgately said:

    On the Prop side, what code would this Python script communicate with? Is it C code from the Learn (SimpleIDE) library or .spin code? Is there a simple example?

    >
    This is the C code, from SimpleIde library, that I used in 2014 sending data to my iPad. xBee initialization obviously needs to be done before. Let me know if you need that one as well, in that case I'll clean up my code before posting it here. The below code sends the Ping distance in cm in two separate bytes, this is because Ping distance can be well over 255, around 373 centimeters if I remember correctly.

    while(1)
    {
        fdserial_txFlush(xbee);
        if (fdserial_txEmpty(xbee) != 0)
        {    
          tx_result = fdserial_txChar(xbee, distHI);                  // HIGH byte
          tx_result = fdserial_txChar(xbee, distLO);                 // LOW byte
          tx_result = fdserial_txChar(xbee, 65);                        // end character
        }
    }  
    
  • dgatelydgately Posts: 1,509

    @banjo said:
    This is the C code, from SimpleIde library, that I used in 2014 sending data to my iPad. xBee initialization obviously needs to be done before. Let me know if you need that one as well, in that case I'll clean up my code before posting it here. The below code sends the Ping distance in cm in two separate bytes, this is because Ping distance can be well over 255, around 373 centimeters if I remember correctly.

    Thanks. I can use this... I just wanted the simplest code, which I may translate to .spin2!

  • Hi banjo, you spoke of error checking and I looked at that a little today. The main scenarios I was thinking of were being unable to establish communication and loss of communication during usage. The first case was fairly straightforward to deal with and I have included it with a sample today. The second case uses the same try/exception handler as the first basically but I wondered if the inclusion of some kind of heartbeat between devices might be a good thing, I'm not sure what to think about that. This sample also puts the status messages into a dictionary and cleans things up some. The biggest change in my opinion was the way 2 variables and an object are passed in and out of a second thread. That's new to me and though it works fine I wondered if you had any experience or thoughts on that topic.

    dgately, I am thinking that this program will communicate with any of the Parallax WiFi units and the microcontrollers that connect to those WiFi units. All the microcontrollers have to worry about are the serial communications the WiFI is completely transparent as far as they are concerned. As it is now we just pass ASCII chars back and forth for testing, the characters are not processed in any way, by removing encode and decode we can just as easily transmit/receive byte packets.

    import socket 
    import threading   
    from tkinter import *
    from threading import *
    
    
    root=Tk()
    
    root.geometry("320x160")
    root.title("TCP Propeller")
    my_var=["","",""]
    
    ip='192.168.1.199'              
    p=9750                             
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 
    
    my_var[2]=s
    
    break_event=threading.Event()
    
    def start_thread():
        break_event.clear()
        t1=Thread(target=worker_thread,args=(my_var,),daemon=True)
        t1.start()
    
    def stop_thread():
       break_event.set()
    
    def worker_thread(my_var):
    
        s=my_var[2]
    
        try:
            Status_msg(1)
            s.connect((ip,p)) 
        except OSError as ex:
            Status_msg(2)
            return
    
        s.setblocking(False)
    
        Status_msg(3)
    
        data=0
        while(True):  
    
            try:
                data=s.recv(1024)
                my_var[1]=data.decode("utf-8")
                lbl_status.configure(text=f"Status: {my_var[1]}")   
            except:
                pass 
    
            if not my_var[0]=="":
    
                try:
                    s.sendall(my_var[0].encode())  
                    my_var[0]=""
                except:
                    break_event.set()
    
            if break_event.isSet():
                Status_msg(4)
                s.close()
                break  
    
    def change_var(new_string,my_var):
        my_var[0]=new_string + "\n"
    
    def shut_down():
        root.destroy()
    
    def Status_msg(my_int):
    
        msg_dict={
        1:"Status: Attempting connection please wait",
        2:"Status: Connection failed",
        3:"Status: Socket open and waiting",
        4:"Status: Offline Socket closed",
        5:"Status: Spare",
        6:"Status: Spare",
        7:"Status: Spare",
        8:"Status: Spare"
        }
    
        lbl_status.configure(text=msg_dict[my_int],anchor="w",justify=LEFT)
    
    frame1=LabelFrame(root,text="Port Control")
    frame1.grid(row=0,column=0,padx=10,pady=10)
    btn1=Button(frame1,text="Connect",width=10,command=lambda:start_thread())
    btn1.grid(row=0,column=0,padx=5,pady=5)
    btn2=Button(frame1,text="Disconnect",width=10,command=lambda:stop_thread())
    btn2.grid(row=1,column=0,padx=5,pady=5)
    btn3=Button(frame1,text="Send",width=10,command=lambda:change_var(e1.get(),my_var))
    btn3.grid(row=2,column=0,padx=5,pady=5)
    btn4=Button(frame1,text="Exit",width=10,command=lambda:shut_down())
    btn4.grid(row=0,column=2,padx=5,pady=5)
    lbl1=Label(frame1,text="Data to send")
    lbl1.grid(row=2,column=1)
    lbl_status=Label(frame1,text="Status:",relief="sunken")
    lbl_status.grid(row=3,column=0,columnspan=3,sticky="we")
    lbl_status.configure(anchor="w")
    e1=Entry(frame1)
    e1.grid(row=2,column=2,padx=5)
    
    root.mainloop()
    
  • banjobanjo Posts: 426
    edited 2022-02-23 20:21

    Hi @Unsoundcode Thx again for your efforts! Both of your programs seem to work well, I'm just struggling to understand how to communicate properly between the Prop and the program. I'm not sure if it's the encoding that is throwing me off, as when sending Ping distance back, it seems to only go up to 90 127 cm. This is when printing unencoded data to the terminal window, like this:

            try:
                data=s.recv(2)
                my_var[1]=data.decode("utf-8")
                lbl_status.configure(text=f"Status: {my_var[1]}")  
                ints=list(data)              # <===
                print(ints, end = " ")    # <===
    

    Initially I also had understanding issues when sending data to the Prop, as I got decimal 10 regardless of what I send. But reading your post above, I understood I just needed to remove the newline character.

    Warning, offtopic rant:
    Damn forum software which stalls the whole Chrome tab every ~20 seconds, for ~1 minute at a time, makes it close impossible to use. This did not happen before to me, but I've understood some others have experienced same problem. It also puts a heavy load on my laptop --> high temp --> screen starts to flicker as introduced by M$ in their "Flickergate" Surface Pro 4 computers.

  • banjobanjo Posts: 426
    edited 2022-02-23 20:52

    @banjo said:
    Hi @Unsoundcode Thx again for your efforts! Both of your programs seem to work well, I'm just struggling to understand how to communicate properly between the Prop and the program. I'm not sure if it's the encoding that is throwing me off, as when sending Ping distance back, it seems to only go up to 90 127 cm. This is when printing unencoded data to the terminal window, like this:

    By adding these I'm able to print the low and high bytes of the word
    ~~ int_val = int.from_bytes(my_var[1], "big")
    print("int_val = ", int_val)~~

    try:
                data=s.recv(1024)
                ints=list(data)                                          # <========
                print(ints[0] + ints[1], end = " ")          # <========       
    
  • Hi banjo, this will read and display 16bit/32bit values

    data=s.recv(1024)
    my_var[1] = int.from_bytes(data ,"little")
    lbl2.configure(text=f"Status: {my_var[1]}")
    

    If this is the point we are at then we should start to give thought to a simple protocol so that the data can be defined in a packet. If you are wanting to do some tests using byte data you should be ok for now just separate your data with a small interval of time.

    If I get the time I will try and set something up on a Prop, I also want to look into passing objects as function arguments on the python side.

  • UnsoundcodeUnsoundcode Posts: 1,452
    edited 2022-02-24 23:33

    Hi banjo, I noticed the socket does not behave as I expected in particular the use of blocking/nonblocking/timeout.

    Nonblocking and timeout were giving too many exceptions, I was able to catch the exceptions and ignore them but I did not think that was the best way to handle it. So I went back to blocking in the secondary thread, which is fine for the receive method, and I moved sendall into a function in the main thread.

    Another thing I noticed is the socket will time out if the receive is idle for approximately 60 seconds or more. This is something we can handle in the Propeller code by sending a heartbeat signal at regular intervals.

    The following two snippets are to test PC to Propeller using Python and SimpleIDE with a XBee wifi connected just as it is in the Parallax tutorial, that is D0 and D1 connected to P9 and P8 respectively. This is really basic stuff that uses ASCII characters for testing.

    For a hobby project I think it is getting very close to being useful and it is at a stage where most beginners could modify it for their projects.

    EDIT I should mention that the Prop code is Run With Terminal

    Python

    import socket 
    import threading   
    from tkinter import *
    from threading import *
    
    
    root=Tk()
    
    root.geometry("320x160")
    root.title("TCP Propeller")
    skt_var=["","",""]
    
    break_event=threading.Event()
    
    def start_thread():
        break_event.clear()
        t1=Thread(target=worker_thread,args=(skt_var,),daemon=True)
        t1.start()
    
    def stop_thread(skt_var):
        s=skt_var[2]
        s.close()
        break_event.set()
    
    def worker_thread(skt_var):
    
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 
        skt_var[2]=s
        ip='192.168.1.199'              
        p=9750   
    
        try:
            Status_msg(1)
            s.connect((ip,p)) 
        except OSError as ex:
            Status_msg(2)
            s.close()
            print(ex)
            return
    
        Status_msg(3)
    
        while(True):  
            data=0
            try:
                data=s.recv(1024)
                skt_var[1]=data.decode("utf-8")
                lbl_status.configure(text=f"Status: Received: {skt_var[1]}")   
            except:
                pass
    
            if break_event.isSet():
                Status_msg(4)
                s.close()
                break  
    
    def change_var(new_string,skt_var):
        skt_var[0]=new_string + "\r\n"
        s=skt_var[2]
        if not skt_var[0]=="":
            try:
               s.sendall(skt_var[0].encode())  
               Status_msg(5)
               skt_var[0]=""
            except:
                break_event.set()
    
    def shut_down():
        root.destroy()
    
    def Status_msg(msg_int):
    
        msg_dict={
        1:"Status: Attempting connection please wait",
        2:"Status: Connection failed",
        3:"Status: Socket open and waiting",
        4:"Status: Offline Socket closed",
        5:f"Status: Sending data {skt_var[0]}",
        6:"Status: Spare",
        7:"Status: Spare",
        8:"Status: Spare"
        }
    
        lbl_status.configure(text=msg_dict[msg_int],anchor="w",justify=LEFT)
    
    frame1=LabelFrame(root,text="Port Control")
    frame1.grid(row=0,column=0,padx=10,pady=10)
    btn1=Button(frame1,text="Connect",width=10,command=lambda:start_thread())
    btn1.grid(row=0,column=0,padx=5,pady=5)
    btn2=Button(frame1,text="Disconnect",width=10,command=lambda:stop_thread(skt_var))
    btn2.grid(row=1,column=0,padx=5,pady=5)
    btn3=Button(frame1,text="Send",width=10,command=lambda:change_var(e1.get(),skt_var))
    btn3.grid(row=2,column=0,padx=5,pady=5)
    btn4=Button(frame1,text="Exit",width=10,command=lambda:shut_down())
    btn4.grid(row=0,column=2,padx=5,pady=5)
    lbl1=Label(frame1,text="Data to send")
    lbl1.grid(row=2,column=1)
    lbl_status=Label(frame1,text="Status:",relief="sunken")
    lbl_status.grid(row=3,column=0,columnspan=3,sticky="we")
    lbl_status.configure(anchor="w")
    e1=Entry(frame1)
    e1.grid(row=2,column=2,padx=5)
    
    root.mainloop()
    
    

    SimpleIDE

    #include "simpletools.h"
    #include "fdserial.h"
    #include "mstimer.h"
    
    fdserial *xbee;
    fdserial *term;
    
    int main()
    {
      xbee = fdserial_open(9, 8, 0, 9600);
      simpleterm_close();
      term=fdserial_open(31,30,0,9600);
    
      char c;
    
      mstime_start();
    
      while(1)
      {
    
        if(mstime_get()>=5000)
        {
          c='#';
          dprint(term, "Heartbeat:%c\n",c);
          writeChar(xbee,c);
          mstime_reset();
          }
    
    
      c = fdserial_rxCheck(xbee);
    
        if(c > 0 && c< 126)
        {
         mstime_reset();
         writeChar(term,c);
        }
    
      }  
    }
    
  • banjobanjo Posts: 426
    edited 2022-02-26 07:13

    @Unsoundcode Great! Also thx for creating a short snippet for the Prop side, the one I have is kind of a monster as I have lots of AT-commands to configure the xBee, and stuff to steer the ActivityBot, originally from an iPad.
    Related to your thoughts about a simple protocol, I wonder if it would be worthwhile to take a look at the xBee API mode before.
    For my part, I have a tight schedule for this project overall (controlling an ActivityBot from an EEG-headband). As I now, thx to you, have what I need on the Prop and Python side for a prototype, I'm focusing on the Machine Learning part and trying to understand why the excellent training results are not reflected in a real situation. I have a hypothesis on that, and am now working on it.

  • dgatelydgately Posts: 1,509
    edited 2022-02-26 15:28

    @Unsoundcode, thanks, as well! Simplifying WiFi comms to ASCII makes it easy to debug on the prop side. I've been looking for a simple example like this. I actually translated the prop side C code to spin2 and am running it on a P2.

    BTW: Using a Parallax WiFi module instead of an XBee, I had to change the port from 9750 to 23.

    dgately

  • UnsoundcodeUnsoundcode Posts: 1,452
    edited 2022-02-26 15:55

    @banjo To be honest because of your schedule I would stick to ASCII text for now while you concentrate on developing the rest of your project. A protocol can be put in place at a later time, if you really want to work with binary data packets perhaps start with a fixed packet length for each instruction that should work well in this situation. It also seems to me that AT commands add an extra level of complexity that might not be needed, I configure the XBee using a FTDI 3.3v cable and XCTU software then I can forget about that side of things and move on to something else, it all depends on where your research is focused. If I get the time I will try for a better Propeller example to go with the Python code it's working but everything is up for improvement. I'm glad you understood the heartbeat concept it was an important piece of the puzzle and something I did not quite expect at first.

    @dgately that's awesome thanks so much for the feedback, I have actually been looking at putting the Python side into it's own class, I think I can do that but it is pushing my Python abilities to a different level. I have already got a version of what is here that accepts host and port as user input.

  • This post is for those who might be new to using WiFi modules and want to experiment a little. Again there is nothing drastic changing here it's all fairly simple stuff, the aim was to emulate something similar to the official Parallax WiFi demo which echoes characters that have been typed into the terminal. This needs an additional line of code in the python program that can be removed or commented when not needed.

    try:
                data=s.recv(1024)
                skt_var[1]=data.decode("utf-8")
                lbl_status.configure(text=f"Status: Received: {skt_var[1]}")  
                s.sendall('~'.encode() + skt_var[1].encode())  #add or remove this line to produce an echo
            except:
    

    The Propeller code is not serious stuff either but it does demonstrate two things which are a progression from the previous examples and of interest to a beginner, the first displays the route each character travels to get to be displayed in the terminal and the second which might go unnoticed at first glance is the use of the tilde character as a header that is used in a logical decision inside the Propeller program.

    #include "simpletools.h"
    #include "fdserial.h"
    #include "mstimer.h"
    
    fdserial *xbee;
    fdserial *term;
    
    int main()
    {
      xbee = fdserial_open(9, 8, 0, 9600);
      simpleterm_close();
      term=fdserial_open(31,30,0,9600);
    
      char c;
      char xb=0;
      mstime_start();
    
      while(1)
      {
    
        if(mstime_get()>=5000)
        {
          c='#';
          dprint(term, "Heartbeat sent from Prop:%c\n",c);
          writeChar(xbee,c);
          mstime_reset();
          }
    
      xb = fdserial_rxCheck(xbee);
         if(xb == '~')
        {
         mstime_reset();
         xb = fdserial_rxChar(xbee);
         dprint(term, "Echo from PC :%c\n",xb);
         xb=0;
        }
    
      c = fdserial_rxCheck(term);
    
        if(c > 0 && c< 127)
        {
    
         mstime_reset();
         dprint(term, "Typed in terminal :%c\n",c);
         writeChar(xbee,c);
    
        }
    
      }  
    }
    
  • dgatelydgately Posts: 1,509

    @Unsoundcode I'm actually trying to get 2 Parallax WiFi modules to 'talk' to each other, in ASCII. And, doing that in spin2 (or spin for a P1, as well). I have no problem getting a JOIN to work (to my local network), but a CONNECT to either another WiFI module or to my MacBook gets an error... The following code:

      wifi.str(string($FE,"CONNECT:"))    ' begin marker & CONNECT command
      wifi.str(@mac_ip)                              ' IP of computer or WiFi Module
      wifi.str(string(","))
      wifi.str(@mac_port)                          ' port (usually 23)
      wifi.str(string(13))                      ' end marker (13)
    

    Returns: "=E,11"

  • @dgately hi, I am by no means a network guru I think I learnt as much as anyone in this last exercise. Having said that there is another snippet that I have that acts as a PC server to one or more clients, does that sound like a road you would go down. I'll look into what's involved, I do have two WiFi modules and a Mac and PC I can try the WiFi hardware and software with.

  • RsadeikaRsadeika Posts: 3,685

    I find this thread very interesting. I have dabbled in tkinter for some of my projects, PC python GUI connection to a P1 and a P2 using the serial method.

    The Python socket was a new discovery for me. I will have to revisit some of my older code and see if using Python socket would be appropriate.

    @banjo, I am also interested in machine learning, I hope you will share some findings. Interested in seeing what sort of algorithm you come up with.

    Ray

  • @Rsadeika hi, I too have had a few good experiences using serial communication talking to a Prop or an Arduino via Python, its a very quick and easy way to develop a reasonable custom GUI. My last project was a "mini" logger that saved data samples to a CSV file, I did that because I thought it performed a little better than PLX-DAQ and was cross platform.

    I am new to sockets and I think the multi client server would be a great project, but it is not quite as straightforward as what we have been doing here so I have to give up on that one.

    @banjo I am also interested in your progress, keep us posted.

  • @banjo @dgately @Rsadeika hi guys,

    dgately got me thinking about this but you all might find it interesting to play around with.

    This is a server example straight out of the Python socket documentation. It looks similar but differs from the previous example in several ways, the host is no longer filled in with the WiFi modules IP, the HOST='0.0.0.0' allows for connection from any device on the network provided the device has a destination IP the same as the PC the server is on. This server will only communicate with one module at a time, when one socket closes the next can have access. The Propeller no longer needs to transmit a 'heartbeat' signal

    My test network is two XBees and one PC or Mac but it will work just as well using one wifi module or PC to PC.

    To get this set up to work in a reasonable way I modified two of the XBee settings, the first is the one I mentioned above and that was to set the destination address of both XBees (DL in XCTU config.) to point at my PC's IP. The second setting I adjusted was TCP client connection time out (TM in XCTU config.), what this does is send data to the PC, receives data from the PC and then closes the socket connection when the timeout value is reached. I arbitrarily set the time out at 5mS, this was short enough to quickly transmit and receive a small data packet then close and allow the next XBee to connect and do the same. I don't know for sure but probably other wifi units have the time out feature.

    To use this with two wifi clients needs a little thought on how it should be synchronized. I would think it needs some kind of ACK from the server for each time a client sent data, some kind of addressing to know where the data is from where it is going etc. A way to go yet but I'm sure I'll play around with this some more.

    For this I transmit '#' and receive the same back

    import socket
    from tkinter import *
    from threading import *
    root=Tk()
    root.geometry("200x200")
    root.configure(bg="dark grey")
    
    
    
    def worker_thread():
        while True:
    
            HOST = '0.0.0.0' 
            PORT = 9750      
    
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    
                s.bind((HOST, PORT))
    
                print("Waiting for client")
                s.listen()
    
                conn, addr = s.accept()
    
                print("Client accepted")
    
                with conn:
    
                   while True:
                        try:
                            data = conn.recv(10)
                            string1=data.decode("utf-8")
                        except Exception as e:
                            print(e)
                        if not data:
                            conn.close()
                            break
                        else:
                            if string1=='#':
                                conn.sendall(string1.encode())
                                string1=""
    
    t1=Thread(target=worker_thread,daemon=True)
    t1.start()
    
    root.mainloop() 
    
Sign In or Register to comment.