Decimal to hex in python - python

So I have kind of a ignorant (maybe?) question. I'm working with writing to a serial device for the first time. I have a frame [12, 0, 0, 0, 0, 0, 0, 0, 7, 0, X, Y] that I need to send. X and Y are checksum values. My understanding in using the pyserial module is that I need to convert this frame into a string representation. Ok that's fine, but I'm confused on what format things are supposed to be in. I tried doing
a = [12, 0, 0, 0, 0, 0, 0, 0, 7, 0, X, Y]
send = "".join(chr(t) for t in a)
But my confusion comes from the fact that X and Y, when using chr, transform into weird strings (assuming their ascii representation). For example if X is 36, chr(x) is '$' instead of '\x24'. Is there a way I can get a string representing the '\xnn' value instead of the ascii code? What's confusing me is that 12 and 7 convert to '\x0b' and '\x07' correctly. Am I missing something?
Update:
So it might be that I'm not quite understanding how serial writes are being done or what my device is expecting of me. This is a portion of my C code that is working:
fd=open("/dev/ttyS2",O_RDWR|O_NDELAY);
char buff_out[20]
//Next line is psuedo
for i in buff_out print("%x ",buff_out[i]); // prints b 0 0 0 0 0 0 0 9 b3 36
write(fd,buff_out,11);
sleep()
read(fd,buff_in,size);
for i in buff_in print("%x ",buff_in[i]); // prints the correct frame that I'm expecting
Python:
frame = [11, 0, 0, 0, 0, 0, 0, 0, 9] + [crc1, crc1]
senddata = "".join(chr(x) for x in frame)
IEC = serial.Serial(port='/dev/ttyS2', baudrate=1200, timeout=0)
IEC.send(senddata)
IEC.read(18) # number of bytes to read doesn't matter, it's always 0
Am I going about this the right way? Obviously you can't tell exactly since it's device specific and I can't really give too many specifics out. But is that the correct format that serial.send() expects data in?

It's perfectly normal for ASCII bytes to be represented by single characters if they can be printed, and by the \x?? notation otherwise. In both cases they represent a single byte, and you can write strings in either fashion:
>>> '\x68\x65\x6c\x6c\x6f'
'hello'
However if you're using Python 2.6 or later then you might find it easier and more natural to use the built-in bytearray rather than messing around with ord or struct.
>>> vals = [12, 0, 0, 0, 0, 0, 0, 0, 7, 0, 36, 100]
>>> b = bytearray(vals)
>>> b
bytearray(b'\x0c\x00\x00\x00\x00\x00\x00\x00\x07\x00$d')
You can convert to a str (or bytes in Python 3) just by casting, and can index the bytearray to get the integers back.
>>> str(b)
'\x0c\x00\x00\x00\x00\x00\x00\x00\x07\x00$d'
>>> b[0]
12
>>> b[-1]
100
As to your serial Python code, it looks fine to me - I'm not sure why you think there is a problem...

The character with the ASCII-code 36 is '$'. Look it up in any ASCII table. Python only displays the hex escapes if the character is not printable (control characters etc).
At the lowest level, it's the same bit pattern anyway - no matter whether Python prints it as a hex escape or as the char with that ASCII value.
But you might want to use the struct module, it takes care of such conversions for you.

I would guess you want struct.
>>> import struct
>>> struct.pack('>B', 12)
'\x0c'
>>> vals = [12, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0xa, 0xb]
>>> ''.join(struct.pack('>B', x) for x in vals)
'\x0c\x00\x00\x00\x00\x00\x00\x00\x07\x00\n\x0b'

What you do is perfectly fine: your send is what you want: a sequence of bytes with the values you want (a).
If you want to see what are the hexadecimal codes of the characters in send, you can do:
import binascii
print binascii.hexlify(send)
or
print ''.join(r'\x%02x' % ord(char) for char in send)
(if you want \x prefixes).
What you see when directly printing repr(send) is a representation of send, which uses ASCII: 65 represents 'A', but character 12 is '\x0c'. This is merely a convention used by Python, which is convenient when the string contains words, for instance: it is better to display 'Hello' than \x48\x65\x6c\x6c\x6f!

Related

How to create bytes list in python?

