SmallButton library

Hi,
Want to share library I've created for buttons. I've checked a few existing libraries, but didn't find one with support almost use cases (for example repeat) and at the same time with small memory footprint. So I decided to implement my own:

It requires only 4 bytes per button, and supports single press, repeat, release, check state and other combinations.
Feel free to provide your feedback, ask questions.

p.s. I didn't create any examples, because thought they are too trivial, like:
TinyButton button(A0);
if(button.get() & TinyButton::PRESS) Serial.println("Button pressed");

Thanks for sharing,

You can register your library in the Arduino library manager,
Need some extra files, not too complex, and make a release.

see link for details

I think it is still worth creating examples, even if the API is obvious to you, it may not be to other people.

1 Like

Thanks for sharing

A piece of feedback:

Using pinMode() in the constructor

is not robust for all architectures if you define (as would almost everyone) your button statically as a global instance. The reason is that the constructor is called very early in the process, before main() is called and thus before the Arduino supplied init() function is called. So if you set the pin as INPUT_PULLUP for example this might be undone later on when main() and init() are called.

This was a bug for example in the OneButton library that appeared on a few recent platforms (see this issue)

That’s why you’ll see lots of libraries having a begin() or init() function that you call from your setup() function and you’ll handle the pinMode in there.


Also your debounce test is an issue upon your 16 bit variable rollover

You need to write

if(currentTime - m_lastTime < TINY_BUTTON_FILTER_TIME_MS) …
1 Like

Thanks for pointing that, I forgot to add casting to uint16_t of subtraction result. But the concept is correct, in your version it won't work on overrun case. I've created test here:

Well, for me it looks like a bug in the Arduino initialization code, but maybe there are some compelling arguments.. My goal was to avoid excessive operations as much as possible. C++ constructor was intended to init object, and reduce programmer mistakes, like forget to call separate init function.
Anyway, I'll add begin() method, but I think to keep it call from the constructor, what do you say?

Since constructors are called in an undefined order, it's a general problem for any C++ code. The constructor should only initialize it's own members, and not make any calls outside the class.

If you don't want to rely on the user, a typical way to do is to set a flag when initialized and call init() if not set.

You have to be careful there, you might get different results depending on the compiler and architecture flags.

Why not run the test code on Arduino?

I'm using standard c++ data type (uint16_t) - it must be processed the same way on all platforms.
Regarding tests on Arduino - it can be added, I just have a concern that it is common practice for Arduino libraries to delivery tests within it.

Added

(post in error)

I've prepared file structure, but realized that there is already registered TinyButton library. Need to invent new name :slight_smile: And as far as I understand, it is better to rename files and class also, to prevent name collision.

prevent name collision

True,
I add _RT (my initials) to the name to solve it.

I've renamed this library to SmallButton, and contributed to Arduino registry.

Have you fixed the potential problem with calling pinMode() in the constructor ?

I've added begin() method which can be called from setup(), so I think I'm following the Arduino guidelines.
But I kept begin() call from the constructor, which should not break anything. Am I wrong?

That’s the way to go.

It’s not a bug, it’s really a consequence of the C++ standard that you need to honor