implementation of simple class for raspberry pi pwm blink program (Embedded Programming)

Pages: 12
If'n you want to learn what C++ experts consider core guidelines for writing good C++ code you should read this:

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

The introduction explains what's going on.

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-introduction
I suggest that Windows programmers who want to catch Ctrl-C should strongly prefer to call SetConsoleCtrlHandler instead


If all you want to do is to terminate a loop with ctrl-c (or any other char) and continue, then you don't need to install a handler. You do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <conio.h>
#include <iostream>

int main() {
	SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0);

	while (!_kbhit() || _getch() != 3)
		_putch('a');

	std::cout << "\nctrl-c\n";
}

@George P:

Thank you so much for the encouragement, for the links provided (for core guidelines, and so on) and for the valuable suggestions offered including the one on using directives.

I plan to revert to these resources as I make progress.


@seeplus:

Wow, I'm speechless! Never knew this was possible : )

Thank you for sharing this.

I would keep you guys posted when other ideas and challenges come to mind while I continue work on this personal project.

Much regards.
Last edited on
What is the OS on your Raspberry Pi, if any? According to Wikipedia it can utilize quite a variety. The default is a *nix variation created for the board, with a zero-price version of Windows 10 IoT Core available for a Pi 2.

https://en.wikipedia.org/wiki/Raspberry_Pi#Operating_systems

Thank you for sharing this. By the way, the OS on my RPi is:

$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 11 (bullseye)"
NAME="Raspbian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"

A couple of things to note, coming from someone who is a still-learning self-taught programming hobbyist who doesn't use Linux.....

1. Using Windows headers (conio.h) and WinAPI functions in program code that runs on a *nix variation is fraught with "dragons be here" peril.

2. I suspect the RPi OS is some form of a stripped down *nix, there is probably some C/C++ functionality that doesn't exist.

Back to your regularly scheduled programming channel....
@George P;@newbieg;@mbozzi;@seeplus;All

Lol! Thanks for the lighthearted response! Apologies for not replying sooner.

I have tried to implement class and client interface for the code. The first version where I have:
1.) CGPIOCleanExit.h (class header with function prototypes and definition)
2.) CGPIOSetup.h (ditto)
3.) gpio_input_intern_toggle_01.cpp (client source code)

This first version above compiled and ran successfully with no errors. (Please see code below:)

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
27
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
56
57
58
// CGPIOCleanExit.h
#include <iostream>
#include <wiringPi.h>
#include <signal.h>

using std::cout;
using std::cin;
using std::endl;

typedef unsigned long int ULONG;
typedef unsigned short int USHORT;
typedef volatile sig_atomic_t VSIGATM;


class CGPIOCleanExit
{
    public:
        CGPIOCleanExit();                 // Constructor prototype
        ~CGPIOCleanExit();                // Destructor "
        bool getBlink ();                 // Member access fxn prototype
        void setBlink (int signal);       // " (aka public accessor method)
    private:
        // Member data:
        VSIGATM m_BlinkState;            // blink status/condition
};


CGPIOCleanExit::CGPIOCleanExit()
{
    cout << "CGPIOCleanExit constructor initialized...\n";
    m_BlinkState = 0;              // To properly initialize class member data
}

// Class destructor declaration:
CGPIOCleanExit::~CGPIOCleanExit()
{
    // destruction process no action other than its intended purpose
    cout << "CGPIOCleanExit destructor initialized...\n";
}

// Class public methods declaration
bool CGPIOCleanExit::getBlink()
{
    return m_BlinkState;
}

void CGPIOCleanExit::setBlink(int b)
{
    m_BlinkState = b;    // Set m_BlinkState, in this case, '0' reps 'False'

    // Clean up and reset wiringPin
    cout << "\nCommence clean up process..." << endl;
    digitalWrite(29, LOW);     // Reset pin to zero (0)
    pinMode(28, INPUT);        // Reset pin to INPUT (that is, IN)
    digitalWrite(28, LOW);     // ditto
    pinMode(28, INPUT);        // ditto
    cout << "Clean up process complete!" << endl;
}


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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// CGPIOSetup.h
// Class for GPIO:
class CGPIOSetup
{
    public:

        CGPIOSetup(int pnval);                 // Constructor prototype
        ~CGPIOSetup();                // Destructor "
        // Member access fxn prototype
        // " (aka public accessor method)
        ULONG getInputPin() const;       // A constant member function
        void setInputPin(int pinNumber);     // "
    private:
        // Member data:
        int m_PinNumber;                // pin number
};

