Multitasking Adafruit neo Pixel und Stepper in einem Sketch

Guten Abend,

ich möchte gern eine WS2812 B Stripe per NeoPixel mit diversen Leuchtmustern versehen und glöeichzeitig einen Stepper Motor langsam vor und zurückdrehen lassen. Alles zusammen in einem Sketch. Der Stepper Motor ist die kleine Version aus den Grundbaukästen. Ich habe mir einen Elegoo Grundbaukasten zugelegt.

  1. Reicht dafür das Netzteil 9V 1A oder sollte ich ein größeres Netzteil verwenden?
  2. Habe mal den Beispiel Code vom NeoPixel Wheel und den Stepper Code nacheinander versucht zu sketchen. Das klappt nicht. Habe ich mir schon gedacht. Gibt es da eine Multitasking Lösung?

Grß

Stefan

Zuerst die schlechte Nachricht:

Nein.

Und jetzt die Gute:

Das geht schon.
Aber nur, 1x setup und 1x loop.
Also mischen.
Nicht aneinander reihen.

Zeig was Du hast - dann kann darauf gebaut werden.

ok, mischen habe ich mir schon gedacht. Werde es mal probieren. Melde mich.

hier der Mixcode (der Editor hat wenigstens nicht gemeckert):

// hier der stepper code
#include <Stepper.h>
const int stepsPerRevolution = 2040;
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);


//hier der neopixel code
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#define PIN 6

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.  Avoid connecting
// on a live circuit...if you must, connect GND first.

void setup() 


{
// stepper code
  myStepper.setSpeed(2);

  //neopixel code
  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code

  strip.begin();
  strip.setBrightness(50);
  strip.show(); // Initialize all pixels to 'off'
}

void loop() 


 


