0
votes

I am making a new post regarding this case because I was misunderstood in the first one...

I have a code that reads the serial data from the Arduino and when some specific digits are pressed on the keyboard it writes these digits to the Arduino. This exact code works perfectly when I run it, it reads the serial data and I am able to write data to the Arduino. I use threading and PySerial library to achieve this.

from pynput import keyboard
import threading
import serial
import sys


ser = None

class SerialReaderThread(threading.Thread):


    def run(self):

        global ser

        ser = serial.Serial('COM3', baudrate = 9600, timeout = 5)

        while True:

            print(ser.readline().decode('utf-8'))


class KeyboardThread(threading.Thread):

    def run(self):

        def on_press(key):

            try:
                format(key.char)

                if key.char == "1":
                    ser.write(b'1\r\n') #serial write - 1

                elif key.char == "2":
                    ser.write(b'2\r\n') #serial write - 2

                elif key.char == "3":
                    ser.write(b'3\r\n') #serial write - 3

                elif key.char == "4":
                    ser.write(b'4\r\n') #serial write - 4

                elif key.char == "5":
                    ser.write(b'5\r\n') #serial write - 5    

                elif key.char == "6":
                    ser.write(b'6\r\n') #serial write - 6

                elif key.char == "0":
                    ser.write(b'0\r\n') #serial write - 0      
            except AttributeError:
                format(key)


        with keyboard.Listener(on_press=on_press) as listener:
            listener.join()

        listener = keyboard.Listener(on_press=on_press)
        listener.start()


serial_thread = SerialReaderThread()
keyboard_thread = KeyboardThread()

serial_thread.start()
keyboard_thread.start()

serial_thread.join()
keyboard_thread.join()

After this I got an idea that I could also write this serial data exactly what I was printing to the .txt file on windows. So I made a new thread called FileWriting and decided to just write ser.readline().decode('utf-8') to it, however it doesn't work anymore... This is the newly modified code which I wrote to write to the .txt file.

from pynput import keyboard
import threading
import serial
import sys
import io


ser = None

class SerialReaderThread(threading.Thread):


    def run(self):

        global ser

        ser = serial.Serial('COM3', baudrate = 9600, timeout = 5)

        while True:

            print(ser.readline().decode('utf-8'))



class FileWriting(threading.Thread):


   def run(self):

       while True:
             with io.open("output.txt", "a", encoding="utf-8") as f:
                    f.write(ser.readline().decode('utf-8'))



class KeyboardThread(threading.Thread):

    def run(self):

        def on_press(key):

            try:
                format(key.char)

                if key.char == "1":
                    ser.write(b'1\r\n') #serial write - 1

                elif key.char == "2":
                    ser.write(b'2\r\n') #serial write - 2

                elif key.char == "3":
                    ser.write(b'3\r\n') #serial write - 3

                elif key.char == "4":
                    ser.write(b'4\r\n') #serial write - 4

                elif key.char == "5":
                    ser.write(b'5\r\n') #serial write - 5    

                elif key.char == "6":
                    ser.write(b'6\r\n') #serial write - 6

                elif key.char == "0":
                    ser.write(b'0\r\n') #serial write - 0      
            except AttributeError:
                format(key)


        with keyboard.Listener(on_press=on_press) as listener:
            listener.join()

        listener = keyboard.Listener(on_press=on_press)
        listener.start()


serial_thread = SerialReaderThread()
keyboard_thread = KeyboardThread()
file_thread = FileWriting()

serial_thread.start()
keyboard_thread.start()
file_thread.start()

serial_thread.join()
keyboard_thread.join()
file_thread.join()

As it's clear I only added a new thread called file_thread, now as I run the code printing of the serial data works fine as well as the writing data to the Arduino, however, the code doesn't write anything to the .txt file and gives me an error:

Exception in thread Thread-3:
Traceback (most recent call last):
  File "C:\Python\lib\threading.py", line 932, in _bootstrap_inner
    self.run()
  File "C:\Users\ultra\Desktop\work\menucode.py", line 32, in run
    f.write(ser.readline().decode('utf-8'))
AttributeError: 'NoneType' object has no attribute 'readline'

If anybody had similar problems with Arduino while reading the serial data and writing to the text file, or if anybody knows how to fix this please let me know I am quite desperate at this point and everything is appreciated.

1

1 Answers

0
votes

At the top of your file, you declare ser = None. The error message you get indicate that the ser object has not yet been set to a Serial object before the FileWriting thread tries to access it.

A quick way to fix this issue is by doing

ser = serial.Serial('COM3', baudrate = 9600, timeout = 5)

before you start any of the threads.

However, that would probably make the program behave strangely, as you have two competing ser.readline() calls in different threads. This would probably result in roughly half of the Arduino's output data being captured by each of the threads (depending on how pyserial handles multiple requests for the same resource). To avoid this issue, I would recommend letting a single thread interface with the ser object, and having that thread pass data to other threads using a queue.

A simple example of how this data exchange could go:

import queue
import serial
q = queue.Queue()
ser = serial.Serial('COM3', baudrate = 9600, timeout = 5)

class SerialReaderThread(threading.Thread):
    def run(self):
        while True:
            # Read output from ser
            output = ser.readline().decode('utf-8')
            print(output)
            # Add output to queue
            q.put(output)

class FileWriting(threading.Thread):
   def run(self):
       while True:
             output = q.get()  # This will wait until an item is available in the queue
             with open("output.txt", "a+") as f:
                    f.write(output)
                    f.write("\n")  # If you want outputs separated by newlines