// Class constructor declaration
// pnval = 29 corresponds to GPIO wiringPi pin #29)
// WARNING: Check GPIO pin is safely connected!
CGPIOSetup::CGPIOSetup(int pnval = 29)   // pnval = pinNumber value
{
    cout << "CGPIOSetup constructor initialized...\n";
    m_PinNumber = pnval;            // To properly intialize class member data
}

// Class destructor declaration:
CGPIOSetup::~CGPIOSetup()
{
    // destruction process no action other than its intended purpose
    cout << "CGPIOSetup destructor initialized...\n";
}

// Class public methods declaration
// getInputPin public accessor fxn defn:
ULONG CGPIOSetup::getInputPin() const
{
    return m_PinNumber;
}

// setOutputPin public accessor fxn defn:
void CGPIOSetup::setInputPin(int pinNumber)
{
    m_PinNumber = pinNumber;    // Initialize with value passed
}


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
27
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// gpio_input_intern_toggle_01.cpp
#include <iostream>
#include <wiringPi.h>
#include <signal.h>
#include "CGPIOCleanExit.h"    // Include header files
#include "CGPIOSetup.h"

using std::cout;
using std::cin;
using std::endl;

typedef unsigned long int ULONG;             // typedef defined (See 101.lv)
typedef unsigned short int USHORT;
typedef volatile sig_atomic_t VSIGATM;       // " (see newbieg (2022),
                                            // cplusplus.com)
// ****************************************** //

// Declare instance of global class object (Recall this is more secure
// than using regular global variable, although still in search for
// alternative that would not involve use of global entities however,
// will support future maintenance of program in future devs.):
CGPIOCleanExit Regular;


// Declare signal handling fxn:
void cKeyboardInterrupt(int signalNum)
{
  //CGPIOCleanExit Regular;
    Regular.setBlink(1);    // Access class member function through object
                            // and set blinkState, accordingly
}


// Main program begins:
int main(void)
{
    signal(SIGINT, cKeyboardInterrupt);    // Call signal() fxn to capture
                                           // keyboard interrupt

    wiringPiSetup();    // Call wiringPi library and numbering system
    //CGPIOCleanExit Regular;
    //Regular.setBlink(0);
    //cout << Regular.getBlink() << endl;


    const int totalPins {2};    // Total number of pins. NOTE:
                              // without modifier const, that compiler will
                             // issue an error while trying to initialize
                             // array in the next line

    // CGPIOSetup Pins[2];
    CGPIOSetup Pins[totalPins - 1];       // Declare array of size =
                                          // number of elements - 1
    Pins[0].setInputPin(29);        // Initialize element of array
    Pins[1].setInputPin(28);        //  with appropriate pin number

    int pin1 = Pins[0].getInputPin();    // Define and initialize pin
    int pin2 = Pins[1].getInputPin();    // with appropriate pin number

    pinMode(pin1,INPUT);    // Set GPIO pin mode
    pinMode(pin2,OUTPUT);

    pullUpDnControl(pin1, PUD_UP);    // Set GPIO pin to pull-up resistor
                                        // (See wiringpi.com for more details)
    // Initialize variable to store measured value:
    // NOTE: 'readValue' in previous code has been replaced
    // by 'buttonState':
    USHORT buttonState = 1, buttonStateOld = 1, LEDState = 0;

    // Initialize while loop and call class member function
    // which gets blinkState value/condition (which acts as counter)
    while(!Regular.getBlink())
    {
        buttonState = digitalRead(pin1);    // Read value from pin specified
        cout << buttonState << endl;      // Print read value to screen
        // If statments:
	// Check previous and current state:
        if ((buttonState == 1) && (buttonStateOld == 0))
        {
	    if (LEDState == 0)         // Recall, relation operators
	    {
	        LEDState = 1;    // return 1 (true) or 0 (false)
		// The next line is another place where the magic happens!:
	        // See where 'LEDState' from previous line is passed as
	        // an argument:
                digitalWrite(pin2,LEDState);       // Turn ON/OFF depending on
	                                           // 'LEDState'
	    }
	    else
	    {
	        LEDState = 0;
		digitalWrite(pin2,LEDState);
	    }
	    
        }

	buttonStateOld = buttonState;      // Update previous state of
	                                   // button
        delay(100);                // sleep for 100ms (0.1 second)
    }

    return 0;
}


