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?
free(value)