Multiline serial read in Python 3 - python

I am playing around with bidirectional serial communication between my PC and a STM32 development board. I am using Python 3.7 with PySerial 3.4 to open the serial port and receive/transceive messages from/to the dev board. Everything is working as expected except when I try to read and print multiline messages. In this case I only get the first line of the message.
The program on the microcontroller is such, that I get a multiline help-message back, if I send 'H' via serial to the controller board.
The multiline message the dev board is sending back looks like this:
"HELP\r\nList of commands:\r\nH: Display list of commands\r\nS: Start the application"
So I am expecting to see the following printout:
HELP
List of commands:
H: Display list of commands
S: Start the application
But instead I only get:
HELP
If I connect to the port with PuTTY and send 'H' manually, I get the full message; so I know that it is not a problem of my microcontroller program.
My python code looks like this:
import serial
import io
class mySerial:
def __init__(self, port, baud_rate):
self.serial = serial.Serial(port, baud_rate, timeout=1)
self.serial_io_wrapped = io.TextIOWrapper(io.BufferedRWPair(self.serial, self.serial))
# receive message via serial
def read(self):
read_out = None
if self.serial.in_waiting > 0:
read_out = self.serial_io_wrapped.readline()
return read_out
# send message via serial
def write(self, message):
self.serial.write(message)
# flush the buffer
def flush(self):
self.serial.flush()
commandToSend = 'H'
ser = mySerial('COM9', 115200)
ser.flush()
ser.write(str(commandToSend).encode() + b"\n")
while True:
incomingMessage = ser.read()
if incomingMessage is not None:
print(incomingMessage)
Any help would be much appreciated.

You need to wait for new data after each line. Based on that, your read method need to be modified as below:
def read(self):
read_out = None
timeout = time.time() + 0.1
while ((self.serial.in_waiting > 0) and (timeout > time.time())):
pass
if self.serial.in_waiting > 0:
read_out = self.serial_io_wrapped.readline()
return read_out

Related

Problem opening same port multiple times in python Digi-XBee API

I'm trying to understand why I'm not able to open multiple times the same serial port with Digi-Xbee (import digi.xbee) API for python while with Xbee (import xbee) API for python I can.
When I run the code bellow the exception digi.xbee.exception.InvalidOperatingModeException: Could not determine operating mode is raised.
from digi.xbee.devices import *
import time
import codecs
class start(object):
while True:
xbeeApi2 = DigiMeshDevice(port='/dev/ttyUSB0', baud_rate=9600)
xbeeApi2.open()
time.sleep(0.5)
message = xbeeApi2.read_data(timeout=None)
if message is not None:
print(codecs.decode(message.data, 'utf-8'))
time.sleep(1)
XBee module is a S2C (XB24C) set as Digimesh 2.4 TH, firmware 9002 (newest) with a USB Dongle.
Python is 3.7 & my host hardware is a Raspberry Pi 3 B+ running Debian.
Any help would be appreciated.
EDIT 1
Exception is raised when, for the second time, {xbeeApi2.open()} is executed.
In fact, my original code has multiple threads that import the class where the port is opened, many times before the previous thread had the chance to close it.
The 'original' piece of code, that runs fine is bellow:
from xbee import ZigBee
import serial
import time
class start(object):
while True:
ser = serial.Serial('/dev/ttyUSB0', 9600)
xbeeApi2 = ZigBee(ser, escaped=True) # S2 e S2C
time.sleep(0.5)
message = ''
try:
message = xbeeApi2.wait_read_frame(timeout=0.5)
except:
pass #timeout exception
if message is not None:
print(message)
time.sleep(1)
Well, you aren't closing it. Why not create the device and open it before your while True loop?
And you could configure it to just sleep for 0.1 seconds when message is None to reduce processor load. You'll be able to keep up with the message queue that way -- if there was a message, you want to immediately check for another queued message before doing any sleeping.
from digi.xbee.devices import *
import time
import codecs
class start(object):
xbeeApi2 = DigiMeshDevice(port='/dev/ttyUSB0', baud_rate=9600)
xbeeApi2.open()
while True:
message = xbeeApi2.read_data(timeout=None)
if message is not None:
print(codecs.decode(message.data, 'utf-8'))
else:
time.sleep(0.1)