I am facing a challenge to create a bytes list in python. I just want to convert the int list into bytes list as mentioned in expected result. The problem statement is that I want to send the expected output to the serial device connected to the com port and with current output the serial device is not encouraging the '\\' as a separator. Please suggest me the correct way to handle the '\' in a list of bytes.
cmdlist = [2, 12, 1, 1, 1, 0, 0, 1, 3, 7, 42, 101, 85, 18]
#Convert CMD list to Hex List
for i in range(len(cmdlist)):
cmdlist[i] = hex(cmdlist[i])
f_cmdList = ''
#Convert hex CMD list to string List
for i in range(len(cmdlist)):
f_cmdList += '\\' + (cmdlist[i])
Final_cmdlist = (bytes(f_cmdList,'utf-8'))
print(Final_cmdlist)
Current output : b'\\0x2\\0xc\\0x1\\0x1\\0x1\\0x0\\0x0\\0x1\\0x3\\0x7\\0x2a\\0x65\\0x55\\0x12'
Expected output : b'\0x2\0xc\0x1\0x1\0x1\0x0\0x0\0x1\0x3\0x7\0x2a\0x65\0x55\0x12'
Thank You !
You can convert a list to bytes simply by using the bytes constructor. Your method is trying to create a string that contains the string representation of a byte array, which won't work when sent to the serial device:
>>> cmdlist = [2, 12, 1, 1, 1, 0, 0, 1, 3, 7, 42, 101, 85, 18]
>>> bytes(cmdlist)
b'\x02\x0c\x01\x01\x01\x00\x00\x01\x03\x07*eU\x12'
You get what you say you expect if you replace your
f_cmdList += '\\' + (cmdlist[i])
with this:
f_cmdList += '\0' + cmdlist[i][1:]
Still not convinced that you really want that, though.

Python bit list to byte list

