modbus-tk getting Modbus Error: Exception code = 3 - python

I am trying to use modbus-tk to serially communicate with a device via Modbus RTU over a RS-485 network.
To understand how to use modbus-tk, I am trying to query:
Input register: Year of Manufacture
Register address: 0x7543 = 30019
Device: 1
Data type: 16-bit integer
Value = 2019 (verified using a free master modbus simulator).
Here is the code I use, based primarily on rtumaster_example.py.
Input:
#!/usr/bin/env python3
import time
from collections import namedtuple
from logging import Logger
from serial import Serial
from modbus_tk import modbus_rtu
import modbus_tk.defines as cst # cst = constants
from modbus_tk.utils import create_logger
PORT = "COM3"
SLAVE_NUM = 1
MODBUS_MASTER_TIMEOUT_SEC = 5.0
ModbusReg = namedtuple("ModbusInputRegister", ["name", "block_type", "address"])
year_of_manuf = ModbusReg(
"year of manufacture", cst.HOLDING_REGISTERS, 18
) # 0x7543 = 30019
logger = create_logger(name="console") # type: Logger
serial_ = Serial(PORT)
modbus_master = modbus_rtu.RtuMaster(serial_)
modbus_master.set_timeout(MODBUS_MASTER_TIMEOUT_SEC)
modbus_master.set_verbose(True)
time.sleep(2) # Per https://github.com/ljean/modbus-tk/issues/73#issuecomment-284800980
logger.info(
modbus_master.execute(
slave=SLAVE_NUM,
function_code=cst.READ_INPUT_REGISTERS,
starting_address=year_of_manuf.address,
)
)
Output:
2020-01-21 10:38:09,031 INFO modbus_rtu.__init__ MainThread RtuMaster COM3 is opened
2020-01-21 10:38:11,048 DEBUG modbus.execute MainThread -> 1-4-0-18-0-0-80-15
2020-01-21 10:38:11,077 DEBUG modbus.execute MainThread <- 1-132-3-3-1
---------------------------------------------------------------------------
ModbusError Traceback (most recent call last)
<ipython-input-2-9afaebcf3a35> in <module>
7 slave=SLAVE_NUM,
8 function_code=cst.READ_INPUT_REGISTERS,
----> 9 starting_address=year_of_manuf.address,
10 )
c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
37 ret = fcn(*args, **kwargs)
38 except Exception as excpt:
---> 39 raise excpt
40 finally:
41 if threadsafe:
c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
35 lock.acquire()
36 try:
---> 37 ret = fcn(*args, **kwargs)
38 except Exception as excpt:
39 raise excpt
c:\path\to\venv\lib\site-packages\modbus_tk\modbus.py in execute(self, slave, function_code, starting_address, quantity_of_x, output_value, data_format, expected_length)
312 # the slave has returned an error
313 exception_code = byte_2
--> 314 raise ModbusError(exception_code)
315 else:
316 if is_read_function:
ModbusError: Modbus Error: Exception code = 3
It looks like this exception is for if the slave has returned an error
What do you think I am doing wrong? I am new to this library.
What I Have Read
Can't connect to slave with Python's modbus_tk, I am using pyserial >= 3.1
And every other question under the modbus-tk tag, some posts in the Google Groups, and the repo's examples + README.md
Device Specifics
Device: SST Sensing's OXY-LC-485
Modbus RTU, 9600/8-N-1
User Guide (section 7.1.2.1 contains set of input registers)
Device is plugged into Windows machine that I run this Python script
Packages
I am using Python 3.6 on Windows 10.
pyserial==3.4
modbus-tk==1.1.0
**EDIT 1**
Per #Brits comment, I updated my registry addresses to be correct function codes and data frame addresses.
**EDIT 2**
Updated question since I am getting a different error after more correct library usage.

My problem was a lack of understanding of the Master.execute method. I did not realize that the quantity_of_x parameter needed to be made non-zero. A special thanks to #Brits for helping point out several key problems I had with my original code.
When the arg function_code == defines.READ_INPUT_REGISTERS, quantity_of_x means the amount of H (C type, unsigned short, length = 2-byte = 16-bit, source) to read (source code modbus.py, line 138).
Since the device's data type for the year_of_manuf register is 16-bit unsigned integer, the correct change was to add quantity_of_x=1 as an argument.
Functional Input:
logger.info(
modbus_master.execute(
slave=SLAVE_NUM,
function_code=cst.READ_INPUT_REGISTERS,
starting_address=year_of_manuf.address,
quantity_of_x=1,
)
)
Output:
2020-01-21 18:42:05,520 DEBUG modbus.execute MainThread -> 1-4-0-18-0-1-145-207
2020-01-21 18:42:05,560 DEBUG modbus.execute MainThread <- 1-4-2-7-227-250-137
2020-01-21 18:42:05,562 INFO <ipython-input-1-2d4d0280e33d>.<module> MainThread (2019,)

