I have a RPI with a USB CH340 dongle connected to a EM340 energy meter. It works fine with the code below.
When I connect 2 x EM340 energy meters I get the following error:
pi#raspberrypi:~ $ python3 modbus_test.py
Traceback (most recent call last):
File "modbus_test.py", line 21, in <module>
freq2 = instrument.read_register(0x0033,1) # Registernumber, number of decimals
File "/home/pi/.local/lib/python3.7/site-packages/minimalmodbus.py", line 486, in read_register
payloadformat=_Payloadformat.REGISTER,
File "/home/pi/.local/lib/python3.7/site-packages/minimalmodbus.py", line 1245, in _generic_command
payload_from_slave = self._perform_command(functioncode, payload_to_slave)
File "/home/pi/.local/lib/python3.7/site-packages/minimalmodbus.py", line 1330, in _perform_command
response, self.address, self.mode, functioncode
File "/home/pi/.local/lib/python3.7/site-packages/minimalmodbus.py", line 1867, in _extract_payload
raise InvalidResponseError(text)
minimalmodbus.InvalidResponseError: Checksum error in rtu mode: '6ý' instead of '\x99ò' . The response is: '\x01\x01\x00\x00\x166ý' (plain response: '\x01\x01\x00\x00\x166ý')
My code:
import minimalmodbus
import serial
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 1) # port name, slave address (in decimal)
#instrument.serial.port # 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.2 # seconds
instrument.address = 1 # this is the slave address number
#instrument.mode = minimalmodbus.MODE_ASCII # rtu or ascii mode
instrument.mode = minimalmodbus.MODE_RTU
instrument.clear_buffers_before_each_transaction = True
instrument.close_port_after_each_call = True
## Read temperature (PV = ProcessValue) ##
freq2 = instrument.read_register(0x0033,1) # Registernumber, number of decimals
txt = "Frekvens: {} Hz".format(freq2)
#0x0034
kwh_total = instrument.read_register(0x0400)
kwh_total_2 = instrument.read_register(0x0402)
txt2 = "Total forbrug: {0}.{1} kWh".format(kwh_total,kwh_total_2)
w_l1 = instrument.read_register(0x0012,1) # Registernumber, number of decimals
txt3 = "Nuværende forbrug: {} W".format(w_l1)
print(txt)
print(txt2)
print(txt3)
I have wired the 2xEM340 according to the https://web.evishine.dk/wp-content/uploads/2019/11/EM340-ENG.pdf page 3.
Any idea why I get Checksum error in rtu mode ?
As per the comments if you attach two Modbus RTU devices with the same ID in parallel then they will both respond to any request addressed to that Slave ID. The responses will probably collide which means your code will receive a garbled response (detected via the CRC).
The solution is to change the ID of one of the devices.
Related
I would like to control an actuator with a python script in MODBUS RTU
master. I tried to use the library minimalmodbus to communicate (write
bit, write & read registers) with my slave.
When I start my code, I have some errors. So, someone can I help me to find a solution?
My code:
import minimalmodbus
import os
import struct
import sys
import serial
import time
instrument = minimalmodbus.Instrument('/dev/ttyRS485', 1)
instrument.serial.port
instrument.serial.baudrate = 9600
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.bytesize = 8
instrument.serial.stopbits = 1
instrument.mode = minimalmodbus.MODE_RTU
instrument.serial.timeout = 0.05
modbus = instrument.write_bit(0x0427, 1)
print (modbus)
alarme = instrument.write_bit(0x0404, 1)
print (alarme)
alarme = instrument.write_bit(0x0404, 0)
print (alarme)
on = instrument.write_bit(0x0403, 1)
print (on)
home = instrument.write_bit(0x040B, 1)
print (home)
position = instrument.write_register(0x9900, 0, number_of_decimals=2,functioncode=16, signed=False)
print (position)
posi = instrument.write_register(0x9901, 6000, number_of_decimals=2,functioncode=16, signed=False)
print (posi)
Errors:
========================= RESTART: /home/pi/test.py =========================
None
None
None
None
None
None
Traceback (most recent call last):
File "/home/pi/.local/lib/python3.5/site-packages/minimalmodbus.py", line 2448, in _pack
result = struct.pack(formatstring, value)
struct.error: 'H' format requires 0 <= number <= 65535
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/pi/test.py", line 36, in <module>
posi = instrument.write_register(0x9901, 6000, number_of_decimals=2, functioncode=16, signed=False)
File "/home/pi/.local/lib/python3.5/site-packages/minimalmodbus.py",line 518, in write_register
payloadformat=_PAYLOADFORMAT_REGISTER,
File "/home/pi/.local/lib/python3.5/site-packages/minimalmodbus.py",line 1166, in _generic_command
payloadformat,
File "/home/pi/.local/lib/python3.5/site-packages/minimalmodbus.py",line 1514, in _create_payload
value, number_of_decimals, signed=signed
File "/home/pi/.local/lib/python3.5/site-packages/minimalmodbus.py", line 1991, in
_num_to_twobyte_string outstring = _pack(formatcode, integer)
File "/home/pi/.local/lib/python3.5/site-packages/minimalmodbus.py", line 2454, in _pack
raise ValueError(errortext.format(value, formatstring))
ValueError: The value to send is probably out of range, as the num-to-bytestring conversion failed.
Value: 600000 Struct format code is: >H
In response to your request in the comments for an alternative library, here is what I use to read modbus with the pymodbus library:
import pymodbus
from pymodbus.pdu import ModbusRequest
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from pymodbus.transaction import ModbusRtuFramer
client = ModbusClient(
method = 'rtu'
,port='/dev/tty.usbserial-AQ00BYCR'
,baudrate=38400
,parity = 'O'
,timeout=1
)
connection = client.connect()
registers = client.read_holding_registers(0,100,unit=1)# start_address, count, slave_id
print (registers.registers)
Note that in the above, the reading begins from address 0 and continues to address 100, for slave_id 1.
To write registers, do the following:
write = client.write_register(1,425,unit=1)# address = 1, value to set = 425, slave ID = 1
I'm trying to use pyfirmata to send and receive data between Arduino Uno R3 and my python program. At Arduino installed StandartFirmata sketch.
Code is:
from time import sleep
import serial
import pyfirmata
com_port_number = str(int(input('Введите номер COM-порта ')))
port = 'COM' + com_port_number # COM port number
print('Выбран порт COM ', port)
try:
board = pyfirmata.Arduino(port)
except serial.SerialException:
print('Не удается подключится к выбранному COM-порту')
com_port_number = str(int(input('Введите номер СОМ-порта')))
port = 'COM' + com_port_number
board = pyfirmata.Arduino(port)
sleep(1)
it = pyfirmata.util.Iterator(board)
it.start()
temp_list = []
potentiomentr = board.get_pin('a:0:o')
acid_control = board.get_pin('a:2:o')
stock_control = board.get_pin('a:3:o')
temperature_pin = board.get_pin('d:4:i') # well, this line is worked fine. Temperature sensor works correctly
in_connection_pc = board.get_pin('d:0:o') #but now i have InvalidPinDefError
triac = board.get_pin('d:6:o')
level = board.get_pin('d:8:i')
in_engine = board.get_pin('d:5:o')
in_triac = board.get_pin('d:10:o')
in_pump = board.get_pin('d:11:o')
drive_control = board.get_pin('d:12:o')
pump_control = board.get_pin('d:13:o')
while 1: # бесконечный цикл
a = temperature_pin.read()
b = in_connection_pc.write(1)
print(a)
list.append(a,b)
print(list)
sleep(3)
board.exit()
But i have some strange mistake:
Traceback (most recent call last):
File "C:/Users/lomil/PycharmProjects/Pyython_and_CSV_love/test_analog.py", line 22, in <module>
in_connection_pc = board.get_pin('d:0:i') #but now i have InvalidPinDefError??
File "C:\Users\lomil\Python_32\lib\site-packages\pyfirmata\pyfirmata.py", line 220, in get_pin
raise InvalidPinDefError('Invalid pin definition: UNAVAILABLE pin {0} at position on {1}'.format(pin_def, self.name))
pyfirmata.pyfirmata.InvalidPinDefError: Invalid pin definition: UNAVAILABLE pin d:0:i at position on COM1
When I commented all lines except
temperature_pin = board.get_pin('d:4:i')
It worked, but I can not understand what's wrong with other pins. They are totally good and worked fine when I wrote test sketch to Arduino.
The error message is actually complaining: UNAVAILABLE pin d:0:i at position on COM1. On the Arduino Uno (and most Arduinos) digital pins 0 and 1 are dual-use pins and are also used for communications over the serial port, aka COM port.
Firmata works by constantly communicating over the serial port so you can't actually use digital pins 0 and 1 for anything else while using Firmata.
So whatever wire you have plugged into your Arduino on digital pin 0, you need to move to another unused digital pin, like pin 3. So, if you move that wire to digital pin 3, then in code you would now need in_connection_pc = board.get_pin('d:3:o').
I am trying to use a variable to pass a string to the serial.Serial function in python. The following works the way I want it to:
port = serial.Serial("/dev/ttyAMA0", baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0)
However if I try to put the string into a variable like so and then call the function with the variable I get an error.
serialString = '"/dev/ttyAMA0",baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0'
port = serial.Serial(serialString)
Here is the error:
Traceback (most recent call last): File "./cncserver.py", line 34,
in
port = serial.Serial(serialString) File "/usr/lib/python2.7/dist-packages/serial/serialutil.py", line 260, in
init
self.open() File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 276, in
open
raise SerialException("could not open port %s: %s" % (self._port, msg)) serial.serialutil.SerialException: could not open port
"/dev/ttyAMA0",baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0:
[Errno 2] No such file or directory:
'"/dev/ttyAMA0",baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0'
I'm guessing it's something to do with the filepath in the string not being quoted properly but I've spent enough time trying to figure it out, can somebody help me here please?
Thanks,
Lorne
Here is my revised code:
#parameter settings of the serial port on the PI must match the control
serialPort = "/dev/ttyAMA0"
serialBaudRate = 9600
serialByteSize = serial.SEVENBITS
serialStopBits = serial.STOPBITS_ONE
serialParity = serial.PARITY_EVEN
serialTimeout = 3.0
port = serial.Serial(serialPort,serialBaudRate,serialByteSize,serialStopBits,serialParity,serialTimeout)
Here is the new error:
Traceback (most recent call last): File "./cncserver.py", line 39,
in
port = serial.Serial(serialPort,serialBaudRate,serialByteSize,serialStopBits,serialParity,serialTimeout)
File "/usr/lib/python2.7/dist-packages/serial/serialutil.py", line
250, in init
self.parity = parity File "/usr/lib/python2.7/dist-packages/serial/serialutil.py", line 344, in
setParity
if parity not in self.PARITIES: raise ValueError("Not a valid parity: %r" % (parity,)) ValueError: Not a valid parity: 1
port = serial.Serial("/dev/ttyAMA0", baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0)
is not the same as
port = serial.Serial('"/dev/ttyAMA0",baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0')
This is not a string, but function parameters that have been passed as key_pair values.
"/dev/ttyAMA0", baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0
sends 6 different parameters to the function, :-
/dev/ttyAMA0
baudrate as 9600
bytesize as serial.SEVENBITS
stopbits as serial.STOPBITS_ONE
parity as serial.PARITY_EVEN
timeout as 3.0
'"/dev/ttyAMA0",baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0'
sends only one parameter as
'"/dev/ttyAMA0",baudrate=9600,bytesize=serial.SEVENBITS,stopbits=serial.STOPBITS_ONE,parity=serial.PARITY_EVEN,timeout=3.0'
as for what you are trying to do,
initialize :
args = ("/dev/ttyAMA0",)
kwargs= {'baudrate':9600,'bytesize':serial.SEVENBITS,'stopbits':serial.STOPBITS_ONE,'parity':serial.PARITY_EVEN,'timeout':3.0}
and call them wherever you want
port = serial.Serial(*args, **kwargs)
I am writing a multi-threaded client/server program. It splits a large file into smaller files in its client side and sends the smaller files to the server concurrently.
The problem is that in every run, the server can only receive two of the smaller files (the first one and another random one). Meanwhile, I encounter the error: "[Errno 32] Broken pipe" in client side of the program in s.sendall(part). The error arises in every thread that starts to send one of the smaller files before reception of the first file (on the server). In other words, every thread that starts to send after the reception the first file (on the server) can complete its task.
I run each of the client and server codes on different computers (both have the following specification: Ubuntu 14.04 desktop, 64 bit, 16GiB ram)
Client side Error:
Traceback (most recent call last):
File "Desktop/File_transmission/Client.py", line 56, in sendSplittedFile
s.sendall(part)
File "/usr/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
error: [Errno 32] Broken pipe
Client.py:
import random
import socket
import time
import threading
import errno
import select
import File_manipulation
import sys, traceback
class Client:
nodesIpAddr = ["....", "...."] #Server = ....
dataPort = 45678
delay = 2
inputFileAddress = 'tosend.dat'
fileOutputPrefix = 'output'
fileOutputSuffix = ".dat"
bufferSize = 2048
max_size_splitted_file = 10*(2**20) # 10 MiB
def __init__ (self, my_ip):
self.ip = my_ip
def send(self, ip_toSend, dataPort):
print "\tSend function is runing."
totalNumSplittedFile = File_manipulation.split_file(Client.inputFileAddress, Client.fileOutputPrefix, Client.max_size_splitted_file , Client.bufferSize)
for i in range(0, totalNumSplittedFile):
thread_send = threading.Thread(target = self.sendSplittedFile, args = (ip_toSend, dataPort, Client.bufferSize, i, Client.fileOutputPrefix, totalNumSplittedFile))
thread_send.start()
def sendSplittedFile(self, ip_toSend, dataPort, bufferSize, fileNumber, fileNamePrefix, totalNumSplittedFile):
# Create a socket (SOCK_STREAM means a TCP socket)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
BUFFER_SIZE = bufferSize
try:
s.connect((ip_toSend, dataPort))
f = open(fileNamePrefix + '.%s' % fileNumber,'rb')
s.send(str(fileNumber) + " " + str(totalNumSplittedFile))
part = f.read(BUFFER_SIZE)
while (part):
s.sendall(part)
part = f.read(BUFFER_SIZE)
f.close()
s.sendall(part)
time.sleep(Client.delay)
s.sendall('EOF')
print "Done Sending."
print s.recv(BUFFER_SIZE)
s.close()
print "\tData is sent to ", ip_toSend,
except socket.error, v:
traceback.print_exception(*sys.exc_info())
s.close()
nodeIP = [(s.connect(('8.8.8.8', 80)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
n = Client(nodeIP)
n.send(n.nodesIpAddr[0], n.dataPort)
Server Side Error:
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
self.process_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 321, in process_request
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 649, in __init__
self.handle()
File "Desktop/File_transmissionServer.py", line 37, in handle
totalFileNumber = int(details[1])
ValueError: null byte in argument for int()
Server.py
import socket
import time
import threading
import errno
import select
import SocketServer
import File_manipulation
class ServerThreadHandler(SocketServer.BaseRequestHandler):
nodesIpAddr = ["....", "...."] #Server = ....
fileOutputPrefix = 'torec '
fileOutputSuffix = '.dat'
dataPort = 45678
delay = 3
maxNumClientListenedTo = 200
timeout_in_seconds = 5
bufferSize = 2048
totalFileNumber = 0 #Total number of splitted files. It should be set by the incoming packets
def handle(self):
BUFFER_SIZE = ServerThreadHandler.bufferSize # Normally 1024, but we want fast response
# self.request is the TCP socket connected to the client
data = self.request.recv(BUFFER_SIZE)
addr = self.client_address[0]
details = str(data).split()
currentFileNum = int(details[0])
totalFileNumber = int(details[1])
print '\tReceive: Connection address:', addr,'Current File Number: ', currentFileNum, 'Total Number of splitted files: ', totalFileNumber
f = open(ServerThreadHandler.fileOutputPrefix + '_Received.%s' % currentFileNum, 'wb')
data = self.request.recv(BUFFER_SIZE)
while (data and data != 'EOF'):
f.write(data)
data = self.request.recv(BUFFER_SIZE)
f.close()
print "Done Receiving." ," File Number: ", currentFileNum
self.request.sendall('\tThank you for data. File Number: ' + str(currentFileNum))
if __name__ == "__main__":
HOST, PORT = ServerThreadHandler.nodesIpAddr[0], ServerThreadHandler.dataPort # HOST = "localhost"
server = SocketServer.TCPServer((HOST, PORT), ServerThreadHandler)
# Activate the server; this will keep running until you interrupt the program with Ctrl-C
server.serve_forever()
I'm sending a list of values (e.g. 80,539,345,677) from Arduino to a Python app running on my RPi. I have not been successful in extracting the values and assigning them to respective variables or objects in the app.
Here's my code:
def read_values():
#if DEBUG:
print "reading arduino data"
ser = serial.Serial('/dev/ttyUSB0', 9600)
print "receiving arduino data"
ser_line = ser.readline()
print ser_line
ser.close()
ser_list = [int(x) for x in ser_line.split(',')]
ambientLight = ser_list[1]
print ambientLight
return ambientLight
What I'm getting from Python is:
reading arduino data
receiving arduino data
80,477,82,2
Traceback (most recent call last):
File "serialXivelyTest4c.py", line 77, in <module>
run()
File "serialXivelyTest4c.py", line 63, in run
ambientLight = read_values()
File "serialXivelyTest4c.py", line 27, in read_values
ser_list = [int(x) for x in ser_line.split(',')]
ValueError: invalid literal for int() with base 10: '8254\r80'
You can see that I'm getting values, but that they're being truncated. Can anyone please tell me where I'm going wrong here. Thanks so much.
I've never used an Arduino but here's how I read from serial with a different board. I used serial.
import streamUtils as su # see below
ser = su.connectPort("/dev/tty.SLAB_USBtoUART") # make sure you have the right port name
data = ""
while True:
try:
data = data + ser.read(1) # read one, blocking
time.sleep(1) # give it time to put more in waiting
n = ser.inWaiting() # look if there is more
if n:
data = data + ser.read(n) # get as much as possible
# I needed to save the data until I had complete
# output.
if data:
# make sure you have the whole line and format
else:
break
except serial.SerialException:
sys.stderr.write("Waiting for %s to be available" % (ser.name))
sys.exit(1)
sys.stderr.write("Closing port\n")
ser.close()
Here's the streamUtils.connectPort():
import serial
def connectPort(portname):
# connect to serial port
ser = serial.Serial()
ser.port = portname
ser.baudrate = 9600
ser.parity = serial.PARITY_NONE
ser.stopbits = serial.STOPBITS_ONE
ser.bytesize = serial.EIGHTBITS
ser.timeout = 15 # need some value for timeout so the read will end
try:
ser.open()
except serial.SerialException:
sys.stderr.write("Could not open serial port %s\n" % (ser.name))
sys.exit(1)
return (ser)