SunFounder Nano v4.0 - Unexpected behavior of RGB LED pwm pins when servo connected

Starting with the SunFounder DIY Bionic Robot Lizard, I am trying to build a chameleon that uses TCS34725 RGB sensor to control a set of eight RGB LEDs so that when the lizard crawls over surfaces of different colors, it changes color to match the surface. At the moment I am using ten swatches of colored construction paper as test surfaces.

The lizard comes with a Nano on an extension board that provides a power supply and a row of triple pins that include GND, VCC and Signal. I am using PWM pins 9, 10, 11 to control the brightness of the RBG LEDs, The total current draw is well less than the maximum 150mA per pin specified in the Nano datasheet. My test program for the RGB sensor with the LEDs works fine (see below)

#include <Wire.h>
#include "Adafruit_TCS34725.h"

Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_101MS, TCS34725_GAIN_60X);

const int PWM_PIN_RED = 9;  // Pulse Width Modulation output pin for Red lead of RGB LED
const int PWM_PIN_GREEN = 10;  // Pulse Width Modulation output pin for Green lead of RGB LED
const int PWM_PIN_BLUE = 11;  // Pulse Width Modulation output pin for Blue lead of RGB LED

class ColorMatcher {
  private:
    // class member variables, initialized upon instantiation
    // First index is color swatch 0..9 ~ Red, Orange .. White; 2nd index is R,G,B ~ 0,1,2
    double outputPWM[10][3] = {{253, 0, 2}, {253, 221, 0}, {230, 253, 0}, {0, 253, 1}, {0, 253, 41}, {0, 0, 246}, {100, 0, 246}, {2, 2, 0}, {0, 0, 0}, {107, 253, 102}};
    double colorSwatch[10][3] = {{143, 57, 61}, {155, 55, 48}, {175, 121, 75}, {72, 102, 81}, {58, 103, 128}, {58, 80, 118}, {82, 76, 100}, {105, 78, 71}, {90, 82, 81}, {196, 191, 194}};
    double reading[3] = {0.0, 0.0, 0.0};

    #define commonAnode false   // set to false if using a common cathode LED                     

    double sepFunction(double x1, double x2, double y1, double y2, double z1, double z2) {
      double separation;
      separation = sqrt(sq(x2 - x1) + sq(y2 - y1) + sq(z2 - z1));
      return separation;
    }
    
  public:
    ColorMatcher() {
      pinMode(PWM_PIN_RED, OUTPUT);
      pinMode(PWM_PIN_GREEN, OUTPUT);
      pinMode(PWM_PIN_BLUE, OUTPUT);
    }

    void Update() {
      uint16_t r, g, b, c;
      tcs.setInterrupt(false);       // turn on LED
      delay(60);                     // takes 50ms to read
      tcs.getRawData(&r, &g, &b, &c);
      tcs.setInterrupt(true);        // turn off LED

      uint32_t sum = c;
      float R, G, B;
      R = r; R /= sum;     // Compute the fraction of light passing through the red filter, relative to no filter
      G = g; G /= sum;
      B = b; B /= sum;
      R *= 256; G *= 256; B *= 256;  // Compute fractions of filtered light relative to 256

      reading[0] = R;
      reading[1] = G;
      reading[2] = B;

      double shortest = 512; //pick an initial separation which is way too big
      int n = -1;  //index matching closest colorSwatch vector
      for (int i = 0; i < 10; i = i + 1) {
        double distance = sepFunction(reading[0], colorSwatch[i][0], reading[1], colorSwatch[i][1], reading[2], colorSwatch[i][2]);
        if (distance < shortest) {
          shortest = distance;
          n = i;
        }
      }
      Serial.print("n = "); Serial.println(n);
      analogWrite(PWM_PIN_RED, outputPWM[n][0]);       //
      analogWrite(PWM_PIN_GREEN, outputPWM[n][1]);     //
      analogWrite(PWM_PIN_BLUE, outputPWM[n][2]);      //
      }
};  // end ColorMatcher class