Related

DS18B20, W1ThermSensor, Raspberry pi Zero W, and Python3.9 - Does not consistently read sensor

Before I start, I have browsed the "similar questions" section before writing this and could not see one that matched a situation like mine. If one is found, please let me know and I will mark it as "answered" if it is in fact similar. I am a .net full stack developer by profession, i only recently started dabbling in Python and Electrical Engineering as a hobby.
I am creating an Automated Aquaponics Control system, a part of the project reads the temp of the grow bed media and with the input of various other sensors, recalculates the frequency at which the pump cycles to flood the bed. I am using a DS18B20 with Python3.9 and the W1ThermSensor v2.0.0a2 library. Here is the init and first of several functions for the sensor. I have the w1thermsensor as a property of the class instead of inheritance just during the initial testing, since it is easier to manipulate the code this way for me.
#!/usr/bin/env python3
from w1thermsensor import W1ThermSensor, Sensor, Unit
from datetime import datetime
import os
import numpy
import traceback
class DS18B20:
def __init__(self, min_temp=18, max_temp=26):
self.sensor = W1ThermSensor()
self.temp_string = "{dt} : Sensor: {id} :: {temp_c}C - {temp_f}F"
self.temp_c = 0.00
self.temp_f = 0.00
self.is_active = False
self.is_alert = False
self.min_temp = min_temp
self.max_temp = max_temp
self.values = [0.00, 0.00, 0.00, 0.00, 0.00]
self.value = 0.00
def start(self):
if self.sensor is None:
return False
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
# Set baseline for values Average
self.is_active = True
self.monitor()
self.values = [self.temp_c, self.temp_c, self.temp_c, self.temp_c, self.temp_c]
self.value = numpy.average(self.values)
This issue that I am running into is that it will have one of 3 issues:
Raises w1thermsensor.errors.NoSensorFoundError
Raises w1thermsensor.errors.SensorNotReadyError
Returns no value in the temp_c property after calling get_temperature()
I looked into this a bit more, If i load up the IDLE in Terminal using the 'sudo python3' command I can enter the following commands and it works no problem:
sudo python3
>>> from w1thermsensor import W1ThermSensor, Sensor
>>> import time
>>> temp_sensor = W1ThermSensor(Sensor.DS18B20)
>>> while True:
... print(str(round(temp_sensor.get_temperature()))
... time.sleep(2)
and it works without issue. I also try the 'cat' command
cd /sys/bus/w1/devices
cd 28-3c01d607414b
cat w1_slave
94 01 55 05 7f a5 81 66 5b : crc=5b YES
94 01 55 05 7f a5 81 66 5b t=25250
The stacktrace shows that it is throwing the Errors when it is calling the W1Termsensor() function in "init()". My question is, is it my code or implementation that is causing the issue, or is it something else. My sleep is set to 2 seconds in the hope that I am just catching it in the middle of an update. Any help would be a big help.
Addtional Info:
the DS18B20 is wired to a separate 5v power source, the capacitor it to stableize the voltage since there is a 5v relay and a LED array on the same 5v power rail of the power supply.
5v+ -------------+---------VCC------
| |
4.7 Kohm |
| |
GPIO4 ---------------------DQ = 1uf polCap
|
|
|
GND ----------------------GND-------
I have double-checked that I have 1-wire enabled.

Python libusb : lacking privilege for USB access

I am trying to connect to my USB bar-code reader, but bump into a lack of rights to open it under Mac OS 10.14.6.
I am trying to use this library: https://github.com/vpelletier/python-libusb1 the lack of documentation is a bit annoying.
import usb1
VENDOR_ID = 8208
PRODUCT_ID = 30264
INTERFACE = 0
with usb1.USBContext() as context:
handle = context.openByVendorIDAndProductID(
VENDOR_ID,
PRODUCT_ID,
skip_on_error=False,
)
if handle is None:
# Device not present, or user is not allowed to access device.
print("no handle")
with handle.claimInterface(INTERFACE):
print("handle = ", handle)
# Do stuff with endpoints on claimed interface.
while True:
data = handle.bulkRead(ENDPOINT, BUFFER_SIZE)
# Process data...
Is there anyone having a clue on what to do ?
The error is :
USBErrorAccess Traceback (most recent call last)
<ipython-input-1-217d9879b039> in <module>
12 # Device not present, or user is not allowed to access device.
13 print("no handle")
---> 14 with handle.claimInterface(INTERFACE):
15 print("handle = ", handle)
16 # Do stuff with endpoints on claimed interface.
/Applications/anaconda3/lib/python3.7/site-packages/usb1/__init__.py in claimInterface(self, interface)
1307 """
1308 mayRaiseUSBError(
-> 1309 libusb1.libusb_claim_interface(self.__handle, interface),
1310 )
1311 return _ReleaseInterface(self, interface)
/Applications/anaconda3/lib/python3.7/site-packages/usb1/__init__.py in mayRaiseUSBError(value, __raiseUSBError)
131 ):
132 if value < 0:
--> 133 __raiseUSBError(value)
134 return value
135
/Applications/anaconda3/lib/python3.7/site-packages/usb1/__init__.py in raiseUSBError(value, __STATUS_TO_EXCEPTION_DICT, __USBError)
123 __USBError=USBError,
124 ):
--> 125 raise __STATUS_TO_EXCEPTION_DICT.get(value, __USBError)(value)
126
127 def mayRaiseUSBError(
USBErrorAccess: LIBUSB_ERROR_ACCESS [-3]
There is some hints :
seems like we should set up rights somewhere, but these pages are about ubuntu :
=> https://askubuntu.com/questions/978552/how-do-i-make-libusb-work-as-non-root
=> https://github.com/vpelletier/python-libusb1/issues/34
here are some things about Mac OS, but it is unclear to me :
=> IOCreatePlugInInterfaceForService returns mysterious error
=> OpenCV command line app can't access camera under macOS Mojave

MinimalModbus read_registry error

I am trying to read registry information from a modbus device using MinimalModbus. However, every time I attempt to read registry 40003 which has a value of 220 I receive this error:
raise ValueError('The slave is indicating an error. The response is: {!r}'.format(response))
ValueError: The slave is indicating an error. The response is: '\x01\x83\x02Àñ'
I know there is a value in 40003 and I am following the communication documents for the device. Here is my code:
import minimalmodbus
import serial
gas = minimalmodbus.Instrument('COM5', 1)
gas.serial.baudrate = 9600
gas.serial.bytesize = 8
gas.serial.parity = serial.PARITY_NONE
gas.serial.stopbits = 1
gas.serial.timeout = 0.05
gas.mode = minimalmodbus.MODE_RTU
temp = gas.read_register(40003, 1)
print (float(temp))
I have this problem for every registry and I cannot find information regarding Àñ.
The problem was the registry number 40003. I guess the modbus protocol doesn't require the full registry number, so I changed it to temp = gas.read_register(3, 1)

Iphython Viewer crash when I view some model

i don't know why but when I'm showing some model on ipython, the viewer crash and send to me an error like XgeModule whas shut down. Why?
This is the code:
def CIRCLE(r):
dom = INTERVALS(2*PI)(50)
def circ(p):
return ([r*COS(p[0]),r*SIN(p[0])])
return SOLIDIFY(MAP(circ)(dom))
vetro = COLOR([1,1,0,0.7])(CIRCLE(0.4))
Dom1D = INTERVALS(1)(60)
Dom2D = PROD([Dom1D,Dom1D])
profile = BEZIER(S1)([[0,0,0],[0.4,0,0],[0.4,0,0.4]])
rot_domain = PROD([INTERVALS(1)(60),INTERVALS(2*PI)(60)])
mapping = ROTATIONALSURFACE(profile)
closing = COLOR(BLACK)(MAP(mapping)(rot_domain))
fanale = R([2,3])(PI/2)(S([1,2,3])([2.5,2.5,2.5])(STRUCT([T([3])([0.4])vetro),closing])))
DRAW(fanale)
And this is the error on Ubuntu Terminal:
X Error of failed request: BadWindow (invalid Window parameter)
Major opcode of failed request: 40 (X_TranslateCoords)
Resource id in failed request: 0x3c00011
Serial number of failed request: 606
Current serial number in output stream: 606
XgeModule::shutdown

Sending a TradeCaptureReport in QuickFIX using Python but cannot receive any TradeCaptureReportAck

I wrote an initiator using the Python API for QuickFIX 1.13.3.
It simply sends a message of type TradeCaptureReport (code: AE) to an acceptor.
I should receive a TradeCaptureReportAck (indeed, I receive it if I use another client), but nothing comes back.
Using Wireshark I could see that logon and logout are performed correctly and the heartbeat is kept properly as well.
The Application I have written is defined as follows:
import sys
import time
import thread
import quickfix as fix
import quickfix44 as fix44
from datetime import datetime
class Application (fix.Application):
orderID = 0
execID = 0
tradeID = 0
global settings
def onCreate (self, sessionID):
self.sessionID = sessionID
print ("Application created - session: " + sessionID.toString ())
def onLogon (self, sessionID):
print ("Logon")
def onLogout (self, sessionID):
print ("Logout")
def onMessage (self, message, sessionID):
print (message)
def toAdmin (self, message, sessionID):
msgType = fix.MsgType ()
message.getHeader ().getField (msgType)
if (msgType.getValue () == fix.MsgType_Logon):
message.setField (fix.Password (settings.get (self.sessionID).getString ("Password")))
message.setField (fix.ResetSeqNumFlag (True))
def fromAdmin (self, message, sessionID):
pass
def toApp (self, message, sessionID):
pass
def fromApp (self, message, sessionID):
pass
def genOrderID (self):
self.orderID += 1
return repr (self.orderID)
def genTradeReportID (self):
self.tradeID += 1
return repr (self.tradeID)
def genExecID (self):
self.execID += 1
return repr (self.execID)
def run (self):
time.sleep (5)
self.queryEnterOrder ()
time.sleep (5)
def queryEnterOrder (self):
print ("\nTradeCaptureReport (AE)\n")
trade = fix.Message ()
trade.getHeader ().setField (fix.BeginString (fix.BeginString_FIX44))
trade.getHeader ().setField (fix.MsgType (fix.MsgType_TradeCaptureReport))
trade.setField (fix.TradeReportTransType (fix.TradeReportTransType_NEW)) # 487
trade.setField (fix.TradeReportID (self.genTradeReportID ())) # 571
trade.setField (fix.TrdSubType (4)) # 829
trade.setField (fix.SecondaryTrdType (2)) # 855
trade.setField (fix.Symbol ("MYSYMBOL")) # 55
trade.setField (fix.LastQty (22)) # 32
trade.setField (fix.LastPx (21.12)) # 31
trade.setField (fix.TradeDate ((datetime.now ().strftime ("%Y%m%d")))) # 75
trade.setField (fix.TransactTime ((datetime.now ().strftime ("%Y%m%d-%H:%M:%S.%f"))[:-3])) # 60
trade.setField (fix.PreviouslyReported (False)) # 570
group = fix44.TradeCaptureReport ().NoSides ()
group.setField (fix.Side (fix.Side_SELL)) # 54
group.setField (fix.OrderID (self.genOrderID ())) # 37
group.setField (fix.NoPartyIDs (1)) # 453
group.setField (fix.PartyIDSource (fix.PartyIDSource_PROPRIETARY_CUSTOM_CODE)) # 447
group.setField (fix.PartyID ("CLEARING")) # 448
group.setField (fix.PartyRole (fix.PartyRole_CLEARING_ACCOUNT)) # 452
trade.addGroup (group)
group.setField (fix.Side (fix.Side_BUY)) # 54
group.setField (fix.OrderID (self.genOrderID ())) # 37
group.setField (fix.NoPartyIDs (1)) # 453
group.setField (fix.PartyIDSource (fix.PartyIDSource_PROPRIETARY_CUSTOM_CODE)) # 447
group.setField (fix.PartyID ("CLEARING")) # 448
group.setField (fix.PartyRole (fix.PartyRole_CLEARING_ACCOUNT)) # 452
trade.addGroup (group)
fix.Session.sendToTarget (trade, self.sessionID)
Which is run thanks to the following snippet:
file = sys.argv[1]
settings = fix.SessionSettings (file)
application = Application ()
storeFactory = fix.FileStoreFactory (settings)
initiator = fix.SocketInitiator (application, storeFactory, settings)
initiator.start ()
application.run ()
initiator.stop ()
Update
The exchange of messages goeas as follows (I = Initiator, A = Acceptor):
1. I ---------- LOGON -----------> A
2. I <--------- LOGON ------------ A
3. I ---- TradeCaptureReport ----> A
4. I ---------- LOGOFF ----------> A
5. I <--------- LOGOFF ----------- A
As you can spot, between 3. and 4. a message of type TradeCaptureReportAck is missing.
Any hint is most welcome!
Try implementing your fromAdmin and fromApp method, you should be seeing your return FIX messages here. Check if your TradeCaptureReportAck reaches here and confirm they follow the same calls as logon, logout and heartbeat. And implement your onMessage method, or check if you receive any FIX message here. This should be your place where you should be seeing the ACK return message.
If your message sequence number is not our of sync, then they never sent the act you are looking for! Instead of logging off, you should at least wait around for a heartbeat to check the sequence number before logging off.

Categories

Resources