Programming a progress bar to a lcd and led ring


:grin:

That plastic didn't protect it...

1 Like

Well for my use, the progress bar animation time is defined by a time that was set before the game started. So if the time was set to 5 second the animation would play for five seconds, if 20 it would do that.

while(demineer && team == 0 )
    {
      cls();
      if(team==0)lcd.print(" TSOONI VÕTMINE ");
      lcd.setCursor(0,1);
      unsigned int percent=0;
      unsigned long xTime=millis(); //start demination time
      while(demineer)
      {
        keypad.getKey();
        //check if time runs out during the demination
        aTime= millis()- iTime;
        if((minut-aTime/60000==0 && 59-((aTime/1000)%60)==0) || minut-aTime/60000>4000000000){ 
          endGame = true;
        }
        timeCalcVar = (millis()- xTime)%1000;

        if( timeCalcVar >= 0 && timeCalcVar <= 20)
        {
          digitalWrite(REDLED, HIGH);  
          if(soundEnable)tone(tonepin,tonoAlarma1,200);
        }
        if(timeCalcVar >= 480 && timeCalcVar <= 500)
        {
          if(soundEnable)tone(tonepin,tonoAlarma2,200);
          digitalWrite(REDLED, LOW);
        }

        unsigned long seconds= millis() - xTime;
        percent = (seconds)/(ACTIVATESECONDS*10);
        drawPorcent(percent);

        if(percent >= 100)
        {
          digitalWrite(GREENLED, LOW);
          team=2;
          iZoneTime=millis();
          delay(1000);
          break;
        }
      }
      cls();
      digitalWrite(REDLED, LOW);
    }

I´m still trying to figure out how I would also include the 12led RGB ring light with neopixel so it would display the LCD progress bar and also the led animation. And no I have not yet been able to write a code for the neopixel leds. As I really didnt find a good tutorial how to make a simple progress bar I'm learning how to make a VU meter so hopefully I can change and simplify the code for my use. I don´t need any other input I just want the sketch to be able to read the same code the progress bar uses to calculate the animation so they would kinda match time wise, after half of the animation for the progress bar has played on the LCD half the led strips would also light up. Later maybe I would change the timing so the red to yellow leds would activate a little faster than the green leds. Idea is that activating it the first time the animation would play from red to green and deactivating would play the animation just mirrored.

Make a function that receives the values of "now" time, "elapsed" time and number of pixels in the ring to create a "percent" for the LEDs.

#include <Adafruit_NeoPixel.h>
#define PIN 5 // Neopixel pin
#define PIX 12 // Number of Neopixels
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIX, PIN, NEO_GRB + NEO_KHZ800);

unsigned long fullTime = 20;

void setup() {
  Serial.begin(115200);
  pixels.begin(); // initialize a Neopixel object/instance on pin 3
  pixels.clear(); // clear any set pixels in the buffer
  pixels.show(); // display the pixel buffer
}

void loop() {
  fuel(fullTime, millis() / 1000); // "full" time and "now" time
}

void fuel(unsigned long whole, unsigned long part) {
  unsigned long value = PIX * part / whole;
  for (int i = 0; i < value; i++) {
    pixels.setPixelColor(i, pixels.Color(255, 255, 255));
    pixels.show();
  }
}
1 Like

That doesn't invalidate the code I wrote for you in any way. Let's see you put it into action in your game, and then at least try to adapt it for your LED ring also. It's the principles you need to understand, and then you can apply them to the LED ring situation.

If there are any parts of the code I posted that are hard to understand, just ask!

1 Like

Its a good start for me, still trying to figure out how to exactly do what I need
Currently have this

#include <Adafruit_NeoPixel.h>
#define PIN 5 // Neopixel pin
#define PIX 12 // Number of Neopixels
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIX, PIN, NEO_GRB + NEO_KHZ800);

unsigned long fullTime = 20;

int start1 = 0;
int start2 = 4;
int start3 = 8;

