I am Sending Commands Through Serial Port in Python But They Are Sent Multiple Times Instead of One

i am sending commands through serial port in python but they are sent multiple times instead of one

There seems to be nothing wrong with your code. At least to the extent I could reproduce, it only sends the command once (I tried your function after setting up my serial port in loopback).

I cannot say for sure but it might be that the terminal you're using has two windows, one for input and another one for output and somehow you're getting confused with what is in and out of your port.

One easy way to deal with this kind of issue is to use a sniffer on your port. You can do that combining com0com and Termite on Windows, as I recently explained here.

As you can see there is only one window on this terminal, and after setting up the forwarding you'll everything that comes in and out of your port. That should make it easier to see what your code is writing and reading.

To give you a conventional scenario to apply the sniffer trick you can refer to the following screenshot:

conventional serial port scenario

In this case, we have two real serial ports on a computer. On the first (COM9) we are running a Modbus server (you can imagine it as a bunch of memory addresses, each of one storing a 16-bit number). On COM10 we have a client that is sending queries asking for the contents of the first 10 addresses (called registers using the Modbus terminology). In a general use case, we have those ports linked with a cable, so we know (theoretically) that the client on COM10 is sending a data frame asking for those ten registers and the server on COM9 is answering with the numbers stored on those registers. But we are only able to see the contents on the server (left side of the picture) and what the client is receiving (right). What we don't see is what is traveling on the bus (yeah, we know what it is, but we don't know exactly how the Modbus protocol looks like on the inside).

If we want to tap on the bus to see what is being sent and received on each side we can create a couple of virtual ports with com0com and a port forwarding connection with Termite, something like the following screenshot:

port forwarding setup with Termite and com0com

Now we have moved our Modbus server to one of the virtual serial ports (COM4 in this case). After installing com0com we got (by default, but you can change names or add more port pairs, of course) a pair of forwarded ports (COM4<-->COM5). Now, if we want to see what is circulating through the ports we open Termite (bottom-right side of the picture) and set up another port forwarding scheme, in this case from virtual port COM5 to the real port COM9.

Finally (and exactly the same as before we were sniffing), we have COM9 connected together with COM10 with a cable. But now we are able to see all data going to and fro on the bus (all those HEX values you see on Termite displayed with the green/blue font).

As you can see, this will offer something similar to what you can do with more professional tools.

Read and send multiple lines instead of one

The second approach looks right to me, the problem is that you are setting commandToSend to lines, which is a list of all the lines, what you want to send instead is line (the current line in the list you are looping on).

So your code should be:

import serial
import time

f = open("Foods.txt", "r")
lines = f.readlines()

for line in lines:
ser = serial.Serial('/dev/cu.SLAB_USBtoUART', 115200, timeout = 1)
commandToSend = line
ser.write(str(commandToSend).encode()).

Also, if you wanted to add a carriage return, you would have to concatenate it as a string, eg:

line + '\r'

or

'%s\r' % line

or

'{}\r'.format(line)

or

f'{line}\r'

Depending on preferences / Python version

Python Shell: writing one byte and reading decimal data from a serial port

My suggestion:

  1. make sure that connection settings are really the same (I would compare them to Matlab)
  2. send correct commands: the first is b'\x80' and the second b'\x82' from your screenshots, although here you are writing b'\x81'
  3. add 5s timeout on both write and read, so you will definitely know if something is waiting
  4. reset in and out buffers as was suggested in the comments
  5. use logging to see the timings of each command
  6. add additional sleep to see if anything changes
  7. as a last resort you could try with a different library https://pyvisa.readthedocs.io/en/latest/index.html

My test script would be something like this (run with python -u to avoid buffering of log output):

import logging
import serial
import time
from pathlib import Path

logging.basicConfig(level='INFO', format='%(asctime)s - %(message)s')

logging.info('open')
rs232 = serial.Serial(
port='COM5',
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=5,
write_timeout=5,
)

logging.info('reset buffers')
rs232.reset_input_buffer()
rs232.reset_output_buffer()

logging.info('write first')
rs232.write(b'\x80')

# time.sleep(1) # see if uncommenting changes anything

