How can I read the response to a Modbus read request? - python

For a project that uses the Raspberry Pi 4, with a special shield as Modbus master I've succesfully written to a register, but I'm having trouble reading from a register. I'm using the Simply Modbus Slave program to test and can see that the request seems to go over well, but I haven't been able to print any data on my Raspberry Pi.
Currently I'm suspecting that the command I use from modbus-tk (cst.READ_INPUT_REGISTERS) send the Read Input Registers command, but doesn't actually receive the response.
I'm therefore seeking help to capture these values, or alternatively save everything received from the slave and find the values among this data.
Also, it could be nice to perhaps print everything sent and received so I can see what's happening in the communication.
This is the current code that I'm using:
## To install dependencies:
## sudo pip3 install modbus-tk
##################################################################################################
import serial
import fcntl
import os
import struct
import termios
import array
import time
#import modbus lib
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus as modbus
#import modbus_tk.modbus_rtu as modbus_rtu
from modbus_tk import modbus_rtu
# RS485 ioctls define
TIOCGRS485 = 0x542E
TIOCSRS485 = 0x542F
SER_RS485_ENABLED = 0b00000001
SER_RS485_RTS_ON_SEND = 0b00000010
SER_RS485_RTS_AFTER_SEND = 0b00000100
SER_RS485_RX_DURING_TX = 0b00010000
# rs 485 port
ser1 = serial.Serial("/dev/ttySC0",19200)
#ser1 = serial.Serial("/dev/ttySC0",9600)
ser2 = serial.Serial("/dev/ttySC1",9600)
def rs485_enable():
buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding
#enable 485 chanel 1
fcntl.ioctl(ser1, TIOCGRS485, buf)
buf[0] |= SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
buf[1] = 0
buf[2] = 0
fcntl.ioctl(ser1, TIOCSRS485, buf)
#enable 485 chanel 2
fcntl.ioctl(ser2, TIOCGRS485, buf)
buf[0] |= SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
buf[1] = 0
buf[2] = 0
fcntl.ioctl(ser2, TIOCSRS485, buf)
#end of rs485_enable():
if __name__ == '__main__':
logger = modbus_tk.utils.create_logger("console")
rs485_enable()
#set modbus master
master = modbus_rtu.RtuMaster(
serial.Serial(port= '/dev/ttySC0',
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1,
xonxoff=0)
)
master.set_timeout(5.0)
master.set_verbose(True)
logger.info("connected")
read = logger.info(master.execute(1, cst.READ_INPUT_REGISTERS, 4, 1))
print(read)
My code is an edited version of my shield's demo code (rtumaster.py) found here (Software/Test Codes/MODBUS).
Thank you in advance

Related

Save UDP packets from x amount of seconds in a pcap file

I am trying to save UDP data packets which I receive from a sensor to my PC through ethernet connection. I want to save the UDP data in the form of a pcap file.
So far I have written the following code to save 1 packet of data. I ran my code then opened up the saved pcap file using wireshark and compared it to the output from the raw wireshark data. Lines 0000 - 0010 from packet 1 are the same but line 0020 is different. test.pcap only has 42 bytes captured whereas from wireshark packet 1 has 1236 bytes captured.
Correct me if I am wrong but I think this is due to my code not giving enough time to collect all the data from the packet. I was hoping to get help in modifying my code below so that it saves the pcap file in time intervals. For example a pcap file will be saved every 5 seconds.
from scapy.all import wrpcap, Ether, IP, UDP
pkts = [Ether(src=" ", dst=" ") / IP(src=" ", dst=" ") / UDP(src=" ", dst=" ")]
wrpcap('test.pcap', [pkts])
Try this code, let me know if you are still getting less packets:
#!/usr/bin/env python3
import sys
import struct
import os
import argparse
from scapy.all import sniff, sendp, hexdump, linehexdump, get_if_list, get_if_hwaddr
from scapy.all import Packet, IPOption
from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
from scapy.all import IP, TCP, UDP, Raw
from scapy.layers.inet import _IPOption_HDR
from scapy.all import raw
from scapy.all import bytes_hex
import hashlib
import pcapng.blocks as blocks
from pcapng import FileWriter
counter = 1
def get_if():
ifs=get_if_list()
iface=None
for i in get_if_list():
if "enp1s0f1" in i:
iface=i
break;
if not iface:
print("Cannot find eth0 interface")
exit(1)
return iface
def main():
global counter
ifaces = [i for i in os.listdir('/sys/class/net/') ]
iface = get_if()
print(("sniffing on %s" % iface))
sys.stdout.flush()
writer = FileWriter(args.outfile, shb)
orig_packets = sniff(filter='tcp and port 5201',iface = iface)
for packet in orig_packets:
spb = shb.new_member(blocks.SimplePacket)
spb.packet_data = bytes(packet)
writer.write_block(spb)
print("C=",counter)
counter=counter+1
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("outfile", type=argparse.FileType("wb"))
args = parser.parse_args()
shb = blocks.SectionHeader(
options={
"shb_hardware": "artificial",
"shb_os": "python",
"shb_userappl": "python-pcapng",
})
idb = shb.new_member(
blocks.InterfaceDescription,
link_type=1,
options={
"if_description": "Hand-rolled",
"if_os": "Python",
"if_filter": [(0, b"tcp port 5201 and host 192.168.1.3")],
},)
main()