(cont'd...)
(cont'd..)
B.) However, when I try to implement the class by separating the interfaces like this:

1.) CGPIOCleanExit.h (class header with function prototypes
2.) CGPIOCleanExit.cpp (class definition source code)
3.) CGPIOSetup.h (ditto)
4.) CGPIOSetup.cpp (ditto)
5.) gpio_input_intern_toggle_01.cpp (client source code)

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
// CGPIOCleanExit.h
#include <iostream>
#include <wiringPi.h>
#include <signal.h>

using std::cout;
using std::cin;
using std::endl;

typedef unsigned long int ULONG;
typedef unsigned short int USHORT;
typedef volatile sig_atomic_t VSIGATM;


class CGPIOCleanExit
{
    public:
        CGPIOCleanExit();                 // Constructor prototype
        ~CGPIOCleanExit();                // Destructor "
        bool getBlink ();                 // Member access fxn prototype
        void setBlink (int signal);       // " (aka public accessor method)
    private:
        // Member data:
        VSIGATM m_BlinkState;            // blink status/condition
};


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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// CGPIOCleanExit.cpp
#include <iostream>
#include <wiringPi.h>
#include <signal.h>
#include "CGPIOCleanExit.h"

using std::cout;
using std::cin;
using std::endl;

typedef unsigned long int ULONG;
typedef unsigned short int USHORT;
typedef volatile sig_atomic_t VSIGATM;


CGPIOCleanExit::CGPIOCleanExit()
{
    cout << "CGPIOCleanExit constructor initialized...\n";
    m_BlinkState = 0;              // To properly initialize class member data
}

// Class destructor declaration:
CGPIOCleanExit::~CGPIOCleanExit()
{
    // destruction process no action other than its intended purpose
    cout << "CGPIOCleanExit destructor initialized...\n";
}

// Class public methods declaration
bool CGPIOCleanExit::getBlink()
{
    return m_BlinkState;
}

void CGPIOCleanExit::setBlink(int b)
{
    m_BlinkState = b;    // Set m_BlinkState, in this case, '0' reps 'False'

    // Clean up and reset wiringPin
    cout << "\nCommence clean up process..." << endl;
    digitalWrite(29, LOW);     // Reset pin to zero (0)
    pinMode(28, INPUT);        // Reset pin to INPUT (that is, IN)
    digitalWrite(28, LOW);     // ditto
    pinMode(28, INPUT);        // ditto
    cout << "Clean up process complete!" << endl;
}


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
27
28
29
// CGPIOSetup.h
#include <iostream>
#include <wiringPi.h>
#include <signal.h>

using std::cout;
using std::cin;
using std::endl;

typedef unsigned long int ULONG;
typedef unsigned short int USHORT;
typedef volatile sig_atomic_t VSIGATM;


// Class for GPIO:
class CGPIOSetup
{
    public:

        CGPIOSetup(int pnval);                 // Constructor prototype
        ~CGPIOSetup();                // Destructor "
        // Member access fxn prototype
        // " (aka public accessor method)
        ULONG getInputPin() const;       // A constant member function
        void setInputPin(int pinNumber);     // "
    private:
        // Member data:
        int m_PinNumber;                // pin number
};


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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// CGPIOSetup.cpp
#include <iostream>
#include <wiringPi.h>
#include <signal.h>
#include "CGPIOSetup.h"

using std::cout;
using std::cin;
using std::endl;

typedef unsigned long int ULONG;
typedef unsigned short int USHORT;
typedef volatile sig_atomic_t VSIGATM;


// Class constructor declaration
// pnval = 29 corresponds to GPIO wiringPi pin #29)
// WARNING: Check GPIO pin is safely connected!
CGPIOSetup::CGPIOSetup(int pnval = 29)   // pnval = pinNumber value
{
    cout << "CGPIOSetup constructor initialized...\n";
    m_PinNumber = pnval;            // To properly intialize class member data
}

// Class destructor declaration:
CGPIOSetup::~CGPIOSetup()
{
    // destruction process no action other than its intended purpose
    cout << "CGPIOSetup destructor initialized...\n";
}

// Class public methods declaration
// getInputPin public accessor fxn defn:
ULONG CGPIOSetup::getInputPin() const
{
    return m_PinNumber;
}

// setOutputPin public accessor fxn defn:
void CGPIOSetup::setInputPin(int pinNumber)
{
    m_PinNumber = pinNumber;    // Initialize with value passed
}


 
// gpio_input_intern_toggle_01.cpp (ditto...) 


