Writing a library for a 7-segment LED, but nothing happens

I wrote a library and finally got it to compile, but now nothing happens. No LEDs light up. I’ve already tested the code outside of a library (in a sketch) and I know it works fine. I think I have a misunderstanding of how to run libraries. Here is my .h file:

#ifndef numberLED_h
#define numberLED_h

#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

class numberLED {

  private:
  
  public:
    numberLED();
    void writeNum(int val);
    void writeChar(int val);
    void dice();
    
    int num;
    int numSegments [] [7];
    int upperCharSegments [] [7];
    int lowerCharSegments [] [7];
    int val;

};

#endif

Here are the (relevant) parts of my .cpp file:

#include "numberLED.h"

numberLED::numberLED() {         //make it so the user can define what pins they want to use.
  for(int i = 0; i < 8; i++) {
    pinMode(i, OUTPUT);
  }
  randomSeed(analogRead(0));
}

int num = 0;

int numSegments [] [7] = {  //0-9
  {1, 1, 0, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 0},
  {0, 1, 1, 1, 0, 1, 1},
  {0, 1, 1, 0, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 0},
  {1, 0, 1, 0, 1, 1, 1},
  {1, 0, 1, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 1},
  {1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 1}
};

void numberLED::writeNum(int val) {
  for( int i = 0; i < 7; i++) {
    digitalWrite(i,numSegments[val][i]==1 ? HIGH : LOW);
  }
}

And here is my sketch. It simply counts upwards:

// Displays numbers and letters on a seven-segment LED

#include <numberLED.h>

numberLED led;

void setup()
{
  
}

void loop()
{
  led.writeNum(led.num);
  led.num++;
  if (led.num >= (sizeof led.numSegments)/(sizeof led.numSegments[0]))
    led.num = 0;
  delay(1000);
}

I know all of the files are in the right place as everything compiles fine. Is there something else I’m missing? Feel free to also critique my library. Thank you!

I see some confusion in your code...

First question IMHO is: what should your library do ?

Run this:

class numberLED {

  public:
    numberLED() {};
    int numSegments [] [7];

};

numberLED led;

void setup ()
{
  Serial.begin (115200);
  Serial.println (sizeof led.numSegments);
}

void loop () {}

Tell me what you see. Then explain why.

Ran the code, and the serial port returns 0. I suspect that I don't quite get arrays (or their use in a class), but I've used this exact same code and it did exactly what I wanted it to: counts upward from 0 to 9, then returns to 0 and repeats. Is the syntax of led.numSegments not correct?

I quickly tried placing all needed integers in my sketch and removed the led. syntax, and still nothing happened.

edit: Thank you both for responding. I hope we can get this to work together.

When the compiler compiles your main sketch it can't know how many elements are in an array in another source file. The simple solutions (although untested) might be to simply tell it:

    int numSegments [10] [7];
    int upperCharSegments [10] [7];
    int lowerCharSegments [10] [7];

Okay, I changed the declaration to int numSegments[10] [7] and changed the if statement in the sketch to only if (led.num > 9), and then something happened… I uploaded the sketch and two LEDs turned on: the LEDs corresponding to pins 6 and 7 of the Arduino periodically lit up, and then turned off. I thought maybe there was a hardware error so I tried this sketch:

int numSegments [10] [7] = {  //0-9
  {1, 1, 0, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 0},
  {0, 1, 1, 1, 0, 1, 1},
  {0, 1, 1, 0, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 0},
  {1, 0, 1, 0, 1, 1, 1},
  {1, 0, 1, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 1},
  {1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 1}
};

int num = 0;

void setup () {
  for(int i = 0; i < 8; i++) {
    pinMode(i, OUTPUT);
  }
}

void loop () {
  writeNum(num);
  num++;
  if (num > 9)
    num = 0;
  delay(500);
}

void writeNum(int val) {
  for( int i = 0; i < 9; i++) {
    digitalWrite(i,numSegments[val][i]==1 ? HIGH : LOW);
  }
}

and it ran fine.

Could it be that led.num is not giving the right value and numSegments is turning on pins randomly?

numberLED::numberLED() {         //make it so the user can define what pins they want to use.
  for(int i = 0; i < 8; i++) {
    pinMode(i, OUTPUT);
  }
  randomSeed(analogRead(0));
}

int num = 0;

That code does not initialize numberLED::num, just some other variable called num.

If I completely comment out num and numSegments in the library and put them both in the sketch I get this error:

numberLED:55: error: ‘numSegments’ was not declared in this scope

If I make the declaration in the .cpp files into int numberLED::numSegments [10] [7] then I get these errors:

numberLED:6: error: ‘int numberLED::num’ is not a static member of ‘class numberLED’
numberLED:8: error: ‘int numberLED::numSegments [10][7]’ is not a static member of ‘class numberLED’

Post the whole thing please.

I assume you mean the whole code. Here is the sketch:

// Displays numbers and letters on a seven-segment LED

#include <numberLED.h>

numberLED led;

void setup()
{
  
}

void loop()
{
  led.writeNum(led.num);
  led.num++;
  if (led.num > 9)
    led.num = 0;
  delay(500);
}

The .h file:

#ifndef numberLED_h
#define numberLED_h

#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

class numberLED {

  private:
  
  public:
    numberLED();
    void writeNum(int val);
    void writeChar(int val);
    void dice();
    
    int num;
    int numSegments [10] [7];
    int upperCharSegments [] [7];
    int lowerCharSegments [] [7];
    int val;

};

#endif

And the .cpp file (some functions cut for brevity):

#include "numberLED.h"

numberLED::numberLED() {         //make it so the user can define what pins they want to use.
  for(int i = 0; i < 8; i++) {
    pinMode(i, OUTPUT);
  }
  randomSeed(analogRead(0));
}

int numberLED::num = 0;

int numberLED::numSegments [10] [7] = {  //0-9
  {1, 1, 0, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 0},
  {0, 1, 1, 1, 0, 1, 1},
  {0, 1, 1, 0, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 0},
  {1, 0, 1, 0, 1, 1, 1},
  {1, 0, 1, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 1},
  {1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 1}
};

void numberLED::writeNum(int val) {
  for( int i = 0; i < 7; i++) {
    digitalWrite(i,numSegments[val][i]==1 ? HIGH : LOW);
  }
}

There’s no point in having a public class member like num if you then pass it to the writeNum method.

I repeat my question: what are you writing that class for ? Probable answer: to encapsulate the function of displaying single numbers and letters on a 7-segment display. Good. Some design decisions immediately come to mind:

  • static methods
  • static data members
  • no num or val, just display configurations

(And btw, if it displays numbers and letters, why call it numberLED ? Also, class names don’t start with lowercase letters.)

Something along these lines:

SevenSeg.h

#ifndef _SEVENSEG_H
#define _SEVENSEG_H

#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

// Displays numbers and letters on a seven-segment LED

class SevenSeg {
public:
    SevenSeg();
    static void writeNum(unsigned short int n);
    static void writeChar(char ch);
private:
    static const int numSegments [] [7];
    static const int upperCharSegments [] [7];
    static const int lowerCharSegments [] [7];
};

#endif

SevenSeg.cpp

#include "SevenSeg.h"

// initialize class data
const int SevenSeg::numSegments[][7] = {  //0-9
  {1, 1, 0, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 0},
  {0, 1, 1, 1, 0, 1, 1},
  {0, 1, 1, 0, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 0},
  {1, 0, 1, 0, 1, 1, 1},
  {1, 0, 1, 1, 1, 1, 1},
  {0, 1, 0, 0, 1, 0, 1},
  {1, 1, 1, 1, 1, 1, 1},
  {1, 1, 1, 0, 1, 0, 1}
};

const int SevenSeg::upperCharSegments[][7] = {    // A-Z
    // insert data here
};

const int SevenSeg::lowerCharSegments[][7] = {
    // insert data here
};


SevenSeg::SevenSeg() {        // fixed pins: 0-7
    for (int i = 0; i < 7; i++) {
        pinMode(i, OUTPUT);
    }
}


void SevenSeg::writeNum(unsigned short int val) {
  for( int i = 0; i < 7; i++) {
    digitalWrite(i, numSegments[val][i]==1 ? HIGH : LOW);
  }
}

Test sketch

// 7-seg led display test

#include "SevenSeg.h"

SevenSeg disp;

void setup() {
}

void loop() {
    for (int i = 0; i <=9; i++) {
        disp.writeNum(i);
        delay(1000);
    }
}

I hope this is of some help to you :slight_smile:

That’s it! Thank you mromani and Nick Gammon! I’m new to writing libraries, so I’ll remember your syntax later on. Do you think it was my crazy if statement in the sketch that was messing it up for the improper use of declarations?

You must qualify numSegments with the class name when initializing it, otherwise you're just declaring a "normal" (i.e. non-class) variable.