Get RSSI of non connected bluetooth device

I am currently using raspberry pi and want to get RSSI of a non-connected Bluetooth address.
I am using
import bluetooth
result=bluetooth.lookup_name('XX:XX:XX:XX:XX:XX',timeout=5)
if(result !=None):
print("user near")
else:
print("user far")
but I want to be a little more precise and go to the else block in a closer distance and hence I need an RSSI value. Please help. I am new with raspberry and Python.
(I am working in python3)
Getting the RSSI value on a Raspberry Pi is supported by the BlueZ device API.
In the example below I have used pydbus as the library to access BlueZ's D-Bus API. This example scans for 60 seconds and writes the device address and RSSI value to a file. You could modify the code to take an action when a particular address and RSSI value is found.
from datetime import datetime
from pathlib import Path
import pydbus
from gi.repository import GLib
discovery_time = 60
log_file = Path('/home/pi/device.log')
def write_to_log(address, rssi):
"""Write device and rssi values to a log file"""
now = datetime.now()
current_time = now.strftime('%H:%M:%S')
with log_file.open('a') as dev_log:
dev_log.write(f'Device seen[{current_time}]: {address} # {rssi} dBm\n')
bus = pydbus.SystemBus()
mainloop = GLib.MainLoop()
class DeviceMonitor:
"""Class to represent remote bluetooth devices discovered"""
def __init__(self, path_obj):
self.device = bus.get('org.bluez', path_obj)
self.device.onPropertiesChanged = self.prop_changed
rssi = self.device.GetAll('org.bluez.Device1').get('RSSI')
if rssi:
print(f'Device added to monitor {self.device.Address} # {rssi} dBm')
else:
print(f'Device added to monitor {self.device.Address}')
def prop_changed(self, iface, props_changed, props_removed):
"""method to be called when a property value on a device changes"""
rssi = props_changed.get('RSSI', None)
if rssi is not None:
print(f'\tDevice Seen: {self.device.Address} # {rssi} dBm')
write_to_log(self.device.Address, rssi)
def end_discovery():
"""method called at the end of discovery scan"""
mainloop.quit()
adapter.StopDiscovery()
def new_iface(path, iface_props):
"""If a new dbus interfaces is a device, add it to be monitored"""
device_addr = iface_props.get('org.bluez.Device1', {}).get('Address')
if device_addr:
DeviceMonitor(path)
# BlueZ object manager
mngr = bus.get('org.bluez', '/')
mngr.onInterfacesAdded = new_iface
# Connect to the DBus api for the Bluetooth adapter
adapter = bus.get('org.bluez', '/org/bluez/hci0')
adapter.DuplicateData = False
# Iterate around already known devices and add to monitor
print('Adding already known device to monitor...')
mng_objs = mngr.GetManagedObjects()
for path in mng_objs:
device = mng_objs[path].get('org.bluez.Device1', {}).get('Address', [])
if device:
DeviceMonitor(path)
# Run discovery for discovery_time
adapter.StartDiscovery()
GLib.timeout_add_seconds(discovery_time, end_discovery)
print('Finding nearby devices...')
try:
mainloop.run()
except KeyboardInterrupt:
end_discovery()
If you need to install the gi.repository library then follow the "Installing the system provided PyGObject" for Debian instructions at: https://pygobject.readthedocs.io/en/latest/getting_started.html#ubuntu-getting-started
Bluepy library looks beneficial for RaspberryPI. Dont forget you should run like
"sudo python3 name.py" from terminal.
For more info: https://github.com/IanHarvey/bluepy/tree/master/docs
from bluepy.btle import Scanner
while True:
try:
#10.0 sec scanning
ble_list = Scanner().scan(10.0)
for dev in ble_list:
print("rssi: {} ; mac: {}".format(dev.rssi,dev.addr))
except:
raise Exception("Error occured")

