7-segement with rgb led strip

Status
Not open for further replies.

amittel

Member
Hello!
I'm thinking of building a big interval timer with an rgb strip in form of a 7-segement. I would like to use the OctoWS2811 LED Library.
Is it possible?
I think I have to write some functions to get the right led numbers for each segement to display a number but that should it be, right?
 
If you are looking to have a big display that shows the time that has passed, you can use a routine that counts, then outputs to the led in a simple loop. Depending on the number of digits you want to have, you could set up the leds in a number of different configurations. You will firstly need to think about the number of LEDs and whether one Teensy will do (it should be sufficient). One way would be to then decide on a layout and devise an array that acts as a refernece for leds position. For example, if you were using multiple 7 segment digits, you could set up an array index that matches digits zero to nine, match this to your led physical arrangement, and then just use an offset value for tens and whole minutes as the counter works its way up (or down). So, if you used this idea, and there will be other ways of doing it, lets say you have 20 leds for each of the 7 segments that make up a single digit, you would need to set up nine reference patterns for 140 leds, with each of these patterns being called by your routine triggered by the counter. Maybe using 'if' statements. Depends on the number of digits you want and how many leds you want. You could start with the nine reference patterns as simple on or off, similar to the way the bitmap text characters are used for text in led matrices.
Anyhow, its doable, and that was just one way of doing it, and i have a feeling that there are better ways, but the teensy 3 with addressable strips is a simple combination, that could either run off of one pin per digit or all pins dending on how you want to configure the array, and how many leds you are going to use.
 
Last edited:
I built a 7-segment digital clock and implemented it much as mortonkopf describes. Each segment is illuminated by two LEDs, and I built a few arrays to make leveraging those LEDs simpler. I numbered the segments top-to-bottom/left-to-right, and then built arrays to track which segments matched which values (for any 7-segment display) as well as which LED's corresponded to which digit+segment.

Code:
I dont have the C handy, but here's some pseudocode to give you the gist:

Given this segment order (left-to-right, top-to-bottom)
 0
1 2
 3
4 5
 6

The numer-to-segment array looked like:
segmentActive = {
  {1,1,1,0,1,1,1}, // value 0
  {0,0,1,0,0,1,0}, // value 1
  {etc}
}

I then created a multi-dimensional array for each digit, containing a map of which LEDs (by OctoWS2811 led index) matched which segments for that digit
ledSegMap = 
{
 {{10,11},{8,9},{etc...}} // first digit, where "10,11" are the LEDs for segment 0 of that digit, "8,9" are the leds for segment 1, etc.
 {{20,21},{16,17},{etc...}} // second digit
}

then to light the correct segments for a given digit, you essentially do:

segstatus = segmentsActive[value]; // where 'value' is 0-9
for (seg = 0; seg < 7; seg++) {
  for (led = 0; led < ledsPerSeg; led++) {
    setPixel(ledSegMap[digit][led], (segstatus[seg]) ? onColor : offColor);
  }
}

This allowed the wiring/layout of the LEDs to be completely abstracted from 7-segment behavior. I hope that's helpful; Good luck!
 
Last edited:
Thank you ver much. That looks promising.

Then I'll start working on my CrossTimer ;)

Is there a difference, if I just put one led strip with 240 leds on one pin of the teensy or if I split the strip? Since the OctoWS-Lib ist using all 8 pins, I just could split it up.
 
Is there a difference, if I just put one led strip with 240 leds on one pin of the teensy or if I split the strip? Since the OctoWS-Lib ist using all 8 pins, I just could split it up.

The Teensy 3.0 should not be used for more than 1000 leds for the sake of reliable performance according to discussions here. If using 240 leds on one pin, you are using a memory of eight lots of 240, and this may have issues. If you split the 240 up your memory will be eight times whatever the longest strip is. There is also the difference in update speed with different lengths of strip, but that would only be noticeable if you were needing very high frame rate for lots of leds, as the update rate would be the rate for one led times the number of leds on the length of the strip. Also, drop in voltage would be reduced on shorter strips, as would annoyance with tweaking wiring issues. All eight pins are committed to the octows2811 lib anyway.
 