I have a long 1-dimensional list of integer 1's and 0's, representing 8-bit binary bytes. What is a neat way to create a new list from that, containing the integer bytes.
Being familiar with C, but new to Python, I've coded it in the way I'd do it with C: an elaborate structure that loops though each bit. However, I'm aware that the whole point of Python over C is that such things can usually be done compactly and elegantly, and that I should learn how to do that. Maybe using list comprehension?
This works, but suggestions for a more "Pythonic" way would be appreciated:
#!/usr/bin/env python2
bits = [1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1,1]
bytes = []
byt = ""
for bit in bits:
byt += str(bit)
if len(byt) == 8:
bytes += [int(byt, 2)]
byt = ""
print bytes
$ bits-to-bytes.py
[149, 107, 231]
You can slice the list into chunks of 8 elements and map the subelements to str:
[int("".join(map(str, bits[i:i+8])), 2) for i in range(0, len(bits), 8)]
You could split it up into two parts mapping and joining once:
mapped = "".join(map(str, bits))
[int(mapped[i:i+8], 2) for i in range(0, len(mapped), 8)]
Or using iter and borrowing from the grouper recipe in itertools:
it = iter(map(str, bits))
[int("".join(sli), 2) for sli in zip(*iter([it] * 8))]
iter(map(str, bits)) maps the content of bits to str and creates an iterator, zip(*iter([it] * 8)) groups the elements into groups of 8 subelements.
Each zip(*iter.. consumes eight subelements from our iterator so we always get sequential groups, it is the same logic as the slicing in the first code we just avoid the need to slice.
As Sven commented, for lists not divisible by n you will lose data using zip similarly to your original code, you can adapt the grouper recipe I linked to handle those cases:
from itertools import zip_longest # izip_longest python2
bits = [1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1,1,0]
it = iter(map(str, bits))
print( [int("".join(sli), 2) for sli in izip_longest(*iter([it] * 8),fillvalue="")])
[149, 107, 231, 2] # using just zip would be [149, 107, 231]
The fillvalue="" means we pad the odd length group with empty string so we can still call int("".join(sli), 2) and get correct output as above where we are left with 1,0 after taking 3 * 8 chunks.
In your own code bytes += [int(byt, 2)] could simply become bytes.append(int(byt, 2))
Padraic's solution is good; here's another way to do it:
from itertools import izip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# Taken from itertools recipes
# https://docs.python.org/2/library/itertools.html#recipes
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
bits = [1, 0, 0, 1, 0, 1, 0, 1,
0, 1, 1, 0, 1, 0, 1, 1,
1, 1, 1, 0, 0, 1, 1, 1]
byte_strings = (''.join(bit_group) for bit_group in grouper(map(str, bits), 8))
bytes = [int(byte_string, 2) for byte_string in byte_strings]
print bytes # [149, 107, 231]
Since you start from a numeric list you might want to avoid string manipulation. Here there are a couple of methods:
dividing the original list in 8 bits chunks and computing the decimal value of each byte (assuming the number of bits is a multiple of 8); thanks to Padraic Cunningham for the nice way of dividing a sequence by groups of 8 subelements;
bits = [1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1,1]
[sum(b*2**x for b,x in zip(byte[::-1],range(8))) for byte in zip(*([iter(bits)]*8))]
using bitwise operators (probably more efficient); if the number of bits is not a multiple of 8 the code works as if the bit sequence was padded with 0s on the left (padding on the left often makes more sense than padding on the right, because it preserves the numerical value of the original binary digits sequence)
bits = [1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1,1]
n = sum(b*2**x for b,x in zip(bits[::-1],range(len(bits)))) # value of the binary number represented by 'bits'
# n = int(''.join(map(str,bits)),2) # another way of finding n by means of string manipulation
[(n>>(8*p))&255 for p in range(len(bits)//8-(len(bits)%8==0),-1,-1)]

How to convert a keycode to an char in python?

I'm writing a program in Linux which reads and distinguish inputs from two USB devices(two barcode readers) which simulates a keyboard.
I've already can read inputs from USB, but it happens before OS translate keycode in a charactere.
For example, when I read 'a' i got 24, 'b' 25, etc....
For example, when I read 'a' i got 4, 'b' 5, etc....
Is there any way to convert that code in a char without manual mapping?
Some output exemples:
KEYPRESS = a output = array('B', [0, 0, 4, 0, 0, 0, 0, 0])
KEYPRESS = SHIFT + a output = array('B', [2, 0, 4, 0, 0, 0, 0, 0])
KEYPRESS = 1 output = array('B', [0, 0, 30, 0, 0, 0, 0, 0])
KEYPRESS = ENTER output = array('B', [0, 0, 81, 0, 0, 0, 0, 0])
thx!
Use the chr function. Python uses a different character mapping (ASCII) from whatever you're receiving though, so you will have to add 73 to your key values to fix the offset.
>>> chr(24 + 73)
'a'
>>> chr(25 + 73)
'b'
I've already can read inputs from USB, but it happens before OS
translate keycode in a charactere.
The problem seems to me in your interface or the driver program.
In ASCII 'a' is supposed to have ordinal value 97 whose binary representation is 0b1100001, where as what you are receiving is 27 whose binary representation is 0b11000, similarly for 'b' you were supposed to received '0b1100010' instead you received 25 which is 0b11001. Check your hardware to determine if the 1st and the 3rd bit is dropped from the input.
What you are receiving is USB scan code. I do not think there is a third party python library to do the conversion for you. I would suggest you to refer any of the USB Scan Code Table and from it, create a dictionary of USB Scan Code vs the corresponding ASCII.

how to configure variable for read_in txt file like '0x12c'

In my c1.txt file, i have something like
var0 = '0x000000000000004080'
And in my python mail file:
def getVarFromFile(filename):
import imp
f = open(filename)
global data
data = imp.load_source('data', '', f)
f.close()
getVarFromFile('c1.txt')
The var0 is a 72-bit variable, and in my python file, I want to assign that to 12 variable with 6-bit for each, how can I do that?
Since var0 is a hex variable, seems I can't do
x = int(data.var1) & 0x3F
Thanks
I think all you need to do in your last line is provide a base for the integer conversion:
values_from_hex = int(data.var1, 16)
Then you can divide the value up into your 6-bit values (least significant bits first):
six_bit_values = [values_from_hex >> i*6 & 0x3f for i in range(12)]
For a value of '0x000000000000004080', this gets [0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0].

Scapy padding.load what is the meaning of 'g' and '|' symbols

I append to tcp packet unsinged long long with value 4 and additional unsigned long long with value 8616616 ( i dont remember the second value ).
I do it in c on ubuntu 32 , so unsigned long long is 8 bytes.
I sniff the packet with scapy and print the padding.load .
In the output i see symbols that i dont undesrtand the meaning of them - g, |
In additional the load should be 16 bytes , but i dont see 16 bytes.
If i append only one unsigned long long i get 8 bytes and i dont see these symbols
>>> pkt = sniff(count=2,filter="tcp")
>>> raw = pkt[1].sprintf('%Padding.load%')
>>> raw
"'\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00g\\xc4|\\x00\\x00\\x00\\x00\\x00'"
>>> print raw
'\x04\x00\x00\x00\x00\x00\x00\x00g\xc4|\x00\x00\x00\x00\x00'
When you are printing out the value of raw, python interprets all bytes that have a value greater than 31 as ASCII characters. When you see g the value for that byte is equal to 103, likewise | is the ASCII code for 124. For characters above 127, python uses a different representation which is why you have \xc4 in your output, the value of that byte is 196.
The actual value of each of the bytes in raw is:
[4, 0, 0, 0, 0, 0, 0, 0, 103, 196, 124, 0, 0, 0, 0, 0]
Which is 16 bytes long.
You can test this by converting the value of each byte back into a character:
>>> values = [4, 0, 0, 0, 0, 0, 0, 0, 103, 196, 124, 0, 0, 0, 0, 0]
>>> as_characters = ''.join(chr(c) for c in values)
>>> as_characters
'\x04\x00\x00\x00\x00\x00\x00\x00g\xc4|\x00\x00\x00\x00\x00'
>>> len(as_characters)
16
I think what you have for raw has had each of the bytes escaped. In my example, when I output as_characters I only see a single backslash, you have two. You may need to use something like pkt[1].sprintf('%Padding.loadr%') to get the non escaped version.

Categories