Voting resources, early voting, and poll worker information - VOTE. ... Adafruit is open and shipping.
0

Help with Project Combining Neopixels and Servo (attempt at
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Help with Project Combining Neopixels and Servo (attempt at

by redsholdrd on Wed Nov 13, 2019 7:36 am

Hello,

This is a continuation of a project mixing through hole NeoPixels and pan-tilt servos to create a flock of glowing, controllable, ET fingers.

Initial info was posted on this question thread that has since been locked, I'm guessing due to inactivity (sorry, been sitting with this project for while).
viewtopic.php?f=47&t=153779

I've been trying to convert the code for controlling the servo to object oriented programming after going through your Multi-tasking the Arduino tutorial, and Neopixel tutorials.

However when I run the servo and neopixel Update functions in the loop, they don't run together as the servo and regular LED code does in the tutorial, but rather the servo pauses to let the NP code finish.
I've made a little video showing the issue. The first half shows the NeoPixel program (Lady A's "rainbow" flash program) running with a 5000ms interval, which shows the servos move then pause a bit for the flashing program to run. The second half shows with a 50ms interval, where the NPs flash consecutively, but where the servos creep along.
https://vimeo.com/372848598/f9cce17bca

I'm guessing the problem is in my programming, converting the NeoPixel programs to object oriented ones so they can run together. I want to be able to run changing programs on both the servos and NPs, but am first just trying to get the two to run together. I've copied my Arduino code for this program below with some notes.

Thank you so much for any suggestions you might have!
-j

Code: Select all | TOGGLE FULL SIZE
#include <Wire.h>  //This library allows you to communicate with
                  //I2C / TWI devices.
#include <Adafruit_PWMServoDriver.h>
#include <Servo.h>  //need this???
#include <Adafruit_NeoPixel.h>

#define LED_PIN 6
#define LED_COUNT 7

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

// our servo # counter
uint16_t servonum = 0;  //change to 16?

void setup() {
  pwm.begin();
  pwm.setPWMFreq(60);  // Analog servos run at ~60 Hz updates
  delay(10);

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
  strip.Color(  0, 255,   0);
}

//make rainbow function into a class

//servo movement

class Sweeper
{
  int pos;                    //the current servo position
  int increment;              //increment to move for each interval
  int updateInterval;         //interval between updates
  unsigned long lastUpdate;   //previous update of position
  int xMin;   
  int xMax;
 
  int xservonum;
 

public:
  Sweeper(int interval, int servonum, int StartPos, int Min, int Max)

  {
    updateInterval = interval;
    increment = 1;               
    pos = StartPos;             
    xMin = Min;               
    xMax = Max;
    xservonum = servonum;
  }
 
 
void Update()   
{
  if((millis() - lastUpdate) > updateInterval)  //update pos if past interval
{
  lastUpdate = millis();   //assign lastUpdate to current time "remember the time"
  pos += increment;   //set pos to (old)pos + (increment)
  pwm.setPWM(xservonum, 0, pos);                    //was servo.write(pos);
   if ((pos >= xMax) || (pos <= xMin))
                                      //if it hits limit up or down
   
                                    //INITIAL POSITION IS IMPORTANT,
                                    //list as part of the called loop function?
  {
    increment = -increment;   //reverse direction
  }
}
}
};

/*the following is my attempt to turn this function from Lady Ada's
 Neopixel strand test into a class
 
 From Ada:
 // Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this outer loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
      // Offset pixel hue by an amount to make one full revolution of the
      // color wheel (range of 65536) along the length of the strip
      // (strip.numPixels() steps):
      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
      // optionally add saturation and value (brightness) (each 0 to 255).
      // Here we're using just the single-argument hue variant. The result
      // is passed through strip.gamma32() to provide 'truer' colors
      // before assigning to each pixel:
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }
}
 */

class rainbow
{
  int wait; //delay
  int updateInterval;         //interval between updates
  unsigned long previousMillis;
  long firstPixelHue;
  long pixelHue;    //int in Ada's but firstpixel hue is long so why not other one?
 
public:
rainbow(int wait)

{
  updateInterval = wait;    //not sure if/why have to swap term here
  previousMillis = 0;
}

void Update()
{
  unsigned long currentMillis = millis();
 
  if((currentMillis - previousMillis >= updateInterval))   //TRY ADD SOME MS HERE TO GIVE TIME TO DO THING
    {
      previousMillis = currentMillis;   
    for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    for(int i=0; i<strip.numPixels(); i++)
      {                                     // see Lady Ada for comments
      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
  }
  }
 
}

};

/*This is my attempt to fit the NeoPixel colorWipe function from the
 * same test program into a class.
 * -->MY DIFFICULTY HERE IS HOW TO FIT A NESTED FUNTION
 * (IN THIS CASE "strip.Color") IN A CLASS...DO I HAVE TO TURN THAN
 * FUNCTION INTO ANOTHER CLASS?
 */

class colorWipe
{
  int wait; //delay
  int updateInterval;         //interval between updates
  long previousMillis;
  uint32_t color;
  long firstPixelHue;
  long pixelHue;    //int in Ada's but firstpixel hue is long so why not other one?
 
public:
colorWipe(uint32_t color, int wait)
{
  updateInterval = wait;    //not sure if/why have to swap term here
  previousMillis = 0;
  color = ( 0, 0, 127);
}

void Update()
{
  unsigned long currentMillis = millis();
 
  if((currentMillis - previousMillis >= updateInterval))   //TRY ADD SOME MS HERE TO GIVE TIME TO DO THING
    {
      previousMillis = currentMillis;   
    for(int i=0; i<strip.numPixels(); i++) // For each pixel in strip...
      {                                     // see Lady Ada for comments
        strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
        strip.show();
      //delay(wait);                  //eliminate delay for class
    }
  }
  }
};

//instances of 16 servos

Sweeper sweeper0(15, 0, 470, 470, 610);  //start at bottom of tilt 400
Sweeper sweeper1(15, 1, 100, 100, 475); // change from 
Sweeper sweeper2(15, 2, 400, 400, 540);
Sweeper sweeper3(15, 3, 215, 215, 590);
Sweeper sweeper4(15, 4, 400, 400, 540);
Sweeper sweeper5(15, 5, 75, 75, 450);
Sweeper sweeper6(15, 6, 240, 240, 380);
Sweeper sweeper7(15, 7, 90, 90, 465);
Sweeper sweeper8(15, 8, 100, 100, 240);
Sweeper sweeper9(15, 9, 225, 225, 600);
Sweeper sweeper10(15, 10, 440, 440, 580);
Sweeper sweeper11(15, 11, 200, 200, 575);
Sweeper sweeper12(15, 12, 450, 450, 590);
Sweeper sweeper13(15, 13, 100, 100, 475);
Sweeper sweeper14(15, 14, 125, 125, 265);
Sweeper sweeper15(15, 15, 105, 105, 480);

//instance of rainbow
rainbow rainbow1(5000);   //long interval (5 sec) to test coordination
                          //between servo and neopixel
colorWipe colorWipe1(strip.Color(  0, 255,   0), 50); // Green
                                            //having trouble with this
                                            //first work out general timing
                                            //btwn servos and neopixels


void loop()
{

  sweeper0.Update();  //update defined above
  sweeper1.Update();
  sweeper2.Update();
  sweeper3.Update();
  sweeper4.Update();
  sweeper5.Update();
  sweeper6.Update();
  sweeper7.Update();
  sweeper8.Update();
  sweeper9.Update();
  sweeper10.Update();
  sweeper11.Update();
  sweeper12.Update();
  sweeper13.Update();
  sweeper14.Update();
  sweeper15.Update();

  rainbow1.Update();
 // colorWipe1.Update();   //work on this later
 

}

redsholdrd
 
Posts: 12
Joined: Sat Jan 28, 2017 5:51 pm

Re: Help with Project Combining Neopixels and Servo (attempt

by adafruit_support_bill on Wed Nov 13, 2019 8:22 am

Your update functions for the neopixels are trying to implement the entire pattern. They need to do just one step at a time so that the servo has time to run in between steps.

Your colorWipe is updating the entire strip instead of one pixel at a time as shown in the guide:
https://learn.adafruit.com/multi-taskin ... color-wipe

And the rainbow cycle is going through the entire color wheel - instead of one color at a time:
https://learn.adafruit.com/multi-taskin ... he-rainbow

adafruit_support_bill
 
Posts: 78736
Joined: Sat Feb 07, 2009 10:11 am

Re: Help with Project Combining Neopixels and Servo (attempt

by redsholdrd on Tue Aug 04, 2020 2:00 pm

Hello again Bill and Team!
I've been working on this same servo/neopixel project on an off for the last year, and I've reached a stuck point in terms of programming.

I want to be able to change variables within a class based on external input: similar to how Bill used the buttons in the Multitasking the Arduino 3 (https://learn.adafruit.com/multi-taskin ... 3/overview) to change the different neopatterns on various neopixel strips in their set up.

In my case I am programming different movement sequences ("dances") for these two servo-motor arms (https://www.adafruit.com/product/1967) to be changed by typing numbers in to the serial monitor.

I made these into a class, with the serial type-in control in the loop where the class instances are updated.
However when I run it I get an error that the variables used are "Private within this context." (code below)

I am having a hard time organizing the member variables, and basic Class set up and format, to be able to access them while they are running.
Any suggestions as to what I am missing to gain access to these variables and be able to change the behavior while the program is running?
Thanks!
J

Code: Select all | TOGGLE FULL SIZE
#include <Wire.h>  //This library allows you to communicate with
                  //I2C / TWI devices.
#include <Adafruit_PWMServoDriver.h>
#include <Servo.h>  //need this???
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

uint16_t servonum = 0;  //change to 1
unsigned long Interval;      //put these in a class?

class Sweeper
{
 private:   //added this, is it needed?
  int pos;                    //the current servo position
  int increment;              //increment to move for each interval
  int updateInterval;         //interval between updates
  unsigned long lastUpdate;   //previous update of position
  int xMin;   
  int xMax;
  int xMid;
  int xservonum;
  bool value;         //a number 1 or 2 to pick which "dance" routine to do
  int xdance;        // which dance to do, STILL not sure why i have to mention varibles in multiple places for Class

 public:
  Sweeper(int dance, int interval, int servonum, int StartPos, int Min, int Max) //*moving this down from guide for completion try

  {
     //member variables:
    updateInterval = interval;
    increment = 1;               
    pos = StartPos;       //starting position of servo, each motor is a little different (variability)           
    xMin = Min;           //minimum position for servo     
    xMax = Max;           //max position
    xMid = Min + ((Max-Min)/2); //mid point position of servo range
    xservonum = servonum;
    xdance = dance; 
  }
 

void Update()
{
 
if (xdance == 1)                        //if "dance" value == 1, do this dance routine:
                                        //a continuous arc back and forth

  if((millis() - lastUpdate) > updateInterval)  //update pos if past interval
  {
     lastUpdate = millis();             //assign lastUpdate to current time "remember the time"
  pos += increment;                     //set pos to (old)pos + (increment)
  pwm.setPWM(xservonum, 0, pos);        //was servo.write(pos);
   if ((pos >= xMax) || (pos <= xMin))
                                        //if it hits limit up or down
                                        //INITIAL POSITION IS IMPORTANT servo might get stuck otherwise,
    {
      increment = -increment;           //reverse direction
    }
  }
}

if (xdance == 2)                        //if "dance" value == 2, do this dance routine: zig zag,
                                        //start middle, (wait), max (wait), min (wait), middle (wait)
 
 {
    if((millis() - lastUpdate) > updateInterval && (millis() - lastUpdate) < (updateInterval*2))
     {
    pwm.setPWM(xservonum, 0, xMid); //start in the middle
      }
    if((millis() - lastUpdate) > (updateInterval*2) && (millis() - lastUpdate) < (updateInterval*4))
      {
         pwm.setPWM(xservonum, 0, xMax);
      }
    if((millis() - lastUpdate) > (updateInterval*4) && (millis() - lastUpdate) < (updateInterval*6))
      {
         pwm.setPWM(xservonum, 0, xMin);
      }
     if((millis() - lastUpdate) > (updateInterval*6) && (millis() - lastUpdate) < (updateInterval*8))
      {
         pwm.setPWM(xservonum, 0, xMid);
      }
     if((millis() - lastUpdate) > (updateInterval*8))
        {
         lastUpdate = millis();
         Serial.println ("reset "); //then has to wait for counter to be <update interval at the beginning of this loop
        }
         
      }
   }
};          //end of class definition
 
//instances of sweeper class
   
Sweeper sweeper0(1, 15, 0, 470, 470, 610);  //15 interval a little rickety, try 5--didn't change much (period of ~16sec each time)
Sweeper sweeper1(1, 15, 1, 150, 150, 475);
Sweeper sweeper2(1, 15, 2, 400, 400, 540);
Sweeper sweeper3(1, 15, 3, 215, 215, 590);
Sweeper sweeper4(1, 15, 4, 400, 400, 540);
Sweeper sweeper5(1, 15, 5, 75, 75, 450);
Sweeper sweeper6(1, 15, 6, 240, 240, 380);
Sweeper sweeper7(1, 15, 7, 90, 90, 465);
Sweeper sweeper8(1, 15, 8, 100, 100, 240);
Sweeper sweeper9(1, 15, 9, 225, 225, 600);
Sweeper sweeper10(1, 15, 10, 440, 440, 580);
Sweeper sweeper11(1, 15, 11, 200, 200, 575);
Sweeper sweeper12(1, 15, 12, 450, 450, 590);
Sweeper sweeper13(1, 15, 13, 100, 100, 475);
Sweeper sweeper14(1, 15, 14, 125, 125, 265);
Sweeper sweeper15(1, 15, 15, 105, 105, 480);


void setup() {
 Serial.begin(9600);
 pwm.begin();           //servo set up
 pwm.setPWMFreq(60);  // Analog servos run at ~60 Hz updates
 delay(10);
 bool value = false;
 //bool value = true;

 int xdance = 2;
 
}

void loop() {

 sweeper0.Update();  //update defined above
 sweeper1.Update();
 sweeper2.Update();
 sweeper3.Update();
 sweeper4.Update();
 sweeper5.Update();
 sweeper6.Update();
 sweeper7.Update();
 sweeper8.Update();
 sweeper9.Update();
 sweeper10.Update();
 sweeper11.Update();
 sweeper12.Update();
 sweeper13.Update();
 sweeper14.Update();
 sweeper15.Update();

 if (Serial.available() > 0) {      //read keyboard to select dance
    // read incoming serial data:
    char ch = Serial.read();       
    if (ch == 1)                    //if serial reads a typed in "1"
    {
       sweeper2.xdance = 1;  //change dance of sweepers 2 and 3 to dance 1
       sweeper3.xdance = 1;
    }
    else if (ch == 2)               //if serial reads a typed in "2"
    {
       sweeper2.xdance = 2;
       sweeper2.xdnance = 2; //change dance of sweepers 2 and 3 to dance 2
    }
   
  }

}   //end of loop

 

redsholdrd
 
Posts: 12
Joined: Sat Jan 28, 2017 5:51 pm

Re: Help with Project Combining Neopixels and Servo (attempt

by adafruit_support_bill on Tue Aug 04, 2020 3:31 pm

I assume you are talking about the xdance member variable that you are trying to update in the loop. In the class definition, you have declared them as 'private:'. If you want them to be accessible outside of the class, you need to define them as 'public:'

https://www.tutorialspoint.com/cplusplu ... ifiers.htm

adafruit_support_bill
 
Posts: 78736
Joined: Sat Feb 07, 2009 10:11 am

Re: Help with Project Combining Neopixels and Servo (attempt

by redsholdrd on Tue Aug 04, 2020 5:33 pm

Thanks.
I just put a "public:" at the beginning of the class Sweeper, before the member variables, and it now compiles without error.
Your description of classes in your mulit-tasking the Arduino part 1 tutorial was the first time I've learned about C++ classes, so i figured I always would need to put a "public:" after the statement of member variables, and before the constrictor. I think I see now that you can designate some components of the class public, private, and protected. Thanks!

However, now the trigger i set up (type and enter a 1 or 2 into the serial monitor to switch between the two servo dances in the class) doesn't seem to be working...
I'm using a serial read-in just as a test button to trigger 2 different dance routines. My end goal is to program a lot of different dances that can be selected randomly, or with another function (for instance select among a number of dances in order, or switch between two of them, create different behaviors, etc.).

1. Would this control function live at the end of the loop like my sample did here and your button triggers did in your Multitasking 3 tutorial?
2. Can you see what might be going wrong with my little serial read test here ?

Thank you!
J

redsholdrd
 
Posts: 12
Joined: Sat Jan 28, 2017 5:51 pm

Re: Help with Project Combining Neopixels and Servo (attempt

by adafruit_support_bill on Tue Aug 04, 2020 6:35 pm

Code: Select all | TOGGLE FULL SIZE
    if (ch == 1)                    //if serial reads a typed in "1"


That is not testing for the character '1'. It is testing for an ASCII SOH.

In C/C++, 1 is not the same thing as "1" or '1'.

1 is an integer value. It corresponds to the non-printable ASCII character SOH (Start Of Header).

"1" is a one character string. That is the ASCII character '1' (integer value 49) followed by an ASCII NULL (integer value 0).

And '1' is the ASCII character '1' (integer value 49).

So to check for someone typing the character '1', you would write:

Code: Select all | TOGGLE FULL SIZE
    if (ch == '1')                    //if serial reads a typed in "1"


https://www.tutorialspoint.com/cplusplu ... trings.htm
https://www.asciitable.com/

adafruit_support_bill
 
Posts: 78736
Joined: Sat Feb 07, 2009 10:11 am

Re: Help with Project Combining Neopixels and Servo (attempt

by redsholdrd on Tue Aug 11, 2020 5:41 am

yes, thank you!

redsholdrd
 
Posts: 12
Joined: Sat Jan 28, 2017 5:51 pm

Please be positive and constructive with your questions and comments.