How to send hid data to device using python / pywinusb? - python

I'm trying to use pywinusb to send output report to a pic18f4550. The device can receive data, and I've tested it with a C# application, which worked fine. Also, I can read data from the device with pywinusb just fine, but I have a problem trying to send data.
Here's the code I'm running:
from pywinusb import hid
filter = hid.HidDeviceFilter(vendor_id = 0x0777, product_id = 0x0077)
devices = filter.get_devices()
if devices:
device = devices[0]
print "success"
device.open()
out_report = device.find_output_reports()[0]
buffer= [0x00]*65
buffer[0]=0x0
buffer[1]=0x01
buffer[2]=0x00
buffer[3]=0x01
out_report.set_raw_data(buffer)
out_report.send()
dev.close()
It produces this error:
success
Traceback (most recent call last):
File "C:\Users\7User\Desktop\USB PIC18\out.py", line 24, in <module>
out_report.send()
File "build\bdist.win32\egg\pywinusb\hid\core.py", line 1451, in send
self.__prepare_raw_data()
File "build\bdist.win32\egg\pywinusb\hid\core.py", line 1406, in __prepare_raw_data
byref(self.__raw_data), self.__raw_report_size) )
File "build\bdist.win32\egg\pywinusb\hid\winapi.py", line 382, in __init__
raise helpers.HIDError("hidP error: %s" % self.error_message_dict[error_code])
HIDError: hidP error: data index not found

Here is my code and it works with an MSP430F chip running TI's datapipe USB stack. This is basically hid input and output endpoints that act as a custom data pipe allowing me to send 64 bytes in any format I want with the exception of the first byte being an ID number (defined by TI) decimal 63 and the second byte being the number of pertinent or useful bytes in the packet (64 byte max packet) with the first two bytes described above. It took me a while to figure this out mostly because of the lack of documentation. The few examples that come with pywinusb are hard to learn from at best. Anyways here is my code. It is working with my micro so this should help you.
filter = hid.HidDeviceFilter(vendor_id = 0x2048, product_id = 0x0302)
hid_device = filter.get_devices()
device = hid_device[0]
device.open()
print(hid_device)
target_usage = hid.get_full_usage_id(0x00, 0x3f)
device.set_raw_data_handler(sample_handler)
print(target_usage)
report = device.find_output_reports()
print(report)
print(report[0])
buffer = [0xFF]*64
buffer[0] = 63
print(buffer)
report[0].set_raw_data(buffer)
report[0].send()
One area that may be screwing you up is here:
out_report = device.find_output_reports()[0]
Try using "out_report = device.find_output_reports()" without the "[0]" at the end.
Then use
out_report[0].set_raw_data(buffer)
and finally
out_report[0].send()
Hope this helps you out.

HID is really powerful but nobody is using proper HID enumeration, HID provides a very flexible (not easy though) schema for describing the format on its reports.
For a simple device I'd recommend using a simple byte array usage to get started, this will give host applications to give context for your data items.
Anyway, raw reports here we go again...
Use starting_data = output_report.get_raw_data()[:] for any given output report, then change any 'raw' element directly.
Of course, ideally you'd have properly defined usage and you'd be able to change report items independently, without guessing bit widths and positions :-)

Related

Python PiCamera - How do I format the coordinates for the Exif?