ColorMatcher colorMatcher; // Create colorMatcher object

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Begin Serial");

  if (tcs.begin()) {
    Serial.println("Ready");
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  colorMatcher.Update();
}
[/code]
type or paste code here

The code for making the lizard crawl also works as expected. However, when I try to integrate the code for the RGB sensor / LEDs, it fails when I try to attach a servo. See below, line number 186



/* Object Libraries for RGB sensor and servos*/
#include <Wire.h>
#include "Adafruit_TCS34725.h"

/* Object for RGB sensor */
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_101MS, TCS34725_GAIN_60X);

#include <Servo.h>

/* Objects for Servos */
Servo Servo_1; // front legs of lizard
Servo Servo_2; // waist of lizard
Servo Servo_3; // back legs of lizard


/* Constants for RGB LEDs */
const int PWM_PIN_RED = 9;  // Pulse Width Modulation output pin for Red lead of RGB LED
const int PWM_PIN_GREEN = 10;  // Pulse Width Modulation output pin for Green lead of RGB LED
const int PWM_PIN_BLUE = 11;  // Pulse Width Modulation output pin for Blue lead of RGB LED

/* Constants and variables for servos */
const bool HALF = true;  // used to indicate half-turn
const bool FULL = false; // used to indicate full-turn

const bool RIGHT = true;
const bool LEFT = false;

const bool CW = true;
const bool CCW = false;

int T1DEG = 17;         // milliseconds to move through one degree

int zeropos[] = {74, 83, 94}; // array of initial positions
int waist_angle = 25;   // angle turned by waist servo
int step_angle = 35;    // angle turned by front and back servos when stepping
int turn_angle = 55;    // angle turned by front and back servos when making a turn

/* ColorMatcher object */
class ColorMatcher {
  private:
    // class member variables, initialized upon instantiation
    // First index is color swatch 0..9 ~ Red, Orange .. White; 2nd index is R,G,B ~ 0,1,2
    double outputPWM[10][3] = {{253, 0, 2}, {253, 221, 0}, {230, 253, 0}, {0, 253, 1}, {0, 253, 41}, {0, 0, 246}, {100, 0, 246}, {2, 2, 0}, {0, 0, 0}, {107, 253, 102}};
    double colorSwatch[10][3] = {{143, 57, 61}, {155, 55, 48}, {175, 121, 75}, {72, 102, 81}, {58, 103, 128}, {58, 80, 118}, {82, 76, 100}, {105, 78, 71}, {90, 82, 81}, {196, 191, 194}};
    double reading[3] = {0.0, 0.0, 0.0};

#define commonAnode false   // set to false if using a common cathode LED                     

    double sepFunction(double x1, double x2, double y1, double y2, double z1, double z2) {
      double separation;
      separation = sqrt(sq(x2 - x1) + sq(y2 - y1) + sq(z2 - z1));
      return separation;
    }

  public:
    ColorMatcher() {
      pinMode(PWM_PIN_RED, OUTPUT);
      pinMode(PWM_PIN_GREEN, OUTPUT);
      pinMode(PWM_PIN_BLUE, OUTPUT);
    }

    void Update() {
      uint16_t r, g, b, c;
      tcs.setInterrupt(false);       // turn on LED
      delay(60);                     // takes 50ms to read
      tcs.getRawData(&r, &g, &b, &c);
      tcs.setInterrupt(true);        // turn off LED

      uint32_t sum = c;
      float R, G, B;
      R = r; R /= sum;     // Compute the fraction of light passing through the red filter, relative to no filter
      G = g; G /= sum;
      B = b; B /= sum;
      R *= 256; G *= 256; B *= 256;  // Compute fractions of filtered light relative to 256

      reading[0] = R;
      reading[1] = G;
      reading[2] = B;

      double shortest = 512; //pick an initial separation which is way too big
      int n = -1;  //index matching closest colorSwatch vector
      for (int i = 0; i < 10; i = i + 1) {
        double distance = sepFunction(reading[0], colorSwatch[i][0], reading[1], colorSwatch[i][1], reading[2], colorSwatch[i][2]);
        if (distance < shortest) {
          shortest = distance;
          n = i;
        }
      }
      Serial.print("n = "); Serial.println(n);
      analogWrite(PWM_PIN_RED, outputPWM[n][0]);       //
      analogWrite(PWM_PIN_GREEN, outputPWM[n][1]);     //
      analogWrite(PWM_PIN_BLUE, outputPWM[n][2]);      //
    }
};  // end ColorMatcher class