Last edited:
Hey guys!
My hardware arrived and I started experimenting :)

So far, so good. I wrote the code kind of tetsuo mentioned. But now I ran into a little problem.
If I display the number 1, 4 or 6 I got wrong leds turned on. And I have no clue why. I made photos, so it's easier to understand.

Code:
#include <OctoWS2811.h>

const int ledsPerStrip = 56;

DMAMEM int displayMemory[ledsPerStrip*6];
int drawingMemory[ledsPerStrip*6];

const int config = WS2811_GRB | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);

  int SegementsActive[10][7] =  {
                            {1,1,1,0,1,1,1}, //value 0
                            {0,0,1,0,0,1,0}, //value 1
                            {1,0,1,1,1,0,1}, //value 2
                            {1,0,1,1,0,1,1}, //value 3
                            {0,1,1,1,0,1,0}, //value 4
                            {1,1,0,1,0,1,1}, //value 5
                            {1,1,0,1,1,1,1}, //value 6
                            {1,0,1,0,0,1,0}, //value 7
                            {1,1,1,1,1,1,1}, //value 8
                            {1,1,1,1,0,1,0}  //value 9
                          };
int LedSegmentMap[10][7][8] =  { 
                            {//Map for 0
                              {0,1,2,3,4,5,6,7},
                              {8,9,10,11,12,13,14,15},
                              {16,17,18,19,20,21,22,23},
                              {32,33,34,35,36,37,38,39},
                              {40,41,42,43,44,45,46,47},
                              {48,49,50,51,52,53,54,55}
                            },
                            {//Map for 1
                              {16,17,18,19,20,21,22,23},
                              {40,41,42,43,44,45,46,47}
                            },
                            {//Map for 2
                              {0,1,2,3,4,5,6,7},
                              {16,17,18,19,20,21,22,23},
                              {24,25,26,7,28,29,30,31},
                              {32,33,34,35,36,37,38,39},
                              {48,49,50,51,52,53,54,55}
                            },
                            {//Map for 3
                              {0,1,2,3,4,5,6,7},
                              {16,17,18,19,20,21,22,23},
                              {24,25,26,7,28,29,30,31},
                              {40,41,42,43,44,45,46,47},
                              {48,49,50,51,52,53,54,55}
                            
                            },
                            {//Map for 4
                              {8,9,10,11,12,13,14,15},
                              {16,17,18,19,20,21,22,23},
                              {24,25,26,7,28,29,30,31},
                              {40,41,42,43,44,45,46,47}
                            },
                            {//Map for 5
                              {0,1,2,3,4,5,6,7},
                              {8,9,10,11,12,13,14,15},
                              {24,25,26,7,28,29,30,31},
                              {40,41,42,43,44,45,46,47},
                              {48,49,50,51,52,53,54,55}
                            },
                            {//Map for 6
                              {8,9,10,11,12,13,14,15},
                              {24,25,26,7,28,29,30,31},
                              {32,33,34,35,36,37,38,39},
                              {40,41,42,43,44,45,46,47},
                              {48,49,50,51,52,53,54,55}
                              
                            },
                            {//Map for 7
                              {0,1,2,3,4,5,6,7},
                              {16,17,18,19,20,21,22,23},
                              {40,41,42,43,44,45,46,47}
                            },
                            {//Map for 8
                              {0,1,2,3,4,5,6,7},
                              {8,9,10,11,12,13,14,15},
                              {16,17,18,19,20,21,22,23},
                              {24,25,26,7,28,29,30,31},
                              {32,33,34,35,36,37,38,39},
                              {40,41,42,43,44,45,46,47},
                              {48,49,50,51,52,53,54,55}
                            },
                            {//Map for 9
                             {0,1,2,3,4,5,6,7},
                              {8,9,10,11,12,13,14,15},
                              {16,17,18,19,20,21,22,23},
                              {24,25,26,7,28,29,30,31},
                              {40,41,42,43,44,45,46,47}
                            }
                          
                        };


#define RED    0xFF0000
#define GREEN  0x00FF00
#define BLUE   0x0000FF
#define YELLOW 0xFFFF00
#define PINK   0xFF1088
#define ORANGE 0xE05800
#define WHITE  0xFFFFFF
#define OFF    0X000000


