Interfacing week.

This week is about interfacing with the Arduino through the serial port PC connection.
The first step about getting anything done is to know what it is you're doing - so I'll be doing a very simple interface:

A trackbar moving between 0 and 10, which will control the rotation speed of a motor, and another trackbar between 0 and 1, which will control the direction. Simply put, the trackbar will tell the microcontroller to output an analog output between 0 to 254, depending on the trackbar's position between 0 to 10, and control the direction based on whether the second trackbar is 0 for left, or 1 for right.
This means that I'll be sending not just a single number through the serial port, but an array, which will look like this: [0-10, 0-1]

First I'll quickly write the Python code, using the PySerial package. Since my computer already has Anaconda installed on it, installing new packages on anaconda is fairly simple. For this one I used the command (from the Anaconda scripts folder):

conda install -c anaconda pyserial

I'll declare the serial port without a timeout component, because I'm not reading anything in the Python code, I'm only writing into the microcontroller. I will also be using the OpenCV package, which I already have installed on my machine, to create the trackbar window. Oh and I also made a tiny logo to display in the interface :)

Here's just the Python code with commented documentation (it's not sending anything to serial yet):

# import packages opencv and pyserial import cv2 import serial # define an empty function, since the trackbar requires a function of some sort def nothing(x): pass # get the logo image logo = cv2.imread('./interacing_pictures/motor.jpg') # declare serial port ####################################### # serial is commented out for the moment because not currently used. will be used when actually running with serial connected # my_serial = serial.Serial('COM4', 9600) ####################################### # create a window to put the trackbars in cv2.namedWindow('Motor Control') # create the two trackbars that will be used cv2.createTrackbar('Speed', 'Motor Control', 0, 10, nothing) cv2.createTrackbar('Direction', 'Motor Control', 0, 1, nothing) # forever loop to display window While True: # show the window with the logo and trackbars cv2.imshow('Motor Control',logo) # quit the loop if keyboard key Q is pressed if cv2.waitKey(1) == ord('q'): break # get the current positions of the two trackbars speed = cv2.getTrackbarPos('Speed','Motor Control') direction = cv2.getTrackbarPos('Direction','Motor Control') # the array that we'll be writing write_array = [speed, direction] # once loop breaks, close the window cv2.destroyAllWindows()

And here's the little interface that opens up when the code is run:

The code will also need to convert the array to a byte array, from what I've gathered, before writing it to the serial. So I'll be adding the commands to the code, at the end of the loop:

write_vals = bytearray(write_array) my_serial.write(write_vals)

So, half the task is done. The second half, of course, is the Arduino code. Since I'm sending an array through the serial, and not just one value, from what I gathered from various forum posts and research into the serial connection and timeout argument in PySerial, on the Arduino end - I'm gonna need to give it a little timeout - which is how long to wait until it gets all bytes - and a loop in which to process the incoming bytes.
A most notable forum post is this:

https://forum.arduino.cc/index.php?topic=46652.0

And this:

http://forum.arduino.cc/index.php?topic=44055.0

The Arduino code is:

#include <SoftwareSerial.h> #define rxPin 0 #define txPin 1 SoftwareSerial serial(rxPin, txPin); //declare the pins used for the motor #define motor1 PA2 #define motor2 PA3 //maximum wait time for bytes is 1/10th second int MAX_MILLIS_TO_WAIT = 100; //unsigned long to call the arduino's "millis()" function unsigned long starttime; //array of two values for direction & speed int array_read[2]; //integer to store calculation result for speed int spd; void setup() { pinMode(rxPin, INPUT); pinMode(txPin, OUTPUT); serial.begin(9600); pinMode(motor1, OUTPUT); pinMode(motor2, OUTPUT); } void loop(){ //call millis function - how much ms passed since loop cycle start starttime = millis(); while ( (serial.available()<2) && ((millis() - starttime) < MAX_MILLIS_TO_WAIT) ) { // hang in this loop until we either get 9 bytes of data or 1/10 second passed } if(serial.available() < 2) { // the data didn't come in - handle that problem here serial.println("ERROR - Didn't get 2 bytes of data!"); } else { //loop over the bytes twice, get 1st and 2nd byte into the list for(int n=0; n<2; n++){ //array_read[n] = serial.read() -'0'; //the bit "serial.read() -'0'" should convert the byte to integer array_read[n] = serial.read(); } } //from the array_read - array_read[0] stands for the speed value and array_read[1] for the direction //small math operation to control the analog write spd = floor((255/10)*array_read[0]); //minor precaution to ensure a small rountup to 255 if(spd > 250){ spd=255; } //now comes the action! The motor spin! if(array_read[1] == 0){ //left //analogWrite(motor2, spd); analogWrite(motor2, spd); digitalWrite(motor1, LOW); } else if (array_read[1] == 1){ //right analogWrite(motor1, spd); digitalWrite(motor2, LOW); } else { //if neither left nor right, dead motor digitalWrite(motor1,LOW); digitalWrite(motor2,LOW); } }

And the whole thing basically works like magic.
A sexy sexy demonstration in video:

There is one thing to note - only a few pins on the Attiny84A (the microcontroller I'm using) are PWM - that is, only a few of them can actually output an analog signal (analogWrite). The pins I connect the motor to are not pins which have this capability. The interface, as well as the code, are built on the ability to use analogWrite, and will work perfectly used with pins that have this capability or microcontrollers which have more such pins. An important thing I learned is that, when using pins that CANNOT do analogWrite, if the analogWrite signal strength is below 255/2, it will not output anything. If it's above 255/2, it will output the full 5V current.

And the files to download:

Arduino sketch
Python
Logo used in Python code

Back home