ColorMatcher colorMatcher; // Create colorMatcher object

void reportangles() {
  Serial.print("Servo_1: "); Serial.println(zeropos[0]);
  Serial.print("Servo_2: "); Serial.println(zeropos[1]);
  Serial.print("Servo_3: "); Serial.println(zeropos[2]);
}

void twist_action(bool half, bool cw) {
  int n, k;
  if (half)
  {
    n = 1;
  }
  else
  {
    n = 2;
  }
  if (cw)
  {
    k = 1;
  }
  else
  {
    k = -1;
  }
  for (int i = 0; i < n * waist_angle; i++)
  {
    Servo_2.write(zeropos[1] + k * i);
    delay(T1DEG);
  }
  zeropos[1] = zeropos[1] + k * n * waist_angle;
  reportangles();
}

void step_action(bool half, bool right) {
  int n, k;
  if (half)
  {
    n = 1;
  }
  else
  {
    n = 2;
  }
  if (right)
  {
    k = 1;
  }
  else
  {
    k = -1;
  }
  for (int i = 0; i < n * step_angle; i++)
  {
    Servo_1.write(zeropos[0] + k * i);
    Servo_3.write(zeropos[2] - k * i);
    delay(T1DEG);
  };
  zeropos[0] = zeropos[0] + k * n * step_angle;
  zeropos[2] = zeropos[2] - k * n * step_angle;
  reportangles();
}

void crawlForward()
{
  twist_action(HALF, CW);
  step_action(HALF, RIGHT);
  twist_action(FULL, CCW);
  step_action(FULL, LEFT);
  twist_action(FULL, CW);
  step_action(FULL, RIGHT);
  twist_action(HALF, CCW);
  step_action(HALF, LEFT);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Begin Serial");

  if (tcs.begin()) {
    Serial.println("Ready");
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1);
  }

  //Servo_1.attach(4);  // Uncommenting this line causes RGB LEDs on pin 9 (PWM) to not illuminate
  // DC voltage on pin 9 is 3.4V when this is commented out;
  // when uncommented, voltage drops to 2.1V
  //Servo_2.attach(3);
  //Servo_3.attach(2);
  //Servo_1.write(zeropos[0]);
  //Servo_2.write(zeropos[1]);
  //Servo_3.write(zeropos[2]);
  //Serial.println("Finished setup");
  //reportangles();
  //delay(1000);
}

void loop() {
  // put your main code here, to run repeatedly:
  //crawlForward();
  //delay(100);
  colorMatcher.Update();
  delay(100);
}
[/code]

The RGB sensor SDA line is connected to pin A4 and SCL to A5.

If I leave line #186 commented out, then everything is fine; the voltage of pin 9 (red LED) is 3.4V. If I uncomment line #186 (Servo_1.attach(4);) the voltage drops to 2.1V and the LED won't illuminate. I have tried using an external power supply to put 5V on the VCC pin, but that seems to make no difference.