void setup() {
  Serial.begin(115200);
  pixels.begin(); // initialize a Neopixel object/instance on pin 3
  pixels.clear(); // clear any set pixels in the buffer
  pixels.show(); // display the pixel buffer
}

void loop() {
  fuel(fullTime, millis() / 1000); // "full" time and "now" time
}

void fuel(unsigned long whole, unsigned long part) {
  unsigned long value = PIX * part / whole;
  for( int i = start2; i < start1+4 ; i++ ) {
   pixels.setPixelColor(i, pixels.Color(250, 0, 0));
   pixels.show();
  }
  unsigned long value = PIX * part / whole;
  for( int i = start2; i < start2+4 ; i++ ) {
   pixels.setPixelColor(i, pixels.Color(250, 250, 0));
   pixels.show();
  }
  unsigned long value = PIX * part / whole;
  for( int i = start2; i < start1+4 ; i++ ) {
   pixels.setPixelColor(i, pixels.Color(0, 250, 0));
   pixels.show();
  }
}

Doesent work still, but I´m reading more into how to code and hopefully I can figure out how to fix it. I´m not a coder in any way, im doing this for the company I work for as a side project. I appreciate the help, this is a good starting point for me to learn into it more. You and @PaulRB have given me a good starting point to and I think I have an idea how I can make it work. Just need to read and learn a little pit. Thanks guys alot.

@xfpd

I actually did it, reading into neopixel documentation and trying to figure out one part of your code I got this

#include <Adafruit_NeoPixel.h>

#define PIN 5   
#define PIX 12
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIX, PIN, NEO_GRB + NEO_KHZ800);

unsigned long fullTime = 20; // Total time for the progress bar (in seconds)(Need to impliment in full code)

void setup() {
  Serial.begin(115200);
  pixels.begin();   
  pixels.clear();   
  pixels.show();   
}

void loop() {
  unsigned long currentTime = millis() / 1000; // Current time in seconds
  if (currentTime <= fullTime) {
    fuel(fullTime, currentTime); // Update progress bar
  } else {
    pixels.clear(); // Reset after the time exceeds fullTime
    pixels.show();
  }
}

void fuel(unsigned long whole, unsigned long part) {
  unsigned long litPixels = PIX * part / whole;
  pixels.clear();

  for (int i = 0; i < PIX; i++) {
    if (i < litPixels) {
      // Colors based on progress range
      if (i < PIX / 3) {
        pixels.setPixelColor(i, pixels.Color(250, 0, 0)); // Red 
      } else if (i < 2 * PIX / 3) {
        pixels.setPixelColor(i, pixels.Color(250, 250, 0)); // Yellow 
      } else {
        pixels.setPixelColor(i, pixels.Color(0, 250, 0)); // Green 
      }
    } else {
      pixels.setPixelColor(i, 0);
    }
  }

  pixels.show();
}

I dont know how good it is, but it works with the Wokwi.
Guys you helped me out a lot with this!
Again much appreciated, It has been doing my head in for two days, but I think I understand now better how it all actually works and how to read it.
Also started to use more comments as I was advice to do it for my own ease of use.

1 Like

I was beginning to get the feeling that I and the other forum members were being taken advantage of. Thanks for being so honest and admitting it so openly.

Needless to say, I won't be helping you again in future.

This is the "hardest" part of the sketch, calculating where to color the pixels by multiplying the percent (part over whole) by the number of pixels to see where on the "whole" scale the "part" is located.

The rest you seem to be using effectively:

  • The pixel to color is given by the first parameter in setPixelColor().
  • The colors for this library are R, G, B (in order) each with a range of 0 to 255.
1 Like

I don't understand the sudden hostility, was not my intention to use anybody but actual help as Im new to this. Im sorry you feel that way, and apologize if I somehow overstepped. I just needed some help with understanding how the system would work, the base code that @xfb actually got me on the right track and made my understand how I have to go about the led progress bar. Again I'm sorry if I overstepped or said something wrong. Thank you still for helping me understand coding and programming a little better still.

