adafruit / Adafruit_NeoPixel

Arduino library for controlling single-wire LED pixels (NeoPixel, WS2812, etc.)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible bug in RP2040 code resulting in occassional incorrect output

penguintutor opened this issue · comments

  • Arduino board: Arduino Nano RP2040 connect

  • Arduino IDE version (found in Arduino -> About Arduino menu): 1.8.13

  • List the steps to reproduce the problem below (if possible attach a sketch or
    copy the sketch code in too):

I am currently working on a project to run NeoPixel sequences over WiFi. The project appears to work correctly when using an Arduino UNO WiFi (rev 2), and a MKR Wifi 1010, but when using the Arduino RP2040 then it results in occasionally showing incorrect colors on the NeoPixels. The colors are wrong and show brighter (currently set to brightness 50, but appear to be full brightness).

The sketch I am using is too long to include here. The code I am using is available from github at: https://github.com/penguintutor/arduino-pixels My code is currently under development, but as this works correctly with the UNO and the MKR WiFi (use the same buffer circuit), but has a problem when using the Arduino RP2040 I suspect this is likely a problem with the RP2040 code.

I am testing this with a strip of 45 WS2812B LEDs.

The code runs correctly for 25 seconds or so, but will then flash up the entire line in different colours and much brighter. It then goes back to running the program normally. I temporarily added debugging to the code to see if it was my code sending the wrong color to the NeoPixel, but it does appear to be requesting the correct color. Also it does not appear to be consistent with the sequence number and also does the same when I'm repeatedly sending the same colour sequence.

The files below show what it should look like:
pixel-should-show
Followed by two examples of it showing the wrong color (approx 24 seconds and then 34 seconds after colors set):
pixel-error01
pixel-error2

My thoughts are that this is a buffer / counter overflow in the RP2040 specific code resulting in incorrect data being sent to the NeoPixels.

The pin used is pin 15 (D3). Using Pin 25 (D2) resulted in a completely different problem where the WiFi communications were corrupted when the Pixels were enabled.

@Bodmer wanna take a look?

I've done some more research and this may be a possible conflict with WiFiNINA library.
I tried to recreate using a bare minimal code without the WiFi and it didn't show the symptoms. I tried with my initial code which is basically the WiFi LED Color example on the Arduino website with the NeoPixel library controlling LEDs. This has the problem.

The code to make this happen is below (add your own arduino_secrets.h).

After loading the code open a web page on a browser - choose a color and wait approximately 15 seconds. Initially it will work, but then a single line of incorrect colors (flickers once), then a few more seconds and a flicker again. The timing appears to be random.

Source code below (it's quite long, but the part that uses the NeoPixels is only a few lines of code)

#include <SPI.h>
#include <WiFiNINA.h>
#include <Adafruit_NeoPixel.h>
#include "arduino_secrets.h"


// Sensitive information in arduino_secrets.h
char ssid[] = SECRET_SSID;        // network SSID (name)
char pass[] = SECRET_PASS;        // network password (use for WPA, or use as key for WEP)
int keyIndex = 0;                 // network key index number (needed only for WEP)

#define LED_PIN 15 // Pin where NeoPixels are connected - Do not use PIN 25 (D2) - possible conflict with WiFi
#define LED_COUNT 45  // How many NeoPixels?

int status = WL_IDLE_STATUS;
WiFiServer server(80);

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

#define SEQ_ALLOFF 0
#define SEQ_ALLON 1

int colorR = 0;
int colorG = 0;
int colorB = 0;

void setup() {
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  Serial.begin(9600);      // initialize serial communication

  WiFi.config({192,168,0,44}, {8,8,8,8}, {192,168,0,1}, {255,255,255,0});
  
  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);  
  
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  
  }
  // attempt to connect to WiFi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to Network named: ");
    Serial.println(ssid);                   // print the network name (SSID);

    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
    // wait 10 seconds for connection:
    delay(10000);
  }

  server.begin();                           // start the web server on port 80
  printWifiStatus();                        // you're connected now, so print out the status

  // Now setup NeoPixels
  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)
}

void loop() {
  WiFiClient client = server.available();   // listen for incoming clients
  
  if (client) {                             // if you get a client,
    Serial.println("new client");           // print a message out the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        if (c == '\n') {                    // if the byte is a newline character

          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            showWebPage(client);
            // break out of the while loop:
            break;
          } else {    // if you got a newline, then clear currentLine:
              currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
              
        // Check to see if the client request was /X
        if (currentLine.endsWith("GET /RH")) {
          colorR = 255;
          digitalWrite(LEDR, HIGH);
        }
        if (currentLine.endsWith("GET /RL")) {
          colorR = 0;
          digitalWrite(LEDR, LOW);              
        }
        if (currentLine.endsWith("GET /GH")) {
          colorG = 255;
          digitalWrite(LEDG, HIGH);              
        }
        if (currentLine.endsWith("GET /GL")) {
          colorG = 0;
          digitalWrite(LEDG, LOW);           
        }
        if (currentLine.endsWith("GET /BH")) {
          colorB = 255;
          digitalWrite(LEDB, HIGH);              
        }
        if (currentLine.endsWith("GET /BL")) {
          colorB = 0;
          digitalWrite(LEDB, LOW);             
        }
      }
    }
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
  setPixels(SEQ_ALLON, strip.Color(colorR, colorG, colorB));
}

