In python 2.7 I didn't have any problem sending char type variables, but due to problems when using Pyrebase I need to use Python 3.5, the problem arises when sending the data to the Arduino, I can't solve it. The arduino should receive data of type char. I clarify that I am working on Raspbian. I leave the code and the error. Thanks in advance. Code:
import pyrebase
import serial
puerto = serial.Serial('/dev/ttyACM0', 9600)
config = {
"apiKey": "Axxxxxxxxxxxxxxxxxxxxxxxxx",
"authDomain": "pxxxxxxxxxx.firebaseapp.com",
"databaseURL": "https://pxxxxxxxxxxxxxx.firebaseio.com",
"storageBucket": "pxxxxxxxxxx.appspot.com"
}
puerto = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
firebase = pyrebase.initialize_app(config)
db = firebase.database()
res = db.child("cambio").child("valor1").get()
while True:
res = db.child("cambio").child("valor1").get()
if (res == "True"):
puerto.write('p')
else:
puerto.write('q')
Mistake:
Traceback (most recent call last):
File "pyreb.py", line 30, in <module>
puerto.write('q')
File "/usr/lib/python3/dist-packages/serial/serialposix.py", line 518, in write
d = to_bytes(data)
File "/usr/lib/python3/dist-packages/serial/serialutil.py", line 63, in to_bytes
raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq))
TypeError: unicode strings are not supported, please encode to bytes: 'q'
It's telling you that it doesn't support unicode encoding for passing strings, and that you have to pass them as bytes. Try something like:
source: https://www.mkyong.com/python/python-3-convert-string-to-bytes/
TL;DR: You have to call the method
encode()
on your string before you can send it.Long explanation of the hows and whys:
Strings in Python 2
In python 2, the type
str
actually represents a string of bytes , rather than characters. What happened is that every time your program used a string, the interpreter implicitly handled it as a byte string, converting it on the fly.The problem is that to convert a character to a byte, this can only be done with certain encodings, such as ASCII (but it is restricted to the English alphabet), or the ISO-8859 standards (but there are different standards depending on the alphabet used).
Unicode solves the problem by breaking the (1 character) -> (1 byte) equivalence. In Unicode each character is no longer a single byte. For example, using UTF-8, a character can occupy 1 byte (if it is an ascii, that is, a code less than 128), or 2 bytes (if it is a code between 128 and 2048), or 3 bytes (if it is a code between 2048 and 65536), or 4 bytes (if it is a code greater than 65536).
Python2 "decided" for you which encoding to use when converting your strings to bytes. Once decided, I always worked with the bytes. This could lead to surprises, like this:
and the answer could be 3 if the chosen encoding was ISO-8859-1 (in which each character, including the
ñ
, occupies a single byte). But it could also be 4, if the encoding chosen was UTF-8 (because in this theñ
, being a code greater than 128, occupies two bytes). Actually python doesn't choose which encoding to use at random, but it takes a (mandatory) comment that you had to put at the beginning of the file, of the type:# coding: utf-8
for example.Now when you're doing input/output, the bytes you want to output may not follow the same encoding as your source code.
For example, you have written the python program in UTF-8, and therefore you have put the comment
# coding: utf-8
at the beginning. Your program then initializes the variabletexto = "año"
, which implies that ittexto
will be a 4-byte string. Finally you send the variabletexto
to an output device, such as an arduino display.What encoding is that display expecting? It will receive 4 bytes (specifically, its values would be:
61, C3, B1, 6F
, in hexadecimal). What the display will show depends on what encoding you are expecting:"año"
.año
(maybe have you ever seen this...)Strings in Python3
In python3 it was decided to end implicit conversions from characters to bytes.
Internally, python stores all characters as "unicode dots" and not as bytes. Each character results in a single unicode point. This avoids the problems seen before when calculating
len("año")
, which could give 3 or 4 depending on whether we used ISO-8859-1 or UTF-8 (it could even give 6 if we used UTF-16, since in this encoding each character is two bytes). In python 3len("año")
it always returns 3, which simplifies a lot of things when we want to adjust the length of what is displayed to a certain width, for example.But when we want to input/output a string, it will be necessary to convert it to bytes explicitly . That is, python won't do it automatically for us.
This has the disadvantage that our python2 programs are no longer compatible, since all the conversions must be made explicit (also the input ones, because when we read something from a device we will read bytes, and if we want to treat it as a string we will have to convert it to char) . But it has the advantage that we have full control over the encoding used, especially important when our different I/O devices don't all use the same encoding.
In the example above, if we know that our display expects ISO-8859-1, even though our editor saved the text in UTF-8, that doesn't matter, since what python stores in the variable
texto
is 3 unicode dots. When we are going to send it to the display we "encode" it to a sequence of bytes, specifying which encoding the display uses, for example like this:or this way:
or, if you were to use utf-8, like so:
Now we have two different types.
texto
is of typestr
(which is no longer a sequence of bytes but a sequence of unicode dots, I insist), andcodificado
which is of typebytes
and would be the equivalent of the old python2 strings.If you print to the console (with
print
) the value ofcodificado
, you will see that it puts ab
before it, to indicate that it is a string of bytes . For example:The
b
indicates that it is a string of bytes, and then we see the representation of those bytes. If the byte in question can be represented as ASCII (as ina
oro
) it will display it "as is". If not, it will show\x
followed by the hexadecimal code of the byte (in this case we see that theñ
has been encoded with a value bytef1
).That sequence of bytes can now be used in an input/output operation, such as sending it to a file, or through a socket, or through a serial port. Of course, keep in mind if whoever is "on the other side" of the communication is going to understand the encoding you are using. At the very least, by having to use
encode()
python3 explicitly, it forces us to be aware of this problem and code it properly.The error says that you can't send a character directly, but instead requires you to send a byte/bytearray.
You can achieve this by putting a "b" just before the string:
Therefore the solution could be the following:
You can check more here: http://learning-python.com/strings30.html