<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Arduino on Schallbert's Blog</title><link>https://blog.schallbert.de/en/tags/arduino/</link><description>Recent content in Arduino on Schallbert's Blog</description><generator>Hugo</generator><language>en</language><atom:link href="https://blog.schallbert.de/en/tags/arduino/index.xml" rel="self" type="application/rss+xml"/><item><title>🎄 Breadboard Fireplace</title><link>https://blog.schallbert.de/en/projects/fireplace/</link><pubDate>Mon, 01 Jan 0001</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/en/projects/fireplace/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/fireplace/fireplace-thumb.jpg"&#10; class="post-cover"&#10; alt="LED-Fireplace, Arduino-controlled"&#10; title="🎄 Breadboard Fireplace" /&gt;&#10;&lt;h2 id="project-stats"&gt;Project stats&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Difficulty: beginner 1/5&lt;/li&gt;&#10;&lt;li&gt;Cost: 8&amp;hellip;25€&lt;/li&gt;&#10;&lt;li&gt;Time: ~1h&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="abstract"&gt;Abstract&lt;/h2&gt;&#10;&lt;p&gt;This is a quick and easy project that demonstrates how to use an embedded device and some LEDs to create a warm and comfortable living room environment. Tied to a powerbank, this little thing will glow all evening and create a warm and comfy atmosphere. I borrowed parts of the idea for this project from a book called &amp;ldquo;Tiny AVR for the evil Genius&amp;rdquo; which I read when I started with embedded software design in &lt;code&gt;C&lt;/code&gt; years ago.&lt;/p&gt;&#10;&lt;p&gt;Project cost varies on the components you choose. If you take an original Arduino, it will be on the upper end - but in this case you pay tribute to the hard-working developers and engineers at &lt;a href="https://www.arduino.cc/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Arduino&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, creating open source solutions for all of us 🥳&lt;/p&gt;&#10;&lt;h2 id="the-hardware"&gt;The Hardware&lt;/h2&gt;&#10;&lt;p&gt;Of course, you can use any microcontroller board that supports at least 8 digital outputs and 1 analog input. In my example I&amp;rsquo;m using an &lt;a href="https://www.arduino.cc/hardware#nano-family" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Arduino Nano&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; replica board I had flying around somewhere. Time to upgrade to the &amp;ldquo;original&amp;rdquo;&amp;hellip;&#10;You should have a small breadboard, a couple of wires and eight LEDs at the ready. I recommend using 5 warm-white LEDs, 2 yellow LEDs, and 1 red LED with similar brightness (lumen count) to yield colors that are similar to a real fire.&lt;/p&gt;&#10;&lt;h2 id="the-software"&gt;The Software&lt;/h2&gt;&#10;&lt;p&gt;The software creates a flickering lights effect with varying update speed.&lt;/p&gt;&#10;&lt;h3 id="setup"&gt;Setup&lt;/h3&gt;&#10;&lt;p&gt;On startup, in &lt;code&gt;setup()&lt;/code&gt; I define pins the LEDs are connected to as outputs.&#10;I make it read an open analog pin once and takes its (undefined) value as seed for the pseudo random number generator discussed later.&lt;/p&gt;&#10;&lt;h3 id="loop"&gt;Loop&lt;/h3&gt;&#10;&lt;p&gt;In the infinite loop, the program calculates a new random number and then hands this number over to the downstream functions. Here, a random number generator creates value which is then used to&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;determine which of the eight LEDs should be on&lt;/li&gt;&#10;&lt;li&gt;set the time for which this LED status shall be maintained.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Finally, the LEDs are set accordingly and the loop repeats at the beginning.&lt;/p&gt;&#10;&lt;h3 id="linear-feedback-shift-register"&gt;Linear Feedback Shift Register&lt;/h3&gt;&#10;&lt;p&gt;The Random Number Generator is implemented as a so-called &lt;a href="https://en.wikipedia.org/wiki/Linear-feedback_shift_register" target="_blank" rel="noopener noreferrer" class="external-link"&gt;linear feedback shift register (LSFR)&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; with a length of 32bit (repeats its pattern after max. 2^32 steps). This implementation uses the &amp;ldquo;Galois type&amp;rdquo; of LFSRs.&lt;/p&gt;&#10;&lt;h3 id="galois-lfsr-implementation"&gt;Galois LFSR implementation&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;loop&lt;/span&gt;() &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iRandNum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (iRandNum &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;^&lt;/span&gt; (&lt;span style="color:#f92672"&gt;-&lt;/span&gt;(iRandNum &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1u&lt;/span&gt;) &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0xd0000001u&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [...]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s a lot going on in this line of code, so please let me explain it down to the core.&lt;/p&gt;&#10;&lt;p&gt;Essential to this sort of random number generators is the &lt;a href="https://en.wikipedia.org/wiki/XOR_gate" target="_blank" rel="noopener noreferrer" class="external-link"&gt;&lt;code&gt;XOR&lt;/code&gt; operation&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; indicated by a &lt;code&gt;^&lt;/code&gt; in the center of the code line. &lt;code&gt;XOR&lt;/code&gt; means &amp;ldquo;eXclusive OR&amp;rdquo;, which outputs logic &lt;code&gt;1&lt;/code&gt; only when logic inputs &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; are &lt;em&gt;different&lt;/em&gt;. When &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; are both &lt;code&gt;0&lt;/code&gt;, or both &lt;code&gt;1&lt;/code&gt;, this gate will output &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Example binary XOR:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt; A B Out&#10;0b00001111 ^ 0b00110011 = 00111100&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Left to the &lt;code&gt;XOR&lt;/code&gt; operator, the current randum number is shifted to the right (&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;) by &lt;code&gt;1&lt;/code&gt;. All of its contents is made less significant by one bit. In numerical terms it means that a value is integer-divided by two.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Example binary SHIFT:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt; A B Out numerical:&#10;0b00001111 &amp;gt;&amp;gt; 1 = 0b00000111 15 &amp;gt;&amp;gt; 1 = 7&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;OK, let&amp;rsquo;s further analyze the above code line.&#10;Right to the &lt;code&gt;XOR&lt;/code&gt; operator, there&amp;rsquo;s another operation taking place: A logic &lt;code&gt;AND&lt;/code&gt; (&lt;code&gt;&amp;amp;&lt;/code&gt;) between our current random number and &lt;code&gt;1u&lt;/code&gt; which means &amp;ldquo;unsigned 1&amp;rdquo;. What it does is masking all higher bits of the randum number generator, the result of this action is a simple &lt;code&gt;0&lt;/code&gt; in case the number has a &lt;code&gt;0&lt;/code&gt; as its least significant bit and vice versa.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Example binary AND:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt; A B Out &#10;0b00001111 &amp;amp; 1 = 0b00000001&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The negation at the beginning of this term means that, when the result of the before &lt;code&gt;&amp;amp;&lt;/code&gt; was &lt;code&gt;0&lt;/code&gt;, the whole term will be &lt;code&gt;0b00000000...&lt;/code&gt; again. If it&amp;rsquo;s &lt;code&gt;-1&lt;/code&gt;, the 2&amp;rsquo;s complement binary code will instead look like this: &lt;code&gt;0b1111111...&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;This is an easy way to apply a single boolean value to any longer data type without branching logic like using an &lt;code&gt;if()&lt;/code&gt; clause.&lt;/p&gt;&#10;&lt;p&gt;And finally, there&amp;rsquo;s another logic &lt;code&gt;AND&lt;/code&gt; between this term and a fixed bitmask &lt;code&gt;0xd0000001u&lt;/code&gt;. The latter are the &amp;ldquo;taps&amp;rdquo; of the LFSR, i.e. which bits of the current value are fed back into the system. Unravelling this hex value to boolean shows: &lt;code&gt;0xd = 0b1101&lt;/code&gt; so the bits 32, 31, 29, and 1 are &lt;code&gt;&amp;amp;&lt;/code&gt;&amp;lsquo;ed with the current random number, leaving only their values untouched while all others are set to &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="galois-lfsr-summary"&gt;Galois LFSR Summary&lt;/h3&gt;&#10;&lt;p&gt;Whew, that was a lot. It&amp;rsquo;s time for a couple of minutes break. Then please read the summary below for the beloved code line&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;iRandNum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (iRandNum &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;^&lt;/span&gt; (&lt;span style="color:#f92672"&gt;-&lt;/span&gt;(iRandNum &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1u&lt;/span&gt;) &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0xd0000001u&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Meaning: &amp;ldquo;If the least significant bit of the current random number is zero, then just shift the random number to the right. Else, in addition XOR with the tapped bits of the current random number.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;LFSRs are widely used in &lt;a href="https://courses.cs.washington.edu/courses/cse466/12au/calendar/06-Modulation-posted.pdf" target="_blank" rel="noopener noreferrer" class="external-link"&gt;wireless communication technology&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, they are interesting for &lt;a href="https://stackoverflow.com/questions/25415724/understanding-two-different-ways-of-implementing-crc-generation-with-lfsr" target="_blank" rel="noopener noreferrer" class="external-link"&gt;checksum calculations&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href="https://medium.com/@czapfel/an-introduction-to-lfsrs-for-cryptography-bf2602640e91" target="_blank" rel="noopener noreferrer" class="external-link"&gt;cryptographers&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; and can even be useful when &lt;a href="https://evoniuk.github.io/posts/pitfall.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;programming games&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, e.g. for procedural map generation. LFSRs are easy to implement (as you can see they even may only use a single line fo code!) and don&amp;rsquo;t use many resources. Here&amp;rsquo;s an excellent &lt;a href="https://datagenetics.com/blog/november12017/index.html" target="_blank" rel="noopener noreferrer" class="external-link"&gt;blog post&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; dealing with LFSRs in depth if you&amp;rsquo;re more the visual type (and less the logic one).&#10;On the down side, their &amp;ldquo;quality of randomness&amp;rdquo; is bad because they are deterministic.&#10;All in all I found it a very interesting topic which is why I decided to manually implement an LFSR here instead of using Arduino&amp;rsquo;s stock &lt;code&gt;random()&lt;/code&gt; function.&lt;/p&gt;&#10;&lt;figure class="media-frame media-frame--center"&gt;&#10; &lt;img src="https://www.c64-wiki.de/images/9/9d/Pitfall_Animation2.gif" alt="Pitfall! Game scene"&gt;&lt;figcaption class="media-caption"&gt;&#10; &lt;span class="caption-text"&gt;Pitfall! Game scene: procedural level design with LFSR&lt;/span&gt;&lt;a&#10; href="https://www.c64-wiki.de/wiki/Datei:Pitfall_Animation2.gif#file"&#10; class="attr-link"&#10; aria-label="Attribution 1"&#10; &gt;&#10; &lt;sup class="attr-id"&gt;[1]&lt;/sup&gt;&#10; &lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h3 id="possible-issue"&gt;Possible issue&lt;/h3&gt;&#10;&lt;p&gt;Well, if you fully understood the code line, you see what happens if the random number contains only &lt;code&gt;0&lt;/code&gt;s.&lt;/p&gt;&#10;&lt;p&gt;It will never get out of this state again 💀&lt;/p&gt;&#10;&lt;p&gt;That&amp;rsquo;s why you should never initialize this type of LFSR with &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="driving-the-leds"&gt;Driving the LEDs&lt;/h3&gt;&#10;&lt;p&gt;The rest of the software has much more lines, but also does much more boring stuff. It takes the generated 32-bit random value and runs it past the 8 LED outputs, i.e. every LED &amp;ldquo;sees&amp;rdquo; each bit of the value. Between each of these iterations, a randomized delay is used so the flickering speed is limited to a change rate we still can perceive.&lt;/p&gt;&#10;&lt;h2 id="the-hardware-1"&gt;The Hardware&lt;/h2&gt;&#10;&lt;p&gt;&lt;figure class="media-frame media-frame--center"&gt;&#10; &lt;img src="https://blog.schallbert.de/assets/images/fireplace/fireplace_detail.jpg" alt="fireplace breadboard design detail view"&gt;&lt;/figure&gt;&#10;Thankfully, the hardware is more easy to explain than the software:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;An Arduino Nano board is connected to a prototyping board.&lt;/li&gt;&#10;&lt;li&gt;Eight LED&amp;rsquo;s anodes &lt;code&gt;(+)&lt;/code&gt; are connected to the digital outputs &lt;code&gt;D2...D9&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Their cathodes are bridged with jumpers and connected to the Arduino&amp;rsquo;s &lt;code&gt;GND&lt;/code&gt;.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;This is the simplest design I could think of to do the job.&lt;/p&gt;&#10;&lt;h2 id="improvements"&gt;Improvements&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Each LED should get a series resistor.&#10;&lt;ul&gt;&#10;&lt;li&gt;In my design, the current is only limited by the maximum current the Arduino Pin is able to drive.&lt;/li&gt;&#10;&lt;li&gt;Either the LED can handle well above &lt;code&gt;40mA&lt;/code&gt; peak current or it will inevitably break down at some point in time.&lt;/li&gt;&#10;&lt;li&gt;Resistor calculation should happen with the LED datasheet in mind as different LED colours have different voltages.&lt;/li&gt;&#10;&lt;li&gt;Example resistor calculation &lt;code&gt;@5V&lt;/code&gt; supply: Warm-White LED: &lt;code&gt;20mA @ 3.1V --&amp;gt; R = U/I = (Usup-Uled) / I = 1.9 / 0.02 = 95Ohm&lt;/code&gt;. Take &lt;code&gt;100Ohm&lt;/code&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;Each LED could get a parallel capacitor.&#10;&lt;ul&gt;&#10;&lt;li&gt;This would smooth the flickering effect.&lt;/li&gt;&#10;&lt;li&gt;I could take videos in which the camera wasn&amp;rsquo;t irritated by LED flickering. That would be nice.&lt;/li&gt;&#10;&lt;li&gt;Example Capacitor calculation: Average current when switched &amp;ldquo;off&amp;rdquo; &lt;code&gt;20mA&lt;/code&gt;, allowed voltage drop &lt;code&gt;0.6V&lt;/code&gt; within &lt;code&gt;1ms&lt;/code&gt;. Proposed Capacitance is then &lt;code&gt;20mA * 1ms / 0.6V = 33uF&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="you-want-a-diy-replica"&gt;You want a DIY replica?&lt;/h2&gt;&#10;&lt;p&gt;Here you go!&#10;&lt;a href="https://blog.schallbert.de/en/led-fireplace-diy/"&gt;Breadboard Fireplace DIY instructions&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>ClickEncoder</title><link>https://blog.schallbert.de/en/projects/encoder/</link><pubDate>Mon, 01 Jan 0001</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/en/projects/encoder/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/encoder/encoder-thumb.jpg"&#10; class="post-cover"&#10; alt="Encoder on Breadboard"&#10; title="ClickEncoder" /&gt;&#10;&lt;h2 id="project-stats"&gt;Project stats&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Difficulty: medium 3/5&lt;/li&gt;&#10;&lt;li&gt;Cost: 0€&lt;/li&gt;&#10;&lt;li&gt;Time: ~6h&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="abstract"&gt;Abstract&lt;/h2&gt;&#10;&lt;p&gt;&lt;figure class="media-frame media-frame--right"&gt;&#10; &lt;img src="https://raw.githubusercontent.com/isocpp/logos/64ef037049f87ac74875dbe72695e59118b52186/cpp_logo.svg" alt="C&amp;#43;&amp;#43;"&gt;&lt;/figure&gt;Library for the &lt;a href="https://www.arduino.cc/en/Reference/Libraries" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Arduino Framework&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, written in c++.&lt;/p&gt;&#10;&lt;p&gt;It is lightweight because the source code itself is a few hundred lines only, and powerful, because of the improved algorithm uses less calculations while providing a better state interpretation than the original project this is forked from. It is officially released on &lt;a href="https://platformio.org/lib/show/11808/ClickEncoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PlatformIO IDE&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; and hosted on &lt;a href="https://github.com/Schallbert/encoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;. It contains examples both for Arduino and PlatformIO IDEs and provides unittests that can be run upon customization.&lt;/p&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;I used the original library published by &lt;a href="https://github.com/0xPIT/encoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;0xPIT&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; at first but that one caused some compiler warnings for me, plus the encoder readings were not flawless and sometimes there was jitter or bouncing I didn&amp;rsquo;t want to happen.&lt;/p&gt;&#10;&lt;h2 id="solution"&gt;Solution&lt;/h2&gt;&#10;&lt;p&gt;As the repo looked unmaintained, I decided to fix these issues by rewriting some code. For my project at that time, I also needed a &amp;ldquo;repeat&amp;rdquo; signal when a button is pressed continuously, so I added this, too. And finally, I separated the encoder algorithm from the button handling and provided separate classes so that, if needed, any combination of button and encoder could be used, each with configurable behavior.&lt;/p&gt;&#10;&lt;h2 id="encoder-algorithm-explained"&gt;Encoder Algorithm explained&lt;/h2&gt;&#10;&lt;h3 id="api"&gt;API&lt;/h3&gt;&#10;&lt;p&gt;ClickEncoder&amp;rsquo;s API is simple and easy to understand: &lt;code&gt;service()&lt;/code&gt; runs the business logic and does the state interpretation. It has to be run regularly to check if the encoder has been moved since its last call, into which direction, how many steps, and has these values saved in an accumulator variable.&#10;&lt;code&gt;getIncrement()&lt;/code&gt; returns the encoder step changes since its last call. &lt;code&gt;getAccumulate()&lt;/code&gt; returns the sum of steps taken since startup. &lt;code&gt;getButton()&lt;/code&gt; returns the current Button state.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;service&lt;/span&gt;();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getIncrement&lt;/span&gt;();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getAccumulate&lt;/span&gt;();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Button&lt;span style="color:#f92672"&gt;::&lt;/span&gt;eButtonStates getButton();&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="encoder-state"&gt;Encoder state&lt;/h3&gt;&#10;&lt;p&gt;The encoder uses a &lt;a href="https://en.wikipedia.org/wiki/Gray_code" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Gray Code&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; to encode the steps. &lt;code&gt;getBitCode()&lt;/code&gt; converts this code transmitted via the Encoder&amp;rsquo;s hardware pins A and B to a &lt;code&gt;0...3&lt;/code&gt; notch interpretation, numbers increasing when turning right, and decreasing when turning left. This is done efficiently without use of &lt;code&gt;if()&lt;/code&gt; or other branching logic.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;getBitCode()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// GrayCode convert&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// !A &amp;amp;&amp;amp; !B --&amp;gt; 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// !A &amp;amp;&amp;amp; B --&amp;gt; 1&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// A &amp;amp;&amp;amp; B --&amp;gt; 2&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// A &amp;amp;&amp;amp; !B --&amp;gt; 3&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; currentEncoderRead &lt;span style="color:#f92672"&gt;=&lt;/span&gt; digitalRead(pinA);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentEncoderRead &lt;span style="color:#f92672"&gt;|=&lt;/span&gt; (currentEncoderRead &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// invert result&amp;#39;s 0th bit if set&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentEncoderRead &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; digitalRead(pinB);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; currentEncoderRead;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I used a trick here, so you may have to look at the code snippet above several times. The state of &lt;code&gt;PinA&lt;/code&gt; is written to bit1 of &lt;code&gt;currentEncoderRead&lt;/code&gt; by me shifting it to the left with the &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; operation. &lt;em&gt;But at the same time the state of the 0th bit is preserved by &lt;code&gt;|=&lt;/code&gt;&lt;/em&gt;. If I now bind &lt;code&gt;PinB&lt;/code&gt; simply by logical &lt;code&gt;&amp;amp;=&lt;/code&gt;, then I would violate the Gray code, after which &lt;code&gt;A&amp;amp;&amp;amp;B=2&lt;/code&gt; should be and not &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;So if &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; are both not set, the result remains &lt;code&gt;0&lt;/code&gt;, because &lt;code&gt;00^00=00=0&lt;/code&gt;. If only &lt;code&gt;A&lt;/code&gt; is set, but &lt;code&gt;B&lt;/code&gt; is not, the result is &lt;code&gt;11^00=11=3&lt;/code&gt;. If &lt;code&gt;B&lt;/code&gt; is set, but &lt;code&gt;A&lt;/code&gt; is not, the result is &lt;code&gt;00^01=01=1&lt;/code&gt;. If both are set, finally &lt;code&gt;11^01=10=2&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;For more information about how logical binary operators work, please refer to &lt;a href="https://blog.schallbert.de/en/projects/fireplace/#galois-lfsr-implementation"&gt;my LED fireplace project&lt;/a&gt;&lt;/p&gt;&#10;&lt;h3 id="state-interpretation"&gt;State interpretation&lt;/h3&gt;&#10;&lt;p&gt;After the bit code is read, &lt;code&gt;rawMovement&lt;/code&gt; variable is set that tells us how much the encoder has been turned between two runs of the &lt;code&gt;service()&lt;/code&gt; routine. Here you can see the time criticality because there will be aliasing errors if the encoder changes notches in a time similarly short to the service interval.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;handleEncoder()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; encoderRead &lt;span style="color:#f92672"&gt;=&lt;/span&gt; getBitCode();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// bit0 set = status changed, bit1 set = &amp;#34;overflow 3&amp;#34; where it goes 0-&amp;gt;3 or 3-&amp;gt;0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; rawMovement &lt;span style="color:#f92672"&gt;=&lt;/span&gt; encoderRead &lt;span style="color:#f92672"&gt;-&lt;/span&gt; lastEncoderRead;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastEncoderRead &lt;span style="color:#f92672"&gt;=&lt;/span&gt; encoderRead;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// This is the uint-&amp;gt;int magic, converts raw to: -1 counterclockwise, 0 no turn, 1 clockwise&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int8_t&lt;/span&gt; signedMovement &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ((rawMovement &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; (rawMovement &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;));&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; encoderAccumulate &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; signedMovement;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; encoderAccumulate &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; handleAcceleration(signedMovement);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is another trick: &lt;code&gt;signedMovement&lt;/code&gt; is calculated by converting the movement into a signed integer representing &lt;code&gt;0&lt;/code&gt; as &amp;ldquo;not rotated, &lt;code&gt;-1&lt;/code&gt; as &amp;ldquo;rotated left&amp;rdquo; and &lt;code&gt;1&lt;/code&gt; as &amp;ldquo;rotated right&amp;rdquo;. This exploits the integer overflow behavior, in combination with the interpretation of signedness.&lt;/p&gt;&#10;&lt;p&gt;Finally, the accumulation is performed and the acceleration, if configured, is added to the accumulator depending on how fast the encoder is rotated.&lt;/p&gt;&#10;&lt;p&gt;To program these two methods without branching took me several evenings of grubbling and practically half a pad of squared paper. Frankly, these few lines of code have brought me to the edge of my brain&amp;rsquo;s capacity. I am just not a &amp;ldquo;real&amp;rdquo; programmer 😅&lt;/p&gt;&#10;&lt;h3 id="the-getaccumulate-method"&gt;The getAccumulate() method&lt;/h3&gt;&#10;&lt;p&gt;It&amp;rsquo;s really simple, it just returns the internal accumulated value accounted to the encoder&amp;rsquo;s configuration about how many notches make a step.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;getAccumulate()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (encoderAccumulate &lt;span style="color:#f92672"&gt;/&lt;/span&gt; stepsPerNotch);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="the-getincrement-method"&gt;The getIncrement() method&lt;/h3&gt;&#10;&lt;p&gt;Returns how much the encoder&amp;rsquo;s values have changed since its last call.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;getIncrement()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; accu &lt;span style="color:#f92672"&gt;=&lt;/span&gt; getAccumulate();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; encoderIncrements &lt;span style="color:#f92672"&gt;=&lt;/span&gt; accu &lt;span style="color:#f92672"&gt;-&lt;/span&gt; lastEncoderAccumulate;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastEncoderAccumulate &lt;span style="color:#f92672"&gt;=&lt;/span&gt; accu;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (encoderIncrements);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="button-state-calculation-explained"&gt;Button state calculation explained&lt;/h2&gt;&#10;&lt;h3 id="the-handlebutton-method"&gt;The handleButton() method&lt;/h3&gt;&#10;&lt;p&gt;All button states derive from the two basic states: pressed or not pressed. This is reflected in the logic as well:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Button&lt;span style="color:#f92672"&gt;::&lt;/span&gt;handleButton()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (lastGetButtonCount &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; ENC_BUTTONINTERVAL)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastGetButtonCount &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (digitalRead(pinBTN) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; pinActiveState)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handleButtonPressed();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handleButtonReleased();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (doubleClickTicks &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;--&lt;/span&gt;doubleClickTicks;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The button can be configured to be read less often than the encoder. If this is the case, this method will be left without any action.&lt;/p&gt;&#10;&lt;h3 id="button-pressed"&gt;Button pressed:&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;handleButtonPressed()&lt;/code&gt; can either return &lt;code&gt;Closed&lt;/code&gt;, &lt;code&gt;Held&lt;/code&gt;, or &lt;code&gt;LongPressRepeat&lt;/code&gt; depending on how the button is configured and how long it has been pressed already.&lt;/p&gt;&#10;&lt;h3 id="button-not-pressed"&gt;Button not pressed:&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;handleButtonReleased()&lt;/code&gt; can either return &lt;code&gt;Clicked&lt;/code&gt;, &lt;code&gt;Released&lt;/code&gt;, or &lt;code&gt;Doubleclicked&lt;/code&gt; depending on button configuration and click count within a certain time.&lt;/p&gt;&#10;&lt;h3 id="library-configuration"&gt;Library configuration&lt;/h3&gt;&#10;&lt;p&gt;The encoder/button detection behavior can be modified in the &lt;code&gt;encoder.h&lt;/code&gt; file using the constants shown below. The values have been tweaked already and feel natural to me, at least. They should be modified if you choose to use a different service interval than 1ms because those values all take this interval as reference.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ----------------------------------------------------------------------------&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Acceleration configuration (for 1ms calls to ::service())&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; ENC_ACCEL_START &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;150&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// The smaller this value, the quicker you must turn to activate acceleration.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; ENC_ACCEL_SLOPE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;75&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// the smaller this value, the stronger the acceleration will manipulate values.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Button configuration (values for 1ms timer service calls)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; ENC_BUTTONINTERVAL &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// check button every x ms, also debouce time&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; ENC_DOUBLECLICKTIME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// second click within x ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; ENC_LONGPRESSREPEATINTERVAL &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// reports repeating-held every x ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; ENC_HOLDTIME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1200&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// report held button after x ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ----------------------------------------------------------------------------&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="use-it-in-your-project"&gt;Use it in your project!&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The library is open source software, using the MIT license.&lt;/li&gt;&#10;&lt;li&gt;PlatformIO user? Easy, just search it in the &amp;lsquo;Libraries&amp;rsquo; view, Tags are Arduino, Encoder, Schallbert. It&amp;rsquo;s then just one more click to add it to your solution&lt;/li&gt;&#10;&lt;li&gt;Alternatively, just clone or fork the &lt;a href="https://github.com/Schallbert/encoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;repository&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; and use the header/cpp file as you deem fit.&lt;/li&gt;&#10;&lt;li&gt;Contribute? Maybe you have a more efficient state calculation of the button? I&amp;rsquo;d be happy to review your pull request :)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item></channel></rss>