Five things I never use in Arduino projects

I’ve been using Arduino for a few months now, and designing electronics and embedded systems for a lot longer. In reading the Arduino forum, I’ve seen a few areas that Arduino users often find difficulty with. It seems to me that some of the perceived Arduino wisdom is a little misplaced. So here are my top five things to avoid using in Arduino projects. This isn’t meant to cover very basic errors like drawing too much current from an Arduino pin, or not using decoupling capacitors, or omitting the series resistor when driving an LED. Rather, I’m talking about things which look as if they could be a good idea but are either more complex than necessary or can turn round to bite you.

1. The String class

On the face if it, the String class in the Arduino library makes string handling easier. It provides a host of functions to do things that you can’t easily do if you represent strings as pointers to char arrays, as is usual in C. So what’s the problem?

The problem is that String operations allocate memory dynamically and in ways that are hard to predict when the inputs to the program are variable, combined with the fact that Arduinos have a very limited amount of RAM (2K on the Arduino Uno). Dynamic memory allocation typically causes memory fragmentation. This means that your program may work correctly for some inputs or a short while, but crashes with other inputs or after a longer time, due to memory exhaustion. See http://critical.eschertech.com/2010/07/30/dynamic-memory-allocation-in-critical-embedded-systems/ for more about why dynamic memory allocation is a bad idea in embedded software implemented in C/C++.

When might I use String? When writing a quick-and-dirty sketch to try something out, as long it doesn’t have to hold together for more than a few minutes!

2. SoftwareSerial and NewSoftSerial

Arduinos based on the ATmega328p have a single hardware UART. What if you need more than one serial port? Easy, use SoftwareSerial to get as many additional UARTs as you want! What’s the problem?

The problem is that in the absence of hardware support, SoftwareSerial has to time the transmitted and received data bits accurately using timing loops. In order to do this, it has to disable interrupts, because an interrupt could cause the timing to go wrong. This in turn is liable to mess up any time-sensitive tasks I have. If I need more than one UART, I’ll use an Arduino Mega instead; or perhaps a Teensy, which has a direct USB interface, so that I can debug or communicate with the PC through that and save the UART for genuine serial communications.

When might I use SoftwareSerial or NewSoftSerial? When the software UART is used only for sending debug information and I can afford for interrupts to be disabled for the duration.

3. Pullups or pulldown resistors on digital input pins used for reading switches

One of the early tutorials in the Arduino official site is Digital Read Serial. As the beginning of a tutorial in the principles of interfacing switches to digital electronics, this is OK. However, this is NOT the best way to connect a push button to an Arduino! Microcontrollers have built-in switchable pullup resistors precisely so that you don’t need external resistors. To connect a switch to an Arduino, connect it between the input pin and ground, and then in setup() enable the internal pullup resistor by using digitalWrite() to write HIGH to the pin (that’s right, you do a write to an input pin – that’s what you need to do to enable an internal pullup resistor on the Arduino).

When might I use external pullup/pulldown resistors? Only when the internal 20k pullup resistor isn’t suitable, for example, if the switches are connected with long wires that pick up noise.

4. Darlington power transistors

Compared to ordinary transistors, Darlington transistors offer much greater gain, allowing you to switch a much higher current from an Arduino pin than an ordinary transistor. This makes them very useful, right?

Wrong. The other parameter of a Darlington that is much higher than a normal transistor is its saturation voltage. A normal transistor has a saturation voltage of less than 1v, perhaps a little more at or near its maximum current. A typical 5A NPN switching transistor has a Vce(sat) of no more than 0.5v at 5A. The TIP120 5A NPN Darlington transistor has a Vce(sat) of 2v maximum at 3A, and 4v maximum at 5A. So, if you want to switch 5A with a TIP120, you need to heatsink it very well, because it may dissipate up to 20W!

My reason for avoiding Darlingtons is that there are better solutions that reduce voltage drop and therefore power dissipation. For lower currents, that solution is a single transistor. For example, a BC337 (NPN) or BC327 (PNP) with a base resistor of 220 ohms (for 20mA base current) will switch 500mA. Above that, use a logic-level MOSFET. The voltage drop across the MOSFET when it is turned on is determined by its Rds(on) value at the gate drive voltage you are using. A MOSFET with Rds(on) of 0.05 ohms will drop just 250mV at 5A, dissipating only 1.25W of power, so it will not generally require a heatsink.

When might I use Darlingtons? The ULN2803 octal Darlington driver can be a useful device, not because its 8 elements are Darlingtons, but because it combines 8 transistors and associated base resistors into a single package – perfect for providing higher current drive from the output pins of a 74HC595 shift register, provided your application can tolerate the saturation voltage.

5. External debouncing hardware for rotary encoders

