How do you chose which slave to control in PyModbus? - modbus

So I'm doing a project where I have a Raspberry Pi 4 as master controlling 4 actuators with Modbus. I'm using PyModbus (with python 3) and I've been using code like this to test the Pi (with a PC slave simulator).
client = ModbusClient(method = "rtu", port="/dev/ttySC0", stopbits = 1, bytesize = 8, parity = 'E' baudrate= 19200)
connection = client.connect()
print(connection)
result = client.read_input_register(1, 1, unit= 0x01)
#client.write_register(address, value, **kwargs)
It's been great so far. But now I've switched from testing with the simulator to testing on the real actuators and I've only just now realized that there doesn't seem to be a way to "switch" the slave you read/write to. Considering I'm aiming for 4 slaves this is a problem.
So after searching online didn't reveal an answer I'm hoping someone here has a solution. I'd rather like to avoid using something other than PyModbus since it took awhile to find a Modbus library that worked for me.

Related

How to fix a python "pyverbs" application with RDMA write getting a not acknowledge (NACK)

Im currently working on a RoCE (RDMA over Converged Ethernet) python application with the library pyverbs. First, i want to do a simple loopback test with an RDMA Write. I tested the setup with ib_write_bw from perftest, which worked like a charm.
This is my setup:
OS: Ubuntu 20.04.05 LTS
Kernel: 5.15.0-56-generic
NIC: Mellanox ConnectX-5 MC516A-GCA_Ax 50Gbe dual-port QSFP28
I'm developing the application with a jupyter notebook. The ports are connected together with a QSFP28 cable. I set up a "client" and "server" on the same system. Both use one port of the NIC. The client performs the "RDMA Write" action. In the future, metadata will be exchanged over tcp, but for ease of debugging i exchange metadata locally in the same notebook.
Now i was able to perform an "RDMA Write" action and capture the packets.
Captured RDMA packets
I keep getting not acknowledeges (NACK) from the "server". The RDMA Write packet look correct to me. It got the right payload and the headers are the same as i configured (i can post it if it would help).
I got three ideas in my head, why it would work.
wrong server memory adress/rkey used for rdma write
missing flags at server memory allocation
missing/wrong flags at server queue pair modification
I tried all different combinations of flags and values at the queue pair modification and memory allocation.
I start the NIC in the shi
ell with
sudo mst start
Then i run my python application. I posted some codesnipped below, which i am not sure i implemented those right.
Server and client qp modification init to RTR
gid_index = 0
port_num = 1
server_attr.qp_state = e.IBV_QPS_RTR
server_attr.path_mtu = e.IBV_MTU_4096
server_attr.rq_psn = 0
server_attr.min_rnr_timer = 12
server_attr.max_dest_rd_atomic = 10
server_attr.dest_qp_num = client_qp.qp_num
server_attr.qp_access_flags = e.IBV_ACCESS_LOCAL_WRITE | e.IBV_ACCESS_REMOTE_READ | e.IBV_ACCESS_REMOTE_WRITE
server_gr = GlobalRoute(dgid=client_ctx.query_gid(port_num=port_num,index=gid_index), sgid_index=gid_index)
server_ah_attr = AHAttr(gr=server_gr, is_global=1, port_num=1)
server_attr.ah_attr = server_ah_attr
server_qp.to_rtr(server_attr)
client_attr.qp_state = e.IBV_QPS_RTR
client_attr.path_mtu = e.IBV_MTU_4096
client_attr.rq_psn = 0
client_attr.min_rnr_timer = 12
client_attr.max_dest_rd_atomic = 10
client_attr.dest_qp_num = server_qp.qp_num
client_attr.qp_access_flags = e.IBV_ACCESS_LOCAL_WRITE | e.IBV_ACCESS_REMOTE_READ | e.IBV_ACCESS_REMOTE_WRITE
client_gr = GlobalRoute(dgid=server_ctx.query_gid(port_num=port_num,index=gid_index), sgid_index=gid_index)
client_ah_attr = AHAttr(gr=client_gr, is_global=1, port_num=port_num)
client_attr.ah_attr = client_ah_attr
client_qp.to_rtr(client_attr)
Client qp modification RTR to RTS
client_attr.qp_state = e.IBV_QPS_RTS
client_attr.timeout = 14
client_attr.retry_cnt = 7
client_attr.rnr_retry = 7
client_attr.sq_psn = 0
client_attr.max_rd_atomic = 10
RDMA Write instruction to client
client_sge = SGE(client_mr.buf,len(SEND_STRING),client_mr.lkey)
send_wr = pwr.SendWR(num_sge = 1, sg = [client_sge],opcode=e.IBV_WR_RDMA_WRITE)
send_wr.set_wr_rdma(rkey = server_mr.rkey, addr = server_mr.buf)
client_qp.post_send(send_wr)
sleep(1)
print(server_mr.read(len(SEND_STRING),0))
If there is someone with knowledge in RMDA/RoCE/Pyverbs, i would be glad for some help. I don't have any prior knowledge in those topics. This is why i have choosen to write an application in python. I have knowledge in C, but python is for me much more convient for prototyping :)
Thank for your help!

