简体   繁体   中英

Simple 2-way serial communication between Raspberry Pi and Arduino

I want to do simple 2-way serial communication between my Raspberry Pi and my Arduino. This is for a project in which I will replace the Arduino with another serial device which I don't yet have.

I've done 1-way communication ( https://maker.pro/raspberry-pi/tutorial/how-to-connect-and-interface-raspberry-pi-with-arduino ) from the Arduino to the Raspberry Pi, but am having a little trouble with 2-way. The Arduino code I use is from this example: https://www.arduino.cc/en/Serial/Read :

int incomingByte = 0;   // for incoming serial data

void setup() {
        Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
}

void loop() {

        // send data only when you receive data:
        if (Serial.available() > 0) {
                // read the incoming byte:
                incomingByte = Serial.read();

                // say what you got:
                Serial.print("I received: ");
                Serial.println(incomingByte, DEC);
        }
}

And the Python code I use is this:

import serial
import time
ser = serial.Serial('/dev/ttyACM1',9600)
var1 = "3"
while True:
    ser.write(var1.encode())
    time.sleep(0.2)
    read_serial=ser.readline()
    print read_serial

After looking through the net I have changed the value to be sent from just ser.write('3') to a string 'var1' and put '.encode()' after in order to encode to bytes. No errors come, but nothing happens/is being written out.

The goal for this is for Raspberry Pi to send a '3' to the Arduino and the Arduino to respond with 'I've received: 3' which should be printed in the Raspberry Pi/Python's terminal window. From there I imagine that I can make it more complex towards my goal of sending a command like this: '0 30 50 100' which the device I don't have would respond to.

I appreciate any help. Thank you.

In my project my goal is to establish two-way data exchange between Arduino and Raspberry Pi via serial interface. The Raspberry Pi sends the Arduino command to execute, Arduino sends the Raspberry Pi sensor readings (currently a random number).

Currently, the project includes two scripts for Raspberry Pi, written on Python, and a program for Arduino. The first script for Raspberry Pi using the URWID library organizes the graphical interface and command input, the second script is used to communicate with a serial port. The sources are given below. The result of the operation is quite satisfactory, but maybe somewhere I'm doing something wrong? Is this solution of the problem correct?

Arduino software:

#define SERIAL_SPEED 19200 // the speed of the serial port
#define READ_SENSOR_INTERVAL 1000UL  // frequency of output to the serial port 

int IN1 = 7; 
int IN2 = 6;
int IN3 = 5;
int IN4 = 4;
int ENA = 9;
int ENB = 3;

char command     = 'S';
char prevCommand = 'A';
int velocity = (4 + 1) * 10 + 100; // the fill factor of the PWM

unsigned long timer0 = 2000; 
unsigned long timer1 = 0;    

long randNumber;
long myflag = 0;

void setup() {
  Serial.begin(SERIAL_SPEED);
  pinMode (ENA, OUTPUT);
  pinMode (IN1, OUTPUT);
  pinMode (IN2, OUTPUT);
  pinMode (ENB, OUTPUT);
  pinMode (IN4, OUTPUT);
  pinMode (IN3, OUTPUT);
}

void loop()
{
  static unsigned long prevSensorTime = 0;
  if (millis() - prevSensorTime > READ_SENSOR_INTERVAL) {
    prevSensorTime = millis();
    if (myflag == 1)
    {
      randNumber = random(300);
      Serial.print(command);
      Serial.println(randNumber);
    }
  }


  if (Serial.available() > 0) {
    timer1 = millis();
    prevCommand = command;
    command = Serial.read();
    myflag = 1;
    if (command != prevCommand) {
      switch (command) {
        case 'W':
          // Вперёд
          analogWrite(ENA, 0);
          analogWrite(ENB, 0);
          delay(20);
          digitalWrite (IN2, LOW);
          digitalWrite (IN1, HIGH);
          digitalWrite (IN4, LOW);
          digitalWrite (IN3, HIGH);
          analogWrite(ENA, velocity);
          analogWrite(ENB, velocity);

          break;
        case 'A':
          analogWrite(ENA, 0);
          analogWrite(ENB, 0);
          delay(20);
          digitalWrite (IN2, LOW);
          digitalWrite (IN1, HIGH);
          digitalWrite (IN4, HIGH);
          digitalWrite (IN3, LOW);
          analogWrite(ENA, velocity);
          analogWrite(ENB, velocity);
          break;
        case 'S':
          analogWrite(ENA, 0);
          analogWrite(ENB, 0);
          delay(20);
          digitalWrite (IN2, HIGH);
          digitalWrite (IN1, LOW);
          digitalWrite (IN4, HIGH);
          digitalWrite (IN3, LOW);

          analogWrite(ENA, velocity);
          analogWrite(ENB, velocity);
          break;
        case 'D':
          analogWrite(ENA, 0);
          analogWrite(ENB, 0);
          delay(20);
          // A
          digitalWrite (IN2, HIGH);
          digitalWrite (IN1, LOW);
          // B
          digitalWrite (IN4, LOW);
          digitalWrite (IN3, HIGH);
          analogWrite(ENA, velocity);
          analogWrite(ENB, velocity);
          break;
        case ' ': 
          //velocity = 0;
          analogWrite(ENA, 0);
          analogWrite(ENB, 0);
          break;
        default:  
          if ((command >= 48) && (command <= 57)) {
            if (command == 48)
            {
              velocity = 0;
            }
            else
            {
              velocity = (command - 48 + 1) * 10 + 100;
            }
          }
      }
    } 
  }
  else {
    timer0 = millis();  // Получение текущего времени
    if ((unsigned long)(timer0 - timer1) > 20000) {
      analogWrite(ENA, 0);
      analogWrite(ENB, 0);
      prevCommand = ' ';
    }
  }
}

