Unable to communicate with python script

I'm trying to have an Arduino Uno communicate via serial with a python script and I'm having issues with the responses. The arduino has a number of commands to execute based on whatever is received.

Everything works fine using the IDE's serial monitor, I introduce number, if it's a valid command I get a response if it's not valid whatever was sent is echoed back.

This is the main loop in the arduino:

void ProcessCommand(char command){
  switch(command){
     ...
    case '8':
      break;
  
    default:
      Serial.print(command);
      break;
  }
}

void loop() {
  if(Serial.available()>0){
    int command=Serial.read();
    ProcessCommand(command);
  }
}

Here's a very stripped down version of my python code that I've been using for testing:

arduino=serial.Serial('COM25',9600,timeout=1)
arduino.write(b'3')
response=arduino.read(1)
print(response)

With this code no response is ever displayed. 3 happens to be a valid command, but invalid commands give me the same result. Looking at the arduino the RX LED never flashes, but the TX LED does, so it is receiving something, so I at least should get an echo back, but no. As far as I can tell Serial.available() is always 0.

As a sanity test I tried using docklight to send commands to the board,and it appears to work except I only the arduino only replies after 3 commands have been sent, which is incredibly weird.

void ProcessCommand(char command){
  switch(command){
     ...
    case '8':
      break;
  
    default:
      break;
  }
}

void loop() {
  if(Serial.available()>0){
    char command=Serial.read();
    Serial.print(command);
    ProcessCommand(command);
  }
}
arduino=serial.Serial('COM25',9600,timeout=1)
arduino.write(b'3')
if arduino.in_waiting > 0:
  response=arduino.read(1).decode()
  print(response)

So, decode the arduino's response in the python script and change the type from int to char of the 'command' variable in the Arduino code?

The behavior remains the same, Docklight only gets a response after 3 tries, and the python script doesn't get anything at all

Your description seems to be different from the code snippet you have shown us.
Python code sends a byte containing the ASCII code for the character '3', but your switch..case does nothing (at least it seems to do nothing, even if you show us just the '8' command) except for "default" (invalid?) commands, where it sends the command back to sender. Some more code lines (a full switch..case, just to start, and possibly variable definitions ans setup procedures) could help us to better understand what's happening. For instance: are you sure on setup() you set the same serial speed as the Python one ("Serial.begin(9600);")?

Here's the entire arduino code

const int AVD_PIN=2;
const int AVDLEG_PIN=24;
const int MDB_PIN=3;
const int ALARM_PIN=4;
const int RS232A_PIN=5;
const int RS232B_PIN=6;
const int RS232C_PIN=7;
const int RS232D_PIN=8;
const int USB_PIN=9;
const int ANDROID_PIN=10;
const int RJ45_PIN=11;

volatile unsigned long currentTime;

unsigned long edges[10];

volatile int edgeFlag=0;

void setup() {
  Serial.begin(9600);

  pinMode(AVD_PIN,INPUT);
  pinMode(AVDLEG_PIN,INPUT);
  pinMode(MDB_PIN,INPUT);
  pinMode(ALARM_PIN,INPUT);
  pinMode(RS232A_PIN,INPUT);
  pinMode(RS232B_PIN,INPUT);
  pinMode(RS232C_PIN,INPUT);
  pinMode(RS232D_PIN,INPUT);
  pinMode(USB_PIN,INPUT);
  pinMode(ANDROID_PIN,INPUT);
  pinMode(RJ45_PIN,INPUT);

  attachInterrupt(digitalPinToInterrupt(AVD_PIN),AVD_Edge,CHANGE);
}

void AVD_Edge(){  //AVD rising edge ISR
  edgeFlag=1;
  currentTime=micros();
}

void AVD_StandardUnlockTest(){
  edgeFlag=0;
  for(int i=0;i<10;){
    if(edgeFlag){ //change ocurred
      edges[i++]=currentTime;
      edgeFlag=0;
    } 
  }
}

