I am trying to use a Raspberry Pi 3B (run Ubuntu Mate 16.04 operating system)as a Master to read values from an electric energy meter which supports Modbus-RTU protocol.
I used a RS232/USB adapter and a RS485/RS232 adapter to link the meter and the USB port on the Raspberry Pi. I have tried the modbus_tk 0.5.7 and MinimalModbus to implement the communication under Modbus-RTU protocol.
When I use modbus_tk 0.5.7 and run the following code:
import sys import serial
#add logging capability import logging import modbus_tk import modbus_tk.defines as cst import modbus_tk.modbus_rtu as modbus_rtu
logger = modbus_tk.utils.create_logger("console")
if __name__ == "__main__":
try:
#Connect to the slave
master = modbus_rtu.RtuMaster(serial.Serial(port="/dev/ttyUSB0", baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0))
master.set_timeout(5.0) #Change the timeout value/Defines a timeout on the MAC layer
master.set_verbose(True) #print some more log prints for debug purpose
logger.info("connected")
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 49))
except modbus_tk.modbus.ModbusError, e:
logger.error("%s- Code=%d" % (e, e.get_exception_code()))
The parameters such as port, baudrate, bytesize,parity,and stopbits were set correctly, but it always returns this:
2017-08-10 19:24:34,282 INFO modbus_rtu.__init__ MainThread RtuMaster /dev/ttyUSB0 is opened
2017-08-10 19:24:34,283 INFO rtumaster_example.<module> MainThread connected
2017-08-10 19:24:34,284 DEBUG modbus.execute MainThread -> 1-3-0-0-0-49-132-30
2017-08-10 19:24:39,291 DEBUG modbus.execute MainThread <-
Traceback (most recent call last):
File "rtumaster_example.py", line 34, in <module>
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 49))
File "build/bdist.linux-x86_64/egg/modbus_tk/utils.py", line 39, in new
modbus_tk.exceptions.ModbusInvalidResponseError: Response length is invalid 0
When I use MinimalModbus and run the following code:
#!/usr/bin/env python
import minimalmodbus
instrument.serial.port='/dev/ttyUSB0' # this is the serial port name
instrument.serial.baudrate = 9600 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 0.05 # seconds
#instrument.address # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 1) # port name, slave address (in decimal)
energy = instrument.read_register(10, 1) # Registernumber, number of decimals
print energy
It always returns this:
raise IOError('No communication with the instrument (no answer)')
IOError: No communication with the instrument (no answer)
And then I use the same serial transmission line to link the meter and the laptop, and use a debugging tool running on Windows XP, which was developed by the manufacturer of the meter. The debugging tool sends the same Request (1-3-0-0-0-49-132-30) as before, but the debugging tool can get correct Responses. (Maybe it's because it ignored some incorrect Responses and keep on send Requests regularly) And it can represent that the Request message is correct and the connection of serial transmission has no problem.
I also used the CuteCom(a graphical serial terminal) and the RS232/USB adapter to confirm that the USB port can send and receive correctly. It is also useless to add a resistor between two RS485 lines
I have tried many times, but the Raspberry Pi never gets a Response, and always returns the same error information. I also try to run the same code on a Ubuntu virtual machine, it returned the same message as above and never get a Respond.
I am new to Modbus and serial communication, so any help would be appreciated.
I have solved my problem by using a more expensive USBtoRS485 connector.
The problem took me a lot of time to try different library and different code.
It turns out that the adapter "QinHeng Electronics HL-340 usb-serial" that I bought works well on windows but does not work with Linux. It can achieve send and receive message on Linux, but it just can`t support Modbus communication.
So I suggest you buy a more expensive connector, and it may save your a lot of time and energy.
That`s all, thank you!
I was having the same problem. After use mbpoll tool I have realized the parity was wrong. Just updated to parity.EVEN and it is ok now.
Try connecting RS232-end of a cable into PC with serial port (if you have it ofc) to ensure usb/rs232 works
If you have oscilloscope (like fluke), you can observe RS485 line (it is not difficult in fact) to understand where is the issue.
It happened to me to find out that serial parameters (like baud rate) can be configured it 2 places for Windows - when you open the port and in driver configuration.
Try equalize the potential for all devices. I know rs485 is supposed to be immune to it but rs232 not
You have a too short time out on your Pi, try increasing it to 100 ms and it should work.
EDIT: As requested in the comment below I'm giving some more details on my suggestion. Based on the details given on the question and subsequent answers and comments the hardware setup seemed to be correct. I've seen this happen from time to time: when you set a very short timeout on one side the link suddenly stops working (it makes sense too, a timeout error occurs when you have not received an answer within the timespan you determine with the timeout parameter), and it goes back alive as soon as you increase it again. I've also noticed this effect is hardware dependent, some devices are able to answer faster than others
Related
I have a serial Python program, Linux environment (Raspbian / Raspberry Pi), that uses a serial port via a USB-to-serial adapter. I need to handle a situation when the user unplugs the USB adapter and then reinsert it.
The problem is that, on reconnect, the ttyUSB0 becomes ttyUSB1 and so the port is no longer found. However, if I stop the Python program (keyboard interrupt) and again unplug and reinsert the USB adapter, then the port goes back to ttyUSB0 (and so I can start over again). This can happen only when the Python program is stopped.
I tested the program in a flip-flop mode (and it seems to be working) in order to use ttyUSB1 when ttyUSB0 is no longer found and then vice versa, use ttyUSB0 back in case ttyUSB1 is no longer found, etc., but this looks like a weird solution to me.
Is there a better way to force pySerial to "forget" it has ever been connected to ttyUSB0 in case of error and release the current port to the system while the program is still running?
Here is a working flip-flop test program:
import serial
import time
p = "/dev/ttyUSB0"
while True:
error_flag = False
try:
s = serial.Serial(port=p, baudrate=9600, bytesize=8, parity="N", stopbits=1, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False, inter_byte_timeout=None)
except Exception as e:
error_flag = True
if "ttyUSB0" in str(e):
p = "/dev/ttyUSB1"
print ("port is now", p)
elif "ttyUSB1" in str(e):
p = "/dev/ttyUSB0"
print ("port is now", p)
else:
print (e) # none of the above
# if not error_flag, do whatever, etc.
time.sleep(1)
You could try creating a udev rule that would create a symbolic link to that USB device and then you would be able to use something like /dev/myUSB that would always stay the same for that specific USB device.
First, you will need to find some identifying information for the USB drive. Typing in lsusb should display some information that looks like:
Bus 001 Device 004: ID 0403:6001 Future Technology Devices International
In this example 0403 is the Vendor Id and 6001 is the Product Id.
Create a file named 99_usbdevice.rules (I don't think the name matters, just the directory):
sudo nano /etc/udev/rules.d/99_usbdevices.rules
Note that the directory above may be specific to Raspbian.
Copy/paste the line below into the file and save it:
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="myUSB"
Restart your Raspberry Pi or unplug the USB and reinsert it. There should now be a /dev/myUSB entry that you can use the same way you would the ttyUSB# entry.
If the opened port file is closed when unplugged (or on error handler), then the port name will not change on subsequent connection of the USB device. If left open then it will create a different name each time.
Do not forget to close the file descriptor of /dev/ttyUSB0 as soon as you detect that the user unplugged the USB adapter (read or write with error), and before the reinsertion.
If you properly close the device, the ttyUSB1 device will never appear. On the other hand, you can see in some cases also ttyUSB2, ttyUSB3, and so on, if all the previous ttyUSBx's are blocked because not closed.
I have a simple Python script to communicate with a micro-controller (STM32F103C8T6) using the serial port. I'm using pySerial to write a couple of 44-bytes messages at a time.
[...]
serial = serial.Serial(serial.tools.list_ports.comports()[0].device, 115200)
packet0 = bytearray(INSERT_RELEVANT_44-BYTES)
packet1 = bytearray(INSERT_RELEVANT_44-BYTES)
serial.write(packet0)
time.sleep(0.1) # Delay between communications
serial.write(packet1)
[...]
I had to insert a delay between the communications, otherwise it wouldn't work. My reasoning is that for a baud rate of 115200 bps the messages should take 44*8/115200 = ~0,003 seconds to be sent, thus this should be the minimum ideal interval between sending the packets. The code, however, doesn't work for anything smaller than 0,1.
Why? Am I missing something? I suppose there is some delay due to the operating system and the USB, but it shouldn't account for ~0,7 seconds. How can I optimize this to use the minimum possible delay?
Rather then calculating a nominal delay based on the UART link, you could simply poll the serial driver to determine whether the Tx buffer is empty:
serial.write(packet0)
while serial.outWaiting() > 0 :
pass
serial.write(packet1)
This has the advantage of accounting automatically for any latency, software overhead and buffer limitations anywhere in the chain of application code, library, driver, USB-Serial bridge. It will not however solve any problem with the STM32 serial I/O implementation, you should probably address the root problem of why the data cannot be streamed, which is most likely down to poor implementation of the at the STM32 device end.
I'm trying to use pyserial for reading out some bluetooth sensor data via rfcomm0.
Setting up the connection using the linux rfcomm command and its rfcomm.conf is working fine. But after a reading attempt using pyserial the device is freezing after a few seconds (until that poing delivering correct data!) and only useable after a complete device reset. Using direct connection with a FTDI level shifter my code is working fine without problems.
rfcomm.conf:
rfcomm0 {
Automatically bind the device at startup
bind no;
device 98:D3:31:70:23:90;
channel 1;
comment "Example Bluetooth device";
}
Command being used for setting up the connection
# rfcomm -i hci0 connect 0
Connected /dev/rfcomm0 to 98:D3:31:70:23:90 on channel 1
Relevant python code snippet (looped heavily with magic algorithms read(1)-ing bytes):
ser = serial.Serial('/dev/rfcomm0')
start_frame = ser.read(1)
(...)
Python trace when when deadlocking:
serialposix.py(443): if not self._isOpen: raise portNotOpenError
serialposix.py(444): read = bytearray()
serialposix.py(445): while len(read) < size:
serialposix.py(446): ready,_,_ = select.select([self.fd],[],[], self._timeout)
Seems as the linux host bluetooth device is stopped delivering data. When I'm attaching a FTDI directly to the bluetooth sensor it' still sending data.
Why is it doing this?! Do I need to flush some magic bluetooth buffers? I even tried two different bluetooth adapters.
On SF i found this article:
https://askubuntu.com/questions/114171/why-is-dev-rfcomm0-giving-pyserial-problems
But using BluetoothSocket and replacing read(1) with recv(1) only 50% of the received data is correct, rest containing garbage (upper solutions gives me 100% data but dying after ~5 seconds).
Any suggestions?
Thank you for helping!
Okay, nailed that beast down:
The whole thing dies when the rfcomm0 buffer on the host side is empty.
read(1)-ing from empty rfcomm0 buffer = deadlock without comeback
Solution:
Fine: Looking if there's some data in the buffer- when yes, read it
Ugly: Use some time.delay() and hope for god that there's some data
I am attempting to receive data from the qu-bot at http://www.qu-bot.com. The robot has an ATML atmega16 microcontroller. I have written a program that runs on the robot which outputs data to its serial port. The program however stops whenever I connect to the controller. I tested this by adding a beep statement. The robot beeps as long as the program is running. The beeping stops when I connect to the robot. I tried qu-bo support and they suggested disabling the dtr flag on the serial port. I did that but no joy.
Is there anything else I can try?
[start of code running on the qu-bot]
Note:
This is written in some kind of proprietary variant of C which they call quick c.
// This code displays the uart functions.
int main(void)
{
INIT();
UART_INIT(57600);
UART_PRINT("HELLO!!\n");
DELAYS(1);
UART_PRINT("MY NAME IS QU-BOT.\n");
DELAYS(1);
UART_PRINT("HELLO!!\n");
UART_PRINT("YOU ARE USING UART SAMPLE CODES.\n");
while(1)
{
UART_PRINT("test\n");
BEEP();
DELAYS(60);
}
}
now for my python serial port reading program. I have tried this program both on raspbian and on windows 7 64bit. I am pasting the windows version. The raspbian version has a different name for the linux.
import serial
import time
ser=serial.Serial()
ser.port=8
ser.baudrate=57600
ser.setDsrDtr(False)
print 'initialized'
flag = ser.isOpen()
if flag:
print 'port already open.'
pass
else:
ser.open() # opening the port 'ser' that was just created to receive data
time.sleep(0.5)
print 'ready to read'
print ser
ser.write('a')
s=ser.read(4)
print s
ser.close()
Pranav
P.S. I have consulted the following links amongst others.
<https://learn.sparkfun.com/tutorials/terminal-basics/all>
<http://www.plainlystated.com/2013/06/raspberry-pi-serial-console/>
<http://elinux.org/RPi_Serial_Connection>
<https://learn.sparkfun.com/tutorials/terminal-basics/all>
Based on my experience in serial communications with microcontrollers the most likely cause of this problem is that when you connect the serial cable to the robot it causes a phantom byte (due to electrical noise that happens when you make the connection) to look like it's coming from the controller - either this or the controller is sending a legitimate byte to the robot. In either case it is likely that the arrival of a byte at the robot's serial port (even if it was only electrical noise mistaken to be a data byte - this is a very common occurrence) caused the robot's microcontroller to invoke the UART receive interrupt. If you don't have an appropriate UART receive handler (ISR - Interrupt Service Routine) written and linked into the correct interrupt vector then your robot's microcontroller can go off into "deep space" upon the detection of the first incoming serial data byte - and make your robot appear to "hang". If you intend to do "polled" serial communications (your code manually checks for received bytes in its main loop) instead of interrupt-driven (hardware detection of an incoming byte causes your UART Rx ISR to be invoked) then all you have to do is to disable UART interrupts and your problem should go away.
All right, so I am positive my Arduino circuit is correct and the code for it. I know this because when I use the serial monitor built into the Arduino IDE and send 'H' an LED lights up, when I send 'L' that LED turns off.
Now I made a Python program
import serial
ser = serial.Serial("COM4",9600)
ser.write("H")
When I run the code the LED blinks on for a second then goes back off.
However when I do each of these lines separately in the shell it works just like it is supposed to.
Any ideas?
When you open the serial port, this causes the Arduino to reset. Since the Arduino takes some time to bootup, all the input goes to the bitbucket (or probably to the bootloader which does god knows what with it). If you insert a sleep, you wait for the Arduino to come up so your serial code. This is why it works interactively; you were waiting the 1.5 seconds needed for the software to come up.
I confirmed that opening the serial port resets my Arduino Uno; I flashed a program which will blink the LED from the setup() routine -- calling open("/dev/ttyACM0") was sufficient to trigger the reset. This is IMHO a confusing and undocumented wrinkle in the serial support.
I had the same problem and it works if I add a delay of about 2 seconds from opening the serial connection to writing on it, 1 second was not enough.
Just to make it a bit more clear I'll modify the code so everyone can see what needs to be added!
import serial
import time
ser = serial.Serial("COM4",9600)
time.sleep(3)
ser.write("H")
Adding in a sleep statment helps to let the serial open up without any problems!
Assuming you are using an Arduino Uno
The USB port and the Uno serial bus exposed on pins 1 and 0 share the same RX/TX lines. I suggest getting a USB to TTL adapter like the one here so that you can communicate to the Arduino without using the USB port. The Arduino IDE has its own method for disengaging from the USB driver such that a virtual serial port can be created. Have your Ardunio use SoftwareSerial instead.
Here is an example I found on the internet where somebody had clashing bus issues.