Python GUI script

from __future__ import print_function, absolute_import, division
import subprocess
import urwid
import serial
from subprocess import Popen, PIPE
from time import sleep


def exit_on_q(key):
    global power
    global ser
    global spower
    global p
    global currc

    if key in ('q', 'Q'):
        p.stdin.write(b'Q\n')
        p.stdin.flush()
        sleep(1)
        raise urwid.ExitMainLoop()


    if key in ('w', 'W'):
    # forward
        currc = 'W - Forward'
        p.stdin.write(b'W\n')
        p.stdin.flush()

    if key in ('a', 'A'):
    # Left
        currc = 'A - Left'
        p.stdin.write(b'A\n')
        p.stdin.flush()

    if key in ('s', 'S'):
    # Backward
        currc = 'S - Backward'
        p.stdin.write(b'S\n')
        p.stdin.flush()

    if key in ('d', 'D'):
    # Right
        currc = 'D - Right'
        p.stdin.write(b'D\n')
        p.stdin.flush()


    if key in (' '):
    # Stop
        currc = 'Space - Stop'
        p.stdin.write(b' \n')
        p.stdin.flush()

    if key in ('+'):
        if (power < 99):
            power = power + 10 
            spower = spower + 1
            txt_CP.set_text(('banner', str(power)))

    if key in ('-'):
        if (power > 0):
            power = power - 10
            spower = spower - 1
            txt_CP.set_text(('banner', str(power)))

    txt_CCV.set_text(('banner', currc))

def enter_idle():
    loop.remove_watch_file(pipe.stdout)

def update_text(read_data):
    txt_Q.set_text(('banner', read_data))

if __name__ == '__main__':

    currc = "No command"

    palette = [
        ('banner', 'black', 'light gray'),
        ('streak', 'black', 'dark blue'),
        ('bg', 'black', 'dark blue'),]

    # spower = 0..9 (48 .. 57)
    spower = 4
    power = spower * 10

    txt_F = urwid.Text(('banner', u"W - Forward (\u2191)"), align='center')
    txt_LRS = urwid.Text(('banner', u"\u2190 A - Left | Space - Stop | D - Right \u2192"), align='center')
    txt_B = urwid.Text(('banner', u"S - Backward (\u2193)"), align='center')
    txt_P = urwid.Text(('banner', u"'+' Increase motor power | '-' Decrease motor power"), align='center')
    txt_C = urwid.Text(('banner', u"Current power:"), align='center')

    txt_CP = urwid.Text(('banner', str(power)), align='center')

    # current command
    txt_CC = urwid.Text(('banner', u"Current command: "), align='center')
    txt_CCV = urwid.Text(('banner', u"No command"), align='center')

    txt_Log = urwid.Text(('banner', u"Log: "), align='center')
    txt_LogV = urwid.Text(('banner', u""), align='center')

    txt_Q = urwid.Text(('banner', u"Q - Quit"), align='center')

    #empty string
    txt_E = urwid.Text(('banner', u""), align='center')

    pile = urwid.Pile([txt_F, txt_LRS, txt_B, txt_E, txt_P, txt_C, txt_CP, txt_E, txt_CC, txt_CCV, txt_E, txt_Log, txt_LogV, txt_E, txt_Q ])
    top = urwid.Filler(pile, top = 5)


    loop = urwid.MainLoop(top, palette, unhandled_input=exit_on_q, handle_mouse=False)

    stdout = loop.watch_pipe(update_text)
    stderr = loop.watch_pipe(update_text)   
    p = subprocess.Popen(['python3', 'shell_edt.py'], stdin = PIPE, stdout = stdout, stderr = stdout, shell = False)    
    loop.run()

Python communication script

import sys
import threading
import serial
from time import sleep

global currcomm

readtimer = 1 # 


def read():
    global serialport
    global currcomm

    threading.Timer(readtimer, read).start()

    if (currcomm != -1):
        data = serialport.read(10);
        print(str(data) + " : "  + str(len(data)))
        sys.stdout.flush();
        #sleep(0.5)     '''     


serialport = serial.Serial("/dev/ttyACM0", 19200, timeout=0.2)
data = serialport.read(100);


currcomm = -1

threading.Timer(readtimer, read).start()
sleep(1)

while True:
    currcomm = input()

    if (currcomm == 'S') or (currcomm == 'D') or (currcomm == 'W') or (currcomm == 'A') or (currcomm == ' '):
        serialport.write(bytes(currcomm, encoding = 'utf-8'));
    if (currcomm == 'Q'):
        serialport.close() # Only executes once the loop exits

I am late in replying to you but i hope this will help some one else. I was trying to have a two way communication where i can send and receive string data from both sides and here is what i did:-
Arduino side:-

void setup() {
   Serial.begin(9600); // begin transmission
}
void loop() {
  String val;
  while (Serial.available() > 0) {
    val = val + (char)Serial.read(); // read data byte by byte and store it
  }
  Serial.print(val); // send the received data back to raspberry pi
}

On the raspberry side i have(python):-

import serial
port = "/dev/ttyACM0"#put your port here
baudrate = 9600
ser = serial.Serial(port, baudrate)
def tell(msg):
    msg = msg + '\n'
    x = msg.encode('ascii') # encode n send
    ser.write(x)

def hear():
    msg = ser.read_until() # read until a new line
    mystring = msg.decode('ascii')  # decode n return 
    return mystring

while True:
    val = input() # take user input
    tell(val) # send it to arduino
    var = hear() # listen to arduino
    print(var) #print what arduino sent

I hope it is clear that Arduino receives message from raspberry pi and sends the same thing back to Arduino. Similarly, you could do some other stuff with it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM