proper communication between python and Arduino IDE

135 Views Asked by At

I am currently working on a project that uses emotion detection to turn one of two motors based on the emotion it picks up. The issue im having is that whenever i input "happy" or "sad" the corresponding motor does not move at all. Before i had this issue, the motor would twitch a little and thats it. All of my code compiled correctly and i have double checked that all connections are correct and there is infact power running through the system and reaching the motors. The motor i am using is a PWM motor, MG995 with the Aduino Uno and the Adafruit 16x12-bit PWM & Servo Shield.

This is the Arduino code:

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <Servo.h>
#include <SoftwareSerial.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();// Create an instance of the Adafruit PWM Servo Driver

// Create two instances of the Servo library
Servo servo1;
Servo servo2;
// Software serial instance to receive serial communication from Python
SoftwareSerial serial(2, 3); 

void setup() {
  // Start the serial communication with the Python script
  serial.begin(9600);
  Serial.println("Setup Complete");

  // Initialize the PWM driver
  pwm.begin();
  pwm.setPWMFreq(50);

  // Attach the two motors to the Servo library
  servo1.attach(0, 500, 2500);
  servo2.attach(1, 500, 2500);

  // Set servo1 and servo2 to center position
  servo1.write(90);
  servo2.write(90); 
}

String input = "";

void loop() {
// Check if there is serial data available
if (Serial.available() > 0) {
// Read the serial data as a string
  char input [10];
  Serial.readBytesUntil('\n' , input , sizeof(input));
  // Check if the input is "happy"

  if (strcmp(input, "happy") == 0 ) {
  // Move servo1 to 0 degrees
    servo1.write(0);
    delay(1000);
  // Move servo1 to 180 degrees
    servo1.write(180);
    }
// Check if the input is "sad"
  else if (strcmp(input, "sad") == 0) {
  // Move servo2 to 0 degrees
    servo2.write(0);
    delay(1000);
  // Move servo2 to 180 degrees
    servo2.write(180);
    }
  }
}

and this is the python code:

from pyfirmata import Arduino
import serial
import time

ser = serial.Serial('COM4', 9600)

while True:
    input_str = input("Enter 'happy' or 'sad': ")
    if input_str == 'happy':
        #print(input_str)
        ser.write(input_str.encode())
        time.sleep(2)
        ser.flushInput()
    elif input_str == 'sad':
        #print(input_str)
        ser.write(input_str.encode())
        time.sleep(2)
        ser.flushInput()
    else:
        print("Invalid input. Try again.")

ser.close()

I have double checked that the baud rate and the Port are the same in both the arduino and python, and I'm aware that you cannot have both serial terminals active at the same time.

When i use pythoon send the input, the code runs as expected, asking user to enter 'happy' or 'sad', waits a couple seconds for the motor to turn and then asks again "Enter 'happy' or 'sad'". Only thing is that none of the motors turn, so i am currently unsure if this is a communication issue or if its something else.

This is the output from python after an input:

Enter 'happy' or 'sad': happy
Enter 'happy' or 'sad': sad
Enter 'happy' or 'sad': angry
Invalid input. Try again.
Enter 'happy' or 'sad': 

i have tried to play with the different output types and rewriting the code but to no avail. I also noticed that some of the output written in the Void setup dont work as well.

2

There are 2 best solutions below

1
On

Alright, after some hours of trial and error, and with the help of those who gave suggestions, I was able to figure it out. So the problem was not with the python code but with the Arduino code. I changed one command which was the ** Serial.readBytesUntil('\n' , input , sizeof(input));** to String input_str = Serial.readStringUntil('\n');. I believe the issue was that python was sending strings but the Arduino was looking for a byte input, which was never being sent in the first place.

Here's the rewritten Arduino Code:

'''

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

//Defining the minimum and maximum the servo can rotate
#define SERVOMIN 125
#define SERVOMAX 700

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

void setup() {
Serial.begin(9600);
pwm.begin();
pwm.setPWMFreq(50);  //setting Frequency (~50Hz)
pwm.setPWM(0, 0, SERVOMIN); //setting starting position for servo 1 at 0 
degrees
pwm.setPWM(1, 0, SERVOMIN); //setting starting position for servo 2 at 0 
degrees
}

void loop() {
if (Serial.available() > 0) {
String input_str = Serial.readStringUntil('\n');

if (input_str == "happy") {
  for (int i = SERVOMIN; i < SERVOMAX; i++) {
  pwm.setPWM(0, 0, i);
  delay(3);
  }
  for (int i = SERVOMAX; i > SERVOMIN; i--) {
  pwm.setPWM(0, 0, i);
  delay(3);
  }
  }

else if (input_str == "sad") {
  for (int i = SERVOMIN; i < SERVOMAX; i++) {
  pwm.setPWM(1, 0, i);
  delay(3);
  }
  for (int i = SERVOMAX; i > SERVOMIN; i--) {
  pwm.setPWM(1, 0, i);
  delay(3);
  }
  }

else {
  Serial.println("Invalid input. Try again.");
  }
}
}

'''

and here is the accompanying Python Code:

'''

from pyfirmata import Arduino
import serial
import time

ser = serial.Serial('COM4', 9600)

while True:
input_str = input("Enter 'happy' or 'sad': ")
if input_str == 'happy':
    ser.write(input_str.encode())
    time.sleep(5)
elif input_str == 'sad':
    ser.write(input_str.encode())
    time.sleep(5)
else:
    print("Invalid input. Try again.")

ser.close()

'''

2
On

If you're not sure if it's a connection issue, I suggest you change the arduino code to just print back the input to your computer.

Like so in your arduino code:

if (strcmp(input, "happy") == 0 ) {
  Serial.println(input);

}
// Check if the input is "sad"
else if (strcmp(input, "sad") == 0) {
    Serial.println(input);
}

And so in you python code:

if input_str == 'happy':
    #print(input_str)
    ser.write(input_str.encode())
    time.sleep(2)
    print(ser.readline())
elif input_str == 'sad':
    #print(input_str)
    ser.write(input_str.encode())
    time.sleep(2)
    print(ser.readline())

But from what you're telling I'm assuming it's a hardware issue. I recommend you test the motors with a simple program that doesn't use input.