logging.info('read first')
reply = rs232.read(size=1)
logging.info(reply)

logging.info('write second')
rs232.write(b'\x82')

# time.sleep(1) # see if uncommenting changes anything

logging.info('read second')
# reply = rs232.read_until(expected=b'\x0a\x0d') # read single line
reply = rs232.read_until(expected=b'\x80')
Path('result.csv').write_text(reply.decode().replace('\n\r', '\r\n'))
logging.info(reply)

logging.info('close')
rs232.close()

How to deal with multiple serial ports for R/W using twisted?

Note on your example code

I don't see you instantiating your classes before registering them to the reactor. I expect that will fail badly. Here is a similar snippet of running code of mine:

# stuff to process messages coming from the serial port
class SerialEater(basic.LineReceiver):
statusCallback = None

def __init__(self):
self.keyinprocess = None

def lineReceived(self, data):
self.dealWithSerial(data)

def connectionLost(self, reason):
if(reactor.running):
print "Serial lost but reactor still running! reason: " + str(reason) + " at time " + time.asctime()
[...etc...]

# Register the serialport into twisted
serialhandler = SerialEater() # <------------- instantiate
SerialPort(serialhandler, '/dev/ttyUSB0', reactor, baudrate=115200)

How/where do I accept CLI input from user, that then triggers sending a set of AT command to the modems ?

Much like how you can register Serial handlers into Twisted, you can register handlers for standard io, for instance:

# stuff to pull cbreak char input from stdin
class KeyEater(basic.LineReceiver):

def __init__(self):
self.setRawMode() # Switch from line mode to "however much I got" mode

def connectionLost(self, reason):
if(reactor.running):
self.sendLine( "Keyboard lost but reactor still running! reason: " + str(reason) + " at time " + time.asctime())

def rawDataReceived(self, data):
key = str(data).lower()[0]
try:
if key == '?':
key = "help"
[...etc...]

# register the stdio handler into twisted
keyboardobj = KeyEater()
keyboardobj.serialobj = serialhandler
stdio.StandardIO(keyboardobj,sys.stdin.fileno())

Correlate the information received on command port for ttyUSB0 & ttyUSB1 for modem1, and similarly for the other pair for modem2 ? Note that each modem has it's own state-machine (device-state and connection-state)

In normal use, each connection-instance is going to have its own state machine (wrapped up in the instance of the class that you register into the reactor along with the connection).

You as the programmer choose how you want to connect the states of the classes, but often its via pushing reference to the partner classes.

Below, this answer contains runnable code that will illustrate how data is connected between state-machines/interface. This is also illustrated in this SO: Persistent connection in twisted


Does twisted provide any mechanism for management of multiple state-machines by application ?

If by "application" you mean "your twisted code" then then the answer is absolutely YES!

The typical Twisted app is an array of state-machines, all with some amazingly well defined interfaces. I started my Twisted adventure intending to write an app with two state-machines (a serial and keyboard), but when I became comfortable with twisted was doing I realized it was trivial to add on extra interfaces and state-machines (through all the wonder of the tx libraries). All in one afternoon I added on a rough web interface, a websocket interface, then laid SSL over both and even added on an SSH debug interface. Once you get a rolling, adding interfaces and state-machines become trivial.

In many (all?) cases, the twisted model is that a state-machine will reside in an instantiated class that is tied to a connection and that has been registered into the (one-and-only-one) main event-loop.

With connection types that spawn off new state-machines (think http connections) you register one factory-class/state-machine along with the listening connection which together enable the app of spawning off new classes/state-machines for each new connection. Twisted applications routinely 10s or even 100s of thousands of concurrent instances of state when run at scale.

Twisted is amazing if your trying to glue together different protocols and states (... with all of it being in a event loop of your choice (select/epoll/kqueue/etc))

The following is runnable sample code that should illustrate many of these points. Read the comments before def main() for more background on the code:

#!/usr/bin/python
#
# Frankenstein-esk amalgam of example code
# Key of which comes from the Twisted "Chat" example
# (such as: http://twistedmatrix.com/documents/12.0.0/core/examples/chatserver.py)