void setup() {
 leds.begin();
  //leds.show();
 
}

void loop() {

/*
//Countdown timer
for(int x = 0; x < 10; x++){
setSecond(x,RED);
delay(1000);
resetLeds();

}
delay(4000);
*/
 
 setSecond(1,BLUE);
 
 
}

void setSecond(int digit, int color) {
  //int digit = 1;
  int ledsPerSeg = 8;
  //int segstatus[7] = SegementsActive[digit]; // where 'value' is 0-9

  for (int seg = 0; seg < 7; seg++) {
        for (int led = 0; led < ledsPerSeg; led++) {
      leds.setPixel(LedSegmentMap[digit][seg][led], color);
      //leds.setPixel(LedSegmentMap[6][0][0], color);
    }
  }
  leds.show();
}

void resetLeds() {

for (int led = 0; led < 56; led++) {
      leds.setPixel(led, OFF);
    }



}
And here are the pics. As you can see, led 0 (top right corner) and 7 (top left corner) is on, which shouldn't.
digit1.jpg digit4.jpg digit6.jpg
 
Actually, what you've implemented is not what I was suggesting. The idea was to abstract the idea of 7 segments away from specific LED indexes. What you've done is combine them in a 3-dimensional array, so your structure is a lot more complex (and repetitive) than it needs to be. Additionally, it's less explicit: you're doing a blind "reset all" followed by turning select LEDs on.

I think you're missing what I meant by "digit". If you have a clock like this:

HH:MM:SS

then when my mock code referred to "digit", it was referring to one of 6 possible digits (positions) in the display. So digit 0 is the "tens" column of HH, digit 1 is the "ones" column of HH and so-on. So the first map generically describes segment behavior (which segments are on/off for a given value, regardless of how many LEDs you have per segment, and regardless of which digit (a 7 is always the same set of segments whether it's an hour, minute, or second)), while the second map need only contain the mapping of which LEDs correspond to ALL segments for a given digit. You can then use the combination of both to determine which LEDs to turn on/off. I have working code @ home; I'll try to remember to send it to you tonight.

Quick question: how many 7-segment digits does your display have?
 
Here you go. This ~should~ be a single-digit version of what I was trying to describe that is based off of your code (and what I believe to be your pixel mappings). There may be parse errors... hacked this in a text editor on my lunch break! :D

As you can see, this approach eliminates all of the duplication from your maps and allows you to add columns (digits) to your display later. [EDIT]It also always sets EVERY LED explicitly on or off instead of "all-off, then some-on." In any case, I hope that helps![/edit]:

[edit] one more edit... just came back to this tab and noticed a couple bugs [/edit]
Code:
const int ledsPerStrip = 56;

DMAMEM int displayMemory[ledsPerStrip*6];
int drawingMemory[ledsPerStrip*6];

const int config = WS2811_GRB | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);

int SegmentsActive[10][7] =  {
        {1,1,1,0,1,1,1}, //value 0
        {0,0,1,0,0,1,0}, //value 1
        {1,0,1,1,1,0,1}, //value 2
        {1,0,1,1,0,1,1}, //value 3
        {0,1,1,1,0,1,0}, //value 4
        {1,1,0,1,0,1,1}, //value 5
        {1,1,0,1,1,1,1}, //value 6
        {1,0,1,0,0,1,0}, //value 7
        {1,1,1,1,1,1,1}, //value 8
        {1,1,1,1,0,1,0}  //value 9
};

// I think you might only have a 1-digit display. If so, you can expand this later if you add more digits:
int LedSegmentMap[0][7] =  { 
        {// first digit:
                {0,1,2,3,4,5,6,7}, // segment 0
                {8,9,10,11,12,13,14,15}, // segment 1
                {16,17,18,19,20,21,22,23}, // segment 2
                {24,25,26,7,28,29,30,31}, // segment 3
                {32,33,34,35,36,37,38,39}, // segment 4
                {40,41,42,43,44,45,46,47}, // segment 5 
                {48,49,50,51,52,53,54,55} // segment 6
        }
        // more digits can go here if you ever want to display larger numbers (like 99!)
};                          


#define RED    0xFF0000
#define GREEN  0x00FF00
#define BLUE   0x0000FF
#define YELLOW 0xFFFF00
#define PINK   0xFF1088
#define ORANGE 0xE05800
#define WHITE  0xFFFFFF
#define OFF    0X000000


void setup() {
        leds.begin();
        //leds.show(); 
}

void loop() {

        //Countdown timer
        for(int x = 0; x < 10; x++){
                setSecond(0, x, RED);
        }
        delay(4000);
  
}

void setSecond(int digit, int value, int color) {
        int ledsPerSeg = 8;

        for (int seg = 0; seg < 7; seg++) {
                for (int led = 0; led < ledsPerSeg; led++) {
                        leds.setPixel(LedSegmentMap[seg][led], SegmentsActive[value] ? color : OFF);
                }
        }
        leds.show();
}
 
Last edited:
So sorry... I was way too hasty. If you tried the first version I posted, it had some silly mistakes. They jumped out when I tabbed back over. Make sure if you're playing w/ that code that that you grabbed around the time of this follow-up!
 
Thanks!
Now I got your Idea and worked it out!
Btw. My little pixel problem was selfmade :-/ Looking at the LedSegmentMap array at seg3 there was a typo... Found it, after I changed the led, thinking it was damaged and the new one also didn't work.
Now it works like a charm!
I still needed to fiddle with the arrays. Arduino seems to count differnt (starts at 1 and not zero, strange) and I still needed more dims.
The fix number 8 in the map array could be change to a varibale.

My timer will have the format MM:SS. And now I have to design some kind of housing for all the strips, before I start extending the code.

One other question: Have to follow the 5+ & GND the data line, like in my pics? Or could the run another way and with junctions?
Code:
#include <OctoWS2811.h>

#define RED    0xFF0000
#define GREEN  0x00FF00
#define BLUE   0x0000FF
#define YELLOW 0xFFFF00
#define PINK   0xFF1088
#define ORANGE 0xE05800
#define WHITE  0xFFFFFF
#define OFF    0X000000

const int ledsPerStrip = 56;

DMAMEM int displayMemory[ledsPerStrip*6];
int drawingMemory[ledsPerStrip*6];

const int config = WS2811_GRB | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);

