Raspberry Pi and SPI interface (Python): how to transfer data? - python

I'm using the SPI interface on my Raspberry Pi.
I can read the registers of the chip (first 20 registers) attached to SPI0 with the following code:
spi=spidev.SpiDev()
spi.open(0,0)
x=spi.readbytes(20)
k=0
for i in x:
print(k,hex(i),i)
k=k+1
My question is: how do I set the value of a specific register using "spi.xfer"? I have no idea what parameters the xfer(...) function takes.
How do I read a value from a specific register using "spi.xfer"?
Although I do have the original C code; I still cannot decipher how to use xfer(...).
Here is the C code:
https://github.com/doceme/py-spidev/blob/master/spidev_module.c

I believe this line of code is key:
PyArg_ParseTuple(args, "O|IHB:xfer", &list, &speed_hz,
&delay_usecs, &bits_per_word)
The documentation tells us that the formatting string means:
O - a Python object
| - indicates that the following are optional
I - an int
H - a short
B - a char
: - start of function name string (xfer)
Of these, it seems sensible to assume that the list is the data to send, and indeed it seems to be interpreted as a list of bytes to send.
Since SPI tends to send and receive in parallel, futher reading of the code leads me to believe that each value in list will be replaced by the byte that was received when that particular byte was sent. It's weird, but that's SPI for you.

Related

(embedded) How to read data from uart (Python+C)

I need to transmit data through UART from different sensors.
I need to form packet with data from sensors on microcontroller. Then transmit it through uart. and then decode it on laptop.
For 1 sensor i have a code:
while (1)
{
get_Temperature();
HAL_Delay(2000);
char buffer[100];
seg = Temp[0]; //here our temperature
sprintf(buffer, "%d",seg); //transform temperautre to string
HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer), 1000); //string transmit
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
For decode it on laptop i have a code(python with pyserial):
cons = wox.read_all().decode("utf-8")
*in loop
And my output is a value of temperature in string format:
24
24
25
24
depends on temperature of the sensor.
I can use the second sensor with another value Temp[1]
But i need to form packet with that data and decode it on laptop. How can i do it?
You might use delimiters to separate and carry the data in a single message and you should also use a delimiter for the end (or start, or both) of the data rather than relying on the delay to separate "messages". You might later want the data at a much higher rate or streaming, and "24,3224,32" would be ambiguous. For example
sprintf(buffer, "%d,%d\n", Temp[0], Temp[1] ) ;
Then the output would be clearly delimited:
24,32<newline>
24,32<newline>
...
Then at the receiver (in Python - not my area of expertise), you can use read line semantics (message = wox.readline().decode("utf-8")?) to get the entire message rather than relying on any specific timing.
A more flexible alternative, that would be extensible to more sensors of different types, outputting at different intervals in multiple threads, would be to tag the data with its sensor ID. For example, you might independently have:
sprintf( buffer, "T0:%d\n", Temp[0] ) ;
and
sprintf( buffer, "T1:%d\n", Temp[0] ) ;
Or even an accelerometer with:
sprintf( buffer, "AC:%d,%d,%d\n", x, y, z ) ;
So the output stream might look like:
AC:24,2,33<newline>
AC:24,2,33<newline>
AC:24,2,33<newline>
AC:24,2,33<newline>
T1:33<newline>
AC:24,2,33<newline>
AC:24,2,33<newline>
AC:24,2,33<newline>
AC:24,2,33<newline>
T0:24<newline>
...
Where the messages are free to arrive in any order, and at any frequency. Of course if multi-threaded, you'd probably want a single thread actually outputting to the UART with sensor threads placing messages in a queue, or otherwise ensure the independent messages were correctly serialised and not "interleaved".
You might split out sensor type from sensor instance so you could extend to any number for sensors of a particular type, so rather then say T0:, T1: and AC: you might have T#0:, T#1: and AC#0: for example followed by comma delimited parameters then <newline>.
If synchronisation/timing is critical and you have a source of time (relative or absolute), you might even add a timestamp to the message; e.g.:
sprintf( buffer, "$%lu,T0:%d\n", tick_ms(), Temp[0] ) ;
to the output message might look like:
$1256,T1:33<newline>
$1256,T0:55<newline>
$3256,T1:34<newline>
$3256,T0:54<newline>
The specific delimiters are arbitrary - your design.
If you want a great deal of flexibility and compatibility you could even output the data as an XML or JSON schema. A bit heavyweight for the embedded end perhaps, but there are no end of parsers available for Python to handle that, and the schema would be publishable to allow others to easily parse your data.

Differences between serial communication in matlab vs. python 3.7? Sending int values over 128 via python serial to an arduino

