How to cut and convert bytes in micropython - can-bus

I am trying to decode Can frames with micropython. I miss a lot of functions from python 3.x. I get 8 hex long bytes from the Rx SPI buffer of an MCP2515. For information, I'm on openMV IDE. I would like to extract the 1st and 2nd to make an integer, the 3rd and 4th to make another one, etc...
Along my researches, I found a function that convert a byte to signed integer (as the sign=True argument is not implemented in the from_byte function).
The main problem is that the solutions provided on others python's forum are only concerning python 3.x for pc but not micropython and a lot of solutions end with "function doesn't exist".
byte = b'\x12\x34\x56\x78\x9A\xBC\xDE\xFF'
def byte2int(bList, signed:bool=False):
if signed :
r = 0
for i in range(2):
d = 32 - ((i + 1) * 8)
r += bList[i] << d
return r
else : return int.from_bytes(byte, 'big')
Moreover, I don't think that I can trust the print function as print(b) return :
> print(b)
b'\x124Vx\x9a\xbc\xde\xff'
I don't understand why does the 3 vanished and was replaced by a 'V' after the 4 ! Sometime, my MCP2515 return some '|', '?', '.', '>', '<' or '=' when I print the can frames which are not hex characters !?
My hypothesis is that these are corrupted frames or SPI transmission.
Please, can someone help me to understand why micropython give me these messy results ?
Thank you very for your help.

I wrote something. Maybe not the best optimization possible, but I have to deal with micropython which is not as advanced than Python 3.x
This is a solution (surely not the most optimal). But it seems to work.
byte = b'\xFF\xC8\x56\x78\x9A\xBC\xDE\xFF'
byte2 = b'\x00\x00\x00#\x00\x00\x00\x00'
def extract(trame, signed:bool=False):
r = [0,0,0,0]
trame = [int(x) for x in bytearray(byte)]
for i in range(len(trame)/2):
val = (trame[i*2]<<8)+trame[i*2+1]
if signed and val > 0x8000 :
r[i] = -(65535-int(val)+1)
else :
r[i] = int(val)
return r
print(extract(byte, True))

Related

How to flip an integer that represents a 8-digit hex string in Lua?

In my Lua script, I have a variable that stores an integer found from some function that represents a hex string. I want to flip it so that, for example, if it has a value of 0x006E57E8, it will become 0x8E75E600. How do I do it in Lua?
As an alternative to Skriptunoff's very concise & readable function, here's a function doing it using bit ops:
function reverse_nibbles(int)
res = 0
for i = 0, 7 do
res = res | (((int >> (4*i)) & 0xF) << (28 - 4*i))
end
return res
end
This won't create a garbage string (and would hopefully be faster). You might want to hand-unroll the loop.

Keeping a hex addition result as hex lua

I've got the following code in lua:
array = {}
for i=1, 30 do
mem = tostring(0x29A300 + (i * 0x11CC))
array[i] = "Ref Mem" .. i .. ": " .. mem
end
But when I do the hexadecimal addition to store in mem, I get a decimal value out.
Is there a way to keep the hex formatting that I used to do the calculation?
Also is there a way for me to remove the 0x in the string?
I've tried using tonumber but that gives me an error.
There hasn't been anything else I've tried as I'm completely new to lua.

How to read 2-byte float in Erlang?