import sys # so I can get at stdin
import os # for isatty
import termios, tty # access to posix IO settings
from random import random
from twisted.internet import reactor
from twisted.internet import stdio # the stdio equiv of listenXXX
from twisted.protocols import basic # for lineReceiver for keyboard
from twisted.internet.protocol import Protocol, ServerFactory

class MyClientConnections(basic.LineReceiver):

def __init__(self):
self.storedState = "Idle"
self.connectionpos = None

def connectionMade(self):
self.factory.clients.append(self) # <--- magic here :
# protocol automagically has a link to its factory class, and
# in this case that is being used to push each new connection
# (which is in the form of this class) into a list that the
# factory can then access to get at each of the connections
self.connectionpos = str(self.factory.clients.index(self)) # figure out
# where I am in the connection array
print "Got new client! (index:", self.connectionpos + ")"
self.transport.write("---\nYour connection: " + self.connectionpos + "\n---\n")

def connectionLost(self, reason):
print "Lost a client!"
self.factory.clients.remove(self)

# used to pretend that something was typed on a telnet connection
def fakeInput(self, message):
self.transport.write("FAKING Input: '" + message + "'\n")
self.lineReceived(message)

#this is only in a def on its own so I can lump my demo callLater
def stateUpdate(self, newState, delay):
self.storedState = newState
# the following is a hack to fake data coming in this interface
reactor.callLater(delay, self.fakeInput, newState + " DONE")

def processInput(self, newState):
# all the logic in here is junk to make a demo, real code may or may-not look like
# this. This junk logic is an example statemachine though
if self.storedState == "Idle":
if newState == "start":
self.stateUpdate("State A", 1)
# send a message to this connection
self.transport.write("starting state machine\n")
# send a message to the term in which the script it running
print "Connection [" + self.connectionpos + "] starting state machine"
elif self.storedState == "State A":
if newState == "State A DONE":
self.transport.write("Beginning state B\n")
self.stateUpdate("State B", 2)
elif self.storedState == "State B":
if newState == "State B DONE":
self.transport.write("Beginning state C\n")
self.stateUpdate("State C", 2)
elif self.storedState == "State C":
if newState == "State C DONE":
self.storedState = "Idle"
# send a message to this connection
self.transport.write("Returning to Idle state\n")
# send a message to the term in which the script it running
print "Connection [" + self.connectionpos + "] return to Idle state"

def lineReceived(self, line):
# print "received '" + line +"' from connection", self.factory.clients.index(self)
self.processInput(line)

class MyServerFactory(ServerFactory):
protocol = MyClientConnections

def __init__(self):
self.clients = [] # this gets filled from the class above

def sendToAll(self, message):
for c in self.clients: # Read MyClientConnections class for background
c.transport.write(message)

def randStart(self, width):
for c in self.clients:
startDelay = random() * width
print "Starting client " + str(c.connectionpos) + " in " +str(startDelay) + " secs"
reactor.callLater(startDelay, c.processInput, "start")

# to set keyboard into cbreak mode -- just because I like it that way...
class Cbreaktty(object):
org_termio = None
my_termio = None

def __init__(self, ttyfd):
if(os.isatty(ttyfd)):
self.org_termio = (ttyfd, termios.tcgetattr(ttyfd))
tty.setcbreak(ttyfd)
print ' Set cbreak mode'
self.my_termio = (ttyfd, termios.tcgetattr(ttyfd))
else:
raise IOError #Not something I can set cbreak on!

def retToOrgState(self):
(tty, org) = self.org_termio
print ' Restoring terminal settings'
termios.tcsetattr(tty, termios.TCSANOW, org)

class KeyEater(basic.LineReceiver):

def __init__(self, factoryObj):
self.setRawMode() # Switch from line mode to "however much I got" mode
# the following is one of the key connecting ideas in twisted, the object
# that contains another state machine (really all of the tcp statemachines)
# has been passed into this class via its init.
self.factoryObj = factoryObj