Python3 RSPI3 Serial Object Programming

I'm using today for a project a GSM/GPRS RSPI module. My job is to send, with AT commands, files to a FTP server (it's working by a simple program in Python or with putty by sending all AT commands one by one).
Today, to simplify my code, i chose to translate the code in object.
Also, i create my class with all my methods like send SMS, connectGPRS, sendFTP... (these methods do not appear to simplify the code)
But when i'm launching my program, i don't receive my confirm's reply from the module.
When isReady() start, the program send serial command to test the module. But i don't have any reply. My serial port configuration seems like right (debug() return ttyAMA0), and i can control my module by using Putty. But when i'm doing a short circuit with Tx and Rx, i can't see the request from the program on Putty.
Then my program stop in line sys.exit(0) with ser.isReady() returning false.
So my question is : It is possible to use Serial port, like i used it, in object programming ? Or do i make a mistake in my code ?
Regards. (sry btw for my frenchglish)
import serial
print("Reset du module")
resetModem()
time.sleep(5)
ser = ConnexionModule(SERIAL_PORT, 9600, 5)
if not ser.isReady():
print("Failed reboot, maybe a another program connected on serial, or the device isn't lauched")
sys.exit(0)
#debug() is a print function
def debug(text):
if VERBOSE:
print("Debug:---", text)
# This class is in reality in a another file imported in main
class ConnexionModule():
def __init__(self,serial_port,baudrate,timeout):
self.ser = serial.Serial(serial_port, baudrate, timeout)
# Testing if the module is ready to be used
def isReady(self):
# Resetting to defaults
cmd = 'ATZ\r'
# When i send 'ATZ' the module return 'OK'
debug("Cmd: " + cmd)
self.serialwrite(cmd,2)
reply = self.ser.read(self.ser.inWaiting())
reply = reply.decode("utf-8")
time.sleep(8) # Waiting for a reply
debug("Reply: " + reply)
return ("OK" in reply)
def serialwrite(self,cmd,slp):
debug("Sending:")
debug(self.ser.port)
debug(cmd)
self.ser.write(cmd.encode())
time.sleep(slp)
This code working :
import serial
print("Reset du module")
resetModem()
ser = serial.Serial(SERIAL_PORT, baudrate = 9600, timeout = 5)
if not isReady(ser):
print("Fail reboot")
sys.exit(0)
def isReady(pserial):
# Resetting to defaults
cmd = 'ATZ\r'
debug("Cmd: " + cmd)
serialwrite(pserial,cmd,2)
reply = pserial.read(pserial.inWaiting())
reply = reply.decode("utf-8")
time.sleep(8)
debug("Reply: " + reply)
return ("OK" in reply)
def debug(text):
if VERBOSE:
print("Debug:---", text)
def resetModem():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(P_RESET, GPIO.OUT)
GPIO.output(P_RESET, GPIO.LOW)
time.sleep(0.5)
GPIO.output(P_RESET, GPIO.HIGH)
time.sleep(0.5)
GPIO.output(P_RESET, GPIO.LOW)
time.sleep(3)

How to receive data from a raw socket in Python?

I am trying to create a port scanner (using SYN packets) with the sockets library (yes I know scapy would make this much easier, but I'm mostly doing this for a learning exercise.) I have crafted the packet and successfully sent it, however I'm having troubled receiving and parsing the subsequent response.
So far I've tried the s.recv(1024) and 4096, as well as recvfrom().
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.sendto(packet, (dstip, 80))
r = s.recv(1024)
print(r)
However, I am having trouble receiving the response, I can see that the packet is being sent correctly via Wireshark, and the SYN-ACK is sent to my machine, however I am unable to properly receive and print it. Is there a better way I can use the s.recv() function for this sort of input? Or am I using the wrong function?
Any help is appreciated, I'm new to the sockets library. Thanks.
The book Black Hat Python has en example using the socket library to create a scanner, unfortunately not a port scanner. They check if a host is up, and they use a raw socket to receive data. The code is available here.
They are sending SYN-packets with one socket object in a new thread, and sniffing the replies using another socket object.
In the example they use socket.IPPROTO_IP or socket.IPPROTO_ICMP instead of socket.IPPROTO_RAW depending on if it is Windows or not.
For the sniffer they use the function setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) for sniffing, where IPPROTO_IP is a dummy-protocol for TCP, IP_HDRINCL is to include headers in the IP packets, and 1 is mapped to the ICMP-protocol in the code.
Good luck!
Below is a recent module I wrote with the help from various sources for socket IO, take what you would like from it.
import socket
import threading
import time
import pygogo as gogo
from icentralsimulator.bridgeio.read_packets import PacketFactory
from icentralsimulator.bridgeio.write_packets import WritePacket
from icentralsimulator.configurations.interfaces import IServerInfoProvider
logger = gogo.Gogo(__name__).logger
send_lock = threading.Lock()
class BridgeConnection:
def __init__(self, bridge_info_provider: IServerInfoProvider):
info = bridge_info_provider.get_bridge_server_info()
self.callback = None
self.bridge_ip = info.IpAddress
self.bridge_port = info.Port
self._connection = None
self._terminate_wait_for_incoming = False
#property
def is_connected(self):
return self._connection is not None
def connect(self, callback):
"""
The purpose of this method is to create (and hold) a connection to the server. At the same time,
it creates a new thread for the purpose of waiting on incoming packets.
"""
if self._connection is not None: return
self._connection = socket.create_connection((self.bridge_ip, self.bridge_port))
self._connection.settimeout(0.5)
self.callback = callback
t = threading.Thread(target=self._wait_for_incoming)
t.start()
time.sleep(5)
def disconnect(self):
"""
Breaks existing connection to the server if one is currently made and cancels the thread that is waiting
for incoming packets. If the connection is not currently open, simply returns silently -- thus it is safe
to call this method repeatedly.
"""
self._terminate_wait_for_incoming = True
while self._terminate_wait_for_incoming:
time.sleep(0.1)
self._connection.close()
self._connection = None
def send_packet(self, packet: WritePacket):
"""
Sends an arbitrary packet to the server.
"""
with send_lock:
logger.debug(f"Sending packet: {packet.payload_plain_text}")
payload = packet.payload
self._connection.sendall(payload)
def _wait_for_incoming(self):
"""
Continually runs a loop to wait for incoming data on the open socket. If data is received, it is converted
to a receive packet and forwarded to the consumer as part of a callback.
"""
self._terminate_wait_for_incoming = False
buf_len = 4096
try:
while not self._terminate_wait_for_incoming:
data = None
try:
_cnx = self._connection
if _cnx is None: break
data = _cnx.recv(buf_len)
if data is not None and len(data) > 0:
while True:
new_data = _cnx.recv(buf_len)
if new_data is None or len(new_data) == 0:
break
data = data + new_data
except socket.timeout:
if data is not None and self.callback is not None:
packet = PacketFactory.get_packet(data)
self.callback(packet)
logger.debug(f"Received packet: {data}")
time.sleep(0.5)
except OSError: # Happens when stopping the application
logger.info("Application aborted")
return
finally:
self._terminate_wait_for_incoming = False
Note that I don't include IServerInfoProvider, or the PacketFactory here. Those are pretty custom to my application. You will need to interpret the packet according to the packet data that arrives in your specific use case.

Can't receive reply using PySerial but hyperterminal works

I have a device (Pololu Wixel) that I'm trying to communicate with using a serial connection over USB. Hyperterminal works fine but I'm trying to use Python for more flexibility. I can send commands to the device, but when I try to receive all I get is the command I just sent. However, if I open Hyperterminal, I'll receive the reply there to the command sent from the script. My code is below. I'm at a bit of a loss and it seems like this should be fairly straightforward. I appreciate any help.
import serial
import time
'''
Go through 256 COM ports and try to open them.
'ser' will be the highest port number. Fix this later.
'''
for i in range(256):
currentPort = "COM" + str(i+1)
try:
ser = serial.Serial(currentPort,baudrate=115200,timeout=5)
print("Success!!")
print(ser.name)
except:
pass
print(ser.isOpen())
str = "batt" #Command to request battery levels.
ser.write(str.encode())
x = ser.inWaiting()
print(x)
while ser.inWaiting() > 0:
out = ser.readline()
print(out.decode())
Add a break after finding an active port,
Try passing a different eol value to readline(), "\r" or "\r\n".