I’ve seen a few people on the Arduino forum discuss using capacitors and logic ICs for debouncing rotary encoders. Typically, they debounce one of the 2 pins in hardware, then use that signal as a clock to sample the other pin. While that works, these people have missed the point that rotary encoders output a 2-bit Gray code, and the effect of a bounce on the code values is to make it look as if the switch has moved on to the next state, then back a state, then on a state again. The final result is the same, regardless of the number of bounces. Given that each detent on the encoder normally corresponds to moving on 4 states, it is only necessary to introduce a 2-state hysteresis in the software for bounces to be completely hidden. So a rotary encoder should be connected to the Arduino just like 2 switches, that is, common pin to ground, and left and right pins to two digital input pins (with internal pullup resistor enabled).

When might I use external debouncing hardware with a rotary encoder? The code I use is immune to bounces provided that only one contact bounces at a time. This should normally be the case, because only one contact makes or breaks at a time. If I had an encoder that appeared to suffer from bounces on both contacts at once, I might try connecting 10nF capacitors across its contacts. But I haven’t come across such an encoder yet.

I’ll finish with the Arduino code I use to interface to rotary encoders.

// Class to interface Arduino to rotary encoders
// D Crocker, Escher Technologies Limited, October 2011.
// This code may be freely used for any purpose
// but is supplied without warranty.
//
// Declare a rotary encoder like this:
//
// RotaryEncoder encoder(leftPin, rightPin, pulsesPerClick);
//
// where pulsesPerClick is normally 4.
// Every 1 millisecond or so, call:
//
// encoder.poll();
//
// To find how much the encoder has moved since you last asked, do this:
//
// int movement = encoder.getChange();

class RotaryEncoder
{
  unsigned int state;
  int pin0, pin1;
  int ppc;
  int change;

unsigned int readState()
{
  return (digitalRead(pin0) == HIGH ? 1u : 0u)
       | (digitalRead(pin1) == HIGH ? 2u : 0u);
}

public:
  RotaryEncoder(int p0, int p1, int pulsesPerClick) :
  pin0(p0), pin1(p1), ppc(pulsesPerClick), change(0), state(0) {}

  void init();
  void poll();
  int getChange();
};

void RotaryEncoder::init()
{
  pinMode(pin0, INPUT);
  pinMode(pin1, INPUT);
  digitalWrite(pin0, 1);  // enable internal pullup
  digitalWrite(pin1, 1);  // enable internal pullup
  change = 0;
  state = readState();
}

void RotaryEncoder::poll()
{
  // State transition table
  static int tbl[16] =
  { 0, +1, -1, 0,
    // position 3 = 00 to 11, can't really do anythin, so 0
    -1, 0, -2, +1,
    // position 2 = 01 to 10, assume a bounce, should be 01 -> 00 -> 10
    +1, +2, 0, -1,
    // position 1 = 10 to 01, assume a bounce, should be 10 -> 00 -> 01
    0, -1, +1, 0
    // position 0 = 11 to 10, can't really do anything
  };

  unsigned int t = readState();
  int movement = tbl[(state << 2) | t];
  if (movement != 0)
  {
    change += movement;
    state = t;
  }
}

int RotaryEncoder::getChange()
{
  int r;
  noInterrupts();
  if (change >= ppc - 1)
  {
    r = (change + 1)/ppc;
  }
  else if (change <= 1 - ppc)
  {
    r = -((1 - change)/ppc);
  }
  else
  {
    r = 0;
  }
  change -= (r * ppc);
  interrupts();
  return r;
}
About these ads
This entry was posted in Electronics and tagged , . Bookmark the permalink.

5 Responses to Five things I never use in Arduino projects

  1. Mike Cook says:

    You said:-
    While that works, these people have missed the point that rotary encoders output a 2-bit Gray code, and the effect of a bounce on the code values is to make it look as if the switch has moved on to the next state, then back a state, then on a state again.

    I have not missed the point at all. I have used rotary switches extensively and the result of a bounce on an edge can lead to the code being interpreted as if the encoder was being rotated in the opposite direction. This is a result of real experience over the years of using them.

    • davidcrocker says:

      As I said, a bounce makes it look at the hardware level like the encoder was moved slightly back in the direction opposite to that in which it is being turned, then forward again. A 2- or 3-state hysteresis (as in my code) makes the bounce invisible to the software that calls getChange().

  2. Pingback: Blog J.Schweiss | Five things never to use in Arduino projects

  3. «External debouncing hardware for rotary encoders»
    I use same bulletproof method of software debouncing, and I’m very satisfied with its stability. Didn’t find any similar solution in Arduino materials. At last I see sombody that use same method as I.

  4. Tod Flak says:

    Nice article, thanks for posting this. I have only been using Arduino for a short time, and I have pretty quickly come to the same realizations about the memory usage/fragmentation caused by the String class. It is amazing how one needs to think differently about program structure when constrained to 2KB of RAM!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s