def rawDataReceived(self, data):
key = str(data).lower()[0]
if key == 's':
# The following line is going to call (from within the factory object)
# the random start def
self.factoryObj.randStart(5)
elif key == 'd':
print "State Dump of connections"
print "-------------------------"
for c in self.factoryObj.clients:
print "#" + str(c.connectionpos) + " " + c.storedState
elif key == 'q':
reactor.stop()
else:
print "--------------"
print " If you haven't already, connect to this script via a"
print " 'telnet localhost 5000' at least one (multiple connections"
print " are better)"
print "Press:"
print " s - randomly start all clients"
print " d - dump the state of all connected clients"
print " q - to cleanly shutdown"
print " Note: you can type commands in the connections, things"
print " most useful of which is 'start'"
print "---------------"

# Custom tailored example for SO:30397425
#
# This code is a mishmash of styles and techniques. Both to provide different examples of how
# something can be done and because I'm lazy. Its been built and tested on OSX and linux,
# it should be portable (other then perhaps termal cbreak mode). If you want to ask
# questions about this code contact me directly via mail to mike at partialmesh.com
#
# While it isn't directly using serial ports, the tcp connections that its using are a good
# parallel.
#
# It should be used by running the script and then opening up many windows telnet'ing into
# localhost 5000.
#
# Once running press any key in the window where the script was run and it will give
# instructions.
# The normal use case would be to type "s" to queue statemachine
# start-ups, then repeatedly press 'd' to dump the status of all the state machines
#
# 'start' can be typed into any of the telnet connections to start them by hand too.

def main():
client_connection_factory = MyServerFactory()

try:
termstate = Cbreaktty(sys.stdin.fileno())
except IOError:
sys.stderr.write("Error: " + sys.argv[0] + " only for use on interactive ttys\n")
sys.exit(1)

keyboardobj = KeyEater(client_connection_factory)

stdio.StandardIO(keyboardobj,sys.stdin.fileno())
reactor.listenTCP(5000, client_connection_factory)
reactor.run()
termstate.retToOrgState()
if __name__ == '__main__':
main()

It is possible that the USB-serial connection to the modem is destroyed due to the modem being unplugged, and re-established on being plugged back-in. How can I detect such events and add the monitoring of the corresponding device-ports to the reactor ? Currently, I'm doing it statically in the main application.

After research I don't have an easy answer. I still suspect that the following logic will be close to a solution but I didn't have any luck finding code that implements this today.

My guess is there will be a reasonable way to figure out if a USB event has occurred, and work out if a serial device has been added. But I doubt there will a good way to figure out if it is one of your serial devices - much less if its your Command or Diagnostic interface (Unless your building hardware and can control the USB IDs of the devices)

Events are fired on errors with serial ports (at least from my experience on linux), but I'm unsure how/where a USB unplug would register.


Other links that might be of use to you

  • Twisted implementation of Utilities for talking to a GSM modem over
    USB via AT commands
    : https://github.com/smn/txgsm
  • Twisted implementation of weather station through USB:
    https://gist.github.com/claws/2464017

Python Pyserial read data form multiple serial ports at same time

First change ReadCOM.py to receive arguments

import sys
import serial

ser = serial.Serial(port=sys.argv[1],baudrate=int(sys.argv[2]))
while True: # The program never ends... will be killed when master is over.
# sys.stdin.readline()

ser.write('serial command here\n') # send command to serial port
output = ser.readline() # read output

sys.stdout.write(output) # write output to stdout
sys.stdout.flush()

and after pass it in main.py:

from subprocess import Popen, PIPE

# call subprocess
# pass the serial object to subprocess
# read out serial port

# HOW TO PASS SERIAL OBJECT HERE to stdin
p1 = Popen(['python', './ReadCOM.py', "COM1", "9600"], stdin=PIPE, stdout=PIPE, stderr=PIPE) # read COM1 permanently
p2 = Popen(['python', './ReadCOM.py', "COM2", "9600"], stdin=PIPE, stdout=PIPE, stderr=PIPE) # read COM2 permanently

for i in range(10):
print "received from COM1: %s" % p1.stdout.readline() # print output from ReadCOM.py for COM1
print "received from COM2: %s" % p2.stdout.readline() # print output from ReadCOM.py for COM2


Related Topics



Leave a reply



Submit