I just got my first Raspberry Pi product last week. I got a Raspberry Pi Zero W, and a PiCamera. I created a program (to be ran each day by crontab) that will take a photo, store the photo locally, transfer a copy of the photo to a local server, make a CSV log of the photo, and make an easily readable text daily report. I've gotten almost everything working correctly, but I have one particular issue I can't seem to figure out.
I'm using the Python PiCamera library to capture the photos everyday, and I wanted to set some static GPS data to each photo's EXIF metadata. (The GPS data needs to be static because the Pi is suction cupped to a window and wont be moving for at least a year) The issue I need help with is that I do not know how EXACTLY I need to format my GPS data for PiCamera's exif_tag property. Here are some of the formats I've tried and have not given me appropriate results:
Attempt #1 - Float
camera.exif_tags["GPS.GPSLatitude"] = 41.1027
camera.exif_tags["GPS.GPSLongitude"] = -85.1362
I get this error:
Traceback (most recent call last):
File "main.py", line 13, in <module>
camera.capture(local_path)
File "/usr/lib/python3/dist-packages/picamera/camera.py", line 1418, in capture
encoder.start(output)
File "/usr/lib/python3/dist-packages/picamera/encoders.py", line 1125, in start
self._add_exif_tag(tag, value)
File "/usr/lib/python3/dist-packages/picamera/encoders.py", line 1097, in _add_exif_tag
ct.sizeof(mmal.MMAL_PARAMETER_EXIF_T) + len(tag) + len(value) + 1)
TypeError: object of type 'float' has no len()
Attempt #2 - String
camera.exif_tags["GPS.GPSLatitude"] = "41.1027"
camera.exif_tags["GPS.GPSLongitude"] = "-85.1362"
No error is produced when running this code, however the output data from the exif after the image is captured is still inaccurate:
Latitude |0.040
Longitude |3162715.1775
Follow Up Attempts - Other Strings
# Style 1
camera.exif_tags["GPS.GPSLatitude"] = "(41) (6) (10.711)"
camera.exif_tags["GPS.GPSLongitude"] = "(85) (8) (8.968)"
# This produces
Latitude |1.00
Longitude |1.00
# Style 2
camera.exif_tags["GPS.GPSLatitude"] = "41 6 10.711"
camera.exif_tags["GPS.GPSLongitude"] = "85 8 8.968"
# This produces
Latitude |6.8
Longitude |10.6
# Style 3
camera.exif_tags["GPS.GPSLatitude"] = "41, 6, 10.711"
camera.exif_tags["GPS.GPSLongitude"] = "85, 8, 8.968"
#This produces
Latitude |6.8, 0.014, 1.000
Longitude |10.6, 0.008, 2.141837875
So, in short, can anyone help me by providing me with the means to format my strings? I am at a complete loss. The documentation for PiCamera only lists the different tags but does not explain how to format each of them.
Alright, so after several more test cases, a couple hours of research, and finally posting an issue on the PiCamera GitHub repo I finally figured it out. There is no direct documentation on this particular part of the PiCamera library as stated in my question. But someone on the repo was able to point me to the RaspiStill repo on adding GPS exif data, which is written in C. I've never completely learned C and my C++ skills are a bit rusty, but it gave me an idea. The end result came out to this:
camera.exif_tags["GPS.GPSLatitude"] = "41/1,6/1,9577/1000"
camera.exif_tags["GPS.GPSLongitude"] = "85/1,8/1,10223/1000"
When looking over the RaspiStill repo it got me thinking of how you would use the commands in the terminal to set exif data, so I looked up how to set the coordinates via RaspiStill and found this documentation which gave an example.
RaspiStill Documentation
Some examples of how to represent the data in the form of degrees, minutes, and seconds:
52 = 52 / 1 (52 units)
40.44 = 4044 / 100 (4044 hundredths)
52.97790 = 52977900 / 1000000 (52977900 millionths)
0 = 0/1
This is the source of where I found how to do the above "calculations":
Geotag - Exif Data
Hopefully this can help someone else too.

Receiving the correct length of data over serial

Originally, I am using putty to display the output of my embedded device. However, I want to manipulate the data as I receive it, thus I used python to connect to the port(w timeout 0). All the C code does is print the buffer (lets say file length of size = 22112 bytes) as following:
for (x = 0; x < size; x++) {
printf("%x", buffer[x]);
}
printf("\r\n");
The python code just reads the data as a line since I end the C code with a new line after the loop:
ser_bytes = ser.readline()
print(len(ser_bytes))
But python prints the length to be 34742 when it should be 22112. As you may have noticed, I print the values in hex, other formats give other values.
So what is the correct way to receive the data to get the correct size?
And is there any other way to do this?
UPDATE:
Currently I am using:
write(1, fileBuffer, size);
To write the raw data, but Python STILL does not receive all the data. Out of 22112, only around 8000 is received. Printing to Putty shows the correct number of data though.
For some visuals, this is the end of the file (around 8000 bytes) which is stored by python when the data is received over serial.
This is the original file itself (directly from the SD card). I highlighted where the data stops exactly.
UPDATE:
The problem seems to be a buffer issue. Since I am running on windows, I used the following to increase the buffer size:
ser.set_buffer_size(rx_size = 25000, tx_size = 25000)
ser.inWaiting(), shows a total of 22208 in the buffer when it should be 22112. I read it and store it anyways using ser.read(ser.inWaiting()). To see where the extra bytes are coming from, I print two lines from the original file and from the file after it is received over serial:
There seems to be an 'extra' carriage return in the file over serial. Where did it come from?