Raspberry Pi GPIO stops working after some time

when using the GPIO library on the Raspberry PI and having an example code like this:
while True:
GPIO.setup(21, GPIO.OUT)
pwm = GPIO.PWM(21, 50)
pwm.start(0)
for i in range(0, 101, 2):
pwm.ChangeDutyCycle(i)
time.sleep(0.03)
for i in range(100, -1, -2):
pwm.ChangeDutyCycle(i)
time.sleep(0.03)
pwm.stop()
GPIO.cleanup(21)
time.sleep(1)
The code might stop suddenly after a while. No error, just no changes via pwm are recognized any longer. Anyone got an idea why this is?
This issue has been mentioned here in the old sourceforge repo:
https://sourceforge.net/p/raspberry-gpio-python/tickets/111/
https://sourceforge.net/p/raspberry-gpio-python/tickets/94/
Its because the GPIO library by default creates a new pthread for every call without cleaning up afterwards, but the number of threads might be limited to a number of 250 or so.
I created a fork of this repo (https://github.com/wuestkamp/raspberry-gpio-python) which solves this and contains instructions on how to use this on your PI.

LPUART1 not working on STM32L476 (based on VisualGDB)

Hi I am developed a board based on the Discovery L476 board (STM32L476VGT6) using MBED and after porting it to VisualGDB everything works great. The only thing that doesn't work is LPUART1. I hooked it to PB10(LPUART1_RX), PB11(LPUART1_TX) but whenever I declare the port in my code and download it, the program hangs and doesn't even start:
Serial RS232(PB_11, PB_10);
If I remove this line, the code works great (but I can't use this port)
I changed the pin definitions in PeripheralPins.c so PB10 and PB11 will function as the LPUART TX and RX pins: (I added the lines)
const PinMap PinMap_UART_RX[] = { {PB_10, LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_LPUART1)},
//
const PinMap PinMap_UART_TX[] = { {PB_11, LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_LPUART1)},
but it still doesn't work. Any ideas?
See https://github.com/ARMmbed/mbed-os/issues/5389, the baud rate needs to be set at [sys_clk / 4096 ... sys_clk / 3]. Sys clock on this device is running at 80MHz. You could fix it in the HAL for this board until a real fix is deployed.

serial monitoring method to test communication via com ports without a serial communication device

I have a Verilog code simulated and synthesized on ISE design toolkit. I've got an FPGA spartan 6 device which is to be used for the implementation. But there is a problem with the device (probably a power issue) which makes the device unavailable in any of the COM ports when I connected it to my PC. So I want to check whether my Matlab code which I made for serial communication through the device does the desired job. So I need a method to test serial communication via any of the COM ports without connecting a serial com device to the PC. Is there any such method that I can Tx Rx serial data from Matlab to COM ports? Any software or any other method would be highly appreciated :)
I found a way to test Matlab serial communication using virtual serial ports.
Download "Freeware Virtual COM Ports Emulator" from: http://freevirtualserialports.com/
I installed it in Windows 10, and it's working (as trial).
Add a pair of two serial ports:
Execute the following Matlab code sample to verify it's working:
s3 = serial('COM3','BaudRate',115200);
s4 = serial('COM4','BaudRate',115200);
fopen(s3);
fopen(s4);
fwrite(s3, uint8([1, 2, 3, 4, 5]));
%fprintf(s3, '12345');
pause(0.1);
RxBuf = fread(s4, 5)
fclose(s3);
delete(s3);
clear s3
fclose(s4);
delete(s4);
clear s4
The output is:
RxBuf =
1
2
3
4
5
Bypassing the problem "it only stays for a single test session".
There is a problem when creating a pair of virtual ports using the software, it only stays for a single test session.
I guess it's a problem with the COM port emulation software.
The following solution, is not a good practice (and not a true solution).
Declare the serial object as global, keeping the object persistent.
Create the serial object only if it's not created.
Don't delete and don't clear the serial object.
See the following code sample:
global s3 s4
if isempty(s3)
s3 = serial('COM3','BaudRate',115200);
end
if isempty(s4)
s4 = serial('COM4','BaudRate',115200);
end
fopen(s3);
fopen(s4);
fwrite(s3, uint8([1, 2, 3, 4, 5]));
pause(0.1);
RxBuf = fread(s4, 5)
fclose(s3);
%delete(s3);
%clear s3
fclose(s4);
%delete(s4);
%clear s4
You can also look for a better virtual COM port software.
As Rotem suggested, if you need to communicate via serial line between 2 program of your PC you need a virtual COM port emulator.
It seems you are running on Windows OS so I would recommend a completely free emulator (not a trial one). For Windows I use com0com Null-modem emulator (from SourceForge).
In the example below I will show how to communicate with "another" device so Matlab will not handle both side of the communication. The other device will be simulated by a simple terminal. For windows I use RealTerm: Serial/TCP Terminal (also from SourceForge).
Setup:
Execute the setup of both program with all default options. by default com0com will create a virtual pair COM3/COM4 but if these port already exist on your system the program may assign other numbers. Check the numbers before you run the example. (it will also create a CNCA0/CNCB0 pair but you can ignore this one for now).
For RealTerm, once installed (don't forget to activate the server registration at the end of the setup, it should be ticked by default though), it will look like below. Keep all default options, just set the port number and the baud rate if they need to be changed.
Test MATLAB -> Terminal
You are ready to send Ascii characters or binary values from MATLAB to your device. The animation below shows you an example of both option:
you can click on the picture to see it full size. It is running in loop so you may want to wait until it restart from the beginning.
Test Terminal -> MATLAB
Below animation shows you how to test the communication in the other way:
Don't forget to tick [CR] [LF] on RealTerm when you send Ascii characters and want to use the '%s' format specifier on MATLAB, as it needs these characters to detect the end of the string.
Note:
If you have another terminal program that you are more used too, it
will work the same.
If the RealTerm option does not suit you, or if you want to handle
both sides of communication from Matlab, then you can use the code
provided by Rotem in his first answer. Just install com0com but
ignore all the RealTerm part.

Problems using a MCP3008 and a MCP23S17 on SPI with WebIOPi

I'm very new to WebIOPi and I'm trying my first tests. First of all I apologize for my english.
I'm trying to get to work a RPi with a MCP3008 on CE0 and a MCP23S17 on CE1 with SPI bus.
My problem is that devices only work when connected on CE1 (so, when 23017 is on CE0 I am not able to set pins to be inputs or outputs and to set it on 1 or 0, but 3008 is on CE1 and I see its levels changing. When - vice versa - 23017 is on CE1 it is fully functional, but 3008 outputs stay still).
Due to this, I think it is not an hardware issue (I don't have much expertise in electronics, but luckily I don't build my circuits by myself :) ), I think it is a problem in WebIOPi config. Here is my WebIOPi config:
[DEVICES]
mcp1 = MCP23S17 chip:1 slave:0x27
adc0 = MCP3008 chip:0
I only added these two lines to my config file.
I did not touch anything else of my original WebIOPi installation.
In this case (adc0 fully functional, mcp1 not working), when loading the WebIOPi devices monitor I see adc0 levels working good and mcp1 pins randomly changing between being a input and an output and from 0 and 1.
May it be a config error?
Use python and spidev module instead! Look my answer on another thread for function for the mcp3008 chip.