[gelöst] LEDPixels-Library mit ESP32 kompatibel machen (SPI?)

Hallo zusammen,

ich habe vor gut 10 Jahren einen LED Coffee Table gebaut.
Der fristet nun sein Dasein ungenutzt, vor sich hin.
Es wurden LED´s mit LPD6803 von Bliptronics verbaut, welche die LEDPixels Library verwendet haben.

Mein Wiederbelebungsversuch ist natürlich nun daran gescheitert, dass es die

  • avr/io.h
  • avr/interrupt.h

auf dem ESP32 natürlich nicht gibt.
Deshalb meine Hoffnung, dass einer von Euch Profis mir weiterhelfen kann eine Alternative zu finden.
Oder natürlich mich soweit erleuchtet, dass ich den Kommunikationslayer neu machen kann.

LEDPixels.cpp (Reduziert, vollständig im Anhang)

/*  LEDPixels.cpp - A Library for use with LED Pixels using the LPD6803 Controller chip */
#include "LEDPixels.h"
#include "glcdfont.c"

char clockPin = 12;  //GREEN wire
char dataPin  = 11;   //YELLOW wire
char NoOfLEDs = 100;  //Total number of LEDs in your string.

// Globals used by interrupt code.
char  SendMode=0;   // Used in interrupt 0=start,1=header,2=data,3=data done
char  BitCount=0;   // Used in interrupt
unsigned int  LedIndex=0;   // Used in interrupt - Which LED we are sending.
char  BlankCounter=0;  //Used in interrupt.
unsigned int BitMask;   //Used in interrupt.

//Display[] array holds the 15 bit RGB values for each LED.
int * Display;
LEDPixels LP1;              //Preinstatiate

ISR(TIMER1_OVF_vect)        // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  LP1.doOutput();
}
LEDPixels::LEDPixels()
{
    //Set the pins to use here
}

void LEDPixels::doOutput()
{
 switch(SendMode)
  {    
    case 3:                        //Done..just send clocks with zero data
      digitalWrite(dataPin, 0);    //You'll need 255 clocks for a LED to diplsay 1 color pwm.
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
    case 2:               //Sending Data
      if (BitCount==0)    //First bit is always 1 followed by 15 bits of LED color.
        {  digitalWrite(dataPin, 1);
            BitMask=0x8000;//Init bit mask
        }
      else if(BitMask & Display[LedIndex])  //If not the first bit then output the next bits (Starting with MSB bit 15 down.)
        digitalWrite(dataPin, 1);
      else
        digitalWrite(dataPin, 0);
      
      BitMask>>=1;
      BitCount++;
      
      if(BitCount == 16)    //Last bit?
      {
        LedIndex++;        //Move to next LED
        if (LedIndex < NoOfLEDs) //Still more leds to go or are we done?
        {
          BitCount=0;      //Start from the fist bit of the next LED             
        }
        else
          SendMode=3;  //No more LEDs to go, we are done!
      }
      // Clock out data.
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;      
    case 1:            //Header
        if (BitCount < 32)              
        {
        digitalWrite(dataPin, 0);
        BitCount++;
        if(BitCount==32) 
          {
            SendMode++;      //If this was the last bit of header then move on to data.
            LedIndex=0;
            BitCount=0;
          }
        }
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      
      break;
    case 0:            //Start
      if(!BlankCounter)    //AS SOON AS CURRENT pwm IS DONE. BlankCounter 
      {
        BitCount=0;
        LedIndex=0;
        SendMode=1; 
      }  
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      
      break;   
  }
  //Keep track of where the LEDs are at in their pwm cycle. 
  BlankCounter++;
}

unsigned int LEDPixels::Translate(unsigned int x,unsigned int  y)
{
    if(x%2)
        return(((x+1) * gridHeight)- 1 - y);
    else
        return((x * gridHeight) + y);
}

void LEDPixels::setPixel(unsigned int x, unsigned int y, unsigned int color)
{
    Display[Translate(y,x)]=color;
}

//Swap the values of two variables, for use when drawing lines.
void LEDPixels::swap(unsigned int * a, unsigned int * b)
{
  int temp;
  temp=*b;
  *b=*a;
  *a=temp;
}

void LEDPixels::setCursor(uint8_t x, uint8_t y) {
  cursor_x = x; 
  cursor_y = y;
}

void LEDPixels::setRange(unsigned int startLED, unsigned int endLED, unsigned int color )
{
  // set a linear range of LEDs. The color value must be created with the Color function (15 bit rgb)
  while(startLED <= endLED)
    Display[startLED++]=color;
}
// Set a LED, give it r,g,b
void LEDPixels::setLEDFast(unsigned int LED, byte rr, byte gg, byte bb)
{
  if(LED < NoOfLEDs)
    //Display[LED] = color(bb,rr,gg);
    Display[LED] = color(rr,gg,bb);
}