PN-RT packet malformed

I tried with that codes to comunicate with Ethernet/Profinet protocol.I found that type of codes. But when I run to the program with that line-1
And i did not get any error. My program running but when i watching the wireshark side my connection information said-2
1
sudo python3 discovery.py
2
Wireshark malformed packet PN-RT
Yes it know that is Profinet protocol, but why is the malformed ? How can i fix ?
That is my python code:
#!/usr/bin/env python
import binascii
from socket import *
from fcntl import ioctl
import struct
import fcntl, struct
value='!6s6sH'
s=socket(AF_PACKET, SOCK_RAW)
s.bind(('enp2s0',1))
class EtherPacket:
def __init__(self, dst='25:36:73:32:74:48', src='c3:82:c5:b8:c2:81', protocol=0x8892):
self.dst = dst # Destination MAC
self.src = src # Source MAC
self.protocol = protocol # Protocol Types
self.raw ="" # Raw Data
self.assemble_eth_feilds()
def assemble_eth_feilds(self):
# Assemble All Feilds Of Ether Packet
self.raw = struct.pack( \
value.encode('ascii'),\
binascii.unhexlify(self.dst.replace(":","")),\
binascii.unhexlify(self.src.replace(":","")),\
self.protocol,\
)
return self.raw
def main():
pkt = EtherPacket()
s.sendto(pkt.raw, ('enp2s0' , 0 ))
if __name__=='__main__':
main()

Turtlebot subscriber pointcloud2 shows color in Gazebo simulator but not in robot

I am subscribing to topic "/camera/depth/points" and message PointCloud2 on a turtlebot (deep learning version) with ASUS Xtion PRO LIVE camera.
I have used the python script below under the gazebo simulator environment and i can receive x, y, z and rgb values successfully.
However, when i run it in the robot, the rgb values are missing.
Is this a problem of my turtlebot version, or camera or is it that i have to specify somewhere that i want to receive PointCloud2 type="XYZRGB"? or is it a sync problem? Any clues please thanks!
#!/usr/bin/env python
import rospy
import struct
import ctypes
import sensor_msgs.point_cloud2 as pc2
from sensor_msgs.msg import PointCloud2
file = open('workfile.txt', 'w')
def callback(msg):
data_out = pc2.read_points(msg, skip_nans=True)
loop = True
while loop:
try:
int_data = next(data_out)
s = struct.pack('>f' ,int_data[3])
i = struct.unpack('>l',s)[0]
pack = ctypes.c_uint32(i).value
r = (pack & 0x00FF0000)>> 16
g = (pack & 0x0000FF00)>> 8
b = (pack & 0x000000FF)
file.write(str(int_data[0])+","+str(int_data[1])+","+str(int_data[2])+","+str(r)+","+str(g)+","+str(b)+"\n")
except Exception as e:
rospy.loginfo(e.message)
loop = False
file.flush
file.close
def listener():
rospy.init_node('writeCloudsToFile', anonymous=True)
rospy.Subscriber("/camera/depth/points", PointCloud2, callback)
rospy.spin()
if __name__ == '__main__':
listener()
The contents of Published topics are determined by the software that provides them - i.e. the drivers for your camera. To fix this you therefore need to get the right driver and use the topic that it says contains the required information.
You can find recommended drivers for your cameras on the ROS wiki or on some community websites - like this. In your case, the ASUS devices should use openni2 and set depth_registration:=true - as documented here.
At this point, /camera/depth_registered/points should now show the combined xyz and RGB point cloud. To use it, your new listener() code should look something like this:
def listener():
rospy.init_node('writeCloudsToFile', anonymous=True)
# Note the change to the topic name
rospy.Subscriber("/camera/depth_registered/points", PointCloud2, callback)
rospy.spin()