You don't seem to have a clear undestanding of how classes work or what they're for. I'd suggest you start by writing a sketch with all the code in it, organized in functions. Then you can put some functions which you think belong together in a single .h file, that you would #include in your sketch. That would not properly qualify as "library", but would be quite close. Making the jump from a bunch of related functions to a proper class{} requires some theoretical knowledge of OO programming. Sorry if I sound opinionated, but that's the sad truth, IMHO.

Feel free to ask any further questions, of course :slight_smile:

I would never presume to have a good idea about classes. The most I know about libraries comes from this tutorial: http://arduino.cc/it/Hacking/LibraryTutorial, so obviously I have a long way to go.

This was more of a training exercise for me. I don't plan on publishing this. Now that I have the setup working I'll be adding more functions. Right now I have a random dice roller and I'm thinking about a very rudimentary multimeter (rounding to a single sig fig, pretty lame). Maybe I'll make my single seven seg into a mini ebook reader! Hah.

Do you have any advice for continuing on in training? Any advanced guides? Again, thank you.

I don't have a link to a good OO introduction in C++, sorry. I'll try to give you some tips here, but you'll definitely have to learn a bit from the books. It's a potentially very complex and broad topic, that can't be condensed in a forum post.
Anyway, here's my attempt at getting you started.

Usually arduino libraries are implemented as C++ classes. A class, in OO terms, is a "thing" that contains methods (i.e. functions) and data (i.e. variables). Classes are just a description. To use them you create objects, each of which behaves exactly as described in the class definition, but has its own private data (i.e. variables). Methods are called on an object, and operate on that object's data.
Methods and data members can be public or private. The first ones can be called by the code that uses the class, while the last ones can be accessed (data) or called (methods) only by the the methods of the class itself.
Let's make a very simple example: the Counter class (for clarity I'll omit things like #ifdef _CLASS_H etc.).

Counter.h
Contains method declarations and data member description.

class Counter {
public:
    Counter();    // constructor -- doesn't have a return type
    void inc();    // increment counter value
    int getValue();     // get the current value of the counter
    void reset();    // reset the counter to its initial value
private:
    int value;    // we make this private because we don't want the user to mess directly with the internals of the class
};

Counter.cpp

#include "Counter.h"    // it's mandatory to include the class definition

// constructor; performs object initialization
Counter::Counter() {
    reset();    // calls the reset method, which will set the data member "value" to an initial value
}

// set the counter to an initial value
void Counter::reset() {
    value = 0;
}

// increments counter value
void Counter::inc() {
    value++;             // notice that a private data member is directly accessible by class methods
}

// we provide a method to access the current value of the counter
// this is called a [i]getter[/i] method
int Counter::getValue() {
    return value;
}

How do we use this class ? We create Counter objects. Creating objects is called class instantiation.

CounterTest.ino

#include "Counter.h"

Counter c1;        // we create object c1; it has its own private [i]value[/i]
Counter c2;        // we create object c2; it has its own private [i]value[/i]

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

void loop() {
    Serial.print("c1=");
    Serial.println(c1.getValue());    // we call getValue() [i]on[/i] object c1, therefore we get the contents of c1's "value"
    Serial.print("c2=");
    Serial.println(c2.getValue());    // here we get the value of c2's "value"

    // let's increment only c2, and see what happens
    c2.inc();

    // let's slow things down so we can see what's going on
    delay(1000);
}

The above (rather stupid, I admit) code simply counts the number of loop() invocations using counter c2. Counter c1 remains at 0. This shows what "calling a method on an object" means: the method "knows" which variable it has to access, or, put another way, on which object it has to "work".

To understand what it means to make a data member private, try to replace c2.inc() with the following line:

c2.value++;

You should get a compiler error.

This might seem a convoluted way of coding. And for such a simple example, it is. Let's make one last step forward to understand why it's useful.
Data members (i.e. "variables") describe the state of an object. And while the number and type of the data members is the same for all objects of the same Class, their value is specific to each object.
The state of an object varies as actions are taken on it, i.e. by calling methods on it. Those methods change the state of the object in a consistent and predictable way (provided the Class has been coded properly, of course). If the user could directly modify the value of a data member, then she would run the risk of "ruining " the state, making it inconsistent, eventually causing a misbehaviour of the code.
For example, you don't want to mess with the internals of the Serial class (nor should you need to know what they are - that's called encapsulation). So even if you read the Serial.h code and find out the name of the low level buffer, the compiler won't let you access it, forcing you to use the Serial public methods.

An example is due here, but nothing simple enough comes to mind, right now. Sorry, maybe later :wink:

I hope you can get something out of this too long post. Feel free to ask again, though I can't guarantee a timely answer.