Firmata: строки, отправленные с компьютера, повреждаются после Arduino
У меня есть программа на Python, которая действует как клиент Firmata. Эта программа подключается к Arduino через указанный пользователем COM-порт (с
Firmata
на борту скетч можно увидеть ниже), отправляет запрошенное пользователем строковое сообщение Arduino и ждет ответа Arduino. Если ответ присутствует, программа выводит его в виде списка кодов символов ASCII.
Я ожидаю, что программа и скетч будут работать таким образом (блок-схема ниже), но строки, идущие от Arduino, повреждены. Они не содержат всех символов, которые были отправлены, но они содержат только первые символы в некотором случайном порядке, с символом \0
, присутствующим после каждого символа в строке. Например,
Строка, отправленная на Arduino | Полученные в виде кодов символов ASCII | Получено в виде строки |
---|---|---|
A |
(65, 0) |
A\0 |
AB |
(65, 0) где находится B ? |
A\0 |
АЗБУКА |
(65, 0, 67, 0) В отсутствует присутствие |
A\0C\0 |
ABCD |
(65, 0, 67, 0) длина полученных данных остается прежней, но B и D отсутствуют. |
A\0C\0 |
ABCD(10) |
(65, 0, 67, 0, 40, 1, 48, 1) ЧТО ЭТО ТАКОЕ? |
A\0C\0(\10\1 |
DHTt(4) |
(68, 0, 84, 0, 40, 0, 41, 1) |
D\0T\0(\0)\1 |
Скриншот программы:
Блочная схема алгоритма работы:
Программный код Python:
import pyfirmata, time
class CustomArduino(pyfirmata.Arduino):
def __init__(self, port: str):
pyfirmata.Arduino.__init__(self, port)
self.iterator = pyfirmata.util.Iterator(self)
self.iterator.start()
self.add_cmd_handler(0x71, self.receiveString)
def receiveString(self, *args, **kwargs):
print(f'Received: {args}')
def sendString(self, data):
self.send_sysex(0x71, bytearray(data, 'utf-8'))
def send_sysex(self, sysex_cmd, data=[]):
"""
Sends a SysEx msg.
:arg sysex_cmd: A sysex command byte
: arg data: a bytearray of 7-bit bytes of arbitrary data
"""
msg = bytearray([pyfirmata.pyfirmata.START_SYSEX, sysex_cmd])
msg.extend(data)
msg.append(pyfirmata.pyfirmata.END_SYSEX)
self.sp.write(msg)
print('STRING TESTER\nPlease input your COM port: ', end = '')
myCom = str(input())
device = CustomArduino(myCom)
while True:
device.sendString(input('Send: '))
time.sleep(1)
Скетч Arduino:
#include <Firmata.h>
void stringCallback(char *myString)
{
//Функция принимает одну кодировку.
Firmata.sendString(myString); //отправить *ту же* кодировку обратно
}
void sysexCallback(byte command, byte argc, byte *argv)
{
Firmata.sendSysex(command, argc, argv);
}
void setup()
{
Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
Firmata.attach(STRING_DATA, stringCallback); //когда в Arduino поступает СТРОКА, вызовите 'stringCallback'.
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.begin(57600);
pinMode(13, OUTPUT);
}
void loop()
{
while (Firmata.available()) {
Firmata.processInput();
}
}
Как я могу с этим справиться?
@Starter, 👍2
Обсуждение3 ответа
Лучший ответ:
Вместо использования кодировки UTF-8
,
def receiveString(self, *args, **kwargs):
print(f'Received: {args}')
def sendString(self, data):
self.send_sysex(0x71, bytearray(data, 'utf-8'))
необходимо использовать специальные методы манипуляции pyfirmata bytearray:
two_byte_iter_to_str
и str_to_two_byte_iter
:
def receiveString(self, *args, **kwargs):
print(f'Received: {pyfirmata.util.two_byte_iter_to_str(args)}')
def sendString(self, data):
self.send_sysex(0x71, pyfirmata.util.str_to_two_byte_iter(data))
Вы должны понимать протокол Firmata, чтобы написать свою собственную клиентскую библиотеку. Спецификация здесь: https://github.com/firmata/protocol.
В данном конкретном случае вам нужно знать, что отправляемые данные (в данном случае строка) никогда не могут использовать бит 7 данных, так как эти значения имеют особые значения. Поэтому Firmata отправляет каждый байт как два байта, первый бит 0-6 и второй бит 7 как бит 0 следующего байта. Поэтому преобразования в UTF-8 недостаточно. Вам нужно сделать кодировку два байта на 8 бит. То же самое относится и к ответу.
Клиентская библиотека pyfirmata - это нормально, учитывая, что любая библиотека будет плохой в случае ее неправильного использования, как и в моей ситуации. В моем случае проблема была на стороне программы Python, @Starter
В качестве альтернативы, если вам нужна система без багажа древних MIDI-ограничений Firmata, таких как ее причудливый 7-битный предел, вы можете рассмотреть DaqPort https://www.daqarta.com/dw_rraa.htm
ваш вариант слишком сложен для учебы., @Starter
- Динамически обновить масштаб виджета Tkinter из портов Arduino с помощью python и firmata
- Цифровые входные контакты на arduino nano со стандартными firmata и pyfirmata всегда «Нет»
- Firmata: как установить определенный PIN на высокий уровень при загрузке?
- Как работать с аналоговыми показаниями с помощью RPi и Firmata?
- Как заставить Arduino Nano управлять шаговым двигателем с помощью Firmata
- Firmata.SendString не работает с конкретными переменными `char`
- Плата Arduino с StandardFirmata не отвечает на запросы клиентов C# и Python
- Diecimila со стандартом Firmata не работает
Firmata обычно не делает что-то со строкой (команды двоичные), так что же вы хотите, чтобы arduino делал на самом деле? То, что вы видите (и почему в ответе так много 0), заключается в том, что Firmata кодирует тексты, как при отправке, так и при получении., @PMF
@PMF, вставка
\0 не была бы проблемой, если бы строка, возвращаемая Arduino, содержала **все** символы, присутствующие в строке. Однако, наряду со вставкой
\0, не каждый символ, присутствующий в отправленной строке, существует в полученной строке., @Starter@PMF, отредактировал мой вопрос, чтобы он ответил на ваш комментарий., @Starter