pinToggle library - comments please

I would have put this in the Exhibition/Gallery section of the forum but it does not seem to be very active. If the mods think that it should be moved then please go ahead and move it.

Despite having used the Arduino for a while I have never really investigated the OOP ability of C++ beyond using libraries provided with the IDE and others available for installation.

With that in mind I set about creating a class and putting it in a library, but what should I create ? I decided that I would create a library that allowed an output pin to toggle between HIGH and LOW with the time in each state defined by the user. I hope that it goes without saying that the code is non blocking and that it uses millis() for timing.

Having created the basic class and library I could not resist adding functionality to it and the result is the following set of functions which includes the ability to toggle the output pin a user defined number of times and to set and get the state of the output pin within the context of the object. ie setting the state stops the object toggling unlike doing it with digitalWrite()

pinToggle functions
---------------------

pinToggle();
Call this to name and create the pinToggle object.  

init();
Set up pinMode(byte pin)
Parameters : pin the pin number assotiated with the object

startToggling(byte state, unsigned long lowPeriod, unsigned long highPeriod, unsigned int toggleCount = 0);
Start toggling the pin state
Parameters : state the initial state of the pin (HIGH or LOW)
 lowPeriod        the period in milliseconds that the pin should be LOW
 highPeriod the period in milliseconds that the pin should be HIGH
 toggleCount the number of times the pin should toggle before stopping.  Zero (default) to disable the count and toggle continuously

restartToggling();
Restart toggling the pin state with the original parameters

update();
Update the state of the pin if the current period has ended

setOutputState(byte state);
Set the output state for the pin and stop toggling
Parameters : state the state (HIGH or LOW) into which the pin should be put

getOutputState(); 
Get the current state of the output

stopToggling(byte state);
Stop toggling and go into the specified state
Parameters: state the state into which the pin should be put (HIGH or LOW)

resumeToggling();
Resume toggling from where it was stopped

getTogglingState();
Get toggling state (true or false)

getToggleCount();
Get the number of toggles remaining

The more functions that I added the easier it became and the more that I could see the advantages of OOP. What fun !

Here are the library files
pinToggle.h

/*
 pinToggle.h - control pin toggling
 Created by Bob Burton, 25/03/2018
*/

#ifndef pinToggle_h
#define pinToggle_h

#include "Arduino.h"

class pinToggle
{
  private:
    byte _pinNum;
    byte _startState;
    byte _startStartState;
    unsigned long _startLowPeriod;
    unsigned long _startHighPeriod;
    unsigned int _toggleCount;
    unsigned int _startToggleCount;
    boolean _started = false;
    unsigned long _periods[2];
    byte _periodIndex;
    unsigned long _startTime;
    unsigned long _currentTime;
    boolean _toggling = false;
    byte _currentState;
    boolean _counting;

  public:
    pinToggle();  //constructor
    init(byte pin); //set pinMode
    startToggling(byte state, unsigned long lowPeriod, unsigned long highPeriod, unsigned int toggleCount = 0);  //set initial state and periods
    restartToggling(); //restart toggling with original parameters
    update();  //check whether period has ended and change state if true
    setOutputState(byte state); //set the output state for the pin and stop toggling
    getOutputState(); //get the current state of the output
    stopToggling(byte state); //stop toggling and go into the given state
    resumeToggling(); //resume toggling
    getTogglingState(); //get toggling state
   getToggleCount(); //get the number of toggles remaining
};
#endif

pinToggle.cpp

/*
  pinToggle.cpp - control pin toggling
  Created by Bob Burton, 25/03/2018
*/

#include "Arduino.h"
#include "pinToggle.h"

pinToggle::pinToggle()  //constructor
{
}

pinToggle::init(byte pin)
{
 _pinNum = pin;
 pinMode(_pinNum, OUTPUT);
}