// set an LED but just pass it a 15 bit color value
void LEDPixels::setLEDFast(unsigned int LED, unsigned int color)
{
  if(LED < NoOfLEDs)
    Display[LED] = color;
}

// Create a 15 bit color value from R,G,B
unsigned int LEDPixels::color(unsigned char r,unsigned char g,unsigned char b)
{
  //Take the lowest 5 bits of each value and append them end to end
  return( ((unsigned int)r & 0x1F )<<10 | ((unsigned int)g & 0x1F)<<5 | (unsigned int)b & 0x1F);
}

void LEDPixels::show()
{
  // The interrupt routine will see this as re-send LED color data.
  SendMode = 0;
}

void LEDPixels::initialize(long microseconds, int * DisplayAddress,unsigned int LEDCount , char clkPin, char dPin )
{
  byte Counter;

  clockPin = clkPin;
  dataPin = dPin;
  Display = DisplayAddress;
  NoOfLEDs = LEDCount;

  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  show();                     // Kick off display.

  TCCR1A = 0;                 // clear control register A 
  TCCR1B = _BV(WGM13);        // set mode as phase and frequency correct pwm, stop the timer

  if(microseconds > 0) setPeriod(microseconds);
  //isrCallback = isr;                                       // register the user's callback with the real ISR
  TIMSK1 = _BV(TOIE1);                                     // sets the timer overflow interrupt enable bit
  sei();                                                   // ensures that interrupts are globally enabled
  start();
}

void LEDPixels::setPeriod(long microseconds)
{
  long cycles = (F_CPU * microseconds) / 2000000;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS10);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10);  // prescale by /1024
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum
  ICR1 = pwmPeriod = cycles;                                                     // ICR1 is TOP in p & f correct pwm mode
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
  TCCR1B |= clockSelectBits;                                                     // reset clock select register
}

void LEDPixels::detachInterrupt()
{
  TIMSK1 &= ~_BV(TOIE1);                                   // clears the timer overflow interrupt enable bit 
}

void LEDPixels::start()
{
  TCCR1B |= clockSelectBits;
}

void LEDPixels::stop()
{
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));          // clears all clock selects bits
}

void LEDPixels::restart()
{
  TCNT1 = 0;
}

Das müsste doch eine SPI-Schnittstelle sein, oder?
FastLED wäre die Lösung, wenn dort nicht explizit stehen würde:

LPD6803, HL1606, and "595"-style shift registers are no longer supported by the library. The older Version 1 of the library ("FastSPI_LED") has support for these, but is missing many of the advanced features of current versions and is no longer being maintained.

Version 1 unterstützt aber noch keinen ESP :confused:

glcdfont.c (8.35 KB)

keywords.txt (462 Bytes)

LEDPixels.cpp (12.9 KB)

LEDPixels.h (3.08 KB)

Dein erster Link (zum Tisch) ist defekt.
Auch wenn es nicht für den ESP32 ist, evtl. kannst Du auch mit dem ESP8266 leben :wink:

Ich habe meinen Weihnachtsstern auch mit LPD6803 und ESP8266 gebaut. In dem anhängenden Zip ist eine angepasste Lib für den LPD6803 mit drin. Evtl. hilft die Dir weiter. Das Original gibt es hier.

Gruß Tommy

TriB:
Das müsste doch eine SPI-Schnittstelle sein, oder?

Ja, sowas wie SPI. Ob man beim ESP32 wirklich Hardware-SPI benötigt, habe ich noch nicht getestet, beim Teensy 3.2 braucht man es nicht, ist auch so schnell genug.

FastLED 3.4.0 Beispiel FirstLight.ino gerade mal probiert:

FastLED.addLeds<LPD6803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
[sup]Der Sketch verwendet 212217 Bytes (16%) des Programmspeicherplatzes. Das Maximum sind 1310720 Bytes.
Globale Variablen verwenden 15588 Bytes (4%) des dynamischen Speichers, 312092 Bytes für lokale Variablen verbleiben. Das Maximum sind 327680 Bytes.
[/sup]

Einen Versuch wäre es wert :smiley:

Hi Tommy,

der Link ist repariert. Glaube der wird zerstört, sobald man einmal in die Vorschau geht...
Oder alternativ in die >9.000 Zeichen-Meldung.

Dank Dir für die Library! Werde mir mal Deine Anpassungen anschauen und dann sehen ob ich das auf den ESP32 gewuppt bekomme oder einfach in meiner Kiste nach einem 8266 wühle.
Auf jeden Fall ein guter Startpunkt mit dem ich was anfangen kann :smiley:

@agmue dank Dir! Dann hätte ich ja im Zweifel noch eine Alternative. Offenbar habe ich nur einen Fork von FastLED gefunden. Hier wird´s ja noch offiziell unterstützt.

Kurzer Zwischenstand:
Es sieht vielversprechend aus :slight_smile:

Die (richtige) FastLED Library von Daniel Garcia gezogen, auf den ESP32 geschmissen und ausprobiert.
Der Test konnte noch nicht so richtig durchlaufen, da mein LevelShifter erst gleich im Briefkasten landet.
Aber zumindest "zucken" die LED´s schon in der korrekten Reihenfolge.

//LEDS
#include <FastLED.h>

#define LED_PIN  13
#define CLK_PIN  15
#define COLOR_ORDER RGB
#define CHIPSET     LPD6803
#define BRIGHTNESS 64

// Params for width and height
const uint8_t kMatrixWidth = 10;
const uint8_t kMatrixHeight = 10;
#define NUM_LEDS (kMatrixWidth * kMatrixHeight)

// Param for different pixel layouts
const bool    kMatrixSerpentineLayout = true; //ZigZag
const bool    kMatrixVertical = false;

CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(115200);

  FastLED.addLeds<CHIPSET, LED_PIN, CLK_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);

  Serial.println("Initialisiert");
  delay(500);
  SetAllBlue();
  Serial.println("Blued");    
}

void loop() {  
}

void SetAllBlue(){
  for(uint8_t i = 0; i < NUM_LEDS; i++)
    leds[i] = CRGB::Blue;
  FastLED.show();
}

Daher beschäftige ich mich schon mal mit der Vorbereitung für einen Webserver, OTA, SPIFFS und ein paar Demo-Ausgaben.
Ziel soll dann sein, dass man per Handy ein Programm wählen kann, welches dann in Dauerschleife läuft, sobald Spannung anliegt.
Ein Programm kann ein sanftes fading sein, eine fest hinterlegte Farbe, zufällige Farben, usw.
Einfach etwas Ambiente um seine Bierflasche adäquat abstellen zu können ;D

BTW: Gerade die Arduino IDE 2.0 beta 3 ausprobiert.
Schaut erstmal ungewohnt aus, aber arbeitet relativ schnell. Vorallem wenn man den Board oder Library-Manager verwendet. Es wird sich nun pro Sketch gemerkt, welchen Port und welches Board verwendet wurde!
Ansonsten sehe ich da noch sehr, sehr viel Arbeit! Starte ich eine *.ino, öffnet es die vorige.
Der COM-Port erscheint selten und bei zwei geöffneten Fenstern, maximal in einem. So wie auch die Examples.
OTA schein es noch nicht zu geben.
Die Errors sind sehr nichtssagend. In der alten IDE kompilierts, in der neuen nicht.
Und kopiere ich den Code ins Forum, werden diese "Color"-Tags angefügt, die man hin und wieder sieht.

TriB:
Ziel soll dann sein, dass man per Handy ein Programm wählen kann, welches dann in Dauerschleife läuft, sobald Spannung anliegt.

Ich habe dafür die Seiten von Fips genommen, die Du als eifriger Leser dieses Forums zusammen mit meiner Anleitung: Einführung zu fipsok.de sicherlich schon kennst :wink:

HTML-Datei im SPIFFS und Aktualisierungen mit JSON.

Klar, kenne ich doch 8)

Habe ich ja alles schon teilweise bei meinem ersten Versuch mit dem Tisch (noch auf Basis Arduino Mega + LAN-Shield) umgesetzt.
Dann, keine 10 Jahre später mit dem ESP32 bei meiner digitalen Wohnwagen Wasserwaage optimiert :stuck_out_tongue:

Daher sollte es dann ein Leichtes sein, den Part umzusetzen! Was nicht gleich bedeutend mit "wenig Arbeit" ist...
Sobald die neue Hardware steht und funktioniert, werde ich ein paar Zwischenstände durchgeben!

Seltsames Verhalten!
Die LED werden punktuell korrekt angesteuert. Manche erstrahlen in allerschönster Farbe, andere flackern bedenklich dunkel herum, so dass man sie kaum erkennen kann.

Die Spannungsversorgung mehrfach geprüft, die LED-Kette mal testweise halbiert, alle LED ausgeschaltet und nur einzeln angesteuert... Nix!
Allerdings waren es immer exakt die selben LED´s die normal erstrahlten.

Zwei graue Haare später:

#define BRIGHTNESS 255 //64

Tja... würde ja Sinn ergeben, wenn alle LED´s dunkel vor sich hin geflackert hätten.
Aber tatsächlich ist es des Rätsels Lösung!
Wenn mein Web-Interface steht, werde ich das mal dynamisch einstellbar machen und damit herumspielen. Aber für´s erste klappt es, wie es soll :slight_smile:

Es ist erstmal nur alles gesteckt. Nun wird ein Gehäuse aus dem 3D Drucker konstruiert und gedruckt.
Wenn alles hineinpasst, anständig verlötet und die Spielerei Programmierung kann beginnen :smiley:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.