However, when I try to compile the code, here is the error message that I get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ g++ -Wall -c gpio_input_intern_toggle_01.cpp -lwiringPi
gpio_input_intern_toggle_01.cpp: In function ‘int main()’:
gpio_input_intern_toggle_01.cpp:73:34: error: no matching function for call to ‘CGPIOSetup::CGPIOSetup()’
   73 |     CGPIOSetup Pins[totalPins - 1];       // Declare array of size =
      |                                  ^
In file included from gpio_input_intern_toggle_01.cpp:27:
CGPIOSetup.h:43:9: note: candidate: ‘CGPIOSetup::CGPIOSetup(int)’
   43 |         CGPIOSetup(int pnval);                 // Constructor prototype
      |         ^~~~~~~~~~
CGPIOSetup.h:43:9: note:   candidate expects 1 argument, 0 provided
CGPIOSetup.h:39:7: note: candidate: ‘constexpr CGPIOSetup::CGPIOSetup(const CGPIOSetup&)’
   39 | class CGPIOSetup
      |       ^~~~~~~~~~
CGPIOSetup.h:39:7: note:   candidate expects 1 argument, 0 provided


I'm wondering, does it mean I need to write a separate constructor for an array type? I have also been trying to figure this out since last time. Please do you guys have any suggestions? Thank you!

The default value for a function goes in the declaration and not the definition if a function is declared and defined separately. So

L20
CGPIOSetup(int pnval); // Constructor prototype

becomes
CGPIOSetup(int pnval = 29); // Constructor prototype

L19
CGPIOSetup::CGPIOSetup(int pnval = 29) // pnval = pinNumber value

becomes
CGPIOSetup::CGPIOSetup(int pnval) // pnval = pinNumber va
@seeplus:

Thank you! Going forward, should I just stick to declaring default value in the function prototype declaration as opposed to the definition?

Please could you also post the link to the documentation so I could read up more on this subject? That way, I might be able to decode the error messages better next time especially since it's suggestion aren't as intuitive as one might think.

Plus, I got another error saying undefined reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ g++ -Wall -o gpio_input_intern_toggle_01 gpio_input_intern_toggle_01.cpp -lwiringPi
/usr/bin/ld: /tmp/ccMbRMPx.o: in function `cKeyboardInterrupt(int)':
gpio_input_intern_toggle_01.cpp:(.text+0x18): undefined reference to `CGPIOCleanExit::setBlink(int)'
/usr/bin/ld: /tmp/ccMbRMPx.o: in function `main':
gpio_input_intern_toggle_01.cpp:(.text+0x6c): undefined reference to `CGPIOSetup::CGPIOSetup(int)'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0x88): undefined reference to `CGPIOSetup::setInputPin(int)'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0x9c): undefined reference to `CGPIOSetup::setInputPin(int)'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0xa8): undefined reference to `CGPIOSetup::getInputPin() const'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0xc0): undefined reference to `CGPIOSetup::getInputPin() const'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0x10c): undefined reference to `CGPIOCleanExit::getBlink()'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0x1e0): undefined reference to `CGPIOSetup::~CGPIOSetup()'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0x220): undefined reference to `CGPIOSetup::~CGPIOSetup()'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0x248): undefined reference to `CGPIOSetup::~CGPIOSetup()'
/usr/bin/ld: /tmp/ccMbRMPx.o: in function `__static_initialization_and_destruction_0(int, int)':
gpio_input_intern_toggle_01.cpp:(.text+0x2bc): undefined reference to `CGPIOCleanExit::CGPIOCleanExit()'
/usr/bin/ld: gpio_input_intern_toggle_01.cpp:(.text+0x2f0): undefined reference to `CGPIOCleanExit::~CGPIOCleanExit()'
collect2: error: ld returned 1 exit status 


Thank you!
Where do you compile
CGPIOCleanExit.cpp
CGPIOSetup.cpp

These now also need to be compiled - along with gpio_input_intern_toggle_01.cpp
@seeplus:

Thank you for the links.

Also, I'll run the commands for CGPIOCleanExit.cpp CGPIOSetup.cpp as well. Thank you!

I'm thinking I could as well create a make file for this purpose, right?
@seeplus:

Source code compiled and object code created and ran successfully.
 
$ g++ -Wall -o gpio_input_intern_toggle_01 CGPIOCleanExit.cpp CGPIOSetup.cpp gpio_input_intern_toggle_01.cpp -lwiringPi


Thank you!
I'm thinking I could as well create a make file for this purpose, right?


Yes.
@seeplus: Thanks! :)
Topic archived. No new replies allowed.
Pages: 12