How to Send/Receive SMS using AT commands?

Can anyone help me to send and receive SMS using AT commands in Python?
In case it matters, I'm using Fedora 8.
Which phone will be better with Linux (Nokia, Sony Ericson, Samsung,.....)?
Will all phones support sending and receiving SMS using AT commands?
Here's some example code that should get you started (in Python 3000):
import time
import serial
recipient = "+1234567890"
message = "Hello, World!"
phone = serial.Serial("/dev/ttyACM0", 460800, timeout=5)
try:
time.sleep(0.5)
phone.write(b'ATZ\r')
time.sleep(0.5)
phone.write(b'AT+CMGF=1\r')
time.sleep(0.5)
phone.write(b'AT+CMGS="' + recipient.encode() + b'"\r')
time.sleep(0.5)
phone.write(message.encode() + b"\r")
time.sleep(0.5)
phone.write(bytes([26]))
time.sleep(0.5)
finally:
phone.close()
You need to do two additional things:
Encode the message in the appropriate format (mostly GSM 03.38, there's a handy translation table at unicode.org). If you really don't care about any characters other than ASCII, you can just check if every character is in string.printable.
Check the length of the message (I'm not sure if it's to do with the encoding, but it's sometimes 140 characters, sometimes 160).
You can use phone.readall() to check for errors, but it's best to make sure your message is OK before you send it off to the phone. Note also that the sleeps seem to be necessary.
Most phones will understand this. In order to get my old Nokia C5 to open up the serial connection, I had to select "PC Suite" from the menu that pops up when you insert the USB cable. This should work equally well over Bluetooth.
The code uses the PySerial package, available for python 2 and 3.
See also:
How do i go about writting a program to send and receive sms using python?
to see send sms using At command this will help.
import serial
import time
class TextMessage:
def __init__(self, recipient="+2348065777685", message="TextMessage.content not set."):
self.recipient = recipient
self.content = message
def setRecipient(self, number):
self.recipient = number
def setContent(self, message):
self.content = message
def connectPhone(self):
self.ser = serial.Serial('COM70', 460800, timeout=5, xonxoff = False, rtscts = False, bytesize = serial.EIGHTBITS, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE)
time.sleep(1)
def sendMessage(self):
self.ser.write('ATZ\r')
time.sleep(1)
self.ser.write('AT+CMGF=1\r')
time.sleep(1)
self.ser.write('''AT+CMGS="''' + self.recipient + '''"\r''')
time.sleep(1)
self.ser.write(self.content + "\r")
time.sleep(1)
self.ser.write(chr(26))
time.sleep(1)
def disconnectPhone(self):
self.ser.close()
sms = TextMessage("+2348063796720","Mummy i sent this message from my computer")
sms.connectPhone()
sms.sendMessage()
sms.disconnectPhone()
print "message sent successfully"
To recieve sms using At command this should help
import serial
import time
import sys
class HuaweiModem(object):
def __init__(self):
self.open()
def open(self):
self.ser = serial.Serial('COM70', 406800, timeout=5)
self.SendCommand('ATZ\r')
self.SendCommand('AT+CMGF=1\r')
def SendCommand(self,command, getline=True):
self.ser.write(command)
data = ''
if getline:
data=self.ReadLine()
return data
def ReadLine(self):
data = self.ser.readline()
print data
return data
def GetAllSMS(self):
self.ser.flushInput()
self.ser.flushOutput()
command = 'AT+CMGL="REC UNREAD"\r\n'#gets incoming sms that has not been read
print self.SendCommand(command,getline=True)
data = self.ser.readall()
print data
h = HuaweiModem()
h.GetAllSMS()
Talking to the phone is easy. You just need to open the appropriate /dev/ttyACM* device and talk to it. Which phone is trickier. Any phone that supports "tethering" and the full AT command set for SMS messages should be fine.
I would suggest replace the time.sleep with condition loop waiting for the response from the modem "OK" before continue next state.

Categories

Resources