Here is a rough schematic:
![chameleon-schematic|386x500](upload://7nMC47UNr3a0fYbrsVkFyrGwCR6.jpeg)


Why would attaching a servo cause my RGB sensor/LED code to fail?

Thanks.

David
type or paste code here

What type of servo, and how is it powered?

About 95% of the servo-associated problems reported on this forum are due to inadequate servo power supplies. Plan on 1 Ampere per servo for small ones, 3 A/servo for larger ones.

Really 150mA per pin!?

Got that wrong mate, read it again. You should not exceed 30mA although some people stick with a limit of 20mA. At 40mA you start getting damage done to the Arduino pin, if you are lucky just the pin burns out, if not the whole Arduino.

You will find two PWM pins that do not work when you have the servo library installed due to both functions needing the same timer.

Thank you for the suggestion, jremington. I will look more closely at the current requirements.

There are three servos-- SF006C Clutch Gear Digital Servo from SunFounder. I have them connected to pins 2, 3, 4. When I measure the current through the ground pin, I get 22-34 mA when the servo is swing back and forth.

My mistake.

Could you say more about the PWM pins that do not work when I have the servo library installed? Should I try switching my LED pins to someplace other than 9, 10, 11? Do you know which pins should not be used?

Thanks for the advice.

According to the data sheet,the start/stall current of that servo is about 650 mA, which it draws every time it starts moving. So for three of them, the servo power supply must be able to supply at least 2 Amperes. If not, you can expect erratic behavior.

Datasheet may be downloaded at
http://wiki.sunfounder.cc/images/5/52/SF006C_Clutch_Gear_Digital_Servo180607.pdf

Pins 9 and 10 are derived from timer1. The standard servo library also uses timer1. So when you use the servo library you can't use those pins to produce PWM.

Do you need PWM for dimming LEDs or will just turning them on and off suffice?

Good point. The Robot Lizard kit comes with a Nano plugged into a SunFounder Server Control Board which uses a 9V battery. The signal lines for the three motors are connected to digital pins 2,3,4, but it appears that only pin D3 is capable of PWM, so I don't know how they control the other two servos on pins 2 and 4. In any case, the original kit has enough power to drive the three servos.

Thanks for the suggestion to avoid pins 9 and 10 for controlling my RGB LEDs. Yes, I do need PWM control, as I am adjusting the color of the RGB LED by using three different inputs for each color red, green and blue.

I attached the LEDs to pins 5, 6, and 11. They work fine, controlling the colors, as long as I don't try to operate the servos at the same time. I think I need to install a separate power supply for the RGB LEDs.

Stay tuned. I'm hopeful that will do the trick.

NO, for the servo motors.

How are you wiring up the LED ( from what I can see you only need one )?
Each component LED needs its own current limiting resistor.

Here is a schematic for the whole project. So far, I have not been incorporating the IR receiver, which comes with the Robot Lizard kit. Eventually, I hope to include commands sent from an IR remote.

Current status:
The RGB sensor picks up the surface color and the eight RGB LEDs do a good job of displaying that color. The LEDs stay illuminated after the servo attach commands (Servo_1.attach(4); etc) in setup()). However, as soon as servos start operating, the LEDs go out.

I'm using a bench power supply at the moment, which should provide sufficient current for the LEDs, the RGB sensor and the servos.

I think my next step is to look at the PWM signals on an oscilloscope.

Thanks for any other ideas you may have.

Each led needs its own current limiting resistor. This is because raw LEDs in parallel do not share current evenly. You will find that the resistors then come out at sensible values.
However you can only have 20 to 30mA out of an Arduino pin in total so the LEDs will not be very bright.

You should power the servos from a separate supply, not from the Arduino 5V line. I think you have been told this already. If you want the LEDs to be brighter you need a transistor in each of the three colours, and to power the LED's anode line from the same supply as you use for the servos.

You need decoupling on the servos.

Read about decoupling here
http://www.thebox.myzen.co.uk/Tutorial/De-coupling.html

Thank you for the suggestions. Actually, my schematic is not entirely accurate, because the Nano 4.0 is mounted on a Servo Controller Board that provides power to the servos and the IR receiver. Unfortunately, I have not been able to find a circuit diagram for the Servo Controller Board. I am pretty sure the servos are not powered by the Arduino 5V line, but I don't know the exact circuitry.

As far as the LEDs are concerned, the present arrangement works just fine. All LEDs are bright and they are indistinguishable in brightness. The values of 47 ohms for the red and blue inputs, and 220 ohms for the green were determined experimentally and the seem to be just right.

Onward!

That is what your schematic shows. A schematic makes no assumption of the placement of components.

So what servo controller board have you got?

For now they are but they will eventually show physical damaged. This is the problem with beginners and electronics, they think because it seems to works then it is a good circuit, not one that is slowly dissolving away.