pinToggle::startToggling(byte startState, unsigned long lowPeriod, unsigned long highPeriod, unsigned int toggleCount = 0)  //set initial state, periods and count
{
 _startStartState = startState;
 _startLowPeriod = lowPeriod;
 _startHighPeriod = highPeriod;
 _startToggleCount = toggleCount;
  if (toggleCount == 0)
  {
  _counting = false;
  }
  else
  {
 _counting = true;
  }
  _toggleCount = toggleCount;
  _startState = startState;
  _periods[0] = lowPeriod;
  _periods[1] = highPeriod;
  if (_startState == LOW)
  {
    _periodIndex = 0;
  }
  else
  {
    _periodIndex = 1;
  }
  _currentState = _startState;
  digitalWrite(_pinNum, _currentState);
  _startTime = millis();
  _toggling = true;
  _started = true;
}

pinToggle::restartToggling() //restart toggling with original parameters
{
 if (_started)
 {
 startToggling(_startStartState, _startLowPeriod, _startHighPeriod, _startToggleCount);
 }
}
 
pinToggle::update()  //check whether period has ended and change state if true
{
  if (!_toggling)   //no need to update if not currently toggling
  {
    return;
  }
  _currentTime = millis();
  if (_currentTime - _startTime >= _periods[_periodIndex])
  {
      _currentState = !_currentState;
      digitalWrite(_pinNum, _currentState);
    _periodIndex++;
    _periodIndex %= 2;
    _startTime = _currentTime;
    if (_counting)
 {
  _toggleCount--;
  if (_toggleCount == 0)
  {
  _toggling = false;
  }
 }
  }
}

pinToggle::getToggleCount()
{
 return _toggleCount;
}

pinToggle::stopToggling(byte state)
{
 setOutputState(state);
}

pinToggle::setOutputState(byte state)
{
  _currentState = state;
  digitalWrite(_pinNum, _currentState);
  _toggling = false;
}

pinToggle::getOutputState()
{
 return _currentState;
}


pinToggle::resumeToggling()
{
  if (_started)
  {
    _toggling = true;
  }
}

pinToggle::getTogglingState()
{
  return _toggling;
}

A small sketch to test the majority of the functions

#include <pinToggle.h>

pinToggle output1;
pinToggle output2;
pinToggle output3;
pinToggle output4;

int previousToggleCount;
int currentToggleCount;

void setup()
{
  Serial.begin(115200);
  output1.init(13);
  output2.init(12);
  output3.init(11);
  output4.init(10);
  output1.startToggling(HIGH, 500, 100);
  output2.startToggling(HIGH, 500, 111);
  output3.startToggling(HIGH, 1000, 1000, 10);
  output4.setOutputState(LOW);
}

void loop()
{
  output1.update();
  output2.update();
  output3.update();
  output4.update();
  previousToggleCount = currentToggleCount;
  currentToggleCount = output3.getToggleCount();
  if (currentToggleCount != previousToggleCount)
  {
    Serial.print("Toggle count : ");
    Serial.println(output3.getToggleCount());
  }
  if (currentToggleCount == 0)
  {
    output3.restartToggling();
    output4.setOutputState(!output4.getOutputState());
  }
}

I would welcome any comments on the library both on how it is written and the functions provided so that I can improve it/fix it and possibly add to it.

Actually, that is a lie. What I really want is for everyone to say that it is perfect and cannot be improved on but I assume that is not the case !

Many thanks in advance

I would welcome any comments on the library both on how it is written and the functions provided so that I can improve it/fix it and possibly add to it.

Every class I've ever developed has a name that starts with a capital letter.

    unsigned long _startLowPeriod;
    unsigned long _startHighPeriod;

Seeing these, I went looking for the corresponding end variables. None to be seen...

So, I thought that maybe there was a reason to distinguish between the initial periods and the current periods. But, there is no way to change the periods, so I'm scratching my head about the meaning of start in the names.

    byte _startState;
    byte _startStartState;

There's no _startStartStartState...

