I am failing to communicate with a device using minimalmodbus, Modbus RTU.
I have connected the device to a raspberry pi via a usb to rs-485 adapter (FTDI chip) A to A and B to B.
The device configurations are as follows:
Port settings:
Baud rate: 9600
Parity: No
Stop bits: 1
Silent Interval: 70 ms.
Transmission Mode: RTU
Interface 2 Wire RS485
Connector of ECM: DB9
Connections: A on pins 1, 4. B on pins 6, 9
Modbus Holding registers (simplified for the purpose of this example)
Swapped Floating point format:
40001 (address 0) Parameter 1
40003 (address 2) Parameter 2
I have tried using raw serial commands to communicate with the device, as well as using pymodbus, and now on my latest attempt minimal modbus. Please see below my code attempt using minimalmodbus.
Also, i know the serial adapter works as i use it for various other serial sensors. Only difference is that i am working with modbus now.
import minimalmodbus
instrument = minimalmodbus.Instrument('/dev/tty/USB1',1)
instrument.debug = True
instrument.handle_local_echo = True # The serial device echos back every write, hence this
instrument.serial.baudrate = 9600
instrument.serial.timeout = 1
value = instrument.read_register(0,2)
print value
I expected to receive some sort of reply even if an error or incorrect, but i am getting nothing. the debug output of minimalmodbus says "IOError: No communication with the instrument(no answer)"
Is there any obvious mistakes in my code vs the sensor requriements? I confirmed my wiring with the manufacturer, and is correct.
Quoting from the manual of your cable:
The USB-RS485-WE cable allows for local echo to be enabled/disabled by changing a bit in the FT232R
EEPROM. If CBUS4 in the EEPROM is set for “PWRON#” local echo is enabled. If CBUS4 in the EEPROM is
set for “TXDEN” local echo is disabled.
Users can set this with MPROG from www.ftdichip.com
The default for the local echo is disabled (CBUS4 set for “TXDEN)
Phew! lots of info in there. According to the thread of comments on your question you activated the software echo handling on minimalModbus because otherwise your routine would not wait for the response from the device on the other end. That makes one think whether your cable has the local echo enabled or disabled.
Fortunately, you can check that very easily. As the manual says just go get MPROG here. Extract and run (yeap, you need Windows for this tool, but you can run it on a Virtual Machine).
Connect your cable to the USB port (don't forget to send the device to the virtual machine if you are running one) and select Tools-->Read and Parse on MPROG.
This is what you should get:
Make sure you have TXEN selected on box C4. According to the manual, you should have TXEN by default, if you see PWRON# it means the local echo is active. Disable it and you should be good to go to use Modbus.
I don't see an obvious error from your side. That's rather difficult since you are working with hardware.
I'll provide some of my code. I used it for a prototype, which was a Raspberry Pi 3B with a USB to RS485 converter (This one).
from modbus import minimalmodbus
import serial
import time
from mqtt.client import Client as mqtt_client
class Slave:
def __init__(self, serial_port: str = '/dev/ttyUSB0', slave_id: int = 5,
baudrate: int = 38400, byte_size: int = 8,
parity: str = serial.PARITY_NONE, stopbits: int = 1,
timeout: float = 1.0):
self.slave = minimalmodbus.Instrument(serial_port, slave_id)
self.slave.serial.baudrate = baudrate
self.slave.serial.bytesize = byte_size
self.slave.serial.parity = parity
self.slave.serial.stopbits = stopbits
self.slave.serial.timeout = timeout
self.registers = ["header", "zero", "life_beat",
"test_int", "test_float"]
self.output = mqtt_client()
...
When I read a register I used e.g.:
self.slave.read_register(2)
//or
self.slave.read_float(5)
I'm not sure which python version I used. I think it was 3.6.x.
Related
i wrote a script in python for serial communication between my M5Stack Stick C (like raduino) and the raspberry pi.
all work fine. i can send "X","Y" or "Z" from raspberry py to the stick and he will reply the value (G-Force) back to the raspi! so far so good
Codes:
Python on raspy:
import serial
import time
import threading
ser = serial.Serial('/dev/rfcomm5') #init serial port
input_line = []#init input char array
def process_data(_data):
#called every time a sream is terminated by \n
#and the command string is ready to use
command = convert(_data)
print(command)
def convert(s): #convert the char list in a string
new = "" #init string to append all chars from char array
for x in s: # traverse in the string
new += str(x)
return new # return string
def processIncomingByte(inByte):#adding incoming chars to input_line
global input_line# globalize the input_line
if(inByte == '\n'):#if \n is incoming, end the chararray and release process data method
process_data(input_line)
input_line = []#reset input_line for next incoming string
elif(inByte == '\r'):
pass
else:#put all incoming chars in input_line
input_line.append(inByte)
while True:
while(ser.in_waiting > 0):#while some data is waiting to read....
processIncomingByte(ser.read())#.... process bytes whit method
ser.write(b'X\n')
time.sleep(0.5)
before the script work, i have to manually bind the m5Stak Stick-C over Blueman
to /dev/Rfcomm5. it work just fine over GUI or Console....
but now i would like to connect the stick via python to rfcomm5 (just by know the MAC adress, will be found in a config file later on...)
i startet to investigate a bit, but the more i research the more confused i am!!
i read some stuff over sockets and server-client aproaches. over a seperated script and so on....
i tested this code:
from bluetooth import *
target_name = "M5-Stick-C"
target_address = None
nearby_devices = discover_devices()
for address in nearby_devices:
if (target_name == lookup_name( address )):
target_address = address
break
if (target_address is not None):
print ("found target bluetooth device with address ", target_address)
else:
print ("could not find target bluetooth device nearby")
and indeed it found the device (just testing)!
but do i realy need to make a second script/process to connect to from my script?
is the the M5stack Stick-C the server? (i think so)
im so confused about all that stuff. i coded a lot, but never whit sockets, server-client stuff.
basically the communication (server/client?) works.
i just need to connect the device i found in the second script via macadress to rfcomm5 (or whatever rfcomm).
do i need a bluetooth socket? like in this example
https://gist.github.com/kevindoran/5428612
isnt the rfcomm the socket or am i wrong?
There are a number of layers that are used in the communication process and depending where you tap into that stack will depend what coding you need to do. The other complication is that BlueZ (the Bluetooth stack on linux) changed how it works over recent times leaving a lot of out of date information on the internet and easy for people to get confused.
With two Bluetooth devices, they need to establish a pairng. This is typically a one off provisioning step. This can be done with tools like Blueman or on the command line with bluetoothctl. Once you have a pairing established between your RPi and the M5Stack Stick, you shouldn't need to discover nearby devices again. Your script should just be able to connect if you tell it which device to connect to.
The M5Stack stick is advertising as having a Serial Port Profile (SPP). This is a layer on top of rfcomm.
There is a blog post about how this type of connection can be done with the standard Python3 installation: http://blog.kevindoran.co/bluetooth-programming-with-python-3/
My expectation is that you will only have to do the client.py on your RPi as the M5Stack Stick is the server. You will need to know its address and which port to connect on. Might be some trial and error on the port number (1 and 3 seem to be common).
Another library that I find helpful for SPP, is bluedot as it abstracts away some of the boilerplate code: https://bluedot.readthedocs.io/en/latest/btcommapi.html#bluetoothclient
So in summary, my recommendation is to use the standard Python Socket library or Bluedot. This will allow you to specify the address of the device you wish to connect to in your code and the underlying libraries will take care of making the connection and setting up the serial port (as long as you have already paired the two devices).
Example of what the above might look like with Bluedot
from bluedot.btcomm import BluetoothClient
from signal import pause
from time import sleep
# Callback to handle data
def data_received(data):
print(data)
sleep(0.5)
c.send("X\n")
# Make connection and establish serial connection
c = BluetoothClient("M5-Stick-C", data_received)
# Send initial requests
c.send("X\n")
# Cause the process to sleep until data received
pause()
Example using the Python socket library:
import socket
from time import sleep
# Device specific information
m5stick_addr = 'xx:xx:xx:xx:xx:xx'
port = 5 # This needs to match M5Stick setting
# Establish connection and setup serial communication
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
s.connect((m5stick_addr, port))
# Send and receive data
while True:
s.sendall(b'X\n')
data = s.recv(1024)
print(data)
sleep(0.5)
s.close()
I am moving code from Python XBee library to Digi's Python XBee library and I am unable to locate syntax in the docs on how to send a API frame to a remote XBee. I am using S1 and S1 Pro devices where the "local" device is attached to a Beaglebone and the "remote" devices are standalone out in the wild.
I have the basic framework down:
from digi.xbee.devices import XBeeDevice, RemoteXBeeDevice, XBee64BitAddress
PORT = '/dev/ttyO1'
BAUD = 9600
API_FRAME = 'long-hex-string-here'
local_xbee = XBeeDevice(PORT, BAUD)
local_xbee.open()
# Instantiate a remote XBee device object.
remote_xbee = RemoteXBeeDevice(local_xbee, XBee64BitAddress.from_hex_string("0013A20040DD7DCD"))
# Transmit frame to remote_xbee - unsure of correct method and syntax
# perhaps XBeePacket.create_packet(hex string here)
# local_xbee.send_data(remote_xbee, "api frame here?") ??
local_xbee.close()
but I am unable to locate syntax for how to transmit my constructed API frame. Based upon the Introduction section in the docs I presume that is the correct approach. I am not interested in broadcasting to all devices on the network but rather unicast communication.
I have some older model of the source when I could get it to work.
…
I have some WiFi Xbee modules that I used w/ some converter boards (base boards). I used it to attach the communication of the Xbee from one computer to another, e.g. random desktop to BeagleBone Black via USB instead of UART. So, I would use the source, listed below, to attach my USB dongle for the Xbee communication from the BBB to the other field module.
Their I/O stuff can be found here: https://github.com/digidotcom/xbee-python/tree/master/examples/io.
Also...changing just some of their lines in their source w/ the USB dongle WiFi adapter boards proved valuable in signaling LEDs and other sensors.
Oh and you will need what they are now calling Carrier Boards. It is the adapter board I just typed out. So, if you have already got a Carrier Board, use lsusb as the command in Linux to find your USB "name."
So, for instance, if lsusb brings up /dev/ttyUSB0, then that is the port identification.
And you can use that section, from lsusb, to then change your xbee modules in the xtcu software from Digi.
…
from digi.xbee.devices import XBeeDevice
from digi.xbee.io import IOLine, IOMode
import time
import threading
# TODO: Replace with the serial port where your local module is connected to.
PORT = "/dev/ttyUSB0"
# TODO: Replace with the baud rate of your local module.
BAUD_RATE = 9600
REMOTE_NODE_ID = "Xbee_B"
IOLINE_IN = IOLine.DIO2_AD2
IOLINE_OUT = IOLine.DIO4_AD4
def main():
print(" +-----------------------------------------------+")
print(" | XBee Python Library Get/Set Remote DIO Sample |")
print(" +-----------------------------------------------+\n")
stop = False
th = None
local_device = XBeeDevice(PORT, BAUD_RATE)
try:
local_device.open()
print("local device: ", local_device.get_node_id())
# Obtain the remote XBee device from the XBee network.
xbee_network = local_device.get_network()
remote_device = xbee_network.discover_device(REMOTE_NODE_ID)
if local_device is None:
print("Could not find the remote device")
exit(2)
def io_detection_callback():
while not stop:
# Read the digital value from the input line.
io_value = remote_device.get_dio_value(IOLINE_IN)
print("%s: %s" % (IOLINE_IN, io_value))
# Set the previous value to the local output line.
local_device.set_dio_value(IOLINE_OUT, io_value)
time.sleep(2)
th = threading.Thread(target=io_detection_callback)
remote_device.set_io_configuration(IOLINE_IN, IOMode.DIGITAL_IN)
local_device.set_io_configuration(IOLINE_OUT, IOMode.DIGITAL_OUT_HIGH)
time.sleep(1)
th.start()
input()
finally:
stop = True
if th is not None and th.is_alive():
th.join()
if local_device is not None and local_device.is_open():
local_device.close()
if __name__ == '__main__':
main()
So, see the PORT = "/dev/ttyUSB0" section of the source?
This is where I attached my Xbee module to the Carrier Board and then attached the Carrier Board to the BBB by way of USB.
Um, this may not answer a question but give more insight as to how to handle Digi Devices/Modules.
I also think that if you want to venture in this direction of UART communication w/ Xbee and the BeagleBone Black, it may be more complicated. I will keep searching my text.
P.S. This book goes over some methods to connect, Experiment 10 and Experiment 16, your "BBB" to a UART, Xbee, and how to communicate. It is a bit too in depth to get all of the communication ideas from this book but this is it:
The Hands-on XBEE Lab Manual, Experiments that Teach you XBEE Wireless Communications – Jonathan A Titus
I'm currently trying to set up a communication with a power supply (RND 320-KA3005P) through RS232 with pyserial.
The Problem is that after sending "*DIN?", which should return manufacturer, model name and software version, the connection seems to receive nothing. Also no other command was successful. Using the included software, after connecting, a LabView program opens and the device is remote controllable, so I think the problem is in my code.
Code Example:
Connection parameters as described in the manual.
As end-of-line characters I tried "\r\n","\r","\n" which seems to solve many other problems, but here with no success.
import io
import serial
import time
import sys
ser = serial.Serial('COM4',
baudrate = 9600,
bytesize=8,
timeout=1,
stopbits = serial.STOPBITS_ONE,
parity = serial.PARITY_NONE,
xonxoff = False)
eol_char = '\r\n'
sio = io.TextIOWrapper(io.BufferedReader(ser),newline=eol_char)
while True:
sending = input("type:\n")
ser.write((sending + eol_char).encode('utf-8'))
time.sleep(0.2)
ans = sio.read()
sys.stdout.write('received: ' + str(ans))
print('\ntry again\n')
Which after entering the command gives:
type:
*DIN?
received:
try again
type:
Also when I run the script on the loopback
ser = serial.serial_for_url('loop://',timeout=1)
...
output:
type:
*DIN?
received: *DIN?
try again
type:
it seems to work fine. I'm using Windows 10 and a USB to RS232 converter. Does anyone have an idea about what I'm missing here?
Thanks in advance.
The solution I figured out is rather disappointing. Turns out that with a USB to RS232 converter cable, instead of the USB connection I used before, the commands to set and get values work as expected. The command '*DIN?', which I tried initially, which I got from the manual:), still gives no reaction - probably just not supported.
I believe the *DIN? command, found under "Functionality check" which should return: "Manufacturer, model name, software version.", is actually a typo. When all commands are later listed in the manual, number 11 says *IDN? and should return the KA3005P identification.
It is also ironic that the example they give under number 11 says: "* IDN?"...
I'm trying to detect the UART port names for a BananaPi M3 device running a Raspbian so I can do a simple test to send and receive some data on it. I connected the RX and TX pins of the UART connector with a simple jumper but I cannot detect the actual name of the ports. The pins are described here but I don't find exactly their names.
The tests I made so far without success:
ser = serial.Serial('/dev/ttyS1', 115200, timeout = 1)
num = ser.write("test\n")
ser2 = serial.Serial('/dev/ttyS2', 115200, timeout = 1)
data = ser2.read(100)
So, how exactly the UART port names can be detected.
Thank you
I am on a Windows 7 computer, trying to use Python to run a simple program on my Arduino to make it blink.
For some reason, whenever I attempt to run the script in PowerShell, I receive an error that the port is already open. I have tried restarting my computer, changing Arduinos, changing the USB port that I have plugged into, running a different program that I am quite sure closes the computers connection to the port. However, I am still getting the same error.
I am quite new to Python and Arduino, so there might be something stupidly easy that I am forgetting. I have asked a friend who is more experienced and he spoke of it being a problem with Windows.
Here is the relevant code. The class I used in Python is as follows.
import serial
import socket
import glob
import pickle
from time import sleep,time
class Ardustat():
def __init__(self):
self.port = ""
self.ser = serial.Serial()
self.mode = "serial"
self.debug = False
def findPorts(self):
"""A commands to find possible ardustat ports with no Arguments, """
return glob.glob("COM*")
def connect(self,port):
if self.mode == "serial":
self.ser = serial.Serial(port,57600)
self.ser.timeout = 1 # what does this do?
self.ser.open()
return "connected to serial"
And the Python code is
from ardustat_library_simple_dan import Ardustat
import time
serial_port = 'COM6'
ard = Ardustat()
#Connect to the irritating ardustat
ard.connect(serial_port)
for a in range(0,5):
ard.blink(serial_port)
time.sleep(.2)
The Arduino code is long, but in the setup there is the following code.
void setup()
{
//Startup Serial
Serial.begin(57600);
// Serial.println("Hi Dan!");
Is there anything wrong?
I can see a few reasons why this is not working:
You have your serial monitor running while you're running your script:
The serial port can only be open once on a given host, if you try to open it twice, the second program to open it will report it as already opened.
You did not open the serial connection on the Arduino side:
You say you run a blink sketch. If that's the default blink.ino, it does not open the serial port and your system may report the serial port is not openable (and I don't know Windows, but I wouldn't be surprised to see an inconsistent error happening)
You wrote your own blink sketch, but you did not put Serial.begin(115200); in the setup() of your sketch file.
You open the port twice (or more) in the Python code.
You shall have only one serial.open() statement in your code.
So first, check your code to be sure you do actually use the serial port from within the Arduino sketch. You can paste your code in your question so we can check that out.
Then, check your Python code to be sure you do actually open the serial port once and only once within your script.
Finally, be sure to not have another program opening the serial port while you're running your script, usually one tend to test the connection with Arduino's serial monitor, let it open and try to open the port using a script, which does not work. I saw that very often from beginners.
And I vote for... problem #3 \o/
class Ardustat():
def __init__(self):
self.port = ""
self.ser = serial.Serial() # Here you open the port once
self.mode = "serial"
self.debug = False
def findPorts(self):
"""A commands to find possible ardustat ports with no Arguments, """
return glob.glob("COM*")
def connect(self,port):
if self.mode == "serial":
self.ser = serial.Serial(port,57600) # Here you open the port twice
I did not check pySerial's source code, but I'm pretty sure serial.Serial() opens up the first serial port it finds available at its default speed. Therefore, as you don't close the connection from the first instance of self.ser, the second instance gets crazy saying "dude, it's already opened!".
That's why you'd better not add an abstraction (Artustat) over a correct abstraction (pyserial.Serial), but just use correctly the tools and do something like:
with serial.Serial(port, speed) as ser:
do_blink(ser)
where do_blink() is a function where you do your stuff. The with statement over serial.Serial will take care of the life of the serial port object (opening it when you enter the block, closing it as you exit the block).