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;
}
This entry was posted in Electronics and tagged , . Bookmark the permalink.

12 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!

  5. Hey. Great article. Im working with a project that gathers most of the described elements…
    and we were already experiencing problems with strings (due to the low RAM),

  6. Chrissi says:

    Thank you for this method of dealing with rx encoders. I would love to use expensive Grayhill optoelectric encoders but this would kill the budget for my project. The El Cheapo Grande $2 ones I am working with are throwing erratic signals at me using common rx encoder interrupt routines and I’d prefer to save the two interrupts for future use.
    I am trying to implement this with MsTimer2 as you have suggested elsewhere. How would I call for polling from the MsTimer2 line in setup?
    MsTimer2::set(2, (call for poll?) ); //2ms, call for poll?
    Thank you, Chrissi

  7. danielccn says:

    Hi , when u mentioned that not to use pull up or down resistor , but to use the internal one . By doing digitalWrite to HIGH, will that damage the pin since it is an input pin. I read somewhere that to beware when doing digitalWrite to input pin. Thanks

    • davidcrocker says:

      Doing a digitalWrite HIGH to a pin configured as an input pin is one of the ways of enabling the pullup resistor on that pin. It used to be the only way, until pin mode INPUT_PULLUP was introduced. The thing you should never do to a pin that has inputs attached to it is use the pinMode call to configure it as an output pin, except in circumstances in which the pin serves as both an input and an output.

  8. Al.. says:

    Hi,
    thanks for your time on writing this.. great stuff..
    i found your encoder lib at github.. any possibility of an example ino..?
    i’d to try it for a user input device..
    thanks,

  9. Leif says:

    How useful are these hints?

    1) Strings aren’t really a problem as long as they’re fixed and you utilize the F macro. However, if you intend to create dynamic strings, you’ll encounter issues with both strings and character arrays. An Arduino might not be the optimal choice when extensive dynamic memory allocation is required.

    2. SoftwareSerial can be turned on and off. If you examine the code closely, you’ll discover an undocumented end() method that stops listening and sets the RX pin to high. However, I concur that SoftwareSerial is more of a workaround than a recommended solution.

    3. Everyone should grasp the concept of internal and external resistors and be capable of calculating their appropriate sizes, especially when planning to use MOSFETs.

    4. MOSFETs are probably the worst choice you can make. While they’re useful for high frequencies and high currents, they’re not very reliable switches. Easily susceptible to induction, they always need their own pulldown to avoid floating state when the MCU is turned off or the gate pin is floating. Even if you use Logic Level MOSFETs, they will require higher Vgs to fully switch. The pulldown will lower the Vgs though. In the end, they are bratty little beasts that cause nothing but trouble. On the other hand, a Darlington transistor is robust, doesn’t need a pulldown, and will work perfectly as long as you manage the heat dissipation properly.

    5. Just never use rotary encoders. There’s too much code for such little usefulness. Understand hardware and software debouncing and opt for buttons instead.

    • davidcrocker says:

      Thanks for your comments. While I appreciate you reading and commenting on my blog post, we’ll have to disagree about some things.

      When I advised against using String I was referring to the Arduino String class. As you say, C-style string constants are not a problem, and the F macro can be used to keep them out of RAM.

      IMO, power Darlington transistors are rubbish compared to power mosfets – just look at the voltage drop and consequent power dissipation. Turning on a logic-level mosfet from a 5V Arduino is never a problem. A 10K pulldown resistor makes negligible difference to the Vgs seen by the mosfet. I’m not sure what you mean when you say they are “susceptible to induction” – are you talking about gate series inductance, load inductance, or something else? I’ve been using both power and small signal mosfets for years, and they never give me trouble – whereas I haven’t used a power BJT or Darlington for 10+ years except to replace blown ones in old equipment. I still use small signal BJTs occasionally.

      Rotary encoders need very little code (see my library at https://github.com/dc42/arduino/tree/master/Libraries/RotaryEncoder), provide a much nicer user interface than up/down buttons, and the mechanical sort are inexpensive.

      Anyway, thanks again for reading my post, and have fun with electronics!

Leave a comment