This is why we are here, it is to give you the advice that the vast majority of internet tutorials will not give you. This is part of the education aspect that keeps people like us answering question.

Thanks to suggestion from Grumpy_Mike, I have incorporated three 2N2222 transistors into the circuit so now there is plenty of power to the 8 RGB LEDs. Here is the new schematic.

Problem now is that there seems to be an incompatibility (timing, perhaps) between using an IR remote (pin D7) and using pin D11 for controlling the blue LEDs with PWM.

In the test program below, I can use the IR remote to turn lights on and off as long as lines 90 and 97 are commented out. [See example output from Serial Monitor, following. If I uncomment the analogWrite() commands in those two lines, the Serial Monitor locks up and the IR remote no longer works. [See example output with just three lines displayed]

Another interesting note: If I use keyboard input (Serial.read) instead of IR remote input, there is not problem using pin 11 for PWM output.

Any ideas as to how I might fix or workaround this apparent incompatibility?

/*     */

/* Object Libraries for RGB sensor, IR Remote and servos*/
#include <Wire.h>
#include "Adafruit_TCS34725.h"

/* Object for RGB sensor */
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_101MS, TCS34725_GAIN_60X);

#include <IRremote.h>
#include <Servo.h>


/* Constants for RGB LEDs */
const int PWM_PIN_RED = 5;  // Pulse Width Modulation output pin for Red lead of RGB LED
const int PWM_PIN_GREEN = 6;  // Pulse Width Modulation output pin for Green lead of RGB LED
const int PWM_PIN_BLUE = 11;  // Pulse Width Modulation output pin for Blue lead of RGB LED

long KEY_CODE_plus = 0xFF906F;
long KEY_CODE_minus = 0xFFA857;

bool lightsOn = true;  // Originally, lights on

int RECV_PIN = 7;       // pin used by IR receuver

IRrecv irrecv(RECV_PIN);
decode_results results;

/* ColorMatcher object */

class ColorMatcher {
  private:
    // class member variables, initialized upon instantiation
    // First index is color swatch 0..9 ~ Red, Orange .. White; 2nd index is R,G,B ~ 0,1,2
    double outputPWM[10][3] = {{253, 0, 2}, {253, 221, 0}, {230, 253, 0}, {0, 253, 1}, {0, 253, 41}, {0, 0, 246}, {100, 0, 246}, {2, 2, 0}, {0, 0, 0}, {107, 253, 102}};
    double colorSwatch[10][3] = {{143, 57, 61}, {155, 55, 48}, {175, 121, 75}, {72, 102, 81}, {58, 103, 128}, {58, 80, 118}, {82, 76, 100}, {105, 78, 71}, {90, 82, 81}, {196, 191, 194}};
    double reading[3] = {0.0, 0.0, 0.0};

#define commonAnode false   // set to false if using a common cathode LED                     

    double sepFunction(double x1, double x2, double y1, double y2, double z1, double z2) {
      // function used to find colorSwatch vector that most closely matches color reading vector in RGB space
      double separation;
      separation = sqrt(sq(x2 - x1) + sq(y2 - y1) + sq(z2 - z1));
      return separation;
    }
  public:
    ColorMatcher() {
      pinMode(PWM_PIN_RED, OUTPUT);
      pinMode(PWM_PIN_GREEN, OUTPUT);
      pinMode(PWM_PIN_BLUE, OUTPUT);
    }