int SegmentsActive[10][7] =  {
        {1,1,1,0,1,1,1}, //value 0
        {0,0,1,0,0,1,0}, //value 1
        {1,0,1,1,1,0,1}, //value 2
        {1,0,1,1,0,1,1}, //value 3
        {0,1,1,1,0,1,0}, //value 4
        {1,1,0,1,0,1,1}, //value 5
        {1,1,0,1,1,1,1}, //value 6
        {1,0,1,0,0,1,0}, //value 7
        {1,1,1,1,1,1,1}, //value 8
        {1,1,1,1,0,1,0}  //value 9
};

// I think you might only have a 1-digit display. If so, you can expand this later if you add more digits:
int LedSegmentMap[1][7][8] =  { 
        {// first digit:
                {0,1,2,3,4,5,6,7}, // segment 0
                {8,9,10,11,12,13,14,15}, // segment 1
                {16,17,18,19,20,21,22,23}, // segment 2
                {24,25,26,27,28,29,30,31}, // segment 3
                {32,33,34,35,36,37,38,39}, // segment 4
                {40,41,42,43,44,45,46,47}, // segment 5 
                {48,49,50,51,52,53,54,55} // segment 6
        }
        // more digits can go here if you ever want to display larger numbers (like 99!)
};                          

void setup() {
  leds.begin();
         
}

void loop() {

        //Countdown timer
        for(int x = 0; x < 10; x++){
                setSecond(0, x, RED);
                
                delay(1000);
        }
        delay(4000);
  
}

void setSecond(int digit, int value, int color) {
        int ledsPerSeg = 8;
       
        for (int seg = 0; seg < 7; seg++) {
                for (int led = 0; led < ledsPerSeg; led++) {
                      leds.setPixel(LedSegmentMap[digit][seg][led],SegmentsActive[value][seg] ? color : OFF );
                }
        }
        leds.show();
       
}
 
