How to manage files and variables between multiple source files, when writing wrappers

Thread Starter

zazas321

Joined Nov 29, 2015
936
Hello. I am writing a wrapper for Neopixel library. I want to write my own functions for controlling LED to save code space and make it easier for me to understand. I have leds.c and leds.h . The functions from the leds.c will be used in many other source files such as my main.c and others as well.
My leds.c:
Code:
#include "leds.h"


RgbColor nice_purple(153,153,255);
RgbColor orange(255,165,0);
RgbColor bright_purple(204,0,102);
RgbColor cyan(0, colorSaturation, colorSaturation);
RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor yellow(colorSaturation,colorSaturation,0);
RgbColor white(colorSaturation);
RgbColor black(0);
RgbColor purple(colorSaturation, 0, colorSaturation);


NeoPixelBus<NeoGrbFeature, NeoEsp32Rmt0800KbpsMethod> strip(PixelCount, PixelPin);

void setup_leds(){
  strip.Begin();
}
void toggle_led_strip(color_t colour,int number){
      for(int i=0;i<=number;i++){
        Serial.print("colour is =");
        Serial.println(colour);
        strip.SetPixelColor(0, colour);
        strip.SetPixelColor(1, colour);
        strip.SetPixelColor(2, colour);
        strip.SetPixelColor(3, colour);
        strip.SetPixelColor(4, colour);
        strip.SetPixelColor(5, colour);
        strip.SetPixelColor(6, colour);
        strip.SetPixelColor(7, colour);
        strip.Show();
        delay(100);
        strip.SetPixelColor(0, black);
        strip.SetPixelColor(1, black);
        strip.SetPixelColor(2, black);
        strip.SetPixelColor(3, black);
        strip.SetPixelColor(4, black);
        strip.SetPixelColor(5, black);
        strip.SetPixelColor(6, black);
        strip.SetPixelColor(7, black);
        strip.Show();
        delay(100);
      }
    }

void toggle_led_strip_on(RgbColor colour){
        strip.SetPixelColor(0, colour);
        strip.SetPixelColor(1, colour);
        strip.SetPixelColor(2, colour);
        strip.SetPixelColor(3, colour);
        strip.SetPixelColor(4, colour);
        strip.SetPixelColor(5, colour);
        strip.SetPixelColor(6, colour);
        strip.SetPixelColor(7, colour);
        strip.Show();  
      }

      void toggle_led_strip_off(){
        strip.SetPixelColor(0, black);
        strip.SetPixelColor(1, black);
        strip.SetPixelColor(2, black);
        strip.SetPixelColor(3, black);
        strip.SetPixelColor(4, black);
        strip.SetPixelColor(5, black);
        strip.SetPixelColor(6, black);
        strip.SetPixelColor(7, black);
        strip.Show();    
      }

void clear_LED_strip(int number){
  for (int i=0;i<number;i++)
         strip.SetPixelColor(i, black);
        //delay(1);
          strip.Show();
}
void LED_strip_ON(RgbColor colour,int number){
    for (int i=0;i<number;i++)
        strip.SetPixelColor(i, colour);
        strip.Show();
}

and my leds.h:




Code:
#ifndef LEDS_H
#define LEDS_H
#include "NeoPixelBus.h"
#include <Adafruit_I2CDevice.h>
#define PixelCount  8 // this example assumes 4 pixels, making it smaller will cause a failure
#define PixelPin  27  // make sure to set this to the correct pin, ignored for Esp8266
#define colorSaturation 250

extern RgbColor nice_purple;
extern RgbColor orange;
extern RgbColor bright_purple;
extern RgbColor cyan;
extern RgbColor red;
extern RgbColor green;
extern RgbColor blue;
extern RgbColor yellow;
extern RgbColor white;
extern RgbColor black;
extern RgbColor purple;


void setup_leds();
void toggle_led_strip(color_t colour,int number);
void toggle_led_strip_on(RgbColor colour);
void toggle_led_strip_off();
void clear_LED_strip(int number);
void LED_strip_ON(RgbColor colour,int number);

#endif

Notice that I have all the color definitions in my leds.c and in my leds.h I use extern for each colour, that way when I include "leds.h" in any other source file I can use these colours as they are included as extern.


I am curious whether there is a better way of defining colors? Perhaps there is a way to achieve the simmilar thing just without using the extern? I have read that using extern is usually not a good idea. For example, I can create an array of RgbColor objects in my main.c but I have not figured a way how can I make it accessible from other source files without using extern.
 

ApacheKid

Joined Jan 12, 2015
1,533
I will tell you there is a much better way of organizing include files and that is something often given far too little attention.

I've written large complex API libraries in the past and while working at the Financial Times years ago me and my team reviewed the header policy and once we had this underway the benefits were huge (adapting an existing code base to follow these new rules was an effort but we got there).

If you're interested I can post more detail here but don't want to post all that if you're not interested, so let me know.
 

Thread Starter

zazas321

Joined Nov 29, 2015
936
Sorry for the late response.
The functions such as nice_purple and etc are the declarations of the colours from the neopixelbus library. The definition of the rgbcolor is desribed in the code here: https://github.com/Makuna/NeoPixelBus. I simply writing a wrapper arround this library and would like to create an array of possible colors which I define and then any other source file in my program would be able to call the led functions with the given colors.
The code below is taken from the neopixelbus library source code:
Code:
RgbColor(uint8_t r,  uint8_t g, uint8_t b) : R(r), G(g), B(b)
{
};
The above will create a colour variable, this variable is then used to pass as a parameter to functions to control the leds for example:
Code:
    void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color)
    {
        ConvertColor(&color);
        NeoPixelBus<T_COLOR_FEATURE, T_METHOD>::SetPixelColor(indexPixel, color);
    }

```
 
Last edited:

Thread Starter

zazas321

Joined Nov 29, 2015
936
I will tell you there is a much better way of organizing include files and that is something often given far too little attention.

I've written large complex API libraries in the past and while working at the Financial Times years ago me and my team reviewed the header policy and once we had this underway the benefits were huge (adapting an existing code base to follow these new rules was an effort but we got there).

If you're interested I can post more detail here but don't want to post all that if you're not interested, so let me know.
Yes please I would like more details
 

ApacheKid

Joined Jan 12, 2015
1,533
Yes please I would like more details
These rules were developed by me and my team over several weeks of meetings and were done in a pure 'C' environment, I think you could so the same with C++ but I'm no expert on C++ so can't assume there are not aspects of it that won't fit this.

Obviously we cannot force existing system libraries and headers to follow these rules, we must use them as is.

A header file should never include other header files.

Separate different types of includable items like types, function prototypes, constants etc.

Ensure every header file only ever includes one kind of item.

Enforce an ordering of include files into source code.



So function prototypes refer to types and types often refer to constants so we should order these:

#include 'xxx_constants.h'
#include 'xxx_datatypes.h'
#include 'xxx_functions.h'

Break stuff down into functional areas and group them on that basis, for example we maye have a list API:

#include 'list_constants.h'
#include 'list_datatypes.h'
#include 'list_functions.h'

The source code for the list API would be named 'list_functions.c' and would include the above three headers.

So any source code you write that needs to leverage your list API would simply include those three files in that order.

If you have code in which one area needs access to defintions of other areas then your source file will simply incude these three sets in order, for example we might have:

#include 'list_constants.h'
#include 'list_datatypes.h'
#include 'list_functions.h'

#include 'queue_constants.h'
#include 'queue_datatypes.h'
#include 'queue_functions.h'

This would be the case for example where we have some function in our queue API that refers in some way to a list type or a list function.

Sometimes we need to declare or access external static (global) data items, these too can be made to fit into this pattern but I'd need to refresh my memory on exactly how we did that.

The bottom line is that this makes code much more readable, the confusion that can arise when some headers include other headers (and we get recursive header files and so on) completely vanishes, how to use headers in consuming code becomes routine, always follows the same simple pattern.

You should try this with any new code you write, make yourself adhere to it and you'll find that you can grow the codebase into a huge and sophisticated system yet never ever get issues or confusion about headers files, ordering of header files and so on.
 
Top