I would really expect the constructor to take a pin number, and the on and off periods. I would not expect it to do anything with the data, except for storing it.

Why does the example sketch have 4 instances of the class, with only one variable to hold the current and previous count(s)? Someone will think that the current and previous variables can be used to hold all the current and previous counts.

Having an array of periods AND two scalar variables holding the same values is just asking for them to get out of sync.

Thanks Paul. I just knew that you would comment :slight_smile:

Every class I've ever developed has a name that starts with a capital letter.

I'm glad that you started with an easy one !

Seeing these, I went looking for the corresponding end variables. None to be seen...

There are no end variables, because they are not needed. The _start* variables hold the values as they were when toggling started and are used when restarting the toggling (as opposed to resuming it from where it was stopped)

There's no _startStartStartState...

I don't much like the names of the _start* variables either. Perhaps _initial would be better than _start

I would really expect the constructor to take a pin number, and the on and off periods. I would not expect it to do anything with the data, except for storing it.

That is how I originally wrote it but when experimenting with an array of pinToggle objects I decided to change it so that the pin number could be allocated in a for loop using the init() function. Perhaps I went too far by removing both the pin number and the periods from the constructor. Maybe pin number in the constructor and periods and count in the start() function, which is where I think that they belong so that toggling can be started anywhere in the program.

Someone will think that the current and previous variables can be used to hold all the current and previous counts.

Very true and sloppy variable naming on my part.

Having an array of periods AND two scalar variables holding the same values is just asking for them to get out of sync.

The array is only populated with values by the start() function and the scalar variables are not used in the library so there is little chance that they will get out of step. I could, of course, introduce a _currentPeriod variable and lose the array and index method of managing the current period at the expense of an if/else test when it needs to be changed.

Once again, thanks for the comments

I think it looks good, all that's missing is a read.me to suggest its usage. Oh, and perhaps some idea as to what the maximum toggle freq might be. Could it be used as a signal generator. Any thought about a specific attribute that would define a duty cycle? Pulsed toggles as in toggle me at w Hz for x seconds, rest for y seconds then repeat?

  public:
...
    init(byte pin); //set pinMode

In Arduino-land the traditional method name is "begin".

That is how I originally wrote it but when experimenting with an array of pinToggle objects I decided to change it so that the pin number could be allocated in a for loop using the init() function. Perhaps I went too far by removing both the pin number and the periods from the constructor. Maybe pin number in the constructor and periods and count in the start() function, which is where I think that they belong so that toggling can be started anywhere in the program.

If you change the constructor to take the pin number, this:

pinToggle output1;
pinToggle output2;
pinToggle output3;
pinToggle output4;

Could be done like this:

pinToggle outputs[] = { 13,12,11,10 };

Which I think is cleaner.
Using an array makes it easy to loop.

You could implement something like generating a bit pattern which selectable bit time, like pattern "1010011", with 500ms between bits, repeating 5 times, which I think would be useful.

Thanks for the feedback which I have taken on board

@DKWatson
I have created a readme describing possible uses for the library and incorporated the list of functions. I will consider your suggestion as to implementing pulsed toggling but I am conscious that it was meant to be a simple library and is in danger of getting out of hand.

@Coding Badly
I have changed the name from init to begin as suggested to bring it into line with other libraries. There is still a start() function to actually start the toggling. begin() would normally be in setup() and start() could be anywhere in the program. See below for an idea that I have to extend the functionality of start()

@giova014
The constructor has been changed to take the pin number as suggested. That was how I originally had it along with the periods and count but I changed it when experimenting with arrays of objects. When I changed the constructor to make using an array of objects easier I removed all parameters but should have left the pin number in place. With it back in place then creating an array of objects is easy and I may well create an example using it. I will put you suggestion regarding the bit pattern generator on the "maybe" list but as above I am worried about a simple library getting out of hand.

