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

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.

Related

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.

Python combining hex values received over serial

I am developing an application wherein I am receiving serial byte packets of varying length which are marked by framing flags into a buffer and then utilizing the collected bytes to access a dictionary to identify values. Currently, my serial read function watches the serial line until an END_FLAG byte is present or a timeout occurs as such...
def receive_command(self):
self.rxBuff = list(self.ser.read(1))
#Keep receiving bytes until END_FLAG or timeout
while self.rxBuff[-1] != chr(flagDict["END_FLAG"]):
tBuff = self.ser.read(1)
if len(tBuff) != 1:
print "Timeout"
else:
self.rxBuff.append(tBuff)
This ultimately reads the hex bytes into the buffer wherein they are represented with the escape character "\x##" or as their ASCII value. Within the code I have a dictionary which allows me to identify the microcontoller model based on a four byte hex value as such:
STM32_TYPE = {
0x410: "STM32F103RB",
0x415: "STM32L152RG",
0x417: "STM32L053R8",
0x421: "STM32F446RE",
0x431: "STM32F411RE",
0x433: "STM32F401RE",
0x437: "STM32L152RE",
0x439: "STM32F302R8",
0x438: "STM32F334R8",
0x440: "STM32F030R8",
0x442: "STM32F091RC",
0x446: "STM32F303RE",
0x447: "STM32L073RZ",
0x448: "STM32F070RB/STM32F072RB",
0x458: "STM32F410RB",
}
The problem I am having is that in order to access the values in this dictionary I need to combine two separate bytes(bytes 3 & 4 from the following packet).
['\x12','\x03','F','\x04','\x13']
My goal is to reassemble bytes 3 and 4 into the value 0x0446 to use to access the previously mentioned dictionary. However I am at a loss as to how to accomplish this as all attempts to do so have yielded either simply combined strings (which makes sense given that these are string representations), or integer values which are not held to be equivalent when I attempt to access the dictionary. My question is, is there a way to combine these hex values (the 'F' and '\x04') so as to use them to access the dictionary value with key 0x0446? OR, is there a better way to receive the hex bytes during my serial read function so that they are stored in a manner more conducive to combining them later on?
struct
>>> struct.unpack('<H', b'F\x04')
(1094,)

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).

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

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.

Reading a Delphi binary file in Python

I have a file that was written with the following Delphi declaration ...
Type
Tfulldata = Record
dpoints, dloops : integer;
dtime, bT, sT, hI, LI : real;
tm : real;
data : array[1..armax] Of Real;
End;
...
Var:
fh: File Of Tfulldata;
I want to analyse the data in the files (many MB in size) using Python if possible - is there an easy way to read in the data and cast the data into Python objects similar in form to the Delphi records? Does anyone know of a library perhaps that does this?
This is compiled on Delphi 7 with the following options which may (or may not) be pertinent,
Record Field Alignment: 8
Pentium Safe FDIV: False
Stack Frames: False
Optimization: True
Here is the full solutions thanks to hints from KillianDS and Ritsaert Hornstra
import struct
fh = open('my_file.dat', 'rb')
s = fh.read(40256)
vals = struct.unpack('iidddddd5025d', s)
dpoints, dloops, dtime, bT, sT, hI, LI, tm = vals[:8]
data = vals[8:]
I do not know how Delphi internally stores data, but if it is as simple byte-wise data (so not serialized and mangled), use struct. This way you can treat a string from a python file as binary data. Also, open files as binary file(open,'rb').
Please note that when you define a record in Delphi (like struct in C) the fields are layed out in order and in binary given the current alignment (eg Bytes are aligned on 1 byte boundaries, Words on 2 byte, Integers on 4 byte etc, but it may vary given the compiler settings.
When serialized to a file, you probably mean that this record is written in binary to the file and the next record is written after the first one starting at position sizeof( structure) etc etc. Delphi does not specify how thing should be serialized to/from file, So the information you give leaves us guessing.
If you want to make sure it is always the same without interference of any compiler setings, use packed record.
Real can have multiple meanings (it is an 48 bit float type for older Delphi versions and later on a 64 bit float (IEEE double)).
If you cannot access the Delphi code or compile it yourself, just ty to check the data with a HEX editor, you should see the boundaries of the records clearly since they start with Integers and only floats follow.

Categories