Cleaner Code for shift register 24 LEDs?

I almost posted here a few days ago, but re-read the "before you post" message. In the process of collecting the necessary information for the post, based on that message, I found the answer to that problem! The current result is below. It's the preliminary code for a pseudo bar graph using 24 LEDs which will illuminate depending on the signal from a Ping))) sensor. The Ping))) sensor will return a distance from approximately an arbitrary zero to approximately 24" farther away. The zero figure will represent a full tank of fluid, while the 24" figure will represent a nearly empty tank. I expect it to be a simple matter to invert the data to present the proper illumination on the LED strip, using MSBFIRST or LSBFIRST in the code.

I've managed to get the hardware wired properly without the sensor, manually sending integers via the loop and two bytes, but I wanted the math to be performed automatically based on the one number from 1-24, so I build a series of nested loops, splitting the "distance" off into three bytes for the three shift registers, which I believe should perform properly. I've not worked on the integration of the Ping))) sensor, as the math and loops and logic presented the greater challenge, I believe. Now that that particular portion is completed, I can move onto the last segment.

This is my first original code, generated with help from the reference material on this site, as well as the tutorials that abound for the shift registers. I did not find a tutorial that performed the same sequences that I needed, but came up with this solution. Because it's my first code and I have a bit of a learning disability, I entered the lines and addressed the errors that appeared until they no longer appeared. I have to use other code written as reference, but not always with a full understanding of what is being done and why.

That said, I would appreciate it if anyone who wishes to would provide constructive criticism aimed specifically at the structure. I tried to use logical comments within the code without being as verbose as this message.

/*
 code for converting 1-24 into three bytes
 for illuminating 24 LEDs with shift registers
 as a pseudo bar graph
 */
int i;
double cLED;      //counter for LED
int nTD0;         //number to display 0 to 2
int nTD1;
int nTD2;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  for (i=1;i<25;i++)                        //loop scope to be 1 to 24, this number to be replaced with data from Ping))) sensor
  {
    if (i<9)                                //first byte to be converted if total is eight or less
    {
      cLED = pow(2,i) - 1;                  //two to the power of count minus one = sequential LED illumination via
      nTD0 = ((cLED * 100) + 5) /100;       //shift register, leaving previous LEDs illuminated
      nTD1 = 0;                             //math necessary to remove rounding errors
      nTD2 = 0;                             //bytes two and three are zero
    }
    else
    {
      if (i<17)                              //second byte for nine to sixteen means first byte will always be 255
      {
        cLED = pow(2,i-8) - 1;               //and third byte will allways be zero
        nTD0 = 255;
        nTD1 = ((cLED * 100) + 5) /100;
        nTD2 = 0;
      }
      else
      {
        if (i<25)                            //third byte for seventeen to twenty-four, first and second bytes always 255
        {
          cLED = pow(2,i-16) - 1;
          nTD0 = 255;
          nTD1 = 255;
          nTD2 = ((cLED * 100) + 5) /100;
        }
      }
    }
    //output to confirm that expected data is being generated
    Serial.print("Count\t");
    Serial.print("cLED\t");
    Serial.print("nTD0\t");
    Serial.print("nTD1\t");
    Serial.println("nTD2");
    Serial.print(i);
    Serial.print("\t");
    Serial.print(cLED);
    Serial.print("\t");
    Serial.print(nTD0);
    Serial.print("\t");
    Serial.print(nTD1);
    Serial.print("\t");
    Serial.println(nTD2);    
    delay (1000);
  }
}

The output generated by the code appears like this:

Count	cLED	nTD0	nTD1	nTD2
1	1.00	1	0	0
Count	cLED	nTD0	nTD1	nTD2
2	3.00	3	0	0
Count	cLED	nTD0	nTD1	nTD2
3	7.00	7	0	0
Count	cLED	nTD0	nTD1	nTD2
4	15.00	15	0	0
Count	cLED	nTD0	nTD1	nTD2
5	31.00	31	0	0
Count	cLED	nTD0	nTD1	nTD2
6	63.00	63	0	0
Count	cLED	nTD0	nTD1	nTD2
7	127.00	127	0	0
Count	cLED	nTD0	nTD1	nTD2
8	255.00	255	0	0
Count	cLED	nTD0	nTD1	nTD2
9	1.00	255	1	0
Count	cLED	nTD0	nTD1	nTD2
10	3.00	255	3	0
Count	cLED	nTD0	nTD1	nTD2
11	7.00	255	7	0
Count	cLED	nTD0	nTD1	nTD2
12	15.00	255	15	0
Count	cLED	nTD0	nTD1	nTD2
13	31.00	255	31	0
Count	cLED	nTD0	nTD1	nTD2
14	63.00	255	63	0
Count	cLED	nTD0	nTD1	nTD2
15	127.00	255	127	0
Count	cLED	nTD0	nTD1	nTD2
16	255.00	255	255	0
Count	cLED	nTD0	nTD1	nTD2
17	1.00	255	255	1
Count	cLED	nTD0	nTD1	nTD2
18	3.00	255	255	3
Count	cLED	nTD0	nTD1	nTD2
19	7.00	255	255	7
Count	cLED	nTD0	nTD1	nTD2
20	15.00	255	255	15
Count	cLED	nTD0	nTD1	nTD2
21	31.00	255	255	31
Count	cLED	nTD0	nTD1	nTD2
22	63.00	255	255	63
Count	cLED	nTD0	nTD1	nTD2
23	127.00	255	255	127
Count	cLED	nTD0	nTD1	nTD2
24	255.00	255	255	255

I've not included the shift register addressing code, as it's more or less off-the-shelf from the tutorials.
Again, since there doesn't appear to be anything like this already "in the wild" perhaps someone else can benefit from it. Certainly it's possible that it's out there, but my search came up empty. Too much ambiguity in terms means too many incorrect hits.

thanks

fred

You could avoid using the rather slow "pow" function by simply using bit shifts. This would seem suitable, since you are in fact shifting bits. Here is a modified version:

/*
 code for converting 1-24 into three bytes
 for illuminating 24 LEDs with shift registers
 as a pseudo bar graph
 */
int i;
double cLED;      //counter for LED
int nTD0;         //number to display 0 to 2
int nTD1;
int nTD2;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  for (i=1;i<25;i++)                        //loop scope to be 1 to 24, this number to be replaced with data from Ping))) sensor
  {
    if (i<9)                                //first byte to be converted if total is eight or less
    {
      nTD0 = 0xFF >> (8 - i);       //shift register, leaving previous LEDs illuminated
      nTD1 = 0;                             //math necessary to remove rounding errors
      nTD2 = 0;                             //bytes two and three are zero
    }
    else
    {
      if (i<17)                              //second byte for nine to sixteen means first byte will always be 255
      {
        nTD0 = 0xFF;
        nTD1 = 0xFF >> (16 - i);       //shift register, leaving previous LEDs illuminated
        nTD2 = 0;
      }
      else
      {
        if (i<25)                            //third byte for seventeen to twenty-four, first and second bytes always 255
        {
          nTD0 = 0xFF;
          nTD1 = 0xFF;
          nTD2 = 0xFF >> (24 - i);       //shift register, leaving previous LEDs illuminated
        }
      }
    }
    //output to confirm that expected data is being generated
    Serial.print("Count\t");
    Serial.print("nTD0\t");
    Serial.print("nTD1\t");
    Serial.println("nTD2");
    Serial.print(i);
    Serial.print("\t");
    Serial.print(nTD0, BIN);
    Serial.print("\t");
    Serial.print(nTD1, BIN);
    Serial.print("\t");
    Serial.println(nTD2, BIN);    
    delay (1000);
  }
  
}

Output:

Count	nTD0	nTD1	nTD2
1	1	0	0
Count	nTD0	nTD1	nTD2
2	11	0	0
Count	nTD0	nTD1	nTD2
3	111	0	0
Count	nTD0	nTD1	nTD2
4	1111	0	0
Count	nTD0	nTD1	nTD2
5	11111	0	0
Count	nTD0	nTD1	nTD2
6	111111	0	0
Count	nTD0	nTD1	nTD2
7	1111111	0	0
Count	nTD0	nTD1	nTD2
8	11111111	0	0
Count	nTD0	nTD1	nTD2
9	11111111	1	0
Count	nTD0	nTD1	nTD2
10	11111111	11	0
Count	nTD0	nTD1	nTD2
11	11111111	111	0
Count	nTD0	nTD1	nTD2
12	11111111	1111	0
Count	nTD0	nTD1	nTD2
13	11111111	11111	0
Count	nTD0	nTD1	nTD2
14	11111111	111111	0
Count	nTD0	nTD1	nTD2
15	11111111	1111111	0
Count	nTD0	nTD1	nTD2
16	11111111	11111111	0
Count	nTD0	nTD1	nTD2
17	11111111	11111111	1
Count	nTD0	nTD1	nTD2
18	11111111	11111111	11
Count	nTD0	nTD1	nTD2
19	11111111	11111111	111
Count	nTD0	nTD1	nTD2
20	11111111	11111111	1111
Count	nTD0	nTD1	nTD2
21	11111111	11111111	11111
Count	nTD0	nTD1	nTD2
22	11111111	11111111	111111
Count	nTD0	nTD1	nTD2
23	11111111	11111111	1111111
Count	nTD0	nTD1	nTD2
24	11111111	11111111	11111111

That's the kind of answer I was hoping to get. Since the outputs are a series of ones, how will that be put into the shift registers when sent in three bytes?

If I send a 1 to the shift register, I'll get the suitable LED illuminated, but if I send 11, I won't get the first two LEDs to light up.

I'm thinking that your answer will have to do with the format of the output, but I don't know what that should be.

I have an example of driving 32 LEDs on this page.

The data is in the right format in the byte (or the 3 bytes). If you just shift out a byte at a time it ripples through the shift registers with just 3 calls to SPI.transfer. See that page for more information.

Why mess around with 3 separate bytes when you could use a single long and just mask and ShiftOut as needed.

void setup() {
    Serial.begin(9600);
}

void loop() {
    unsigned long result;

    for (int a=0;a<25;a++){
        Serial.println(a);
        result=0xFFFFFFFF;
        result=result >> (32-a);
        Serial.println(dec2binLong(result));
  
        //shiftOut(dataPin, clock, MSBFIRST, (result >> 16));  
        //shiftOut(dataPin, clock, MSBFIRST, (result >> 8));  
        //shiftOut(dataPin, clock, MSBFIRST, (result));  
   }
    while (true){
    }
}

String dec2binLong(unsigned long myNum){
    int zeros = 32 - String(myNum,BIN).length();
    String myStr;
    for (int i=0; i<zeros; i++) {
        myStr = myStr + "0";
    }
    myStr = myStr + String(myNum,BIN);  
    return myStr;
}

Riva:
Why mess around with 3 separate bytes when you could use a single long and just mask and ShiftOut as needed.

That looks neater. Plus you can still SPI.transfer instead of ShiftOut.

As a beginner to Arduino and having a not-so-capable brain trying to learn programming for the Arduino, simpler is far better for me.

I am able to fully understand the code I presented, and recognize that it was slow and not particularly well suited. The use of bit-shifting is clearer to me now and I've made my preliminary circuit function as desired for testing purposes. I had to change the order in which the bytes were pushed to the shift register, but that was a try-it-and-it-works sort of thing, rather than a think-it-through sort of thing.

The vendor for my LEDs managed to throw a small wrench (spanner?) in the works. Half of them are marked incorrectly for the anode, but no damage done.

I've connected up the ultrasonic module hc-sr04. Using the library was literally a no-brainer to get a display of distance. The rest is another post after researching the forums in case it's already been answered!

thanks for the help

fred