2

I want to send commands to Arduino over USB Serial from my python program and Arduino should send back received command to make sure data wasn't lost. However Arduino randomly stops sending data back. For debugging purposes I made builtin LED blink in the loop function. When Arduino stops sending data the LED slows down blinking (about 2-3 times). I tried this on Arduino Uno and Arduino Nano to make sure it is not damaged board.

Here is my Python code responsible for communication:


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

And my Arduino Code responsible for communication:

#define INPUT_SIZE 50
char input[INPUT_SIZE + 1];
bool check_for_orders(Order& cmd, char *val, uint8_t& place)
{
 if (Serial.available() > 0)
 {
 // Read input from Serial until ; what means command has ended
 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();
 // Check if command is not empty
 if (input[0] != 0)
 {
 // syntax is "order:pattern_place:value"
 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) // If value is empty then syntax is invalid
 {
 return false;
 }
 // Otherwise copy order name and value to c-strings from main function
 strcpy(val, value);
 free(value);
 return true;
 }
 else // Probably never gonna happen
 {
 return false;
 }
 }
 return false;
}

The check_for_orders function is run every time loop function is run.

Here is what python printed when I sent the following commands ["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 */

Do someone know why something here is getting stuck and/or how to solve this problem?

asked Jul 7, 2021 at 10:50
2
  • 4800 baud (I guess that is your baudrate) is pretty show. Depending on how often and how much you send you can easily overload the serial interface. Have you tried using a bigger baudrate? And can you provide a minimal working example, that shows your code? I mean a full working code, but without any non relevant code in it and that still shows your problem. Commented Jul 7, 2021 at 11:05
  • 4
    don't free(value) Commented Jul 7, 2021 at 11:52

1 Answer 1

2

I removed free(value) - rookie mistake - but that didn't completely solved the problem (but reduced frequency of occurrences). Seems like declaring array like char val[50] and passing it to function as char* val was a bad idea. I changed array declaration to char *val = new char[50] and now seems like everything works fine.

answered Jul 8, 2021 at 11:49
1
  • 1
    Sending char* val is fine as long as: 1. you don't overflow the val; 2. you allow the last byte for NULL byte; 3. the val is always NULL terminated, no matter the size or you also send the size of it Commented Jul 10, 2021 at 6:53

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.