Thank you again, that was the part that indeed gave me some trouble, but I think I understand a little better now and this actually might help with my general brute force parts of my overall code, there are some other parts where this logic might actually help me simplify my code.

Thank again for the help, appreciate it. There is still a lot more for me to learn and do, but this was much bigger help than I expected actually.

I always dig back to my gradeschool "cross multiplying" to find percentages.

If I want to find what part (percent) each unit (1) of 12 (the part) is of 20 (the whole) I start with an equivalence equation... 1/12 is "what"/20?

 1     x
--- = ---
 12    20

"Cross multiply" will give

12x = 20
  x = 20 / 12
  x = 1.67

Every pixel gets 1.66\ seconds (I call that close enough to 1.67).

 0 X 1.67 = 0.00 - no LED on
 1 X 1.67 = 1.67 - first LED on
 2 X 1.67 = 3.34 - second LED on
 3 X 1.67 = 5.01 - third LED on...
 4 X 1.67 = 6.68
 5 X 1.67 = 8.35
 6 X 1.67 = 10.02
 7 X 1.67 = 11.69
 8 X 1.67 = 13.36
 9 X 1.67 = 15.03
10 X 1.67 = 16.70
11 X 1.67 = 18.37
12 X 1.67 = 20.04

My brain goes to sleep more than it works, so I need to always give myself a refresher just like this. Even if I explain this again after today, I will need to start again. It is good to find a base formula.

Im also amazed how many times cross multiplication has actually helped me out in adult life.

Paul, & others,

keep in mind that the code in loop() will only work with the hd44780 library.
That is because the createChar() function in the hd44780 works better than all other libraries. Besides being able to automatically support RAM or FLASH data
it doesn't leave the LCD in CGRAM mode when it returns.
All the other LCD libraries will leave the LCD in CGRAM mode when createChar() returns. This means that if you attempt to write to the display using write() or print(), the characters will be stored in CGRAM clobbering any definitions you may already have. This will continue until you do a home(), clear(), or setCursor().
The hd44780 library puts the LCD back into DDRAM mode so that characters can be printed. Also if using a PCF8574 based backpack, it will restore the cursor position back to where it was in DDRAM (the LCD col,row) before it returns.

Another thing to keep in mind is timing as it might be important since it can affect the speed loop() runs or add extra latency to other parts of the code.

To write a custom character to the LCD is 1 byte write to the LCD.
It is an extra byte write if you need to set a cursor position before doing the write.

To define a custom character using a PCF8574 backpack is

  • 1 byte read (to get cursor position)
  • 1 byte write command (to enter CGRAM mode and set address)
  • 8 bytes writes of custom character data
  • 1 byte command (to exit CGRAM mode and restore cursor position)

So there is quite a bit more overhead to define a custom character than to write a custom character to the LCD.
This can affect the s/w design and potentially force using multiple custom characters if timing become important, particularly on i2c based interfaces which are not particularly fast.
While the hd44780 library is by far the fasted at writing bytes to update the LCD using a PCF8574 based backpack, each write using an UNO using the default 100 Khz clock rate still takes about 550us.
Reads are similar but just a bit slower.
So to define a custom character will take about 11 byte writes or at least 6ms where as just setting cursor position and writing custom character takes about 1.1ms

Just something to keep in mind when designing the system.

--- bill

Bill, thanks for your advice as always. Everyone knows your library for hd44780 outperforms the others and is far less buggy!

I think what you are saying is that if performance is critical, and the user-defined character limit of 8 is not a problem, then pre-defining the characters for a bar-chart is preferred. But if the user-defined character limit is a problem but the performance hit is acceptable, then my solution of re-defining a single user-defined character each time is a good option.

@upir_upir on Youtube has very good stuff.

Yes
I'd probably take the easy way with a single custom character if possible.
But sometimes the timing won't allow it.
--- bill