Not sure what you mean by array indices starting at 1 instead of 0... that's certainly not the case. It's just C/C++ w/ a small amount of "convenience" built into the arduino IDE, but you're correct about needing more dimensions in the arrays than what I put in the example code. Your current implementation looks spot-on in that regard.

Regarding the fixed 8 becoming a variable... that's true, but that's the number of LEDs you have per segment. The only hard-coded number you'll end up changing is the "0" that's being passed to setSecond()... once you have multiple digits in your display and have added the entries to LedSegmentMap[], you'll need to decide how you want to break up those values. For example, something like:

Code:
void setMinSec(int min, int sec) {
  int minTens = (min > 10) ? (min/10) : 0;
  int minOnes = min % 10;
  int secTens = (sec > 10) ? (sec/10) : 0;
  int secOnes = sec % 10;

  // NOTE: you should probably rename "setSecond()" to "setDigit()"
  setSecond(0, minTens, RED);
  setSecond(1, minOnes, RED);
  setSecond(2, secTens, RED);
  setSecond(3, secOnes, RED);
  
}

Regarding wiring: no, the power/GND don't have to follow the same layout as DIN/DOUT.
 
Just a quick Update on progress. I finished soldering. That was quite time consuming. But now it's ready.
I also wrote a little script (View attachment array_create.py.txt) to create the led map array. It's working, but it's not nice ;) I'm thinkning about excluding the colon leds into a seperate array.
IMAG0123.jpgIMAG0124.jpg
 
Lookin' very nice! Yes, definitely put the colon mapping elsewhere, as they don't really correspond to segments, digit positions, or numeric values, all of which drive the structure of the map.
 
Last edited:
It's done! I finished wiring the power supply up and had to ajust the led indexes (My script can't do the math ;)) And now everything is working. Next thing will be the counter, bluetooth and some kind of buzzer.
fertig.jpg
 
Update

Hi!
Has been quite some time now and I want to give you an update.
As seen on the picture, the hardware part is almost finished. But what I really underestimated is the software part...
Right now I finally got bluetooth and the serial communication running, so I can send the data from the Android app to the timer and the timer is running through like a charme :)

But now I ran into a problem: If the timer is running I want to have the option to stop/pause it. My first thought was using interrupts. But I only found external ones. Morover am I using delay() for counting, which isn't the best idea. And the stop/pause command comes over Bluetooth serial connection.

IMAG0155.jpgIMAG0156.jpg
 
What you have to do is restructure your code to eliminate the delays. Instead, the loop function handles each sub-task, first checking whether enough elapsed time has occurred before doing whatever task it wants to do. When you do the action, you calculate how many micro-seconds you need to wait for, and update all of the variables. In computer science lingo, this is called writing a state machine.

I tend to think of it as when we were growing up, and my parents would load us into their VW micro-bus and drive across the USA every 4 years or so to visit our aunt, uncle, and their kids (and in between, they would load up their VW micro-bus and drive east to visit us). At times we would be like kids on a trip everywhere, and keep asking our folks are we there yet. Essentially, each device asks is is time to do the next step yet.

In the Arduino world, the normal example to point people to is the Blink Without Delay program, but that example really doesn't tend to help people who have never encountered a state machine before. Instead I tend to think this example is better at showing the steps: http://www.gammon.com.au/blink.

In terms of getting the time, you should solder on a 32.768kHz crystal to the Teensy 3.x, and use the real time clock within the Teensy. If you use the RTC, you should attach a coin cell battery to the ground/Vbat pins in the back of the chip, so that the clock keeps time, even when the main chip is powered off. I think a single coin cell will last a few years. You will have to initially set up the time in the RTC, and then afterward, the chip keeps the time, and you send out a query what time is it.
 
Last edited:
In terms of getting the time, you should solder on a 32.768kHz crystal to the Teensy 3.x, and use the real time clock within the Teensy. If you use the RTC, you should attach a coin cell battery to the ground/Vbat pins in the back of the chip, so that the clock keeps time, even when the main chip is powered off. I think a single coin cell will last a few years. You will have to initially set up the time in the RTC, and then afterward, the chip keeps the time, and you send out a query what time is it.
I don't need real time. It will just count down given intervals.