    void Update() {
      uint16_t r, g, b, c;
      tcs.setInterrupt(false);       // turn on LED
      delay(60);                     // takes 50ms to read; do not interrupt during this time period
      tcs.getRawData(&r, &g, &b, &c);
      tcs.setInterrupt(true);        // turn off LED

      uint32_t sum = c;
      float R, G, B;
      R = r; R /= sum;     // Compute the fraction of light passing through the red filter, relative to no filter
      G = g; G /= sum;     // Compute fraction through green filter, relative to none
      B = b; B /= sum;     // Compute fraction through blue filter, relative to none
      R *= 256; G *= 256; B *= 256;  // Compute fractions of filtered light relative to 256

      reading[0] = R;
      reading[1] = G;
      reading[2] = B;

      double shortest = 512; //pick an initial separation which is way too big
      int n = -1;  //index matching closest colorSwatch vector
      for (int i = 0; i < 10; i = i + 1) 
        {
        double distance = sepFunction(reading[0], colorSwatch[i][0], reading[1], colorSwatch[i][1], reading[2], colorSwatch[i][2]);
        if (distance < shortest)
          {
          shortest = distance;
          n = i;
          }
        }

      Serial.print("n = "); Serial.println(n);
      if (lightsOn)
      {
        Serial.println("Lights on");
        analogWrite(PWM_PIN_RED, outputPWM[n][0]);       //
        analogWrite(PWM_PIN_GREEN, outputPWM[n][1]);     //
        //analogWrite(PWM_PIN_BLUE, outputPWM[n][2]);      //
      }
      else
      {
        Serial.println("Lights off");
        analogWrite(PWM_PIN_RED, 0);      //
        analogWrite(PWM_PIN_GREEN,0);     //
        //analogWrite(PWM_PIN_BLUE, 0);     //
      }
    } 
};  // end ColorMatcher class

ColorMatcher colorMatcher; // Create colorMatcher object

void setup() {
  Serial.begin(115200);
  Serial.println("Begin Serial Monitor");

  irrecv.enableIRIn();  // Enable IR receiver

  if (tcs.begin()) {
    Serial.println("RGB sensor ready");
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1);
  }
}

void loop() {
  /* //Using keyboard input is compatible with pin 11 for blue LEDs
  if (Serial.available())
  {
    char ch = Serial.read();
    if (ch == '0')
    {
      Serial.println("minus");
      lightsOn = false;
    }
    else if (ch == '1')
    {
      Serial.println("plus");
      lightsOn = true;
    }
  }
  */
    //Using IR remote control is incompatible with pin 11 for blue LEDs
  if (irrecv.decode(&results)) {
    Serial.println(results.value,HEX); //Infrared encode of serial printing
    irrecv.resume();
    }
  if (results.value == KEY_CODE_plus) {
    Serial.println("plus");
    lightsOn = true;
    }
  else if (results.value == KEY_CODE_minus) {
    Serial.println("minus");
    lightsOn = false;
    }
  
  colorMatcher.Update();
}
12:18:05.715 -> Begin Serial Monitor
12:18:05.809 -> RGB sensor ready
12:18:05.996 -> n = 3
12:18:05.996 -> Lights on
12:18:06.137 -> n = 3
12:18:06.137 -> Lights on
12:18:06.325 -> n = 3
12:18:06.325 -> Lights on
12:18:06.465 -> n = 3
12:18:06.512 -> Lights on
...
12:18:07.310 -> Lights on
12:18:07.310 -> FFA857
12:18:07.310 -> minus
12:18:07.497 -> n = 2
12:18:07.497 -> Lights off
12:18:07.497 -> FFFFFFFF
12:18:07.638 -> n = 2
12:18:07.638 -> Lights off
12:18:07.825 -> n = 2
12:18:07.825 -> Lights off
12:14:40.817 -> Begin Serial Monitor
12:14:40.911 -> RGB sensor ready
12:14:41.052 -> n = 3

Sadly you have implemented the use of those transistors incorrectly. They are in a configuration known as an emitter follower, and the 100R resistors to ground are making it draw too much current.

You need a configuration know as a common emitter, this is where you have a series resistor in the base, emitter to ground and the collector to the LED's current limiting resistor.
You also need 4K7 pull up resistors on the two I2C lines A4 & A5. Also there should be some decoupling around the IR receiver, see the data sheet of that device to see where to put it but it is normally a 100R resistor in the 5V line and a large 47uF or so on the devices voltage input.

I am not sure if this is contributing to your problem with the digital write to the blue and the IR receiver, but it could be.

This could well be a clash of the use of timers between the IRremote library and the PWM generator. I would suggest you move the blue PWM control to another free PWM output.

Thanks for the suggestion, Mike. I'll look into it.