Finding Bluetooth low energy with python

Is it possible for this code to be modified to include Bluetooth Low Energy devices as well? https://code.google.com/p/pybluez/source/browse/trunk/examples/advanced/inquiry-with-rssi.py?r=1
I can find devices like my phone and other bluetooth 4.0 devices, but not any BLE. If this cannot be modified, is it possible to run the hcitool lescan and pull the data from hci dump within python? I can use the tools to see the devices I am looking for and it gives an RSSI in hcidump, which is what my end goal is. To get a MAC address and RSSI from the BLE device.
Thanks!
As I said in the comment, that library won't work with BLE.
Here's some example code to do a simple BLE scan:
import sys
import os
import struct
from ctypes import (CDLL, get_errno)
from ctypes.util import find_library
from socket import (
socket,
AF_BLUETOOTH,
SOCK_RAW,
BTPROTO_HCI,
SOL_HCI,
HCI_FILTER,
)
if not os.geteuid() == 0:
sys.exit("script only works as root")
btlib = find_library("bluetooth")
if not btlib:
raise Exception(
"Can't find required bluetooth libraries"
" (need to install bluez)"
)
bluez = CDLL(btlib, use_errno=True)
dev_id = bluez.hci_get_route(None)
sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)
sock.bind((dev_id,))
err = bluez.hci_le_set_scan_parameters(sock.fileno(), 0, 0x10, 0x10, 0, 0, 1000);
if err < 0:
raise Exception("Set scan parameters failed")
# occurs when scanning is still enabled from previous call
# allows LE advertising events
hci_filter = struct.pack(
"<IQH",
0x00000010,
0x4000000000000000,
0
)
sock.setsockopt(SOL_HCI, HCI_FILTER, hci_filter)
err = bluez.hci_le_set_scan_enable(
sock.fileno(),
1, # 1 - turn on; 0 - turn off
0, # 0-filtering disabled, 1-filter out duplicates
1000 # timeout
)
if err < 0:
errnum = get_errno()
raise Exception("{} {}".format(
errno.errorcode[errnum],
os.strerror(errnum)
))
while True:
data = sock.recv(1024)
# print bluetooth address from LE Advert. packet
print(':'.join("{0:02x}".format(x) for x in data[12:6:-1]))
I had to piece all of that together by looking at the hcitool and gatttool source code that comes with Bluez. The code is completely dependent on libbluetooth-dev so you'll have to make sure you have that installed first.
A better way would be to use dbus to make calls to bluetoothd, but I haven't had a chance to research that yet. Also, the dbus interface is limited in what you can do with a BLE connection after you make one.
EDIT:
Martin Tramšak pointed out that in Python 2 you need to change the last line to print(':'.join("{0:02x}".format(ord(x)) for x in data[12:6:-1]))
You could also try pygattlib. It can be used to discover devices, and (currently) there is a basic support for reading/writing characteristics. No RSSI for now.
You could discover using the following snippet:
from gattlib import DiscoveryService
service = DiscoveryService("hci0")
devices = service.discover(2)
DiscoveryService accepts the name of the device, and the method discover accepts a timeout (in seconds) for waiting responses. devices is a dictionary, with BL address as keys, and names as values.
pygattlib is packaged for Debian (or Ubuntu), and also available as a pip package.

Categories