Seems there are a lot of half float questions in other languages, but could not find one for Erlang.
So, I have a 2-byte float as part of a longer binary pattern input. I tried to use pattern matching like <<AFloat:16/float>> and got warning/error in compiler, while using 32/float produced no warning.
Question is: What workaround is there in Erlang to convert the 2-byte binary into float?
I saw the other elaborate bit processing answer to "reading from binary file in Erlang", and do not know if it is required in this case.
* Thanks for the answers below. I shall try them out later *
-- two sets of sample input: EF401C3FEA3F, 1242C341C341
It looks like Erlang does not support float widths other than 32 and 64 (at least currently), but I can't seem to find any documentation that says this explicitly.
Update: I checked the implementation of the bit syntax, and it definitely only handles 32 and 64 bit floats. This should really be better documented.
Update again: added to documentation upstream now.
Final update: Just saw your note about hex input. If you solve that as a first step, transforming the hex string H to a binary B with the actual data bytes, you can use the following to repack the data as 32-bit floats (a simple transformation), and then extract those the normal way:
Floats = [F || <<F:32/float>> <- [<<S:1,(E+(127-15)):8,(M bsl 13):23>> || <<S:1,E:5,M:10>> <- B]]
There is no support for 16 bit floats. If you want collect and works on float coming from another system (via a file for example) then you can easily convert this float to the representation used in your VM.
First read the file and extract the float16 as a 2 bytes binary, and call this conversion function:
-module (float16).
-export([conv_16_to_vm/1]).
% conv_16_to_vm(binary) with binary is a 16 bit float representation
conv_16_to_vm(<<S:1,E:5,M:10>>)->
conv(S,E,M).
conv(_,0,0) -> 0.0;
conv(_,31,_) -> {error,nan_or_qnan_or_infinity};
% Sign management
conv(1,E,M) -> -conv(0,E,M);
% sub normal floats
conv(0,0,M) -> M/(1 bsl 24);
% normal floats
conv(0,E,M) when E < 25 -> (1024 + M)/(1 bsl (25-E));
conv(0,25,M) -> (1024 + M);
conv(0,E,M) -> (1024 + M)*(1 bsl (E-25)).
I used the definition provided by Wikipedia and you can test it:
1> c(float16).
{ok,float16}
2> float16:conv_16_to_vm(<<0,1>>). % 0 00000 0000000001
5.960464477539063e-8
3> float16:conv_16_to_vm(<<3,255>>). % 0 00011 1111111111
6.097555160522461e-5
4> float16:conv_16_to_vm(<<4,0>>). % 0 00100 0000000000
6.103515625e-5
5> float16:conv_16_to_vm(<<123,255>>). % 0 11110 1111111111
65504
6> float16:conv_16_to_vm(<<60,0>>). % 0 01111 0000000000
1.0
7> float16:conv_16_to_vm(<<60,1>>). % 0 01111 0000000001
1.0009765625
8> float16:conv_16_to_vm(<<59,255>>). % 0 01110 1111111111
0.99951171875
8> float16:conv_16_to_vm(<<53,85>>). % 0 01101 0101010101
0.333251953125
As you should expect, the traditional problem or "rounding" is much more visible.

Lua: Hexadecimal Word to Binary Conversion