I have some matlab functions which I would like to translate into python 3.7. The functions calculate values for joint angles of a little robot from trossenrobotics and send those values via serial port to the robot which is controlled by an arduino board. The board runs a programm from Trossenrobotics, which interpretes the data send via the serial port and reacts accordingly.
I already managed to translate all the functions and they give the same outputs as the matlab functions, but the serial communication just doesn't work.
In matlab fwrite(s, int_value) and fread(s) are used for the communication. The int_values represent a highbyte and a lowbyte of a joint position(0-1024) and are send seperately.
In python I used pyserial and the functions s.write(byte) and s.read().
I converted the int values into bytes with chr(int).encode().
Since I was struggling with my actual objective, I first wanted to abstract it and make it simpler. Now I am just trying to turn on an LED on the arduino for 2 seconds, when a special byte is received and send the same byte back to python.
I noticed that as long as the value I am sending is smaller that 128 it works just fine, but when it's greater it won't work.
I printed the output of chr(255).encode() which is b'\xc3\xbf', what to me looked like it could be the problem.
The I tried using chr(255).encode('charmap') and printed it which gives back b'\xff', what looks right to me, but it still doesn't work for numbers between 128 and 255.
I also noticed, that when I send the data over the terminal
with s.write(chr(115).encode()) It doesn't return a value, but when I use
s.write(chr(255).encode('charmap')) it returns a 1.
Here's my python pogramm:
python
import serial
from time import sleep
port = 'COM4'
baudrate = 38400
s = serial.Serial(port,baudrate)
sleep(3)
m = 115
s.write(chr(m).encode())
while s.in_waiting == 0:
print('##### waiting #####')
sleep(2)
if s.in_waiting > 0:
r = int.from_bytes(s.read(), byteorder = 'big')
print(r)
s.close()
And here's the arduino programm:
C#
void setup() {
pinMode(13, OUTPUT);
digitalWrite(13,LOW);
Serial.begin(38400);
}
void loop() {
if (Serial.available() > 0)
{
if (Serial.read() == 's')
{
digitalWrite(13, HIGH);
Serial.write('s');
delay(2000);
}
}
else
{
digitalWrite(13, LOW);
}
}
My questions would be:
Regarding my primary problem (sending multiple bytes via matlab, python):
1) Does anybody know if there are any fundamental differences between serial communication in matlab and in python which could cause my problems?
Regarding my abstracted problem (sending one bye via python):
2) How can I send values greater than 128 (up to 255) via the serial port?
There is no fundamental difference between Python and Matlab on this regard.
But in your Matlab code it seems (I'm assuming because what you say):
The int_values represent a highbyte and a lowbyte of a joint position(0-1024) and are send seperately.
that you're sending an int16
to be able to fit up to 1024.
I have no idea what you're trying to do with chr but I have the feeling what you need is replace these lines:
m = 115
s.write(chr(m).encode())
With (on Python 3.x):
m=115
s.write(m.to_bytes(2, byteorder="big"))
That would write: b'\x00s', a mix of hex and ASCII, but you should not worry about that, because that is exactly the same as b'\x00\x73'
And if you do, then you can do: b'\x00s'==b'\x00\x73' and you'll get True.
Thanks for your answer! And sorry for the late reply.
I already tried that right at the beginning but always got an exception. It took me a while to figure out why. It was because I was using numpy.uint8() for my integer values.
After I removed it I didn't get any exception but it didn't work either.
I used chr() because it didn't throw an exception with the numpy.uint8() and honestly because I did not know what else to do...
Today I finally found the solution.
Here is the link to where I found it:
arduino.stackexchange.com
Using s.write(struct.pack('>B', int_value) works and seems to be the equivalent to matlabs fwrite(s, int_value).
Sorry If my question didn't make a lot of sense to you, I am just glad I finally figured it out.

How is the xfer2 function used to read registers using SpiDev?

I'm trying to communicate with my mpu9250 through SPI using the py-spidev module and I'm unable to understand how exactly the read function works.
I found this function snippet that performs the read register function and I'd like to know why the __READ_FLAG (__READ_FLAG = 0x80) is concatenated with the address byte for sending the dummy values to. Won't this change the register address completely?
def ReadReg(self, reg_address):
self.bus.open(self.spi_bus_number, self.spi_dev_number)
tx = [reg_address | self.__READ_FLAG, 0x00]
rx = self.bus.xfer2(tx)
self.bus.close()
return rx[1]
Found the answer for this in another datasheet that follows the same protocol.
Writing is done by lowering CSB and sending pairs control bytes and register data. The control
bytes consist of the SPI register address (= full register address without bit 7) and the write
command (bit7 = RW = ‘0’). Several pairs can be written without raising CSB. The transaction is
ended by a raising CSB.
Reading is done by lowering CSB and first sending one control byte. The control bytes consist
of the SPI register address (= full register address without bit 7) and the read command (bit 7 =
RW = ‘1’). After writing the control byte, data is sent out of the SDO pin (SDI in 3-wire mode);
the register address is automatically incremented.

How to change byte order of interpreting response when using python lib minimalmodbus?

I use python library "minimalmodbus" to communicate with a modbus device:
import minimalmodbus
from minimalmodbus import Instrument
minimalmodbus.BAUDRATE = 9600
m = Instrument('com2', 1)
m.debug=True
print m.read_long(4156)
the result:
MinimalModbus debug mode. Writing to instrument (expecting 9 bytes back): '\x01\x03\x10<\x00\x02\x00\xc7'
MinimalModbus debug mode. No sleep required before write. Time since previous read: 1422431606124.0 ms, minimum silent period: 4.01 ms.
MinimalModbus debug mode. Response from instrument: '\x01\x03\x04\x00\x01\x00\x00\xab\xf3' (9 bytes), roundtrip time: 28.0 ms. Timeout setting: 50.0 ms.
65536
the response data is 65536, which is 0x00010000 in hex. But I have already exactly known the data should be 1, 0x00000001 in hex. The reason is obvious: minimalmodbus interpret response data '\x00\x01\x00\x00' as 0x00010000, while for my modbus device supposed to be 0x00000001. I have referred to the documentation(http://minimalmodbus.sourceforge.net/#known-issues) where I saw this:
For the data types involving more than one register (float, long etc), there are differences in the byte order used by different manufacturers. A floating point value of 1.0 is encoded (in single precision) as 3f800000 (hex). In this implementation the data will be sent as '\x3f\x80' and '\x00\x00' to two consecutive registers. Make sure to test that it makes sense for your instrument. It is pretty straight-forward to change this code if some other byte order is required by anyone (see support section).
I want to ask: Have anyone encountered the same problem and found out an simple and quick method(as the author said) to change the default byte order of minimalmodbus?
EDIT:
I have found a way to resolve this problem, but I don't know whether it's the simplest:
def _performCommand(self, functioncode, payloadToSlave):
'''
reimplement the _performCommand function in subclass of minimalmodbus.Instrument
'''
payloadFromSlave = Instrument._performCommand(self, functioncode, payloadToSlave)
if functioncode in [3, 4]:
#reorder data in response while reading multiple registers
return payloadFromSlave[0] + self._restructure(payloadFromSlave)
else:
return payloadFromSlave
def _restructure(self, byteCode):
'''
reorder byte code for my device, e.g.:
'\x00\x01\x00\x02' --->'\x00\x02\x00\x01'
(byte order may differ for different manufacturers,refer to http://www.simplymodbus.ca/FAQ.htm#Order)
'''
newByteCode = ''
for i in range(len(byteCode)-2, -1, -2):
newByteCode += byteCode[i:i+2]
return newByteCode

Passing a record over a socket

I have basic socket communication set up between python and Delphi code (text only). Now I would like to send/receive a record of data on both sides. I have a Record "C compatible" and would like to pass records back and forth have it in a usable format in python.
I use conn.send("text") in python to send the text but how do I send/receive a buffer with python and access the record items sent in python?
Record
TPacketData = record
pID : Integer;
dataType : Integer;
size : Integer;
value : Double;
end;
I don't know much about python, but I have done a lot between Delphi, C++, C# and Java even with COBOL.Anyway, to send a record from Delphi to C first you need to pack the record at both ends,
in Deplhi
MyRecord = pack record
in C++
#pragma pack(1)
I don’t know in python but I guess there must be a similar one. Make sure that at both sides the sizeof(MyRecord) is the same length.Also, before sending the records, you should take care about byte ordering (you know, Little-Endian vs Big-Endian), use the Socket.htonl() and Socket.ntohl() in python and the equivalent in Deplhi which are in WinSock unit. Also a "double" in Delphi could not be the same as in python, in Delphi is 8 bytes check this as well, and change it to Single(4 bytes) or Extended (10 bytes) whichever matches.
If all that match then you could send/receive binary records in one shut, otherwise, I'm afraid, you have to send the individual fields one by one.
I know this answer is a bit late to the game, but may at least prove useful to other people finding this question in their search-results. Because you say the Delphi code sends and receives "C compatible data" it seems that for the sake of the answer about Python's handling it is irrelevant whether it is Delphi (or any other language) on the other end...
The python struct and socket modules have all the functionality for the basic usage you describe. To send the example record you would do something like the below. For simplicity and sanity I have presumed signed integers and doubles, and packed the data in "network order" (bigendian). This can easily be a one-liner but I have split it up for verbosity and reusability's sake:
import struct
t_packet_struc = '>iiid'
t_packet_data = struct.pack(t_packet_struc, pid, data_type, size, value)
mysocket.sendall(t_packet_data)
Of course the mentioned "presumptions" don't need to be made, given tweaks to the format string, data preparation, etc. See the struct inline help for a description of the possible format strings - which can even process things like Pascal-strings... By the way, the socket module allows packing and unpacking a couple of network-specific things which struct doesn't, like IP-address strings (to their bigendian int-blob form), and allows explicit functions for converting data bigendian-to-native and vice-versa. For completeness, here is how to unpack the data packed above, on the Python end:
t_packet_size = struct.calcsize(t_packet_struc)
t_packet_data = mysocket.recv(t_packet_size)
(pid, data_type, size, value) = struct.unpack(t_packet_struc,
t_packet_data)
I know this works in Python version 2.x, and suspect it should work without changes in Python version 3.x too. Beware of one big gotcha (because it is easy to not think about, and hard to troubleshoot after the fact): Aside from different endianness, you can also distinguish between packing things using "standard size and alignment" (portably) or using "native size and alignment" (much faster) depending on how you prefix - or don't prefix - your format string. These can often yield wildly different results than you intended, without giving you a clue as to why... (there be dragons).

Categories