@PaulS
The library name has been changed to PinToggle, I have tidied up the internal workings to remove the duplicate variables holding the same value and a number of the variables have more sensible names. No more _startStartState and _startToggleCount. Now they are _originalStartState and _originalToggleCount

So, what next ?
I have in mind adding functionality to provide a "delay" period before toggling starts with a default of zero to keep it simple to use.

Any more suggestions before I finalise version 1 and post it here.

Even if this library never sees the light of day beyond here I am learning a lot writing it and would recommend doing it to anyone with an inquiring mind.

UKHeliBob:
I have in mind adding functionality to provide a "delay" period before toggling starts with a default of zero to keep it simple to use.

I have also seen "phase" or "offset" used to describe the concept.

I have also seen "phase" or "offset" used to describe the concept.

"offset" would seem to fit the bill. I don't really want to use the d word !

Good choice. Certainly understandable.

PinToggle

To me toggling implies state change regardless of present state.

Aren’t you generating pulses? Pulses of certain periods, width, quantity?

Fun with C++ classes, yay!

Thanks for looking at this

Aren't you generating pulses?

A pulse consists of two state changes, ie two toggles. The main library function causes the pin state to change state regularly, so the pin state is toggled, but I am not going to get bogged down in the semantics.

Who knew that English would be so important when creating a C++ class :slight_smile:

UKHeliBob:
I am not going to get bogged down in the semantics.

classes are largely semantic in nature as @PaulS comments point out. The member names may have tons of meaning to you when you write it, but look back on it in three months time and you will be scratching your head, thinking: "Who the heck wrote this"? It happens to everyone.

pinToggle::init(byte pin)

I like:

pinToggle::bindToPin(byte pin)

very explicit...

i agree with the previous post, your use of the array for timing periods is a bit wonky.

Interesting choice, not explicitly writing the return types of the functions:

  public:
    pinToggle();  //constructor
    init(byte pin); //set pinMode
    startToggling(byte state, unsigned long lowPeriod, unsigned long highPeriod, unsigned int toggleCount = 0);  //set initial state and periods
    restartToggling(); //restart toggling with original parameters
    update();  //check whether period has ended and change state if true
    setOutputState(byte state); //set the output state for the pin and stop toggling
    getOutputState(); //get the current state of the output
    stopToggling(byte state); //stop toggling and go into the given state
    resumeToggling(); //resume toggling
    getTogglingState(); //get toggling state
   getToggleCount(); //get the number of toggles remaining

UKHeliBob:
Any more suggestions before I finalise version 1 and post it here.

How about a callback function when the pin toggling is completed?

I understand that this is a learning exercise for you, you'll probably never use the class in any real application, but getting comments is extremely useful. Classes are written to make coding easier so getting opinions from colleagues will definitely help you class 'em up. Cheers to you for doing that....

Not at the computer right now to test things out.

Thank you for sharing.

Thanks for the comments which I will take on board.

pinToggle::bindToPin(byte pin)I like the name but I have already moved the binding to the pin back into the constructor where it was originally which makes creating an array of objects easy and the init() function has been renamed to begin() in line with most libraries, whilst startToggling() actually starts the toggling process.

The member names may have tons of meaning to you when you write it, but look back on it in three months time and you will be scratching your head, thinking: "Who the heck wrote this"? It happens to everyone.

I am sure that you are right. See you in 3 months !

Your comment about the lack of explicit return types is interesting because I did it deliberately, but looking back I can only imagine that I did it because the constructor does not have a return type. To quote from http://www.cplusplus.com/doc/tutorial/classes/

This constructor function is declared just like a regular member function, but with a name that matches the class name and without any return type; not even void.

Having written the constructor I can only imagine that I wrongly followed suit with the other functions. I will certainly be fixing it although it seems to cause no problem, or at least not one that I have noticed.

How about a callback function when the pin toggling is completed?

A good idea that I will look into.

Cheers to you for doing that....

