Ардуино случайным образом прекращает отправку данных на python по последовательному

Я хочу отправлять команды Arduino через последовательный USB из моей программы на python, и Arduino должен отправить обратно полученную команду, чтобы убедиться, что данные не были потеряны. Однако Arduino случайным образом прекращает отправку данных обратно. Для целей отладки я заставил встроенный светодиод мигать в функции цикла. Когда Arduino перестает отправлять данные, светодиод замедляет мигание (примерно 2-3 раза). Я попробовал это на Arduino Uno и Arduino Nano, чтобы убедиться, что плата не повреждена.

Вот мой код на Python, отвечающий за связь:


class Communication(ABC):
    def __init__(self) -> None:
        self.history: list = []
        self.response_history: list = []
        self.last_responses: dict = dict()
        self.queue: collections.deque[str] = collections.deque()
        self.is_sending: bool = False
        self.command_recived_condition: threading.Condition = threading.Condition()
    
    def append_commands(self, commands: str) -> None:
        commands = map(lambda x: "c"+x, map(lambda x: x+";", filter(len, commands.split(";"))))
        for command in commands:
            self.queue.append(command)
    
    def append_and_send(self, commands: str) -> None:
        self.append_commands(commands)
        if not self.is_sending:
            self.sender: threading.Thread = threading.Thread(target=Communication.send_commands, args=(self, ))
            self.sender.start()

    
    @abstractmethod
    def send_command(self, command: str) -> bool:
        """ Sends command to the controller, waits for the response and tries again if command was not recived succesfully """
        pass
    
    @abstractmethod
    def listen(self) -> None:
        pass
    
    @abstractmethod
    def check_for_response(self, command: str) -> None:
        pass
    
    def send_commands(self):
        self.is_sending = True
        commands = self.queue
        while len(commands) > 0:
            command = commands.popleft()
            self.send_command(command)
        
        self.is_sending = False
        
    
class Serial_comunication(Communication):
    def __init__(self, port: str) -> None:
        super().__init__()
        self.serialcom = serial.Serial(port, 4800)

        self.listen()

        time.sleep(0.1)
        self.send_command("PING;")
        time.sleep(0.1)
        
    def listen(self) -> None:
        def listen_util() -> None:
            chars = []
            
            while True:
                if self.serialcom.inWaiting() > 0:
                    time.sleep(0.01)
                    try:
                        char: str = self.serialcom.read().decode()
                    except UnicodeDecodeError:
                        print("decoding err")
                        continue
                    print(char, end=" ")

                    if char[-1] == '#':
                        response_str = "".join(chars)
                        category, content = response_str.split("::")
                        self.last_responses[category] = content
                        self.response_history.append(response_str)
                        chars.clear()
                        print("\n", response_str)
                        if category == 'Info: recived':
                            with self.command_recived_condition:
                                self.command_recived_condition.notify()
                    else:
                        chars.append(char)
            
                time.sleep(0.01)
                    
        self.listener = threading.Thread(target=listen_util)
        self.listener.start()
                    
        
        
    def check_for_response(self, command: str) -> None:
        print("check")
        
        if self.last_responses.get('Info: recived') == command:
            print('possitive')
            return True
    
        return False


    def send_command(self, command: str) -> None:

        while True:
            time.sleep(0.1)
            self.serialcom.write(command.encode())
            print("sent", command)

            with self.command_recived_condition:
                self.command_recived_condition.wait()
                print("waited")
            
            if self.check_for_response(command.strip().strip(';')):
                break

И мой код Arduino, отвечающий за связь:

#define INPUT_SIZE 50
char input[INPUT_SIZE + 1];

bool check_for_orders(Order& cmd, char *val, uint8_t& place)
{
    if (Serial.available() > 0)
    {
        // Считывать ввод с последовательного до ; что означает, что команда завершена
        size_t size = Serial.readBytesUntil(';', input, INPUT_SIZE);
        input[size] = 0; // Add null terminator

        Serial.print(F("Info: recived::"));
        delay(10);
        Serial.print(input);
        Serial.print(F("#"));
        Serial.flush();

        // Проверьте, не пуста ли команда
        if (input[0] != 0)
        {
            // синтаксис "порядок:шаблон:место: значение"
            cmd = static_cast<Order>(atoi(strtok(((char*)input)+1, ":"))); // Get order numer and convert to enum
            place = atoi(strtok(NULL, ":")); // Get place number from input (only 0 or 1 is allowed)
            char *value = strtok(NULL, ":"); // Get value from second part of string
            
            if (value == 0) // Если значение пустое, то синтаксис недопустим
            {
                return false;
            }

            // В противном случае скопируйте имя и значение заказа в c-строки из основной функции
            strcpy(val, value);
            free(value);

            return true;
        }
        else // Вероятно, этого никогда не случится
        {
            return false;
        }
    }
    return false;
}

Функция check_for_orders запускается при каждом запуске функции цикла.

Вот что напечатал python, когда я отправил следующие команды ["PING;", "c0:0:0", "c0:1:0; c15:0:100,600,20,2,0,200,255,170,255,200;"]

sent PING;
I n f o :   r e c i v e d : : P I N G # 
 Info: recived::PING
waited
check
possitive
sent c0:0:0;
I n f o :   r e c i v e d : : c 0 : 0 : 0 # 
 Info: recived::c0:0:0
waited
check
possitive
sent c0:1:0;
I n f o :   r e c i v e d : : c 0 : 1 : 0 # 
 Info: recived::c0:1:0
waited
check
possitive
sent c15:0:100,600,20,2,0,200,255,170,255,200;
I n f o :   r e c i v e d : : c 1 5 : 0 : 1 0 0 , 6 0 0 , 2 0 , 2 , 0 , 2 0 0 , 2 5 5 , 1 7 0 , 2 5 5 , 2 0 0 # 
 Info: recived::c15:0:100,600,20,2,0,200,255,170,255,200
waited
check
possitive
sent c1:0:1;
/* and it got stuck */

Кто-нибудь знает, почему что-то здесь застряло и/или как решить эту проблему?

, 👍2

Обсуждение

4800 бод (я думаю, это ваша скорость передачи данных) - это довольно зрелищно. В зависимости от того, как часто и сколько вы отправляете, вы можете легко перегрузить последовательный интерфейс. Вы пробовали использовать более высокий коэффициент бодрствования? И можете ли вы привести минимальный рабочий пример, который показывает ваш код? Я имею в виду полный рабочий код, но без какого-либо не относящегося к делу кода, и это все равно показывает вашу проблему., @chrisl

не " бесплатно(ценность)`, @Juraj


1 ответ


2

Я удалил free(значение) - ошибку новичка - но это не полностью решило проблему (но снизило частоту возникновения). Похоже, объявлять массив как char val[50] и передавать его в функцию как char* val было плохой идеей. Я изменил объявление массива на char *val = новый символ[50], и теперь, похоже, все работает нормально.

,

Отправка char* val допустима до тех пор, пока: 1. вы не переполняете val; 2. вы разрешаете последний байт для байта NULL; 3. " val " всегда заканчивается нулем, независимо от размера, или вы также отправляете его размер, @Binar Web