I'm attempting to create a Lua program to monitor periodic status pings of a slave device. The slave device sends its status in 16-bit hexadecimal words, which I need to convert to a binary string since each bit pertains to a property of the device. I can receive the input string, and I have a table containing 16 keys for each parameter. But I am having a difficult time understanding how to convert the hexadecimal word into a string of 16-bits so I can monitor it.
Here is a basic function of what I'm starting to work on.
function slave_Status(IP,Port,Name)
status = path:read(IP,Port)
sTable = {}
if status then
sTable.ready=bit32.rshift(status:byte(1), 0)
sTable.paused=bit32.rshift(status:byte(1), 1)
sTable.emergency=bit32.rshift(status:byte(1), 2)
sTable.started=bit32.rshift(status:byte(1), 3)
sTable.busy=bit32.rshift(status:byte(1), 4)
sTable.reserved1=bit32.rshift(status:byte(1), 5)
sTable.reserved2=bit32.rshift(status:byte(1), 6)
sTable.reserved3=bit32.rshift(status:byte(1), 7)
sTable.reserved4=bit32.rshift(status:byte(2), 0)
sTable.delay1=bit32.rshift(status:byte(2), 1)
sTable.delay2=bit32.rshift(status:byte(2), 2)
sTable.armoff=bit32.rshift(status:byte(2), 3)
sTable.shieldoff=bit32.rshift(status:byte(2), 4)
sTable.diskerror=bit32.rshift(status:byte(2), 5)
sTable.conoff=bit32.rshift(status:byte(2), 6)
sTable.envoff=bit32.rshift(status:byte(2), 7)
end
end
I hope this approach is understandable? I'd like to receive the Hex strings, for example 0x18C2 and turn that to 0001 1000 1100 0010, shifting the right-most bit to the right and placing that into the proper key. Then later in the function I would monitor if that bit had changed for the better or worse.
If I run a similar function in Terminator in Linux, and print out the pairs I get the following return:
49
24
12
6
3
1
0
0
56
28
14
7
3
1
0
0
This is where I am not understanding how to take each value and set it to bits
I'm pretty new to this so I do not doubt that there is an easier way to do this. If I need to explain further I will try.
tonumber(s, 16) will convert hex representation to decimal and string.char will return a symbol/byte representation of a number. Check this recent SO answer for an example of how they can be used; the solution in the answer may work for you.
I'd approach this in a different fashion than the one suggested by Paul.
First, create a table storing the properties of devices:
local tProperty = {
"ready",
"paused",
"emergency",
"started",
"busy",
"reserved1",
"reserved2",
"reserved3",
"reserved4",
"delay1",
"delay2",
"armoff",
"shieldoff",
"diskerror",
"conoff",
"envoff",
}
Then, since your device sends the data as 0xYYYY, you can call tonumber directly (if not a string). Use a function to store each bit in a table:
function BitConvert( sInput )
local tReturn, iNum = {}, tonumber( sInput ) -- optionally pass 16 as second argument to tonumber
while iNum > 0 do
table.insert( tReturn, 1, iNum % 2 )
iNum = math.floor( iNum / 2 )
end
for i = #tProperty - #tReturn, 1, -1 do
table.insert( tReturn, 1, 0 )
end
return tReturn
end
And then, map both the tables together:
function Map( tKeys, tValues )
local tReturn = {}
for i = 1, #tKeys do
tReturn[ tKeys[i] ] = tValues[i]
end
return tReturn
end
In the end, you would have:
function slave_Status( IP, Port, Name )
local status = path:read( IP, Port )
local sTable = Map( tProperty, BitConvert(status) )
end

Lua base converter

I need a base converter function for Lua. I need to convert from base 10 to base 2,3,4,5,6,7,8,9,10,11...36 how can i to this?
In the string to number direction, the function tonumber() takes an optional second argument that specifies the base to use, which may range from 2 to 36 with the obvious meaning for digits in bases greater than 10.
In the number to string direction, this can be done slightly more efficiently than Nikolaus's answer by something like this:
local floor,insert = math.floor, table.insert
function basen(n,b)
n = floor(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
local sign = ""
if n < 0 then
sign = "-"
n = -n
end
repeat
local d = (n % b) + 1
n = floor(n / b)
insert(t, 1, digits:sub(d,d))
until n == 0
return sign .. table.concat(t,"")
end
This creates fewer garbage strings to collect by using table.concat() instead of repeated calls to the string concatenation operator ... Although it makes little practical difference for strings this small, this idiom should be learned because otherwise building a buffer in a loop with the concatenation operator will actually tend to O(n2) performance while table.concat() has been designed to do substantially better.
There is an unanswered question as to whether it is more efficient to push the digits on a stack in the table t with calls to table.insert(t,1,digit), or to append them to the end with t[#t+1]=digit, followed by a call to string.reverse() to put the digits in the right order. I'll leave the benchmarking to the student. Note that although the code I pasted here does run and appears to get correct answers, there may other opportunities to tune it further.
For example, the common case of base 10 is culled off and handled with the built in tostring() function. But similar culls can be done for bases 8 and 16 which have conversion specifiers for string.format() ("%o" and "%x", respectively).
Also, neither Nikolaus's solution nor mine handle non-integers particularly well. I emphasize that here by forcing the value n to an integer with math.floor() at the beginning.
Correctly converting a general floating point value to any base (even base 10) is fraught with subtleties, which I leave as an exercise to the reader.
you can use a loop to convert an integer into a string containting the required base. for bases below 10 use the following code, if you need a base larger than that you need to add a line that mapps the result of x % base to a character (usign an array for example)
x = 1234
r = ""
base = 8
while x > 0 do
r = "" .. (x % base ) .. r
x = math.floor(x / base)
end
print( r );

Resources