Electronic and Mechanical Devices
Components
Breadboard
The breadboard is an electronics prototyping piece of kit where jumper wires are used to connect between device pins without soldering. The pin holes form a grid which follows a strange pattern: The power rails at the edges of the board are connected vertically, while the interior grid pins are connected horizontally. Additionally, there is a gutter in the middle for mounting double sided through-hole chips. The connectivity is shown below. Note that for double length breadboard the power rails are not powered all the way from one end to the other.
Arduino
The micro controllers used are mainly Arduino Nano. There are some Uno also available as well as Leonardo used in the Ardbox PCL units. Arduino Nano fits nicely in a breadboard and can be used for building glue logic between the robot's controller and external devices. The GPIO pins are labeled on the board and additional information are available at the official documentation page. To program the micro, download and install the Arduino IDE.
The first example below is shown to demonstrate how to power the side rails and how to connect across. It requires no programming, just connect the 5V supply and ground GND to an LED via a resistor of value e.g. 470K. The in-series resistor is used for limiting the current passing through the LED and controls its brightness. Without it, it will still work but burn soon enough. The LED, aka Light Emitting Diode, allows current only in one direction. Diodes behave like this in general. LEDs have two legs, one longer that the other, known as the positive or anode and negative or cathode, respectively. The negative goes to GND and positive to the 5V.
Level Shifter
The robot's controller, like most industrial equipment work with 24V DC instead of 5V or 3.3V used by micro controllers. It is thus not possible to directly connect wires between them without some voltage level shifting logic. The boards shown below allow the output pins of the robot's controller to be connected to input pins of the micros. They use optocouplers to isolate both sides, which behave like switches.
Digital Output
Arduino pins can be configured as either inputs or outputs. Some are digital, as in they use only 5V and 0V to represent 1 and 0, while some can produce interim voltages to represent analog values. LED blinking is the most basic demonstrator for toggling digital output pins. The wiring is not unlike the previous example, however, instead of powering the LED from the positive rail, it is connected to digital pin D3. Then nano must be programmed to toggle the power to the LED.
Wiring
Coding
Arduinos use the C programming language, which is slightly more complicated compared to python. The concepts are the same-ish but the syntax is a bit different. There are three sections in a micro program: (a) general declarations and definitions at the top, (b) the initialization logic upon power up contained in the setup( ) function, and (c) the continuous processing cycle captured by the loop( ) function.
In the code below, first the constant variable DO03 is used for labeling Arduino's pin 3. The setup function declared that DO03 will be used for output. The infinite loop turns on the LED by setting DO03 to HIGH or 1, which sends 5V to it. Then there is delay of 1000 milliseconds, followed by setting the pin back down to LOW or 0, which sets the output to GND or 0V.
const int DO03 = 3;
void setup( )
{
pinMode( DO03, OUTPUT );
}
void loop( )
{
digitalWrite( DO03, HIGH ); delay( 1000 );
digitalWrite( DO03, LOW ); delay( 1000 );
}
LEDs are the simplest form of indicating something from the code side to the user. It is also possible to use more informative devices to do so such as LCD displays. To use them is actually quite easy because there are libraries available, however, wiring up the display, as seen below, is messy. The specification or datasheet for the display can be often found using the part code printed on the board. For example a search for LCD 1206 datasheet brings up the a similar device datasheet.
Wiring
Coding
The code below uses the Liquid Crystal Library to display a simple message. The device is first declared in the globals section with the associated pins used. The setup function initializes the display to a 16x2 character configuration and clears the screen. The loop prints out a string, one per line, clears it after a delay.
#include <LiquidCrystal.h>
LiquidCrystal lcd( A0, A1, A2, A3, A4, A5 );
void setup( )
{
lcd.begin( 16, 2 );
lcd.clear( );
}
void loop( )
{
lcd.print( "Warning" );
lcd.setCursor( 0, 1 );
lcd.print( "Voltage" );
delay( 1000 );
lcd.clear( );
delay( 1000 );
}
Analog Output
Analog output in Arduino is available in some but not all of the pins. These are marked with a tilde ~ or a PWM marker in the datasheet. An analog value between 0V and 5V is created by pulsing on and off an output pin really fast. The duration ratio between on and off produces interim values. The example below dims an LED using the Pulse Width Modulation approach.
Wiring
The digital pin 3 can support PWM so the wiring is exactly the same as previously. Instead of sending either 0V or 5V we will send varying levels of voltage using only code.
Coding
To produce analog voltage, instead of using digitalWrite( ), the analogWrite( ) function is used. The second parameter is a byte value in [0, 255]. Thus, there are 256 levels between 0V and 5V. Additionally, the serial interface is activated such that the current value is sent to the computer terminal. Serial output is used for debugging so comment them out when not needed because they take processing power.
const int DO03 = 3;
void setup( )
{
Serial.begin( 9600 );
pinMode( DO03, OUTPUT );
}
int value = 0;
int delta = 5;
void loop( )
{
analogWrite( DO03, value );
delay( 100 );
Serial.println( value );
value += delta;
if( value < 0 || value > 255 )
{
delta *= -1;
value += delta;
}
}
Digital Input
To receive information, such as external triggers and sensor data, the pins can be configured in either INPUT or INPUT_PULLUP mode. The example below demonstrates reading the state of a push button and printing out a relevant message.
Wiring
Buttons have four legs connected horizontally across. Once pressed they create a continuous path between their pins in a diagonal fashion. Buttons are wired such that once pressed they pull a positive line to the ground. This is because the response is faster and cleaner than pulling a line to the positive side.
However, when a button is not pressed the circuit is broken. This is a problem because it is not possible to read its state from a floating pin. Floating pins produce random noise. Thus it is common to use an in-series pull-up resistor which keeps the line high until the button is pressed. Arduino supports software-controlled internal pull-up input arrangement therefore two options are presented below and in the coding section.
Coding
Depending on the arrangement, a digital pin can be set as either INPUT or INPUT_PULLUP, that is with external or internal resistor, respectively. The state of a digital input pin is read using the digitalRead( ) function. Note that because of the pull-up logic, the relationship between the HIGH vs. LOW and "Pressed" vs. "Released" is inverted.
const int DO02 = 2;
void setup( )
{
Serial.begin( 9600 );
pinMode( DO02, INPUT_PULLUP ); //-- Internal
//pinMode( DO02, INPUT ); //-- External
}
void loop( )
{
int do02 = digitalRead( DO02 );
Serial.println( do02 ? "Released" : "Pressed" );
delay( 100 );
}
Analog Input
Reading analog values is supported by only a subset of Arduino pins, namely these that start with the letter A. The expected input voltage is in the [0, 5] volts range. The example demonstrates reading the value from a rotary potentiometer. More generally analog inputs are used for reading sensors that output analog signals. The potentiometer is a rotation angle measuring device.
Wiring
Pots are variable resistors and they typically feature three legs with two accepting the positive and negative rail, while the third one emits the voltage dropped by the resistor. The pot's resistance used below ranges in the [0, 10,000] Ohms.
Coding
Values are read using the analogRead( ) function. It returns a 10-bit value in the [0, 1023] range. The value by it self is associated with a voltage but this is subject to knowing the exact maximum, typically 5V as well as whether the device produces values in a linear or not sense.
const int AI00 = A0;
void setup( )
{
Serial.begin( 9600 );
pinMode( AI00, INPUT );
}
void loop( )
{
int value = analogRead( AI00 );
Serial.println( value );
delay( 100 );
}
Digital to Digital
The example below represents a scenario where an incoming digital signal triggers some action associated with a digital output. The incoming signal for simplicity is from a button, which once pressed it turns on an LED. Of course the signal can be sent from the robot's controller, read by the Arduino to trigger some external digital device. More on this is below.
Wiring
Coding
The code is very simple requiring reading one pin and setting another. Note that the signal is inverted using the not operator !. This is because of the pull-up logic explained earlier.
const int DO02 = 2;
const int DO09 = 9;
void setup( )
{
pinMode( DO02, INPUT_PULLUP );
pinMode( DO09, OUTPUT );
}
void loop( )
{
int do02 = !digitalRead( DO02 );
digitalWrite( CO09, do02 );
}
Digital to Analog
The following demonstration presents the case for how to convert a group of digital inputs to an analog output. This is for sending a voltage to an external analog device, such as a motor, from the robot's controller, which supports only digital signals. The digital inputs are four buttons which represent a 4-bit binary number, and the output is a dimming LED.
Wiring
Coding
The code is not very different than the previously presented one-button scenario. However, once all button states are read and inverted, they are combined into a single 4-bit value using left bit shifts << and bitwise ors |. The result is a value in the [0, 15] range. In other words, with 4-bits it possible to represent 2^4=16 unique states. Note that the order of combining the bits is important.
Note also that the abstract 4-bit value is often transformed appropriately into something that makes sense. For instance, here it is multiplied by 4 and then sent to analogWrite( ). In this sense it converted to a voltage value in the range of [0, ~1.2] because it is sufficient to fully turn on/off the LED. To fully use the available 8-bit PWM resolution, we would need to multiply the 4-bit value by value * 255 / 15.
#define DEBUG
const int DO02 = 2;
const int DO03 = 3;
const int DO04 = 4;
const int DO05 = 5;
const int DO09 = 9;
void setup( )
{
#ifdef DEBUG
Serial.begin( 9600 );
#endif
pinMode( DO02, INPUT_PULLUP );
pinMode( DO03, INPUT_PULLUP );
pinMode( DO04, INPUT_PULLUP );
pinMode( DO05, INPUT_PULLUP );
pinMode( DO09, OUTPUT );
}
void loop( )
{
int do02 = !digitalRead( DO02 );
int do03 = !digitalRead( DO03 );
int do04 = !digitalRead( DO04 );
int do05 = !digitalRead( DO05 );
int value = ( ( do02 << 0 ) | // LSB
( do03 << 1 ) |
( do04 << 2 ) |
( do05 << 3 ) ); // MSB
analogWrite( DO09, value * 4 );
#ifdef DEBUG
Serial.println( String( do05 ) +
String( do04 ) +
String( do03 ) +
String( do02 ) + " -> " +
String( value ) );
delay( 100 );
#endif
}
Robot to Arduino
The following example demonstrates receiving 4-bits of information from the robot's controller digital outputs into Arduino's digital inputs. Those are combined to build a state which can be used for performing at maximum 16 different independent predefined tasks.
Wiring
Coding
The code is pretty similar to the digital to analog example. However, instead of interpreting the combined value as a continuous quantity, here is used in a switch construct to perform discrete tasks. Note that the default keyword is used for catching all non individually specified cases.
const int DO02 = 2;
const int DO03 = 3;
const int DO04 = 4;
const int DO05 = 5;
void setup( )
{
pinMode( DO02, INPUT );
pinMode( DO03, INPUT );
pinMode( DO04, INPUT );
pinMode( DO05, INPUT );
//-- Outputs ...
}
void loop( )
{
int do02 = digitalRead( DO02 );
int do03 = digitalRead( DO03 );
int do04 = digitalRead( DO04 );
int do05 = digitalRead( DO05 );
int state = ( ( do02 << 0 ) | // LSB
( do03 << 1 ) |
( do04 << 2 ) |
( do05 << 3 ) ); // MSB
switch( state )
{
case 0:
{
Serial.println( "Case 0" );
}
break;
case 1:
{
Serial.println( "Case 1" );
}
break;
case 2:
{
Serial.println( "Case 2" );
}
break;
default:
{
Serial.println( "Cases 3 to 15" );
}
break;
}
}
Relay Module
A mechanical relay is a device used for controlling high voltage loads from a low voltage micro controller safely. They can be used for both AC and DC, inductive and capacitive loads. However, care must be taken to not exceed the capability of the device and cause damage or injuries.
Wiring
Coding
const int AI02 = A2;
const int DO02 = 2;
void setup( )
{
pinMode( AI02, INPUT );
pinMode( DO02, OUTPUT );
}
void loop( )
{
int value = digitalRead( AI02 );
digitalWrite( DO02, value );
}
DC Motor
DC motors are the simplest to operate requiring only effectively one signal that associates voltage to speed. Generally only one PWM signal is required to control the motor's speed. However, motors require significantly more power than the micro can provide so a secondary power supply is used. Note that mixing the two power sources can result to damages so it is recommended to use driver modules to handle the power management.
MOSFET Module
The driver modules typically feature two sides, one for the motor / high voltage and one for the signals / low voltage part of the system. An example using a mosfet module is demonstrated below. It is not very different than the analog output logic presented earlier.
Wiring
Coding
const int AI02 = A2;
const int DO03 = 3;
void setup( )
{
pinMode( AI02, INPUT );
pinMode( DO03, OUTPUT );
}
void loop( )
{
int value = digitalRead( AI02 );
analogWrite( DO03, 80 * value );
}
L293DNE
Using ICs for DC motor driving is presented below, even though using a dedicated module is recommended. This is because the spikes produced by motors can damage the logic side. It is also very easy to make a wrong connection and fry the logic board. The chip is L293 which can drive two motors, see the documentation. Note that the logic and power grounds are connected together which is a typical pattern with motor drivers.
Wiring
Coding
The driver requires three wires for control. The expected PWM voltage/speed signal as well as two for setting the direction of the motor. Additionally, they must be set to 01 or 10 states for switching direction, while 00 represent halting. These combinations are handled in the move( ) function by bit manipulation such that the application logic in the loop( ) function is simple and readable.
const int PWM = 3;
const int IN1 = 5;
const int IN2 = 6;
void setup( )
{
pinMode( PWM, OUTPUT ); digitalWrite( PWM, LOW );
pinMode( IN1, OUTPUT ); digitalWrite( IN1, LOW );
pinMode( IN2, OUTPUT ); digitalWrite( IN2, LOW );
}
void move( int speed )
{
int sign = speed == 0 ? 0 : speed < 0 ? -1 : 1;
int bits = ( sign + 3 ) % 3;
digitalWrite( IN1, ( bits >> 0 ) & 1 );
digitalWrite( IN2, ( bits >> 1 ) & 1 );
analogWrite( PWM, speed * sign );
}
void loop() {
move( 100 ); delay( 1000 );
move( 0 ); delay( 1000 );
move( -100 ); delay( 1000 );
move( 0 ); delay( 1000 );
}
Servo Motor
Servomotors are another type of devices used for mechanical actuation. Again we need an external power source to run the motor effectively even though small servos can be theoretically connected directly to Arduino. Additionally, the power and logic grounds need to be commoned.
Wiring
Coding
#include <Servo.h>
Servo motor;
const int DO09 = 9;
void setup( )
{
Serial.begin( 9600 );
motor.attach( DO09 );
}
void loop( )
{
Serial.println( 0 );
motor.write( 0 );
delay( 2000 );
Serial.println( 180 );
motor.write( 180 );
delay( 2000 );
}
Stepper Motor
Stepper motors are yet another type of motor that are used with micro controllers. They are indexed in that they take discrete positions around a cycle, as is they have a finite number of steps around a circle. For small steppers such as the 28BYJ-48 a simple UL2003 module can be used as seen below, or even directly driven by the IC itself. For larger motors a micro step driver is preferable.
UL2003 Module
Wiring
There are four wires required to drive the motor which map to the two windings of a bipolar stepper, two for each. The 28BYJ-48 stepper features five wires that connect via a molex connector to the module directly.
Coding / Library
The easiest way to run a stepper motor used the Stepper Library. It requires defining the number of steps per revolution and the four pins connected to the driver module. The motor is controlled by issuing the number of steps and direction to use.
#include "Stepper.h"
const int STEPS = 2048;
const int DO02 = 2;
const int DO03 = 3;
const int DO04 = 4;
const int DO05 = 5;
Stepper motor = Stepper( STEPS, DO02, DO04, DO03, DO05 );
void setup( )
{
motor.setSpeed( 10 );
Serial.begin( 9600 );
}
void loop( )
{
motor.step( STEPS / 2 ); delay( 1000 );
motor.step( -STEPS / 2 ); delay( 1000 );
}
Coding / Manual
It is possible to directly drive steppers without the Stepper library and/or without the module by using directly the ULN2003A IC. This is done by pulsing the output pins in a specific sequence as seen below. Again, because it is very easy to make mistakes and fry electronics, it is highly recommended to use dedicated driver modules.
const int DO02 = 2;
const int DO03 = 3;
const int DO04 = 4;
const int DO05 = 5;
// const int STEPS[8][4] =
// {
// { 1, 0, 0, 0 },
// { 1, 1, 0, 0 },
// { 0, 1, 0, 0 },
// { 0, 1, 1, 0 },
// { 0, 0, 1, 0 },
// { 0, 0, 1, 1 },
// { 0, 0, 0, 1 },
// { 1, 0, 0, 1 }
// };
const int STEPS[8][4] =
{
{ 1, 0, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 }
};
int step = 0;
int time = 2000;
void setup( )
{
pinMode( DO02, OUTPUT );
pinMode( DO03, OUTPUT );
pinMode( DO04, OUTPUT );
pinMode( DO05, OUTPUT );
}
void move( int count, int direction )
{
for( int index = 0; index < count; index++ )
{
step = ( step + direction + 8 ) % 8;
digitalWrite( DO02, STEPS[step][0] );
digitalWrite( DO03, STEPS[step][1] );
digitalWrite( DO04, STEPS[step][2] );
digitalWrite( DO05, STEPS[step][3] );
delayMicroseconds( time );
}
}
void loop( )
{
move( 1024, +1 ); delay( 1000 );
move( 1024, -1 ); delay( 1000 );
}
Stepper Driver Module
For larger steppers there are convenient driver devices that require only a pulse and direction wire to hook up. Additionally, they take care of the external power supply circuitry. For bipolar steppers there are four winding motor wires connected to the driver. If it is unclear which wires are on the same coil, use the multimeter in continuity mode to buzz the wires and group them. Arduino-wise, stepping is performed via a pulsing pin and the direction using a digital pin.
Wiring
Coding
const int DO02 = 2;
const int DO03 = 3;
void setup( )
{
pinMode( DO02, OUTPUT );
pinMode( DO03, OUTPUT );
}
void move( int count, int direction, int time )
{
digitalWrite( DO03, direction > 0 ? HIGH : LOW );
for( int index = 0; index < count; index++ )
{
digitalWrite( DO02, HIGH ); delayMicroseconds( time );
digitalWrite( DO02, LOW ); delayMicroseconds( time );
}
}
void loop( )
{
move( 100, +1, 5000 ); delay( 1000 );
move( 100, -1, 5000 ); delay( 1000 );
}