Python Forum

Full Version: Need help using Pyserial
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2 3
I am trying to get pyserial to send/receive ascii text to/from Meade ETX-90 telescope. I have tested the comm link using Putty and Tera term and COM3 is working properly. My requirement is simple. If I send "#:GR# " I should receive "19:00:21#" The program runs and I think I am sending out the string #:GR# but the serial. receive does not seem to work. All I get is b' ' ? I find very little good documentation of these functions. I hope someone sees my problem.


import serial
import time

ser = serial.Serial("COM3", 9600, timeout = 1)
while 1:
name = input("Get a name: ")
print(name)
print(ser.is_open)

if name == "exit" :
ser.close()
exit()
if ser.isOpen():
print("ser is open")
ser.write("hello".encode("utf-8")) #Note 1
print("write completed")
time.sleep(2)
#instr = ser.readline().strip().decode("utf-8") #Note 2
#instr = ser.readline() #Note 3

print(ser.read())
print(ser.read())
print(ser.read())
print(ser.read())
print(ser.read())

#print(instr)

print("End of while")

Following is what I send and reeive.
#python Hello.py
#Get a name: #:GR#
#True
#ser is open
#write completed
#''
#End of while

#Putty, when I send #:GR# I receive 19:00:21#
I don't have an ETX telescope, but from watching a short video on how to communicate with the telescope using a serial emulator and reading the LX200CommandSet.pdf describing the command protocol I don't think you can use readline because the software does not send responses terminated by "\n".

I do not see "hello" among the commands understood by the telescope. If you have a document that says this is a valid command please post a link.

Why do you preface your command with "#" that is the end of command marker. I think your command should be ":GR#". The telescope should return something of the format "HH:MM:T#" or "HH:MM:SS" depending on which "precision" is set for the telescope. According to the PDF the telescope might also return NAK (0x15) as a response to any command.

This makes for a messy protocol. You need to do something like this
ser.write(":GR#".encode())
response = ser.read(1)
if len(response) == 0:
    print("Timed out waiting for response")
elif response[0] == 0x15:
    print("Telescope reply is NAK")
else:
    while True:
        byte =ser.read(1)
        if len(byte) == 0:  
            print(response.decode())
        else:
            response = response + byte
I would write functions to clean this up a bit.
import serial

NAK = b"\x15"
EOL = "#".encode()

def sendCommand(ser, cmdStr):
    """Send command to telescope.  Commands start with : and end with #
    ser.write(f":{cmdStr}#".encode())

def readResponse(ser):
    """Read response.  Convert to str and return."""
    response = ser.read(1)
    if len(response) == 0:
        return None   # Timed out trying to read a response.  Make sure timeout is long enough
    elif response[0] == NAK:
        return NAK  # Possible response to any command according to PDF
    else:
         # Keep reading bytes until there are no more or you encounter #
        while True:
            byte = ser.read(1)
            if len(byte) == 0 or byte == EOL:
                return response.decode()
            else:
                response = response + byte

ser = serial.Serial("COM3", 9600, timeout=1.0)
sendCommand(ser, "GR")
print(readResponse(ser))
All you say is true. Hello is just the name of my program, not a ETX command. As you say, the command should be :GR# but I have found that if there is already some junk in the scopes buffer, then putting a # in front always makes the response correct. As you say, the ending character is a # not a /n

I thought I read that if readline timed out an EOL was appended.

From what you say, it looks like i need to do read over and over again to get the return string.
As you can see from my code, I did try to do multiple "print (ser.read()) to see if I got any o the expected chacters but all I got was " b'' "

I will use your code and see if I can get it to work tomorrow. Have you had success with your code?

Let me know.

I am also planning on using cv2 to show a web cam attached to the scope and to use the joystick portion of Pygame for a joystick, and I am also going to implement some sort of GUI using tkinter.
I have a lot of work to do.

OH, I see that the b'' i was receiving was a NAK, yes???
I just tried to run these programs. Both had errors.
Is """ a comment line? I thought #started a comment.
I am using python 3.8

I will try to follow your logic however.


OH, what is the argument for the read function. You used read(0) one time and read(1) at another.
I cannot find good documentation about pyserial.
Your program sends "hello" out the serial port. Why? I didn't see anything about having to send a program name to the telescope.

I thought readline() returned zero bytes when it times out. Looking at Pyserial documentation it says readline() is provided by io.IOBase.readline() and when I look there it says nothing about what readline does when it times out. BUT I did notice that read_until() behaves as you described. This would make it a good choice for reading the response.
import serial

NAK = b"\x15"
EOL = "#".encode()

def sendCommand(ser, cmdStr):
    """Send command to telescope.  Prepend : and append #"""
    ser.write(f":{cmdStr}#".encode())

def readResponse(ser):
    """Read response.  Strip trailing #"""
    response = ser.read_until(EOL)
    if len(response) == 0:
        return None
    elif response[0] == NAK:
        return NAK
    if EOL in response:
        response =  response[:response.index(EOL)]
    return response.decode()


ser = serial.Serial("COM3", 9600, timeout=1.0)
sendCommand(ser, "GR")
print(readResponse(ser))
NAK is not zero bytes. NAK is b'\15'.

Writing extra "#" is just going to cause confusion. Your telescope is going to say "I see #, where was the command?" and then it is probably going to return a NAK which you will read thinking it is the response to the command you are trying to send instead of a response to the leading "#".

As I said in my post, I do not have an ETX telescope. I guess to be clear I should say I don't have a Meade telescope and have no way to test the code I wrote. I would be surprised if it works.
"""some words""" is a multi-line string. I am using it to write a docstring for each of my functions. It is convention to use this method to write a starting comment that describes what your function does. I forgot the trailing """ for the sendCommand() docstring.

"""some words""" can also be used for long comments in the body of your code or at the top of your file.
OH, what is the argument for the read function. You used read(0) one time and read(1) at another.
I cannot find good documentation about pyserial.

Finally for tonight, did you use response when you meant ReadResponse on lines 22 and 24? HOw does response get passes back?

Do you have a different scope? What kind??

I really appreciate your help. I have used a lot of visual basic in my carreer and am now learning python.
I need to study more.

Final Finally. I have used two terminal programs, putty and teraTerm to test the commands. I find that if I send a bad command like "GR#" with no : then the next time I send a good command it does not respond. However if I send a # it seems to clean out the scopes buffer. So I find that if I always send #:GR# I always get a good response.
I do not have a motorized scope. I have an old 8" Orion reflector.

You won't have to worry about forgetting the leading : when you aren't typing commands in by hand.

The response in lines 22 and 24 is a local variable. I was using that to build up the response array, but now I think using ser.read_until() is a better choice. It returns everything read up to the terminating character or a timeout. See changes in my earlier post

Functions return values using the "return" statement.
I am understanding more and more but I still do not fully understand the ser.read(1) statement.
What does the 1 mean? I thought I saw where you used ser.read(0) at one time but I cannot find it.

You mentioned the read_until(EOF)

Does read get one byte(character) or a string of characters.

Finally for now, where are you finding the use of the Pyserial statements. The documentation on the Pyserial website does not seem to have a good listing of methods, arguments and what they do.
You should read the pyserial documentation. I think their documentation is very good.

https://pyserial.readthedocs.io/en/latest/pyserial.html

In particular the API

https://pyserial.readthedocs.io/en/lates...l_api.html

Which contains
Quote:The following methods may raise SerialException when applied to a closed port.

read(size=1)
Parameters: size – Number of bytes to read.
Returns: Bytes read from the port.
Return type: bytes
Read size bytes from the serial port. If a timeout is set it may return fewer characters than requested. With no timeout it will block until the requested number of bytes is read.

Changed in version 2.5: Returns an instance of bytes when available (Python 2.6 and newer) and str otherwise.

read_until(expected=LF, size=None)
Parameters:
expected – The byte string to search for.
size – Number of bytes to read.
Returns:
Bytes read from the port.

Return type:
bytes

Read until an expected sequence is found (‘\n’ by default), the size is exceeded or until timeout occurs. If a timeout is set it may return fewer characters than requested. With no timeout it will block until the requested number of bytes is read.
Pages: 1 2 3