Arduino 101 Wrap Up

A couple weeks ago on February 4, I presented a 3 hour class at the 10BitWorks Hackerspace for the Arduino, which I called Arduino 101. It’s hardly an original title, but it captures the essence of the class.

We had a great response, as we had 19 people sign up and show up for the class. It was enough to split the group into 2 classes, which was a little taxing on me, but allowed us to accommodate more people in the space. We had folks of all ages attending the class, from kids on up, and I was happy to see them all engaged in the projects.

The class consisted of 3 presentations, covering the topics of:

  • Introduction to Microprocessors (and the Arduino)
  • Basic Circuit Theory
  • Basic Programming Concepts

The presentations were followed by a series of “hands-on” activities that progressively built out some projects on a small solderless breadboard using a parts kit we had ordered for the class. All the presentations and project documentation is available for download.

I’d like to share some of what I learned preparing and presenting the class. For a class of its scope it went surprisingly well, but I have mostly to credit for that the level of skill of the people in the class. Many participants already knew some part of what I was presenting and were very helpful with assisting others who weren’t up to speed. This helped free me up to help those who were truly troubled with either the hardware, software, or both.

First of all, a month lead time to prepare the class was far too short. When you have a full time job and family, that leaves only a few hours late at night to work on such projects. While I am very happy with the materials I turned out (small errors that sneaked through aside), it was extremely taxing. Reducing the material to its barest essentials and creating effective presentations is well known by educators everywhere to be a long and difficult process, but this class had some unique challenges that I hadn’t really faced in my teaching career.

The list of tasks for producing the class included (but weren’t limited to) the following activities:

  • Planning the proposed lab activities
  • Creating a notional parts list for the types of activities I was interested in doing
  • Searching through parts catalogs, data sheets, and price lists to come up with a reasonable, cost effective bill of materials
  • Coordinating the purchase of the BOM in bulk and testing the parts when they arrived
  • Building each lab and writing the Arduino program to go with it
  • Fully documenting each build using Fritzing for the circuit diagrams and schematics
  • Packaging each lab in a document that describes the build and explains the finer points being taught by it
  • Researching and buying the packaging for the kits, as well as assembling the kits from the bulk parts

That’s doesn’t even include the administrative side of things, with which fortunately I had a lot of help, including promotion and payment arrangements. In retrospect I would have left myself at least another month to put it together, but in the end everything turned out remarkably well, thanks to help from other Hackerspace members.

Secondly, as far as the class itself, I discovered that 3 hours is far shorter than I thought it would be. By the time I got to the third presentation (programming concepts), I felt rushed to get it finished in order to have time to work on the hands-on projects. It’s really difficult to give people enough foundation to understand what they’re about to do, considering that the information they need is basically the contents of 2 freshman college classes, plus the Arduino concepts themselves.

Finally, I would make the class more accessible by reducing the cost. Although I might have been able to shop around and find some of the pricier items for a little less from other suppliers, in the end I might have ended up saving only a couple bucks at most, at the expense of the additional headache of coordinating multiple orders. While I was happy we found someone to sponsor 10 students’ kits, it would have been better to find a local business or two to sponsor the entire cost of the class for 20 students, including the kits and the Arduinos themselves. The more visibility we can create for events like this (and upcoming events), the easier it will be to make that happen in the future.

As I wrote above, I’m really thankful that many people in the classes already had some background in one subject or another and were able to help others around them so that nobody got left behind. In the end I just hope I gave people enough information to be able to complete the projects but also to give them a jumping off point for self directed learning or even incentive to take some formal classes.

Decoding a Rotary Encoder

I recently acquired a rotary encoder to use as an input device for my hardware project. Knowing that such encoders make use of Gray code, I cracked open my Digital Design textbook and began coding a sketch for my Arduino. It wasn’t as straightforward as I had hoped.

This isn’t going to be a lesson in Gray code or rotary encoders — you can click the Wikipedia links above for that. This is intended to be a record of the lessons learned while trying to get a reliable, responsive read on the rotary encoder (herein simply “encoder” for brevity) that I have. Google turned up a lot of resources for reading an encoder with an Arduino, but there were a number of problems with them, including:

  • too complicated;
  • too much code (bytes of assembly);
  • too long (cycles); and
  • too unreliable.

Others were just flat out wrong.

