Search code examples
pythonmatplotlibbluetoothmultiprocessingesp32

FuncAnimation in Multiprocessing to get bluetooth data and plot in real time


I have been trying to plot angle data I'm getting via Bluetooth from an ESP32 in python, but I have faced different issues. I tried calling a function to get the data, append it to a list and plot it. However, I guess FuncAnimation was too slow and it was appending twice the same number, so instead of appending 8 it appended 88. Then, I tried multiprocessing where one function appends the data to a list (Which works just fine) and a function that plots using FuncAnimation. I noticed that the function can't call the lists to plot x and y. I tried global variables, and passing arguments to the functions, but I only get empty lists.

It'll be a of great help if you can help me figure it out. Thanks!

from bluetooth import *
from itertools import count
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from multiprocessing import Process
#import sys 
#import socket
#sock = socket.socket ()
plt.style.use('fivethirtyeight')
        
def rx_and_echo():
    while True:
        data = sock.recv(buf_size)
        if data:
            y.append(int(data.decode('utf-8')))
        else:
            y.append(0)
        x.append(next(index))
        print(x[-1],y[-1])
  
def plot(i):
    print("YES"+str(y[:]))
    plt.cla()
    plt.plot(x,y)
    
def animation():
    ani = FuncAnimation(plt.gcf(), plot)
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    x = []
    y = []
    index = count()
    #MAC address of ESP32
    addr = "78:21:84:88:A9:BE"
    service_matches = find_service( address = addr )

    buf_size = 1024;
    if len(service_matches) == 0:
        print("couldn't find the SampleServer service =(")
        sys.exit(0)
        
    first_match = service_matches[0]
    name = first_match["name"]
    host = first_match["host"]

    port=1
    print("connecting to \"%s\" on %s, port %s" % (name, host, port))

    # Create the client socket
    sock=BluetoothSocket(RFCOMM)
    sock.connect((host, port))

    print("connected")
    p1 = Process(target = rx_and_echo)
    p2 = Process(target = animation)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
   
sock.close()

Solution

  • So this is how it worked for me. I added Manager and saved the collected data into manager lists. I also included try and except handlers in the plot function because there were times where x and y didn't have the same dimensions. I guess it was too fast that it still didn't save the data and it was trying to plot it.

    from bluetooth import *
    from itertools import count
    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    from multiprocessing import Process, Manager
    plt.style.use('fivethirtyeight')
    
    def rx_and_echo():
        while True:
            data = sock.recv(buf_size)
            if data:
                x.append(next(index))
                y.append(int(data.decode('utf-8')))
            
    def plot(i):
        try:
            plt.cla()
            plt.plot(x,y)
        except:
            plot(1)
        
    def animation():
        ani = FuncAnimation(plt.gcf(), plot, interval = 20)
        plt.tight_layout()
        plt.show()
    
    if __name__ == "__main__":
        with Manager() as manager:
            index = count()
            x = manager.list([])
            y = manager.list([])
            #MAC address of ESP32
            addr = "78:21:84:88:A9:BE"
            service_matches = find_service( address = addr )
    
            buf_size = 1024;
    
            if len(service_matches) == 0:
                print("couldn't find the SampleServer service =(")
                sys.exit(0)
                
            first_match = service_matches[0]
            name = first_match["name"]
            host = first_match["host"]
    
            port=1
            print("connecting to \"%s\" on %s, port %s" % (name, host, port))
    
            # Create the client socket
            sock=BluetoothSocket(RFCOMM)
            sock.connect((host, port))
    
            print("connected")
            p1 = Process(target = rx_and_echo)
            p2 = Process(target = animation)
            p1.start()
            p2.start()
            p1.join()
            p2.join()
        
    sock.close()