void ProcessCommand(char command){
  switch(command){
    case '1': //test AVD standard
      AVD_StandardUnlockTest();
      for(int i=0;i<9;i++){
        unsigned long duration=(edges[i+1]-edges[i])/100;
        Serial.print(duration,DEC);
        Serial.print(' ');
      }
      break;
    case '2': //test AVD legacy
      break;
    case '3':// Alarm Pw
      Serial.print(digitalRead(ALARM_PIN));
      break;
    case '4'://MDB Pw
      Serial.print(digitalRead(MDB_PIN),BIN);
      break;
    case '5'://USB Pw
      Serial.print(digitalRead(USB_PIN),BIN);
      break;
    case '6'://RJ45 Pw
      Serial.print(digitalRead(RJ45_PIN),BIN);
      break;
    case '7'://RS232 (all 4)
      Serial.print(digitalRead(RS232A_PIN),BIN);
      Serial.print(digitalRead(RS232B_PIN),BIN);
      Serial.print(digitalRead(RS232C_PIN),BIN);
      Serial.print(digitalRead(RS232D_PIN),BIN);
      break;
    case '8': //android Pw
      Serial.print(digitalRead(ANDROID_PIN),BIN);
      break;
    case '9': //testing
      Serial.print(digitalRead(AVD_PIN),BIN); 
    default:
      Serial.print(command);
      break;
  }
}

void loop() {
  if(Serial.available()>0){
    char command=Serial.read();
    ProcessCommand(command);
  }
}








Ok, but start with a very simple code like this, to test the serial communication:

void setup() {
  Serial.begin(9600);
  pinMode(pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

void loop() {
  if(Serial.available()>0){
    char command=Serial.read();
    if (command == '3')
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}

If from Python you send the command '3', it should switch the internal LED.

If it doesn't work, it's probably something either on the link (you crossed TX and RX, yes?) or on Python side like the open mode (must be 9600 baud, 1 stop, no parity, no Xon/Xoff, no CTS/RTS hardware handshake...). Check the python serial open statement.

As an addon, my code as previously worked on a Due. The RX and TX lines can't really be crossed since I'm only connected to the Arduino using the USB port, but I'll try the simplified version

this does not offer all options for debugging
the code should switch on off an LED in case something at all is received

If you happen to have a USB-to-TTL-interface you could connect this interface to IO-pins 0,1 and connect a serial terminal program to let print what gets received
or connect 1 LEDs to IO-pins and light up the ASCII-code of the received character

or use software serial or a microcontroller that hat to hardware UARTS to make visible what really is received

best regards Stefan

I don't have a TTL interface at hand right this instant, but I can get one tomorrow.

As for the LED toggle... the LED didn't toggle, but I suspect that its broken. What I did end up doing is add a serial print statement in one of the switch cases, which as triggered correctly and worked as expected through the serial monitor. This time I tried Tera Term as well which worked, but the python script still doesn't work.

Is there some encoding I should be doing on the python side? Right now I'm sending a literal '3' as bytes

The plot thickens. I decided to follow up on the issue that the arduino only replies on the 3rd try when using literally any other serial terminal other than the one in the IDE(TeraTerm and Docklight) and siply repeated my python code 3 times.

import serial

arduino=serial.Serial('COM25',9600,timeout=1)
arduino.write(b'3')
response=arduino.read(1)
arduino.write(b'3')
response=arduino.read(1)
arduino.write(b'3')
response=arduino.read(1)

print(response)

This appears to work consistently, if I remove the last write it won't work. I'm honestly stumped, does the serial terminal in the IDE send some invisible characters I'm missing? A LF or CR?

The Uno resets when you open the serial port; it can take some time. Maybe that time is the time that the python code sends the first two '3' and only the 3td one is detected by the Arduino.

Put a delay (2 seconds or so, maybe start with 5) in your python script after opening the port.

Although there is not much wrong with your choice of category where you posted the question, I've moved your topic to the section dedicated to communication with the PC.

PS
Maybe Demo of PC-Arduino comms using Python can be useful for you.

Yup, that's it, thanks. A delay after opening the port fixed it, started with 5 but found 2 seconds to be enough, 1 no longer works.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.