Once the UART port is configured to be used on the Raspi, now I can send commands to my microcontroller. For the purposes of showing how this works, I will make the robot move based on typical "WSDA" inputs from the keyboard.
One key point to make about working with the Raspi's UART is that it operates at 3.3V. Arduino boards operate at 5V, so there needs to be some interface to convert the infromation from the Raspi up to 5V from 3.3V and convert the 5V from the Arduino down to 3.3V. Failure to do so can result in permanently damaging one or both devices. A perfect device for this is a cheap little Logic Level Converter. I have an earlier version of the one I linked in, but mine just has two less ports that the newer one. This device works pretty simply: it uses transistors as a switch to either turn 3.3V to 5V or 5V to 3.3V. See the schematic Sparkfun provides to get a better understanding of its operation.
Robot setup |
Motor control electronics |
Raspi and subsequent electronics |
Below is a full schematic of how the electronics are connected for this example.
Schematic for UART control example |
Below is the C code that will run on the Raspi to send commands to the Arduino to control the motors. The Raspi will interpret "WSDA" commands and send the appropriate key characters to the Arduino and then the Arduino will interpret them as movement commands. Visit my Github repository to download the source code directly.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main(int argc, char* argv[]) { // Serial port structure for configuration struct termios serial; // Ports are defined like files in Unix char* port = "/dev/ttyS0"; // Stores input key from keyboard char key; // Print which port is going to be opened to console printf("Opening %s\n", port); // Open port int uartPort = open(port, O_RDWR | O_NOCTTY | O_NDELAY); // Check to make sure port was opened if(uartPort == -1) { perror("Failed to open port"); return -1; } // Configure UART port // See http://man7.org/linux/man-pages/man3/termios.3.html // for details on below configuration parameters serial.c_lflag = 0; // Input modes serial.c_oflag = 0; // Output modes serial.c_iflag = 0; // Control modes serial.c_cflag = 0; // Local modes serial.c_cc[VMIN] = 0; // Special characters serial.c_cc[VTIME] = 0; // Special characters serial.c_cflag = B115200 | CS8 | CREAD; // Apply configuration tcsetattr(uartPort, TCSANOW, &serial); // Run forever while(1) { // Read key entered scanf("%c",&key); // Process key value for direction switch(key) { case 119: // w key printf("Up"); // Write needs port, data pointer, and data length write(uartPort,&key,1); break; case 115: // s key printf("Down"); write(uartPort,&key,1); break; case 100: // d key printf("Right"); write(uartPort,&key,1); break; case 97: // a key printf("Left"); write(uartPort,&key,1); break; } } close(uartPort); }
Here is the code that runs on the Arduino. Note: The serial port used is Serial1, not Serial. Serial1 begins serial communications on the TX/RX pins rather then on the USB cable. When the proper character is received, the Arduino will configure the direction pins properly and then execute a quarter rotation of the motors. Since I am running the steppers at 16th step, there are 1.8 degrees per step for a total of 200 steps per revolution. This means that one complete revolution at 16th step is 200*16=3200 steps/revolution. So a quarter revolution is 800 steps.
// Define pinout int leftStepPin = 7; int leftDirPin = 8; int rightStepPin = 9; int rightDirPin = 10; int curDir = 0; char inChar; void setup() { // Setup serial coms on TX/RX to get commands from raspi Serial1.begin(115200); // Configure right motor pins pinMode(rightDirPin, OUTPUT); pinMode(rightStepPin, OUTPUT); digitalWrite(rightDirPin, HIGH); // Configure left motor pins pinMode(leftDirPin, OUTPUT); pinMode(leftStepPin, OUTPUT); digitalWrite(leftDirPin, HIGH); } void loop() { // Check if serial data available if(Serial1.available()) { inChar = (char)Serial1.read(); // Process data switch(inChar) { case 'w': goForward(); break; case 's': goBackward(); break; case 'd': turnRight(); break; case 'a': turnLeft(); break; } } } void goForward() { // Set both motors for forward rotation digitalWrite(rightDirPin, LOW); digitalWrite(leftDirPin, LOW); // Rotate 1/4 revolution quarterRotation(); } void goBackward() { // Set both motors for backward rotation digitalWrite(rightDirPin, HIGH); digitalWrite(leftDirPin, HIGH); // Rotate 1/4 revolution quarterRotation(); } void turnRight() { // Set left motor forward, right backward digitalWrite(rightDirPin, HIGH); digitalWrite(leftDirPin, LOW); // Rotate 1/4 revolution quarterRotation(); } void turnLeft() { // Set right motor forward, left backward digitalWrite(rightDirPin, LOW); digitalWrite(leftDirPin, HIGH); // Rotate 1/4 revolution quarterRotation(); } void quarterRotation() { for( int i = 0; i < 800; i++) { digitalWrite(rightStepPin,LOW); digitalWrite(leftStepPin,LOW); delay(1); digitalWrite(rightStepPin,HIGH); digitalWrite(leftStepPin,HIGH); delay(1); } }Once all the code is uploaded onto the Arduino and compiled to run on the Raspi, if you set up wifi on the Raspi, you can SSH, or Secure Shell, into the Raspi from a laptop and run the compiled code. There are a lot of great examples of how to connect to wifi and setup SSH on your Raspi out there, I'll leave it to you to google around and find out how to set that up.
With everything hooked up, here is a test run of code: