(.text+0x0): multiple definition of ... running out of ideas

Hi all
being new in the forum, being new in C++ programming (I used to do C some years ago) I started my own project. Which is at the beginning.
I started to introduced *.h and *.cpp files. One pair shall provide library-functions dedicated to my project (–> MyLibrary.h/.cpp), another is to actual execute a part of the intendend functionality (so created not to get the sketch bigger and bigger).
After putting everything together I got following message:

sketch\ICS.cpp.o (symbol from plugin): In function swBright': (.text+0x0): multiple definition of swBright’
sketch\CppProblem_v1d1.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1
Fehler beim Kompilieren für das Board Arduino Uno.

I have removed quite a lot from the code to make it more easy to read - and not to bother you with a long list of error messages.

So what to you see

  1. MyLibrary.h provides the class “button”. It has a function to get the state of a button (GetState()), and a function that updates that state (UpdateState()). A constructor is also defined.
  2. Both functions GetState() and UpdateState() are used in different “locations”. UpdateState() is used within a interrupt routine defines in ICS.h/.cpp, while GetState() is used in the sketch itself.

Here come the files:

  1. MyLibrary.h
#ifndef MYLIB_h
#define MYLIB_h

typedef enum button_t {released, pushed, pushedLong} button_t;

class button
{
  public:                                    
    button(int pin, byte debounceLimit, int pushedLongLimit); // Constructor
    void UpdateState();                         // Updates State of Button
    button_t GetState() const {return _State;}  // Getter for State
  
  private:
    int       _pin;                          // Pin the button is connected to
    button_t  _State;                        // State of Button
    byte      _debounceLimit;                // Limit for debounce
    int       _pushedLongLimit;              // Limit for Threshold Longpushed
    byte      _debounceCnt;                  // Counter to debounced transistion
    int       _pushedLongCnt;                // Counter to detect long push
};
  1. MyLibrary.cpp (I’m not asking to review the code itself, so UpdateState() is just to take as it is: a functions that does something)
#include "Arduino.h"
#include "MyLibrary.h"

// Constructor /////////////////////////////////////////////////////////////////

button::button(int pin, byte debounceLimit,int pushedLongLimit) {
  _pin             = pin;
  _State           = released;
  _debounceLimit   = debounceLimit;     // Limit to detect end of bouncing
  _pushedLongLimit = pushedLongLimit;   // Limit to detect long push
  _debounceCnt     = 0;                 // Counter to debounced transistion
  _pushedLongCnt   = 0;                 // Counter to detect long push
  pinMode(pin,  INPUT);                 // Pin for button is input
}

// Public Methods //////////////////////////////////////////////////////////////

void button::UpdateState() {
  switch (_State) {
    case released:                      
      if (digitalRead(_pin) == LOW) {   // push/LOW detected?
        _debounceCnt++;
      }
      else if (_debounceCnt > 0) {      // otherwise no push or bouncing, go back to 0
        _debounceCnt--;
      }
      if (_debounceCnt >= _debounceLimit) {
        _State         = pushed;        // Make Transition and initialize for 
        _debounceCnt   = 0;             // ... new state: debounceCnt for release
        _pushedLongCnt = 0;             //                Counter for long push
      }
      break;
    case pushed:
      if (digitalRead(_pin) == HIGH)  { // Start counting release/HIGH detected
        _debounceCnt++;
        if (_debounceCnt >= _debounceLimit) {
          _State       = released;      // Make Transition and initialize for 
          _debounceCnt = 0;             // ... new state: debounceCnt for release
        }
      }
      else {              // So button is pushed/LOW (or bouncing), long or released?
        // Take care for counter first
        if (_debounceCnt > 0) {         // No push so it bounces, go back to 0
          _debounceCnt--;
        }
        _pushedLongCnt++;               // One more for long push
        if (_pushedLongCnt >= _pushedLongLimit) {
          _State       = pushedLong;    // Make transition and intialize for
          _debounceCnt = 0;             // ... new state: debounceCnt for release
        }
      }
      break;
    case pushedLong:
      if (digitalRead(_pin) == HIGH) {  // release/HIGH detected?
        _debounceCnt++;
      }
      else if (_debounceCnt > 0) {      // otherwise no release or bouncing, go back to 0
        _debounceCnt--;
      }
      if (_debounceCnt >= _debounceLimit) {
        _State         = released;      // Make Transition and initialize for 
        _debounceCnt   = 0;             // ... new state: debounceCnt for release
      }
      break;
    default:
      _State         = released;        // Assume released as default
      _debounceCnt   = 0;               // 
      _pushedLongCnt = 0;               // 
  }
}

#endif

The ICS that provides also the interrupt routine.
ICS.h

#ifndef ICS_h
#define ICS_h
#include "MyLibrary.h"

// Defines
#define swOnOff         2           // Pin for On/Off  , Button

// Call Constructor for Buttons: Bright, ColorTemperature, On/Off and Mode 

button swBright(swOnOff, 2, 100);   // Create and Init Object for On/Off-Switch

class fclass
{
  // user-accessible "public" interface
  public:
    fclass();
    void Setup();
};
extern fclass ICS;

#endif

and ICS.cpp

/*
  ICS.h - Manage inputs and outputs
*/

#include "Arduino.h"
#include "MyLibrary.h"
#include "ICS.h"

// Constructor /////////////////////////////////////////////////////////////////
fclass::fclass() {
}

// Public Methods //////////////////////////////////////////////////////////////

void fclass::Setup()
{

// Configure Timer 1
  cli();            // stop interrupts during timer configuration
// Configure Timer1 - Timer 1 interrupts used buttons and Status LED Control, Prio 12   
  TCCR1A = 0;       // set timer controller register to zero to ...
  TCCR1B = 0;       // ... know from what we start (TCCR1A and TCCR1B)
  TCNT1  = 0;       //initialize counter value to 0
  // Turn on CTC mode (Table 16-4, ATmega328 datasheet, and TCCR1B)
  TCCR1B |= (1 << WGM12);
  // Set prescaler to 1024 (Table 16-5, ATmega328 datasheet)
  // Interrupt-Frequency =  0.24Hz .. 15.6kHz
  TCCR1B |= (1 << CS12);
  TCCR1B |= (1 << CS10);
  // set compare match register for 20Hz increments (50ms)
  OCR1A = 780;     // = (16*10^6) / (20*1024) - 1 (must be <65535), 1561=100ms
  // enable timer compare interrupt (--> TIMER1_COMPA_vect)
  TIMSK1 |= (1 << OCIE1A);
// Allow interrupts again  
  sei();            // Allow interrupts again
}

// ------------------------------------------------------------------------------- //
// Interrupt-Routine for Timer 1. ------------------------------------------------ //
// ------------------------------------------------------------------------------- //
ISR(TIMER1_COMPA_vect){
  swBright.UpdateState();               // Update Switch-State
}

fclass ICS = fclass();

and finally the sketch.

// This sketch is to manage the control unit
//
// - Evaluate rotary encoders for brightness and colortemperature, ...
//   input switches, RTC and brightness sensor
// - Determine mode of operation
// - Calculate desired brightness and colortemp based in inputs
// - Send command to LED driver unit

#include "Arduino.h"
#include "ICS.h"

void setup() {
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);
  ICS.Setup();
}

void loop() {
    button_t swBrightState;
    
    swBrightState = swBright.GetState();
    switch (swBrightState) {                         // Just do something nice
    case released:
      digitalWrite(13, HIGH);
      digitalWrite(14,  LOW);
      digitalWrite(15,  LOW);
      break;
    case pushed:
      digitalWrite(13,  LOW);
      digitalWrite(14, HIGH);
      digitalWrite(15,  LOW);
      break;      
    case pushedLong:
      digitalWrite(13,  LOW);
      digitalWrite(14,  LOW);
      digitalWrite(15, HIGH);
      break;
    }
}

What I’m struggle with is how to “organize it” properly that the Libriary is used at two different locations. I read some threads about using extern in front of the constuctor or putting the constructor

button swBright(swOnOff, 2, 100); 

into the *.cpp (ICS.cpp then), but not with desired result. Even then I would not know why it may have helped.

Can you help on this?

Thanks
Sebastian

the above is in a .h file, so the object is created in every .cpp/.ino that .h is included in.

that line should be in a single .cpp/.ino

Hey,
unfortunately this doesn’t solve the problem.

I may move that line to ics.cpp, but then I get the error message

Blockquote
C:\Users\Sebastian_2\Documents\Projekte\Arduino\Schreibtischlicht\CppProblem_v1d1\CppProblem_v1d1.ino: In function ‘void loop()’:
CppProblem_v1d1:22:21: error: ‘swBright’ was not declared in this scope
swBrightState = swBright.GetState();
^~~~~~~~
C:\Users\Sebastian_2\Documents\Projekte\Arduino\Schreibtischlicht\CppProblem_v1d1\CppProblem_v1d1.ino:22:21: note: suggested alternative: ‘swBrightState’
swBrightState = swBright.GetState();
^~~~~~~~
swBrightState
exit status 1
‘swBright’ was not declared in this scope

That is, the object swBright is not known in the sketch.

And, well, when I move button swBright(swOnOff, 2, 100); to the sketch, it is vice versa, i.e. I get the error message, that swBright.UpdateState(); is not declared in the scope of ics.cpp (here in particular in ISR(TIMER1_COMPA_vect)):

Blockquote
sketch\ICS.cpp: In function ‘void __vector_11()’:
ICS.cpp:44:3: error: ‘swBright’ was not declared in this scope
swBright.UpdateState(); // Update Switch-State
^~~~~~~~
exit status 1
‘swBright’ was not declared in this scope

So, ok, if object created everytime it is included - how to avoid this? Putting it in one cpp/ino seems not to work … :confused:

swBright does need to be defined as extern in the .h file

You’ll have to learn about the compilation and linking process of C++. Did you follow any C++ tutorials/books/courses?

What you need here is a global variable declaration (not definition) with the extern specifier in your header file, and a definition in exactly one implementation (.cpp or .ino) file. See for example learncpp.com: External linkage.

Pieter

… it has been quite a while ago when I did my last C programming, well 15 years now. And, yes, I try to catch up with C++ by reading tutorials or books (in particular Ulrich Breymann “Der C++ Programmierer”, which is said to be a a good one at least here in Germany). Nevertheless, I’m sure I haven’t understood well the idea of object-oriented yet … so my question was also to get hints in what direction I need to think. It has been an example to start working with it.

Despite, I got the files compiled with the external declaraion is ICS.h

extern button swBright;   

and the definition in ICS.cpp

#define swOnOff         2                  // Pin for On/Off  , Button
button swBright(swOnOff, 2, 5);   

… and now the code is not doing what it is supposed to do, but this is another story … I need to check …

