1

In the code below which is running on a WEMOS D1 (ESP8266), a NeoPixel strip turns on 1 LED in red which moves left to right and then right to left whilst the board attempts to connect to Wi-Fi in the while (WiFi.status() != WL_CONNECTED) loop.

If I hold down the button 'SW' connected to the pin D7 and click the reset button, the code turns on the 7 segment display, however the LED on the NeoPixel strip which was last lit remains on (as though it is 'frozen'). Once I exit the while(configRPM > 0){ loop, all goes back to normal and the NeoPixel strip unfreezes.

I thought strip.clear() would clear the NeoPixel strip near the start of setup() but that does not appear to work. I thought adding a delay might fix it and it hasn't. Why is the NeoPixel strip not turning off when the board is reset and how do I fix it?

Here is the code:

#include <ESP8266WiFi.h> // Include the ESP8266WiFi library.
#include <ELMduino.h> // Include the ELMduino library.
#include <Adafruit_NeoPixel.h> // Include the Adafruit NeoPixel library.
#include <Encoder.h>
#include <TM1637Display.h>
#include <EEPROM.h>

// Define the addresses where you want to store your variables in EEPROM
#define ADDR_MINIMUMRPM 0
#define ADDR_MAXIMUMRPM (ADDR_MINIMUMRPM + sizeof(int))

#define CLK D1
#define DT D2
#define SW D7

#define CLK_TM1637 D5
#define DIO_TM1637 D6

TM1637Display display(CLK_TM1637, DIO_TM1637);
Encoder encoder(CLK, DT);

int configTriggered = 0;
int buttonReleased = 0;
int configRPM = 0;
int minimumRPM = 2500;
int maximumRPM = 6000;
int currentPosition = minimumRPM - 100;
int oldPosition = -1000;
int newPosition = 0;

// WiFi credentials and server details
const char* ssid = "WIFI_OBDII"; // Set the SSID of the ELM327.
WiFiClient client; // WiFiClient is a class that defines how to create objects that represent WiFi clients. client is an object of this class, allowing the ESP8266 to function as a WiFi client.
ELM327 myELM327; // Creates an object named myELM327 of the ELM327 class. This object represents an instance of the ELM327 OBD-II interface, allowing the program to interact with it using methods provided by the ELM327 class.

const int neopixel_Count = 16; // Indicates that there are 16 NeoPixels in the strip, with IDs ranging from 0 to 15.
Adafruit_NeoPixel strip(neopixel_Count, D8, NEO_GRB + NEO_KHZ800); // Initializes a NeoPixel strip object with 16 NeoPixels connected to pin D2, using the color order Green-Red-Blue and a data transmission frequency of 800 KHz.

const int potentiometer_Pin = A0; // Sets the constant variable potentiometer_Pin to A0, being the ID of the analog pin 

int rpm = 0; // RPM variable which is taken from the car and used in the code.

void setup() { // Setup function to initialize NeoPixels and establish WiFi and ELM327 connections.
  pinMode(SW, INPUT_PULLUP);
  Serial.begin(9600);
  display.clear();
  
  strip.begin(); // Initialize NeoPixels.
  delay(100);
  strip.clear(); // Initialize all pixels to 'off'.
  
  EEPROM.begin(sizeof(minimumRPM) + sizeof(maximumRPM));
  EEPROM.get(ADDR_MINIMUMRPM, minimumRPM);
  EEPROM.get(ADDR_MAXIMUMRPM, maximumRPM);

  if (digitalRead(SW) == LOW){
    configRPM = 1;
    display.setBrightness(0x0f); // Set brightness to maximum
    delay(50);
  }
  
  while(configRPM > 0){
    if (digitalRead(SW) == HIGH) {
      buttonReleased = 1;
      delay(50);
    }
    
    if (digitalRead(SW) == LOW && buttonReleased == 1 && configRPM == 1) {
      buttonReleased = 0;
      configRPM = 2;
      minimumRPM = currentPosition;
      Serial.println("Value saved for minimum RPM is " + String(minimumRPM));
      currentPosition = maximumRPM;
      display.showNumberDec(currentPosition);
      delay(50); // Debounce delay
    }

    if (digitalRead(SW) == LOW && buttonReleased == 1 && configRPM == 2) {      
      buttonReleased = 0;
      configRPM = 0;
      maximumRPM = currentPosition;
      Serial.println("Value saved for maximum RPM is " + String(maximumRPM));
      display.clear();

      EEPROM.put(ADDR_MINIMUMRPM, minimumRPM);
      EEPROM.put(ADDR_MAXIMUMRPM, maximumRPM);
      EEPROM.commit();
      EEPROM.end();

      delay(50); // Debounce delay
    }

    newPosition = encoder.read();
    
    if (newPosition != oldPosition) {
      if (newPosition > oldPosition) {
        currentPosition += 100; // Increase by 100 for each clockwise turn
      } else {
        currentPosition -= 100; // Decrease by 100 for each counterclockwise turn
      }
      
      // Limiting the current position within 0 to 9990
      if (currentPosition < 0) {
        currentPosition = 0;
      } else if (currentPosition > 9900) {
        currentPosition = 9900;
      }
      
      display.showNumberDec(currentPosition);
      oldPosition = newPosition;      
    }
  }
  
  Serial.println("Connecting to " + String(ssid)); // Print the SSID in the serial monitor.

  WiFi.mode(WIFI_STA); // Set the Wi-Fi mode as 'station' where the ESP8266 act as a client of a network.
  WiFi.begin(ssid); // Initates the process for connecting to the Wi-Fi network of the ELM327.

  while (WiFi.status() != WL_CONNECTED) { // While the Wi-Fi status of the ESP8266 is not connectied to the ELM327 Wi-Fi network, execute the following code.
    movePixels("right", neopixel_Count, 100); // Move from the left most LED to the right most LED with a delay of 100 milliseconds.
    movePixels("left", neopixel_Count, 100); // Move from right most LED to left most LED with a delay of 100 milliseconds.
    Serial.print("."); // Print "." in the serial monitor.
  }
  
  Serial.println("\nConnected to Wifi. The IP address of the ESP8266 is: " + WiFi.localIP().toString()); // Print the IP address of the ESP8266.

  if (!client.connect(IPAddress(192, 168, 0, 10), 35000)) { // This line tries to connect the client object to a server at the IP address 192.168.0.10 and port 35000. If the connection fails, it executes the code inside the if block.
    Serial.println("Connection to ELM327 failed. Resetting and restarting the ESP8266."); // If the connection fails, print this line.
    ESP.reset(); //  completely resets the ESP8266 microcontroller, disconnecting it from any Wi-Fi network and restarting its setup process defined in the setup() function.
  }
  myELM327.begin(client); // Initialize ELM327.
}

void loop() { // Loop function to continuously read RPM and update NeoPixels.
  rpm = myELM327.rpm(); // Read the RPM from the ELM327.

  if (myELM327.nb_rx_state == ELM_SUCCESS) { // Check for ELM327 communication status. If successful, do the following.
    Serial.print("RPM: " + rpm); // Print the RPM to the serial monitor.
    updateNeopixels(rpm); // Update the NeoPixels based on RPM.
  } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { // Check if the ESP8266 is not currently receiving a message from the ELM327.
    myELM327.printError(); // Print ELM327 error if communication is not successful.
  }
}

void updateNeopixels(int rpm) { // Function to update NeoPixels based on RPM
  strip.clear(); // Clear all NeoPixels.

  if (rpm >= 2500 && rpm < 5900) { // If the RPM is between the two values, do the following. The max RPM for this loop is higher to make all LEDs red for between 5800 and 5900.
    // Specifying the number of LEDs on the left and right to create a converging effect.
    int leftIndex = map(rpm, 2500, 5800, 0, neopixel_Count / 2 - 1); // Map the RPM from 2500 to 5800 to the range of 0 to 7.
    int rightIndex = neopixel_Count - 1 - leftIndex; // Take the leftIndex from 15. 

    // For example, if the RPM is 5800, leftIndex = 7 and rightIndex = 8.

    // Map RPM to colors
    int red = map(constrain(rpm, 2500, 5800), 2500, 5800, 0, 255); // Map RPM to the amount of 'red' of the LEDs. The higher the RPM, the more red.
    int green = map(constrain(rpm, 2500, 5800), 2500, 5800, 255, 0); // Map RPM to the amount of 'green' of the LEDs. The higher the RPM, the less green.

    // For example, if the RPM is 5800, red = 255 and green = 0. This will apply to all LEDs to be lit.

    // Left side of the strip
    for (int i = 0; i <= leftIndex; i++) { // The loop increments i by 1 whilst i <= leftIndex, moving towards the right end of the strip. i++ is done after the body of the loop completes.
      strip.setPixelColor(i, strip.Color(red, green, 0)); // Set the color of the NeoPixel matching the ID of i.
    }

    // Right side of the strip (mirroring the left side)
    for (int i = neopixel_Count - 1; i >= rightIndex; i--) { // The loop decreases by 1 whilst i >= rightIndex, moving towards the left end of the strip. i-- is done after the body of the loop completes.
      strip.setPixelColor(i, strip.Color(red, green, 0)); // Set the color of the NeoPixel matching the ID of i.
    }

  } else if (rpm >= 5900) { // If the RPM is at least 5900, do the following.
    for (int i = 0; i < neopixel_Count; i++) { // The loop increments i by 1 whilst i < neopixel_Count (16) as you can't send a single command to change the colour of all LEDs.
      strip.setPixelColor(i, strip.Color(255, 0, 255)); // Set the color of each NeoPixel to purple.
    }
  }
  strip.setBrightness(map(analogRead(potentiometer_Pin), 0, 1023, 0, 255)); // Map the potentiometer value to brightness range (0-255) and set the strip brightness accordingly. 1023 is the max value obtainabled from the analog-to-digital (ADC) converter.
  strip.show(); // Update the NeoPixel strip.
}

void movePixels(String direction, int numPixels, int delayTime) { // Function to move NeoPixels from left to right and then right to left.
  for(int i = 0; i < numPixels; i++) { // The loop increments i by 1 whilst i < numPixels (16).
    int index;
    if (direction == "right") { // If direction is equal to 'right'...
      index = i; // Set index to i.
    } else if (direction == "left") { // If direction is equal to 'left'...
      index = numPixels - 1 - i; // Set index to numPixels (16) - 1 - i.
    } else { // Handle an invalid direction input (neither right or left).
      return; // Exit the function with no response.
    }
    strip.setPixelColor(index, strip.Color(255, 0, 0)); // Set the LED which shares the ID of index as red.
    strip.setBrightness(map(analogRead(potentiometer_Pin), 0, 1023, 0, 255)); // Map the potentiometer value to brightness range (0-255) and set the strip brightness accordingly. 1023 is the max value obtainabled from the analog-to-digital (ADC) converter.
    strip.show(); // Update the NeoPixel strip.
    delay(delayTime); // Adjust the delay based on potentiometer value.
    strip.setPixelColor(index, 0); // Turn off the LED at the current position.
  }
}
1
  • 3
    I thin you need to call strip.show() also when using clear
    – chrisl
    Commented Feb 21 at 14:59

1 Answer 1

2

We can have a look at the clear() function inside the Adafruit_Neopixel library. In Adafruit_Neopixel.cpp you can find at line 3396 (searching for clear(void) will get you there):

void Adafruit_NeoPixel::clear(void) { memset(pixels, 0, numBytes); }

This is a very short function. memset() sets all bytes in a defined range to the provided value. So all bytes in pixels will be set to zero. Though this is only the libraries internal memory. There is no IO action here, no code sending data to the Neopixels.

That means: clear() only acts on the libraries internal representation of the strip, just like setPixel() and its siblings. To actually send the data to the Neopixels you still need to call strip.show(), which then takes the libraries internal representation of the strip and sends the data out through the digital pin to the Neopixels.

I want to encourage people to look into the source code of libraries, they use. When you learn how to read them, you can get quick answers even without understanding the actual full implementation of the library.

1
  • Thank you very much for your response @chrisl, much appreciated! Commented Feb 22 at 1:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.