void setPixels(int sequence, uint32_t color) {
  if (sequence == SEQ_ALLON) {
    allOn (color);
  }
}

void allOn(uint32_t color) {
  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
  }
}


void showWebPage(WiFiClient client) {
  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
  // and a content-type so the client knows what's coming, then a blank line:
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();
  // the content of the HTTP response follows the header:
  client.print("<style>");
  client.print(".container {margin: 0 auto; text-align: center; margin-top: 100px;}");
  client.print("button {color: white; width: 100px; height: 100px;");
  client.print("border-radius: 50%; margin: 20px; border: none; font-size: 20px; outline: none; transition: all 0.2s;}");
  client.print(".red{background-color: rgb(196, 39, 39);}");
  client.print(".green{background-color: rgb(39, 121, 39);}");
  client.print(".blue {background-color: rgb(5, 87, 180);}");
  client.print(".off{background-color: grey;}");
  client.print("button:hover{cursor: pointer; opacity: 0.7;}");
  client.print("</style>");
  client.print("<div class='container'>");
  client.print("<button class='red' type='submit' onmousedown='location.href=\"/RH\"'>ON</button>");
  client.print("<button class='off' type='submit' onmousedown='location.href=\"/RL\"'>OFF</button><br>");
  client.print("<button class='green' type='submit' onmousedown='location.href=\"/GH\"'>ON</button>");
  client.print("<button class='off' type='submit' onmousedown='location.href=\"/GL\"'>OFF</button><br>");
  client.print("<button class='blue' type='submit' onmousedown='location.href=\"/BH\"'>ON</button>");
  client.print("<button class='off' type='submit' onmousedown='location.href=\"/BL\"'>OFF</button>");
  client.print("</div>");
  // The HTTP response ends with another blank line:
  client.println();
}

                    
void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  
  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  
  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
  // print where to go in a browser:
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
}

This appears to be signal integrity problem. The RP2040 is a 3.3V logic level part and the Neopixels really like a 5V drive so a buffer will be needed. The 45 LEDs can also demand up to ~2.7Amps and so check your power supply and grounding (to avoid "ground bounce" causing glitches. You could try reducing the number of LEDs and maximum brightness for test purposes to see if this affects the fault signature.

The Neopixel drive uses programmable IO and therefore is in hardware, the software just feeds values to this functions. Thus it is not clear to me where a software conflict might be occuring.

Thank you for looking into the problem. I can see that it could be a signal integrity issue, I'd tried investigating that before raising this issue, but perhaps that is worth further investigation.

I am using a level-shifter to increase the signal voltage to 5V, based around a MOSFET essentially a one-directional equivalent of the Adafruit level-shifter.

The NeoPixels are connected to a separate power supply capable of up to 14A.

The Arduino is connected to a separate power supply (typically my laptop whilst programming), but I've also tried that connected to a more powerful power supply in case the USB wasn't providing sufficient power and I still get the problem.

The reason I thought that this was software related is because I'm using the exact same circuit with an Arduino MKR WiFi 1010 which doesn't have the same problem. That has the same 3.3V output and I'm using the same voltage-shifter to drive the pixels.
The code for the MKR and RP2040 is different as the RP2040 is based around PIO so that's why I thought the problem may be there.

I also found a problem when trying to use a different pin: pin D2 / GPIO25. When I tried to use that for the NeoPixels then it resulted in a corrupted WiFi signal. I cannot find anything in the datasheet to say that it shouldn't be possible to use that pin for GPIO when using WiFi. This is why I first suspected there could be some conflict between WiFiNINA and the Pixel code.

Another strange thing is that I only see the flicker when the WiFi code is included, without WiFi then it works correctly. Perhaps there is some additional noise being introduced due to the WiFi module.

I will investigate the hardware further and see if I can pin the problem down that way.

Thanks

It does appear that this may have been a hardware problem with a particular LED. Oddly I don't think this is the first LED in the chain and yet was somehow causing problems with other LEDs upstream, perhaps it was causing a brief short circuit across the power supply.

I had tried with different LEDs as well, but because I was wanting to test long sequences I had always ended up putting the strip back in the circuit (although not necessarily at the beginning of the strip). This is a strip which I have had for several years and has not had any problems before.

The thing that still puzzles me is that it didn't have the same problem when connected to different Arduinos (eg. UNO / MKR series), but did with the RP2040, and even then only when I was using WiFi. Perhaps this was just coincidence, or perhaps the failing LED was more tolerant of the different signals from the different models.

I also appear to be unable to use D2 (GPIO 25), but it is easy enough to use a different pin, so that isn't a major problem.

Anyway the original problem appears to have gone away after changing the hardware. Thank you for investigating and your patience, and please accept my apologies for any time you spent on this. I'm going to mark this as closed now.