This post by @gfvalvo is a useful guide to breaking a large C++ project into separate .h and .cpp files:
(Post # 5) Organising code for a large-scale, modular project

Thanks for the link! It helps in understanding by short examples … very nice :slight_smile:

Thanks. And yes, there are different views on that (e.g. testing, major functions (whatever that is in one’s project) … ). My favorite is putting things intro small units (h/cpp) that do something useful/capsulated, easy to test and then simply rely on them in further development.

the practical guide to structure systems design discusses cohesion and coupling.

cohesion is that a module of code has a well defined purpose that it implements as completely as possible and does nothing outside that purpose.

coupling is the inevitable need that separate modules depend and interact with one another

that authors (??) recommend maximizing cohesion and minimizing coupling

of course this applies more to larger programs.

Well, if I got the your comment right I decided to go for cohesion.

At the end I did the following (all inputs and outputs shall go through ICS-Module). So ICS.h looks like this:

#ifndef ICS_h
#define ICS_h
#include "PinDef_328P_PDIP.h"
#include "MyLibrary.h"

class fclass
{
  // user-accessible "public" interface
  public:
    fclass();
    void Setup();
    int       Get_rdBrightDeltaCnt();
    int       Get_rdColorTDeltaCnt(); 
    button_t  Get_swBrightState();
    button_t  Get_swColorTState();
    button_t  Get_swOnOffState();
    button_t  Get_swModeState();
};
extern fclass ICS;

#endif

and, just as sample, in ICS.cpp I worte the following:

button_t fclass::Get_swBrightState() {        // GET Brightness-Switch
  return(swBright.GetState());
}

At least it works. And provides the desired cohesion, i.e. swBright is just known within ICS.cpp …

so where is swBright?

it’s hardcoded variable that fclass knows about but which is not part of flass? should it be a variable defined within fclass?

Well, don’t know if I got your question right. So, right now it looks like this:

ICS.h

#ifndef ICS_h
#define ICS_h
#include "PinDef_328P_PDIP.h"
#include "MyLibrary.h"

class fclass
{
  // user-accessible "public" interface
  public:
    fclass();
    void Setup();
    int       Get_rdBrightDeltaCnt();
    int       Get_rdColorTDeltaCnt(); 
    button_t  Get_swBrightState();
    button_t  Get_swColorTState();
    button_t  Get_swOnOffState();
    button_t  Get_swModeState();
};
extern fclass ICS;

#endif

and corresponding ICS.cpp

#include "Arduino.h"
#include "PinDef_328P_PDIP.h"
#include "MyLibrary.h"
#include "ICS.h"
#include "LED.h"


// Variables
boolean isr1_Toggle = false;        // Used in Timer 1 - ISR for debugging
boolean isr2_Toggle = false;

// Encoders
RotaryEncoder rdColorT(pin_rdColorTA, pin_rdColorTB); // Define RotEnc of ColorT 
RotaryEncoder rdBright(pin_rdBrightA, pin_rdBrightB); // Define RotEnc of Brightness

// Buttons
Button swBright(pin_rdBrightSw, 3, 400);              // Define Button Bright
Button swColorT(pin_rdColorTSw, 3, 400);              // Define Button ColorTemp
Button  swOnOff(pin_swOnOff,    3, 400);              // Define Button of On/Off
Button  swMode( pin_swMode ,    3, 400);              // Define Button of Mode

// Constructor /////////////////////////////////////////////////////////////////
// Function that handles the creation and setup of instances (nothing to do here)
fclass::fclass() {
}

// Public Methods //////////////////////////////////////////////////////////////
// Functions available in Wiring sketches, this library, and other libraries

void fclass::Setup()
{
// Define Outputs
  pinMode(pin_isr2ToggleLED , OUTPUT);
  pinMode(pin_isr2RunTimeOut, OUTPUT);

// Configure Timer 1 and 2
  cli();            // stop interrupts during timer configuration
// Configure Timer2 - Timer 2 interrupts are used for rotary encoder reading, Prio 8
  TCCR2A = 0;       // set timer controller register to zero to ...
  TCCR2B = 0;       // ... know from what we start (TCCR2A and TCCR2B)
  TCNT2  = 0;       //initialize counter value to 0
  // Turn on CTC mode (Table 18-8, ATmega328 datasheet)
  TCCR2A |= (1 << WGM21);
  // Set prescaler to 256 (Table 18-9, ATmega328 datasheet)
  // Interrupt-Frequency =  244Hz .. 62.5kHz
  TCCR2B |= (1 << CS22);
  TCCR2B |= (1 << CS21);
  // TCCR2B |= (1 << CS20);  
  // set compare match register for 2kHz increments (500us)
  OCR2A = 30;       // = (16*10^6) / (2000*256) - 1 (must be <256)
  // enable timer compare interrupt (--> TIMER2_COMPA_vect)
  TIMSK2 |= (1 << OCIE2A);
// Configure Timer1 - Timer 1 interrupts used buttons and Status LED Control, Prio 12   
  TCCR1A = 0;       // set timer controller register to zero to ...
  TCCR1B = 0;       // ... know from what we start (TCCR1A and TCCR1B)
  TCNT1  = 0;       //initialize counter value to 0
  // Turn on CTC mode (Table 16-4, ATmega328 datasheet, and TCCR1B)
  TCCR1B |= (1 << WGM12);
  // Set prescaler to 1024 (Table 16-5, ATmega328 datasheet)
  // Interrupt-Frequency =  0.24Hz .. 15.6kHz
  TCCR1B |= (1 << CS12);
  TCCR1B |= (1 << CS10);
  // set compare match register for 100Hz increments (10ms)
  OCR1A = 155;     // = (16*10^6) / (100*1024) - 1 (must be <65535)
  // enable timer compare interrupt (--> TIMER1_COMPA_vect)
  TIMSK1 |= (1 << OCIE1A);
// Allow interrupts again  
  sei();            // Allow interrupts again
}

// Get-Routinen
int fclass::Get_rdBrightDeltaCnt() {          // GET Brightness
  return(rdBright.GetDeltaCnt());
}
int fclass::Get_rdColorTDeltaCnt() {          // GET ColorTemperature
  return(rdColorT.GetDeltaCnt());
}
button_t fclass::Get_swBrightState() {        // GET Brightness-Switch
  return(swBright.GetState());
}
button_t fclass::Get_swColorTState() {        // GET Colortemperature-Switch
  return(swBright.GetState());
}
button_t fclass::Get_swOnOffState() {         // GET On/Off-Switch
  return(swOnOff.GetState());
}
button_t fclass::Get_swModeState() {          // GET Mode-Switch
  return(swMode.GetState());
}

// ------------------------------------------------------------------------------- //
// Interrupt-Routine for Timer 1. ------------------------------------------------ //
// ------------------------------------------------------------------------------- //
// This routine is used to
//    - drive LEDs
//    - evalute statii of buttons
ISR(TIMER1_COMPA_vect){
  LED.RunLED();                       // Run LED according to latest command
  swBright.UpdateState();             // Update Switch-State of Bright - Button
  swColorT.UpdateState();             //                        ColorT
  swOnOff.UpdateState();              //                        On/Off
  swMode.UpdateState();               //                        Mode

}

// ------------------------------------------------------------------------------- //
// Interrupt-Routine for Timer 2. ------------------------------------------------ //
// ------------------------------------------------------------------------------- //
// This routine is used to evalute rotary encoders are evaluated
ISR(TIMER2_COMPA_vect){
  rdBright.UpdateState();           // Brightness
  rdColorT.UpdateState();           // Colortemperature
}

fclass ICS = fclass();

using MyLibrary.h

#ifndef MYLIB_h
#define MYLIB_h

typedef enum button_t {released, pushed, pushedLong} button_t;

class Button
{
  public:                                    
    Button(int pin, byte debounceLimit, int pushedLongLimit); // Constructor
    void     UpdateState();             // Updates State of Button
    button_t GetState()         const {return m_State;}   // Getter for State
    int      GetPinRead()       const {return m_pinRead;} //            pin
    byte     GetDebounceCnt()   const {return m_debounceCnt;}   //      counter
    int      GetPushedLongCnt() const {return m_pushedLongCnt;} //      counter
  
  private:
    int       m_pin;                    // Pin the button is connected to
    button_t  m_State;                  // State of Button
    int       m_pinRead;                // latest reading of pin
    byte      m_debounceLimit;          // Limit for debounce
    int       m_pushedLongLimit;        // Limit for Threshold Longpushed
    byte      m_debounceCnt;            // Counter to debounced transition
    int       m_pushedLongCnt;          // Counter to detect long push
};

class RotaryEncoder
{
  public:
    RotaryEncoder(int pinSignalA, int pinSignalB);   // Constructor
    void UpdateState();     // Update internal state (=0b000000AB) of encoder
    int  GetDeltaCnt();     // Return accumulated Delta, and then set Delta to zero
  private:
    byte m_rdState;         // State given through last Signal_A_B reading
    byte m_rdNextState;     // Next State given through actual reading
    volatile char m_rdDeltaCnt;      // Accumulated Delta
    int  m_pinA;            // HW-pin Signal A is connected to
    int  m_pinB;            //        Signal B
};

#endif

with some defintions from

#ifndef PinDef_328P_PDIP_h
#define PinDef_328P_PDIP_h

// Arduino IDE uses strange numbering of ATmega328 Pins, i.e. IDE Pin Number
// is not identical with physical Pin - well, as this may change depending on
// MCU package. Thus, convert here for ATmega328P in 28-PDIP package to be 
// able to use Ports as they will be used in hardware

#define pinPB0  8
#define pinPB1  9
#define pinPB2 10
#define pinPB3 11
#define pinPB4 12
#define pinPB5 13
// #define pinPB6 DontUse  // XTAL_1 can not be used as I/O through IDE
// #define pinPB7 DontUse  // XTAL_2 can not be used as I/O through IDE

#define pinPC0 14
#define pinPC1 15
#define pinPC2 16
#define pinPC3 17
#define pinPC4 18
#define pinPC5 19
// #define pinPC6 DontUse  // RESET can not be used as I/O through IDE
// #define pinPC7 DontUse  // Does not exist, and so in IDE

#define pinPD0  0
#define pinPD1  1
#define pinPD2  2
#define pinPD3  3
#define pinPD4  4
#define pinPD5  5
#define pinPD6  6
#define pinPD7  7

// Mapping on Functions
#define pin_isr2ToggleLED   pinPB5
#define pin_isr2RunTimeOut  pinPB4
#define pin_rdBrightA       pinPC0  // Pin for Signal A, Encoder Brightness
#define pin_rdBrightB       pinPC1  // Pin for Signal B, Encoder Brightness
#define pin_rdBrightSw      pinPC2  // Pin for Switch  , Encoder Brightness
#define pin_rdColorTA       pinPD4  // Pin for Signal A, Encoder ColorTemperature
#define pin_rdColorTB       pinPD5  // Pin for Signal B, Encoder ColorTemperature
#define pin_rdColorTSw      pinPD6  // Pin for Switch  , Encoder ColorTemperature
#define pin_swOnOff         pinPD2  // Pin for On/Off  , Button
#define pin_swMode          pinPD3  // Pin for Mode    , Button


#endif

… and, well the sketch itself

#include "Arduino.h"
#include "MyLibrary.h"
#include "ICS.h"
#include "LED.h"

int brightValue;
int brightDeltaCnt;
int colorTValue;
int colorTDeltaCnt;
button_t swOnOffState, swModeState, swBrightState, swColorTState;

void setup() {
  LED.Setup();
  ICS.Setup();
  // EncoderData
  brightValue    = 0;
  brightDeltaCnt = 0;
  colorTValue    = 0;
  colorTDeltaCnt = 0;
  Serial.begin(9600);
}

void loop() {
  // Read input data
  // Rotary Encoders
  brightDeltaCnt = ICS.Get_rdBrightDeltaCnt();
  brightValue    = min(255,max(0,brightValue + brightDeltaCnt));
  colorTDeltaCnt = ICS.Get_rdColorTDeltaCnt();
  colorTValue    = min(255,max(0,colorTValue + colorTDeltaCnt));

  // Show data
  if (brightDeltaCnt != 0) {
    Serial.print("brightDeltaCnt\t");
    Serial.print(brightDeltaCnt);
    Serial.print("\tbrightValue\t");
    Serial.println(brightValue);
  }
  if (colorTDeltaCnt != 0) {
    Serial.print("colorTDeltaCnt\t");
    Serial.print(colorTDeltaCnt);
    Serial.print("\tcolorTValue\t");
    Serial.println(colorTValue);
  }

  if (true) {
    swOnOffState = ICS.Get_swOnOffState();
    swModeState  = ICS.Get_swModeState();
    switch (swModeState) {
    case released:
      LED.SetLEDMode(Green, On);
      break;
    case pushed:
      LED.SetLEDMode(Red, On);
      break;      
    case pushedLong:
      LED.SetLEDMode(Blue, FlashShort);
      break;
    }
    if (swOnOffState == pushedLong) {
      LED.SetLEDMode(Blue, FlashShort);
    }
  }  
}

… I’m sure it is not be perfect yet. I’m still in ascending learning curve … :wink:

i’m not sure i can make any helpful comments.

looks like you have a class, fclass built on top of a Button class. i don’t see a benefit of have functions in one class calling equivalent functions in another

button_t fclass::Get_swModeState() {          // GET Mode-Switch
  return(swMode.GetState());
}

maybe this is where virtual functions and inheritance.

the major criticism of C++ i’ve heard and experienced to some degree is that it abstracts the design under layers of code making it difficult to follow.

not sure what your goals are. in his 3rd ed, Bjarne Stroustrup warned developers not to use features of C++ unnecessarily. In an interview he said he felt it takes 10 years to become proficient in c++.

This might be right. So let’ leave the discussion where it is right now. As you ask what my goals are … I sorting this out, so not really in the position to exactly describe what I like to achieve with respect to SW architecture … (besides desired functionality, but this was not the rationale for that thread). Thanks for your help.

i believe the goal of an object is to be able think in terms of the problem you’re dealing with. for example, the multiplication operators hides all the details of multibyte binary arithmetic.

in your code it looks like you’re trying deal with the brightness of LEDs that indicate something and something to do with buttons.

shouldn’t the object deal with the behavior the buttons control and what the LED brightness represents?

you might be interested in the Cargill book

Hm, for defining what an object is and doing it smart is for me, I guess, a little to early. I need to finish this (first) C++ project before I can have a look back and see if I could have done more smart. So I’ll see.
And, don’t go to deep into the purpose of the code. I have an object for a button and one for an optical incremental encoder. Additionally the encoder has a built-in button, but functional-wise it’s link to the encoder part is a little weak - so I use the button object. From naming it is just to remind myself that this is the built-in switch of a rotary encoder being used to control the brightness of a LED, or, at least, is part of the control of the LED … In larger projects the object would probably just the LED with its control and surroundings, however, there is just one(!) “surrounding”, so to create an object for this does’t make sense to me. So I keep the objects smaller …
I’ll have a look at the book - thanks for sharing!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.