Sensing & Actuation with Arduino Microcontroller

CS 480: Robotics & 3D Printing Lecture, Dr. Lawlor

Embedded Systems

Tiny (about 1cm across), cheap (normally around $1), and very low power (often less than one milliwatt), fully programmable microprocessors can disappear inside a variety of consumer devices: cars, microwave ovens, toys, and even PC peripherals like mice, hard drives, and such.  These "embedded" computers typically run a fixed and simple program on the bare CPU, without an operating system.  The program to run is stored in nonvolatile ROM or flash memory.  

Plain old C is considered a "high level language" on embedded machines, and many commercial embedded products are still written in assembly language.  20MHz is considered "fast".  Integer multiply instructions are a rare luxury--floating point is almost never found. A $5 chip is considered "expensive".

A few families of embedded microcontrollers out there that I've used:

I've written code for real projects on Arduino, PIC, and some older obsolete systems.  I've used all of the above at least at the demo level.  I mostly use Arduino for one-off projects, due to the full C++ support and good standard library.

Embedded Programming Models

Typically, an embedded program has some initial setup code run at startup, and then loops forever running the attached hardware.  The Arduino IDE makes this official by giving you two functions: "setup" runs once at startup, and "loop" is called again and again.

Embedded Inputs

There are a few types of microcontroller input pins:

The universal is that input pins read voltage, so anything you can convert into voltage, you can input into the controller.  One really common device for this is a voltage divider, which is basically just two resistors in series, where you hook up the middle lead to the microcontroller. 

Many Arduino devices have dedicated internal 20Kohm pull-up resistors, which you can enable with "pinMode(5,INPUT_PULLUP);". 

For example, pushing a button normally closes a contact, brining the resistance of the button from infinity down to zero.  Alone, that's useless.  But if you put 5V on one terminal of the button, and connect the other terminal to both the microcontroller input pin and a 1Kohm "pull-down" resistor (the other end hooked to ground), then you've effectively built a voltage divider with the button as the top resistor.  If the button is unpushed, its infinite resistance allows the pull-down resistor to pull the micro's pin down to ground, zero volts.  When the button is pushed, it shorts the micro's input pin up to 5v.  A little current leaks through the pull-down resistor, which is fine.  Light switches, limit switches, keyboard and mouse buttons, thermostat mercury bulbs, etc all boil down to just contact or no contact, and are interfaced in exactly the same way to a digital input.

One caveat: pushing a button may result in several hundred tiny contact/no contact pulses a few microseconds wide.  You typically clean this up with a "debounce" circuit, either a hardware resistor-capacitor filter circuit, or a software function that only checks the button at 50Hz or so.

Another example, a "thermistor" is a resistor whose resistance varies with temperature.  If you set up a voltage divider as above, but plug a thermistor into the top half, you can convert temperature to voltage.  This is an analog input.

Embedded Outputs

There are several types of microcontroller output pins:

Microcontroller output pins reliably output voltage, usually just either 0v or 5v, but can usually only supply a few dozen milliamps.  This is just enough to light up a small LED, although you usually want a 1Kohm (or so) current limiting resistor inline with the LED.

To switch a useful amount of current, say to run a heater or motor, you usually need an interface device like a transistor.  "Signal" transistors can switch up to a few hundred milliamps, and "power" transistors can switch up to a few amps.  FET transistors can go up to dozens of amps fairly cheaply. 

If talking to another big or little computer, you can speak USB (which is fast enough you usually need special hardware to speak it), plain serial data (where bits are known fixed and *slow* times), I2CSPIB, or any of a bunch of rarer protocols.

The biggest use of serial data on Arduino is to talk to the controlling PC.  You do a Serial.begin(baud rate) in your setup code, and can then do Serial.print or Serial.println (includes a newline) to report stuff to the user.  For data logging, if you print an X axis like the time first, with Serial.print(millis());  Serial.print(" "); then you can copy the data from the serial monitor off to a spreadsheet to chart the data in an XY scatterplot.  You can also use Serial.read to read a character, or Serial.parseInt to read an integer from the user.

void setup() {
    Serial.begin(9600); // slow baudrate
    Serial.println("Started up!"); // banner, to check baudrate
}

void loop() {
   Serial.println("Now in loop!");

   if (Serial.available()) { // user typed something
      int cmd=Serial.read();
      if (cmd=='h') Serial.println("HI!");
      else Serial.println("Did not understand command");
   }
  
   delay(500); // slow enough to read output
}

For Robots

There isn't a standard firmware for robots, but we have one as part of our RobotMoose web-based robot control system.

For 3D printers

For a 3D printer, you can buy an open source "Melzi" control board on eBay for about $40.  This includes an Arduino IDE compatible Atmega 1284P microcontroller, four stepper motor drivers (for X, Y, Z, and E axes), and FETs to switch a heated bed, hotend heater, and fan.

The standard 3D printer firmware is Marlin, which supports gcode parsing, SD card input, and good serial comms.