From what I read, I decided that an Interrupt Service Routine (ISR) based on the changing levels from the 2 outputs from the encoder was the most reliable way of reading the encoder. Using basic Boolean algebra, I was able to come up with a simple expression to determine if the encoder was rotating clockwise. Where A and B are the previous state bits read from the encoder pins, and C and D are the current bits, the expression representing a clockwise movement can be represented in this truth table:

AB
00 01 10 11
00 0 0 1 0
C 01 1 0 0 0
D 10 0 0 0 1
11 0 1 0 0

The table can be reduced to the following boolean expression:
(A XOR D) AND NOT (B XOR C)

The Arduino code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
volatile boolean rotating = false; 
static boolean rotating_cw = false; 
 
void rotEncoder() { 
  static byte old = 0; 
  byte in = PIND & B1100; 
  rotating_cw = (!!(old & B0100) ^ !!(in & B1000)) & ~(!!(old & B1000) ^ !!(in & B0100)); 
  rotating = true; 
  old = in; 
} 
 
void setup() { 
  DDRD |= B1100; 
  PORTD |= B1100; 
  attachInterrupt(0, rotEncoder, CHANGE); // ISR for rotary encoder 
  attachInterrupt(1, rotEncoder, CHANGE); // ISR for rotary encoder 
}

By way of a short explanation, the encoder is hooked up to digital pins 2 and 3 on the Arduino, and I’m using direct port manipulation for faster reads than digitalRead(). The bitwise math should be pretty easy to understand if you have done any real programming; the !! expression, however, is a little trick to normalize a truthy expression (non-zero) to 1 with minimal instructions.

Whenever you need to read the encoder, you just check the rotating variable, and, if true, then read the rotating_cw variable to see which direction the encoder is rotating. After reading, set rotating to false to get ready for the next read. It’s small, fast, simple, reliable, and usable in almost any programming application (save for those that require PD2 or PD3; but you can change the code to use any pin that can be used as an interrupt).

Here’s the problem: it’s too fast and too reliable! I was puzzled why my encoder was generating multiple rotations for each detent on the switch, even with fairly long delays between reads. I agonized over the math, the code, and my own sanity. Finally, by sending each read to serial output, I could see that, although the switch had 24 detents per rotation, each detent clocked a full 4 cycles of Gray code! What I really needed was to generate a rotating signal only once per detent.

It turns out the code for doing that is even faster, simpler, and smaller than the above code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
volatile boolean rotating = false; 
static boolean rotating_cw = false; 
 
void rotEncoder() { 
  byte in = PIND & B1100; 
  rotating_cw = in == B1100; 
  rotating = in & B100; 
} 
 
void setup() { 
  DDRD |= B1100; 
  PORTD |= B1100; 
  attachInterrupt(0, rotEncoder, CHANGE); // ISR for rotary encoder 
  rotating = false; // ignore the first reading, which will be false
}

Basically, I need only check for the ending state of the switch after landing on the detent. This requires checking only one of the lines for a signal change and ignoring all others when checking for rotation.

The moral of this story is that, despite using the same basic principles, different rotary encoders work differently, so check your data sheets or conduct signal tests like I ultimately ended up doing. If you’re interested in a function to handle the reading of the encoder without inspecting and manipulating the rotating and rotating_cw variables, here it is:

1
2
3
4
5
int readEncoder() { 
 int dir = rotating ? (rotating_cw ? 1 : -1) : 0; 
 rotating = false; 
 return dir; 
}

This will work with either of the two routines above. A call to readEncoder() will return 0 if the encoder hasn’t rotated, 1 for a clockwise direction, or -1 for a counter clockwise direction. This makes it really handy to keep a running counter based on the rotation of the encoder.

AnyPWM Revisited

In my original post, I presented an Arduino sketch that could output PWM on any digital pin. It occurred to me spontaneously that the code was horribly inefficient, both in size and execution. So I’ve refactored the code a bit, and now it appears to work at least as well as the built in PWM functions.

The first thing I realized was that the algorithm I was using to track the signals was horribly inefficient; there was way more code and operations than were necessary, and I was tracking too much information. The other thing I realized was that the pulses were not in sync; that might not be too important for most applications, but there might be an application where timing is more critical.

Here’s the code:

1:   // AnyPWM by Nick Borko  
2: // This work is licensed under a Creative Commons
3: // Attribution-ShareAlike 3.0 Unported License
4:
5: // Manually do PWM using FlexiTimer2
6: // (http://www.arduino.cc/playground/Main/FlexiTimer2)
7: #include
8:
9: // LED to pulse (non-PWM pin)
10: #define LED 13
11:
12: // Period of the PWM wave (and therefore the number of levels)
13: #define PERIOD 256
14:
15: namespace AnyPWM {
16: extern volatile byte pinLevel[12];
17: void pulse();
18: void analogWrite(byte pin, byte level);
19: void init();
20: }
21:
22: // Variables to keep track of the pin states
23: volatile byte AnyPWM::pinLevel[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
24:
25: // Set a digital out pin to a specific level
26: void AnyPWM::analogWrite(byte pin, byte level) {
27: if (pin > 1 && pin < 14 && level >= 0 && level < PERIOD) {
28: pin -= 2;
29: AnyPWM::pinLevel[pin] = level;
30: if (level == 0) {
31: digitalWrite(pin + 2, LOW);
32: }
33: }
34: }
35:
36: // Initialize the timer routine; must be called before calling
37: // AnyPWM::analogWrite!
38: void AnyPWM::init() {
39: // (PERIOD * 64) Hertz seems to be a high enough frequency to produce
40: // a steady PWM signal on all 12 output pins
41: FlexiTimer2::set(1, 1.0/(PERIOD * 64), AnyPWM::pulse);
42: FlexiTimer2::start();
43: }
44:
45: // Routine to emit the PWM on the pins
46: void AnyPWM::pulse() {
47: static int counter = 0;
48: for(int i = 0; i < 12; i += 1) {
49: if (AnyPWM::pinLevel[i]) {
50: digitalWrite(i + 2, AnyPWM::pinLevel[i] > counter);
51: }
52: }
53: counter = ++counter > PERIOD ? 0 : counter;
54: }
55:
56: void setup() {
57: AnyPWM::init(); // initialize the PWM timer
58: pinMode(LED, OUTPUT); // declare LED pin to be an output
59: }
60:
61: byte brightness = 0; // how bright the LED is
62: byte fadeAmount = 5; // how many points to fade the LED by
63:
64: void loop() {
65: // set the brightness of the LED:
66: AnyPWM::analogWrite(LED, brightness);
67:
68: // change the brightness for next time through the loop:
69: brightness = brightness + fadeAmount;
70:
71: // reverse the direction of the fading at the ends of the fade:
72: if (brightness == 0 || brightness == 255) {
73: fadeAmount = -fadeAmount;
74: }
75: // wait for 30 milliseconds to see the dimming effect
76: delay(30);
77: }

Download code

The first thing to notice is that I’ve gotten rid of 2 arrays: state and timer. These are completely unnecessary, and it saves 24 bytes right off the top. Second, I’ve upped my multiplier to calculate the timer frequency to 64, which makes for smoother levels when more pins are being pulsed. Finally, a lot of code in the pulse() function has been replaced with a simple comparison against a single rolling counter, making the CPU time spent in the routine much shorter.

The entire code savings is about 80 bytes from the original, which makes this sketch come in at 2114 bytes, compared to the 1252 bytes of the compiled Fade sketch (862 byte difference). Additionally, the performance is much closer to the built in analogWrite() functionality. I don’t know the exact timing, but the difference is fairly imperceptible to a human, even when pulsing 6 pins (the maximum number of PWM pins on an ATmegaXX8 Arduino).

PWM on Any Digital Pin on Arduino

I recently got an Arduino to play around with. It’s been a few years since I’ve done some electronics hacking, and I wanted to get back into the hobby. There are tons of low cost embedded microcontrollers to play with these days, compared to when I was back in college, so I decided to give the Arduino a try. My first experiment was to emit PWM signals on any of the digital output pins.

My real goal was to learn how to use the hardware interrupt timers in general in the Arduino environment. I knew I would have to do this to implement my impending hardware project to build a 16×8 LED matrix light board. Fortunately doing that is pretty much like any microcontroller, so it’s not too difficult to figure out from the documentation. Even more fortunately, Wim Leers abstracted out all the necessary logic to figure out the current clock speed and timer implementations of various Arduino platforms, making it child’s play to set up an interrupt timer routine. His library co-opts timer 2 and is called FlexiTimer2.

Pulse-Width Modulation is simply manipulating the high and low value durations of a fixed period digital waveform to simulate an analog signal level. Well, maybe it’s not that simple, but you can check out the Wikipedia page to get a more technically correct explanation. In any case, the Arduino bootstrap environment provides PWM output on specific pins through a call to analogWrite. While some ATmega pins support hardware PWM, on others it is done in software using timers. My sketch does this as well:

1:   // AnyPWM by Nick Borko  
2: // This work is licensed under a Creative Commons
3: // Attribution-ShareAlike 3.0 Unported License
4:
5: // Manually do PWM using FlexiTimer2
6: // (http://www.arduino.cc/playground/Main/FlexiTimer2)
7: #include
8:
9: // LED to pulse (non-PWM pin)
10: #define LED 13
11:
12: // Period of the PWM wave (and therefore the number of levels)
13: #define PERIOD 256
14:
15: namespace AnyPWM {
16: extern volatile byte pinLevel[12];
17: extern boolean state[12];
18: extern byte timer[12];
19: void pulse();
20: void analogWrite(byte pin, byte level);
21: void init();
22: }
23:
24: // Variables to keep track of the pin states
25: volatile byte AnyPWM::pinLevel[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
26: boolean AnyPWM::state[12] = { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW };
27: byte AnyPWM::timer[12] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
28:
29: // Set a digital out pin to a specific level
30: void AnyPWM::analogWrite(byte pin, byte level) {
31: if (pin > 1 && pin < 14 && level >= 0 && level < PERIOD) {
32: pin -= 2;
33: AnyPWM::pinLevel[pin] = level;
34: if (level == 0) {
35: // reset the PWM state
36: AnyPWM::state[pin] = LOW;
37: AnyPWM::timer[pin] = 255;
38: digitalWrite(pin + 2, LOW);
39: }
40: }
41: }
42:
43: // Initialize the timer routine; must be called before calling
44: // AnyPWM::analogWrite!
45: void AnyPWM::init() {
46: // (PERIOD * 48) Hertz seems to be a high enough frequency to produce
47: // a steady PWM signal on all 12 output pins
48: FlexiTimer2::set(1, 1.0/(PERIOD * 48), AnyPWM::pulse);
49: FlexiTimer2::start();
50: }
51:
52: // Routine to emit the PWM on the pins
53: void AnyPWM::pulse() {
54: for(int i = 0; i < 12; i += 1) {
55: if (AnyPWM::pinLevel[i]) {
56: if (AnyPWM::timer[i] == 0) {
57: AnyPWM::timer[i] = (AnyPWM::state[i] = !AnyPWM::state[i]) ? AnyPWM::pinLevel[i] : (byte)PERIOD - AnyPWM::pinLevel[i];
58: } else {
59: digitalWrite(i + 2, AnyPWM::state[i]);
60: }
61: AnyPWM::timer[i] -= 1;
62: }
63: }
64: }
65:
66: void setup() {
67: AnyPWM::init(); // initialize the PWM timer
68: pinMode(LED, OUTPUT); // declare LED pin to be an output
69: }
70:
71: byte brightness = 0; // how bright the LED is
72: byte fadeAmount = 5; // how many points to fade the LED by
73:
74: void loop() {
75: // set the brightness of the LED:
76: AnyPWM::analogWrite(LED, brightness);
77:
78: // change the brightness for next time through the loop:
79: brightness = brightness + fadeAmount;
80:
81: // reverse the direction of the fading at the ends of the fade:
82: if (brightness == 0 || brightness == 255) {
83: fadeAmount = -fadeAmount;
84: }
85: // wait for 30 milliseconds to see the dimming effect
86: delay(30);
87: }

Download code

Now, a LED attached to pin 13 (or the surface mounted LED on some Arduino boards), a non-PWM pin, will pulse, just like the Fade sketch in the Examples (you’ll note the loop code is nearly identical).

There are some downsides to this approach. First, the code is 942 bytes longer than using the stock analogWrite call on a PWM pin. Second, since all pins share the same timer, the more pins you write to, the slower the execution. This may be an acceptable trade off, and the timing issue may be mitigated by decreasing the duty cycle (PERIOD) while keeping the timer frequency the same (set in AnyPWM:init via FlexiTimer2::set).

This same approach could be used for other applications than PWM. For example, you could control an RGB LED matrix to provide up to 24-bit color, assuming the processor was fast enough; instead of controlling multiple output pins, you could instead be controlling the individual red, green and blue values for each LED through a serial driver. Or, 16 levels of brightness on a single color LED array (if PERIOD is set to 16), which I am thinking about doing for my LED project.

UPDATE: Don’t use this code! Instead, read my follow-up and download the code!