Thanks for the encouragement. You and I have not always agreed in the past, usually when your proposed solutions to a problem, whilst technically correct, seemed (to me at least) to be unnecessarily confusing (ranged based for loops anyone ?). Having caught the class/library disease I will have to be careful in suggesting their use to solve simple problems, won't I :slight_smile: I have already created a small class to turn on a LED on or off for a period using millis(). Will I be suggesting using it the next time I see a "my program is full of delay()s dealing with LEDs and won't respond to button presses" query ? Only time will tell.

Did I miss this, is there a GitHub link?

larryd:
Did I miss this, is there a GitHub link?

No GitHub link (yet) as I am still refining it and adding to it.

I have already changed it since posting it on here and I will post a newer version over the weekend.

UKHeliBob:
Having caught the class/library disease I will have to be careful in suggesting their use to solve simple problems, won't I :slight_smile: I have already created a small class to turn on a LED on or off for a period using millis(). Will I be suggesting using it the next time I see a "my program is full of delay()s dealing with LEDs and won't respond to button presses" query ? Only time will tell.

I'd like to think that classes were developed to make programming easier!

I've been teaching a workshop for 6th, 7th and 8th graders using Arduino and Particle. Most recently I've switched from teaching variables and functions first to teaching classes first.

  • this is a class, an object, a container of things
  • the things that classes may contain are variables and/or functions
  • this is a variable...
  • this is a function...
  • this is what's meant by private and public
  • structs are classes
  • oh, by the way, variables and functions can exist in the wild and on their own...
  • variable scope

the kids got it right away, so I'm going to continue using this sequence for a while. When I introduced classes after variables, functions and scope... they struggled more.

When I introduced classes after variables, functions and scope... they struggled more.

Your experience is interesting.

I suspect that the problem with classes is that they look daunting and you have to know quite a lot before you can actually do anything. Most people just want to get on and do something, hence the "Hello World" and Blink programs. Then, of course, they continue along the same path adding complexity and by the time they ask for help they have invested time in their current flawed program and are often unwilling to throw it away and start again.

Perhaps part of the answer depends on the stage at which help is asked for. Someone asking for help on how to start a project may well be advised to use classes whereas somebody who has a working program with a problem might be helped further along their current path, albeit with advice to consider using classes.

UKHeliBob:
I suspect that the problem with classes is that they look daunting and you have to know quite a lot before you can actually do anything. Most people just want to get on and do something, hence the "Hello World" and Blink programs. Then, of course, they continue along the same path adding complexity and by the time they ask for help they have invested time in their current flawed program and are often unwilling to throw it away and start again.

Perhaps part of the answer depends on the stage at which help is asked for. Someone asking for help on how to start a project may well be advised to use classes whereas somebody who has a working program with a problem might be helped further along their current path, albeit with advice to consider using classes.

The advantage I have is that most of these kids haven't even started with anyone's particular opinion of what are the basics; they just know they want to do something fun or cool. No one told them that any particular part is supposed to be advanced, so they don't know that they are supposed to be struggling. :wink:

The benefit that I believe I see is that they start to think about objects from the very beginning. They are never experiencing a paradigm shift in that regard.

If they want to program a button switch, they design a class. If they want another button... problem already solved. The first lesson we go through is turning on and off led13 using a button switch.

It also makes the understanding of Servo, Serial, LCD and other common libraries much easier to use/understand (and less onerous to investigate) because they understand what the dot operator actually does.

Finally, it makes functional programming easier to understand, I'm seeing them create fewer and fewer functions that take no arguments and return no values.

Once you understand classes (much like pointers) they are incredibly easy. Overcoming that obstacle earlier so far seems to make it all that much easier and fun.

Side Note: As you mention, once you have a hammer in your toolbox (classes) every problem becomes a nail, so now I'm teaching lambdas as procedural functions called from only one place. I admit it was challenging for me to learn the lambda syntax, but just like classes, the kids don't yet know that they are supposed to be difficult!!!