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

Скриншот программы:

Screenshot

Блочная схема алгоритма работы: Block scheme

Программный код 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();
  }
}

Как я могу с этим справиться?

, 👍2

Обсуждение

Firmata обычно не делает что-то со строкой (команды двоичные), так что же вы хотите, чтобы arduino делал на самом деле? То, что вы видите (и почему в ответе так много 0), заключается в том, что Firmata кодирует тексты, как при отправке, так и при получении., @PMF

@PMF, вставка \0 не была бы проблемой, если бы строка, возвращаемая Arduino, содержала **все** символы, присутствующие в строке. Однако, наряду со вставкой \0, не каждый символ, присутствующий в отправленной строке, существует в полученной строке., @Starter

@PMF, отредактировал мой вопрос, чтобы он ответил на ваш комментарий., @Starter


3 ответа


Лучший ответ:

1

Вместо использования кодировки 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))
,

3

Вы должны понимать протокол Firmata, чтобы написать свою собственную клиентскую библиотеку. Спецификация здесь: https://github.com/firmata/protocol.

В данном конкретном случае вам нужно знать, что отправляемые данные (в данном случае строка) никогда не могут использовать бит 7 данных, так как эти значения имеют особые значения. Поэтому Firmata отправляет каждый байт как два байта, первый бит 0-6 и второй бит 7 как бит 0 следующего байта. Поэтому преобразования в UTF-8 недостаточно. Вам нужно сделать кодировку два байта на 8 бит. То же самое относится и к ответу.

,

Клиентская библиотека pyfirmata - это нормально, учитывая, что любая библиотека будет плохой в случае ее неправильного использования, как и в моей ситуации. В моем случае проблема была на стороне программы Python, @Starter


1

В качестве альтернативы, если вам нужна система без багажа древних MIDI-ограничений Firmata, таких как ее причудливый 7-битный предел, вы можете рассмотреть DaqPort https://www.daqarta.com/dw_rraa.htm

,

ваш вариант слишком сложен для учебы., @Starter