How to use an uhf rfid module with python?

I'm trying to read and write data from an RFID tag using python whit this module:
https://es.aliexpress.com/item/32573423210.html
I can connect successfully whit serial but I don't know how to read any tag, because the datasheet from pr9200(the reader that I am working) use this:
Image for pr9200 operation It's like a raw packet whit only hex address that I need to send to the module for it works
my code on python is this:
import serial
ser = serial.Serial(port = "COM27", baudrate=115200, bytesize=8, parity='N', stopbits=1)
while(ser.is_open == True):
rfidtag = ''
incomingByte = ser.read(21)
print(incomingByte)
for i in incomingByte:
rfidtag = rfidtag + hex(i)
Some comments to jump start your coding:
-What you need to do is send a command to your device to ask it to start sending readings in auto mode. To do that you need to use ser.write(command). You can find a good template here.
-To prepare the command you just need to take the raw bytes (those hex values you mentioned) and put them together as, for instance a bytearray.
-The only minor hurdle remaining is to calculate the CRC. There are some nice methods here at SO, just search CRC 16 CCITT.
-Be aware that after writing you can not start immediately waiting for readings, you have to wait first for the device to acknowledge the command. Hint: read 9 bytes.
-Lastly, take a new count of the bytes you will receive for each tag. I think they are 22 instead of 21.
You can use pyembedded python library for this which can give you the tag id.
from pyembedded.rfid_module.rfid import RFID
rfid = RFID(port='COM3', baud_rate=9600)
print(rfid.get_id())
https://pypi.org/project/pyembedded/

Error in writing to multiple slaves in minimalmodbus

I am trying to interface with a temperature controller using Modbus RTU. I am not very familar with the topic, but I have been able to write code that can read the temperature of my three temperature controllers, but when I try to write to the three temperature controllers in sequence I get an error. The code I have been running is :
import minimalmodbus
import time
import serial
from datetime import datetime
minimalmodbus.BAUDRATE= 9600
minimalmodbus.PARITY = serial.PARITY_EVEN
Left = minimalmodbus.Instrument('COM3', 1,)
Middle = minimalmodbus.Instrument('COM3', 2)
Right = minimalmodbus.Instrument('COM3', 3)
print(Left.read_register(4096, 1))
print(Middle.read_register(4096, 1))
print(Right.read_register(4096, 1))
newTemp = 65
Ltemp = newTemp
Left.write_register(4097, Ltemp, 1)
time.sleep(.04)
Mtemp = newTemp
Middle.write_register(4097, Mtemp, 1)
time.sleep(.04)
Rtemp = newTemp
Right.write_register(4097, Rtemp, 1)
Again when I print the current tempearture the code does not give an error, and if I write to only one of the registers at a time (comment out the others and run them) it changes the setpoint temperature and does not throw an error.
More interestingly to me, when I do run this code, the first time I run it the setpoint of the "Left" temperature controller is changed and then an error is thrown. The next time I run the code the setpoint of the "Middle" temperature controller is changed and then an error is thrown. The last time I run the code the set point of the right temperature controller is changed with no error thown.
I tend to get different errors and I cannot really disern a pattern. They include:
Checksum error in rtu mode: '\x10\x01' instead of '\x01ì' . The response
is: '\x01\x10\x10\x01' (plain response: '\x01\x10\x10\x01')
builtins.ValueError: Too short Modbus RTU response (minimum length 4
bytes). Response: '\x02'
builtins.OSError: No communication with the instrument (no answer)
I have tried changing the baud rate to 4800, nothing worked when I did this. I also got similar errors changing from Modbus RTU to ascii. I was also getting the same erros when the parity was set at the defualt none. I also tried to sleep the code and change the timeout, which I think helped with the last error.
Any suggestions of what to try to stop getting these errors would be much appreciated.
Thank you
Edit
I tried deleting the number of decimals but still get the same error, the set point now updates so that 80 turns into 8.0, not that that's a big deal.
this is the full response I get when I run the code, now without the number of decimals:
19.1
19.1
21.1
Traceback (most recent call last):
File "C:/Users/matth/Desktop/tempController.py", line 26, in <module>
Left.write_register(4097, Ltemp)
File "c:\Users\matth\AppData\Local\Programs\Python\Python36-32\Lib\site-packages\minimalmodbus.py", line 296, in write_register
self._genericCommand(functioncode, registeraddress, value, numberOfDecimals, signed=signed)
File "c:\Users\matth\AppData\Local\Programs\Python\Python36-32\Lib\site-packages\minimalmodbus.py", line 697, in _genericCommand
payloadFromSlave = self._performCommand(functioncode, payloadToSlave)
File "c:\Users\matth\AppData\Local\Programs\Python\Python36-32\Lib\site-packages\minimalmodbus.py", line 798, in _performCommand
payloadFromSlave = _extractPayload(response, self.address, self.mode, functioncode)
File "c:\Users\matth\AppData\Local\Programs\Python\Python36-32\Lib\site-packages\minimalmodbus.py", line 1032, in _extractPayload
response))
builtins.ValueError: Too short Modbus RTU response (minimum length 4 bytes). Response: '\x01'
Left's temperature does change though. I still also get the instrument timeout occasionally error, but the error above seems to be the most common one. I think I will check how I wired the interface though, as I am wondering if that is the issue.
Edit II
I think I figured it out, I just had to increase the timeout time (I thought I already had but I guess not enough) minimalmodbus.TIMEOUT = 3 seems to make everything work.
Thanks for the help!
If you are doing read before write, and read succeeds, then you definately have chosen right speed and parity and don't have to change it.
Without seeing the log of exchange, it would be difficult to guess, but I am 99% sure, the problem is numberOfDecimals parameter of write_register. It is not the number of registers, as you probably expect, it is a way to round floating number to integer.
So, change to
Left.write_register(4097, Ltemp)
Middle.write_register(4097, Mtemp)
Right.write_register(4097, Rtemp)