Got myself digging into non-blocking and state machine stuff.
I re-wrote the ticking code, but now I'm running into problems with my vars.
I have a load, pause, preperation and pause after load cycle. Each has a minute and second variable. Next I created states for each cycle and wrote a function for counting down the time with pointers, since I have to vars to change each second. But something goes wrong. It's not changing my vars...
Some code to get it clearer:
First I get the interval vars over serial. Later I would like to implement some sort of packets, because I have a different number of vars in each mode, which are interval, count down and count up. Right now I'm just loading it into a array which is big enough to hold it.
Code:
if (Serial2.available()) {
		while(Serial2.available()>0) {
	    	  inData[g] = Serial2.parseInt();
	    	  g++;
		}
Then I write them into vars by looking in which mode I am from the first int
Code:
switch(inData[0]) {
		case WORKOUT:
			Serial.println("Workout");
			cmd = WORKOUT;
			rounds = inData[1];
			stations = inData[2];
			workmin = inData[3];
			worksec = inData[4];
			pausemin = inData[5];
			pausesec = inData[6];
			prepmin = inData[7];
			prepsec = inData[8];
			pauseafterworkoutmin = inData[9];
			pauseafterworkoutsec = inData[10];

		}
After that I am starting the Interval (I know, that I have to get rid of the loops to be none-blocking)
Code:
if(cmd == WORKOUT) {
		Serial.println("Starting Workout");

		for(int r=1;r<=rounds; r++) {
			Serial.print("Starting round ");Serial.print(r);Serial.print(" of ");Serial.println(rounds);
			for(int s=1; s<= stations; s++) {
				Serial.print("Start station ");Serial.print(s);Serial.print(" of ");Serial.println(stations);
				  Serial.println("Workout");
				  workout();
			}
			
		}
	}//END WORKOUT
My workout() looks like this, which is basically switching states (first state is defined at the beginning) and calling updateTime() with the adress of the var.
Code:
void workout() {
           switch(states) {
	  		  case 1: //Workout
	  			updateTime(&workmin,&worksec);
	  			
	  			  break;
	  		  case 2: //Pause
	  			
	  			  break;
	  		  case 3: //Prep
	  			 
	  			  break;
	  		  case 4: //PauseAW
	  			
	  			  break;
	  }	
}
In my updateTime() I'm using pointers to get the content of the adresses var
Code:
void updateTime(int *timerMinute, int *timerSecond) {
	static unsigned long lastMillis = 0; //Initial time is 0
	 unsigned long m= millis(); //get the current time

	 if((*timerMinute == 0 && *timerSecond == 0)) {
		 //Switch case: case++
		 Serial.println("Case switching");
	 }
	 if (m> (lastMillis + 1000) && (*timerSecond > 0 || *timerMinute >0)) {
		    //if a second has passed, and there is time left on the clock: DO IT! swooosh!
			  Serial.print(*timerMinute);Serial.print(" : ");Serial.println(*timerSecond);
		 Serial.println("Tick");

		    if (*timerSecond == 0) {
		      //reached 0 seconds
		      if(*timerMinute > 0) {
		        //if there are still minutes on the clock, reduce minutes by one and reset seconds to 59
		        *timerSecond = 59;
		        *timerMinute--;
		      }
		    }
		    else {
		    	Serial.println("Tick sec");
		      *timerSecond--;
		    }
		    lastMillis = m; //update last clock time
	 }
}
Looking at the serial monitor I see that he's going into the updateTime() function and prints "Tick sec" but *timerSecond, which should be worksec, is not changing.
 
I could be wrong, I have been using C for a long long time, but operator precedence can be tricky: *timerSecond--;
Probably is not doing what you think it is or want. It is actually probably only updating the pointer timerSecond.
That is if you wrote something like: *timerSeconds-- = 0;
Would store a 0 into timerSeconds and then decrement the pointer.
Now if wrote it something like: (*timerSeconds)--;
It might work or you could do the long hand: *timerSeconds = *timerSeconds - 1;

Kurt
 
Status
Not open for further replies.
Back
Top