{
// stepper code
 myStepper.step(stepsPerRevolution);
  delay(1500);
  myStepper.step(-stepsPerRevolution);
  delay(1500);

  //Neopixel Code
  // Some example procedures showing how to display to the pixels:
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
  // Send a theater pixel chase in...
  theaterChase(strip.Color(127, 127, 127), 50); // White
  theaterChase(strip.Color(127, 0, 0), 50); // Red
  theaterChase(strip.Color(0, 0, 127), 50); // Blue

  rainbow(20);
  rainbowCycle(20);
  theaterChaseRainbow(50);
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

Sooo - und was ist jetzt die Frage :wink:
Ne im ernst.

Da gibt es x Versionen. Nur keinen Grundbaukasten.
War es der?
auch ein StarterKit

Nein, 60 x 60 mA = 3,6 A, also ein Netzteil mit 5 V und 4A oder 20 W.

Nein. Was geht:

  1. Multiprozessor, also zwei Arduinos, die untereinander beispielsweise per I²C kommunizieren.
  2. Einen anderen Streifentyp mit Takt und Daten getrennt wie APA102 (DotStar).

Findet sich in vielen Beispielen, ist aber unmöglich in einem etwas komplexeren Programm.

@my_xy_projekt ->Genau das o.g. Starterkit ist es. Dazu dann Stepper Motor 28BYJ-­‐48 5V am ULN2003 Interface board. Später möchte ich noch die größeren Stepper Motoren aus den 3D Druckern ansteuern.

@agmue: ok, dann nehme ich zwei Arduinos nano. Muss mich da in die Programmierung noch einlesen. Eigentlich müssen die beiden Arduinos ja nicht miteinander kommunizieren. Muss nur ein größeres Netzteil für beide Arduinos verwenden.

Ein 5V-Netzteil an die 5V-Pins anschließen, aber nicht gleichzeitig mit USB. Oder im USB-Kabel den 5V-Draht durchtrennen. Manche Arduinos haben dafür auch eine Lötbrücke, leider sind die selten.

Nimm Dir ein einfaches Beispiel für eine Animation und stelle sie von delay() auf millis() um. Dann hast Du die vermutlich schwierigste Hürde überwunden.

Für Schrittmotoren eignet sich die Programmbibliothek MobaTools, die Du per Bibliotheksmanager der IDE installieren kannst.

ok, die Millisekunden Methode hatte ich auch schon im Hinterkopf. Die MobaTools schaue ich mir an.

Vielen Dank und schönen Abend.

Sooo.. dann kann ich Dir sagen, musst Du lernen.
Nicht viel.
Nur wie Du zwei Funktionen nicht blockierend betreibst.
Das ist für den Strip eine einfache Schrittkette.
Warten - erster Schritt - wieder warten - nächster Schritt - wieder... Aber eben ohne delay.

Zu diesem Zweck schau Dir aus den Beispielen an:
blinkWithoutDelay
und switchCase

Ok - ich seh gerade Du teilst das. Nein, wäre nicht nötig.
Aber gut...

@my_xy_projekt : du meinst, ich kann die beiden Funktionen in einem Arduino betreiben? Das wäre natürlich die beste Lösung.

War das nicht Sinn und Zweck Deiner Frage? :wink:

1 Like

Gut, dann mal ein Beispiel mit zwei dank millis() unabhängigen Funktionalitäten:

  1. Herzschlag
  2. Laufender Punkt
//
//
#include <Adafruit_NeoPixel.h>
#include "Arduino.h"
#define PIN        12
#define NUMPIXELS 144
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

uint32_t jetzt = millis();

void setup()
{
  strip.begin();
  strip.show();
}

void loop()
{
  jetzt = millis();
  bool neu = herzschlag();
  neu = neu || laufenderpunkt();
  if (neu) strip.show(); // Schickt die Daten bei Bedarf an den Streifen
}

bool laufenderpunkt()
{
  bool neu = false;
  static uint16_t pixel = 1;
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 20;

  if (jetzt - vorhin >= intervall)
  {
    strip.setPixelColor(pixel, 0, 0, 0);
    pixel = ++pixel < NUMPIXELS ? pixel : 1;
    strip.setPixelColor(pixel, 0, 0, 255);
    vorhin = jetzt;
    neu = true;
  }
  return neu;
}

bool herzschlag()
{
  bool neu = false;
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 211;
  static byte schritt = 0;

  if (jetzt - vorhin >= intervall)
  {
    switch (schritt)
    {
      default:
        strip.setPixelColor(0, 150, 0, 0);
        break;
      case 1:
        strip.setPixelColor(0, 0, 150, 0);
        break;
      case 2:
        strip.setPixelColor(0, 0, 0, 150);
        break;
    }
    schritt = ++schritt < 3 ? schritt : 0;
    vorhin = jetzt;
    neu = true;
  }
  return neu;
}

Getestet mit ESP32 und WS2815, ist aber unerheblich.

oha, bin noch am Arbeiten. Muss ich heute Abend mal testen.

Top.

Da gibt es 2 prinzipielle Probleme:

  1. Die von dir verwendete Stepper Library ist blockierend. D.h. während die Steps von diesem Aufruf myStepper.step(stepsPerRevolution); ausgeführt werden, ist der Sketch blockiert und kann damit auch die Neopixel nicht aktualisieren. Gegebenenfalls müsstest Du das dann in kleinere Schrittintervalle aufteilen.
  2. Da das Timing der Neopixel extrem kritisch ist, blockiert die Adafruit Lib alle Interrupts während der Strip aktualisiert wird. Wie lange das dauert, hängt von der Zahl der zu aktualisierenden Leds ab. Alle Funktionalitäten, die auf Interrupts angewiesen sind, werden davon beeinflusst. Das ist z.B. millis() und auch die Stepper Klasse der MobaTools. Bei millis() wirkt es sich gegebenenfalls so auch aus, dass die millis() 'Uhr' nachgeht ( es fehlen Millisekunden ). Bei den MobaTools werden Stepimpulse verzögert, wenn nicht - je nach Dauer der Interruptsperre - schlimmeres ( hab's noch nicht ausprobiert ).

Es hängt natürlich alles auch davon ab, wie schnell dein Stepper laufen soll, d.h. wie hoch die Steprate ist und wie lang die Neopixel sind - d.h. wie lang die Aktualisierung dauert.

Die LED Kette wir aufgeteilt in je 30 LEDs.
Ich möchte ruhige, langsam übergehende Farbwechsel im Loop. Nichts hektisches. Fünf Farbwechsel reichen. Bräuchte also nicht unbedingt die Neopixel Library. Wenn da etwas einfacheres vorliegt, würde ich es gerne übernehmen.
Der Stepper Motor soll extrem langsam laufen und 6 Achsen über einen Riemen antreiben.

Jetzt #13 nochmal mit der Bibliothek FastLED, die mir etwas besser gefällt:

#include <FastLED.h>
#define PIN        12
#define NUMPIXELS 144
CRGB leds[NUMPIXELS];

uint32_t jetzt = millis();

void setup()
{
  FastLED.addLeds<WS2812B, PIN, RGB> (leds, NUMPIXELS);
  FastLED.show();
}

void loop()
{
  jetzt = millis();
  bool neu = herzschlag();
  neu = neu || laufenderpunkt();
  if (neu) FastLED.show(); // Schickt die Daten bei Bedarf an den Streifen
}

bool laufenderpunkt()
{
  bool neu = false;
  static uint16_t pixel = 1;
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 20;

  if (jetzt - vorhin >= intervall)
  {
    leds[pixel] = CRGB::Black;
    pixel = ++pixel < NUMPIXELS ? pixel : 1;
    leds[pixel] = CRGB::Yellow;
    vorhin = jetzt;
    neu = true;
  }
  return neu;
}

bool herzschlag()
{
  bool neu = false;
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 211;
  static byte schritt = 0;

  if (jetzt - vorhin >= intervall)
  {
    switch (schritt)
    {
      default:
        leds[0] = CRGB::Red;
        break;
      case 1:
        leds[0] = CRGB::Green;
        break;
      case 2:
        leds[0] = CRGB::Blue;
        break;
    }
    schritt = ++schritt < 3 ? schritt : 0;
    vorhin = jetzt;
    neu = true;
  }
  return neu;
}

cool, muss ich heute Abend unbedingt testen. Habe jetzt ein 2stündiges Webinar. Bin schon gespannt.

Habe mir jetzt mal die Fast LED Strip Variant angesehen. Das Beispielscript habe ich extrem verlangsamt, so dass es jetzt meinen Vorstellungen entspricht.

Hier der Code:

/*
 *  Project     FastLED NeoPixel Library
 *  @author     David Madison
 *  @link       github.com/dmadison/FastLED_NeoPixel
 *  @license    MIT - Copyright (c) 2021 David Madison
 * 
 * This file is part of the FastLED NeoPixel Library.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

 /**
 * @file    StripVariant.ino
 * @brief   Demonstrates how to use a different strip type.
 * @example StripVariant.ino
 * @brief   Demonstrates how to use a different strip type.
 */

#include <FastLED_NeoPixel.h>

 // Which pin on the Arduino is connected to the LEDs?
#define DATA_PIN 12

// How many LEDs are attached to the Arduino?
#define NUM_LEDS 60

// LED brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 50

/*
* Instead of using the typical FastLED_NeoPixel class for WS2812B LEDs,
* we're going to use the FastLED_NeoPixel_Variant class. This provides more
* flexibility with how the data is organized, but requires a bit more setup.
* 
* The two main differences are that:
*     * LED data is stored externally and passed in the constructor
*     * Controller creation must be done "by hand" in setup()
* 
* In the global namespace we are going to define two things: the CRGB array
* that contains the LED data, and the strip object itself (passing the LEDs
* to the constructor).
*/
CRGB leds[NUM_LEDS];
FastLED_NeoPixel_Variant strip(leds, NUM_LEDS);


void setup() {
	/*
	* Instead of using the plain 'strip.begin()', we need to pass the
	* CLEDController reference returned by the 'FastLED.addLeds()' call. This
	* lets the strip use 'show()' and brightness independent of the rest of
	* program and any other present LED strips.
	* 
	* Although this is using the typical WS2812B strip setup, you can customize
	* it for any strip configuration, including strips that use SPI (data + clock).
	*/
	strip.begin(FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS));
	strip.setBrightness(BRIGHTNESS);
}

void loop() {
	rainbow(60);  // run a rainbow animation, 10 ms between each of the 256 colors
}

void rainbow(unsigned long wait) {
	// iterate through all 8-bit hues, using 16-bit values for granularity
	for (unsigned long firstPixelHue = 0; firstPixelHue < 65536; firstPixelHue += 256) {
		for (unsigned int i = 0; i < strip.numPixels(); i++) {
			unsigned long pixelHue = firstPixelHue + (i * 65536UL / strip.numPixels()); // vary LED hue based on position
			strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));  // assign color, using gamma curve for a more natural look
		}
		strip.show();
		delay(wait);
	}
}