Read data from load cell

Your help is badly needed...
I'm trying to read data and print it to the python console from a load cell. My setup is as follow:
The load cell is a MD type from Eilersen connected to a load cell signal converter of type MCE2040 Seriel Communication Module also from Eilersen. The MCE2040 is connected to my PC through a USB to seriel connector like this link_http://www.usbgear.com/USB-COM-I-SI.html (I'm only allowed two links) one.
The load cell is connected to COM 1.
I have tried to run this snippet:
import serial
ser = serial.Serial(0) # open first serial port
print ser.portstr # check which port was really used
#ser.write("hello") # write a string
ser.close()
...and that prints 'COM1' to the console so I guess my connection should be okay.
My problem is that I don't know how to proceed. In the end I'd like to plot a graph of the incoming data and output a data file with time stamps, but for starters I'd like to print some load cell data to the console.
Any help will be highly appreciated. If further information is needed, please let me know.
Thx in advance.
Edit:
I have some documentation re MCE2040:
3.1 EVC Mode (without time stamp)
Specification: RS232/RS4422
Baudrate: 115200 bps
38400 bps (select with SW1.5)
Data bits: 7
Parity: Even
Stop bits: 1
Protocol: EVC protocol described below (Transmit Only)
3.1.1 EVC Protocol Format
After each sample period a new weight telegram is transmitted. The transmitted telegram has the following format:
<LF>WWWWWWWW<CR>
Each telegram contains a line feed character, a weight result and a carriage return character. The telegram contains:
<LF> Line Feed character (ASCII 0Ah).
WWWWWWWW Weight value for the loadcell. The value is an 8 byte ASCII hex number with MSB first.
<CR> Carriage Return character (ASCII 0Dh).
I was able to get some output from the following code:
import serial
ser = serial.Serial(0, baudrate=115000 ,timeout=100)
print ser.portstr
x = ser.read(50)
print x
ser.close()
print 'close'
Output:
COM1
ÆÆÆÆA0·5
ÆÆÆÆA0·6
ÆÆÆÆA0·5
ÆÆÆÆA0·±
ÆÆÆÆA0·±
close
First of all make sure it's really your com port, since COM1 is used by a lot of computers i'm not sure it's your com port.
You can use a simple wire to loop back info by connecting TX to RX at the USB to Serial converter, it will result in an echo (you will read what you write) it's a very simple way to verify that you are talking with the right com port.
Regarding how to continue:
Useful basic commands:
ser.write("command") with this command you send to the device some command.
ser.read(n) is for read n bytes from the device
ser.readline() will read line until it reached \n (new line)
Steps:
Send a command to your device.
Read all the data by some end byte (Frame Synchronization).
Parse data to structure (list or something like that..)
Plot it to graph.
Useful Links:
pyserial docs
tips for reading serial
plotly for graphs in python

Categories