/Arduino-Utils

Utility collection for Arduino programming

Primary LanguageC++GNU General Public License v3.0GPL-3.0

My utility collection for Arduino

Badge License: GPLv3     Badge Version     Badge Commits since latest     Badge Build Status     Badge Hit Counter

Stand With Ukraine

Not yet available as Arduino library.

Button Install     Button API     Button Changelog

If you find this library useful, please give it a star.

🌎 Google Translate


Table of content

SimpleEMAFilters

An EMA (Exponential Moving Average) filter behaves like an RC lowpass filter with RC = SamplePeriod((1-alpha)/alpha) see here.
An EMA filter is implemented by e.g. the following statement:

int16_t sLowpass3;
int16_t Lowpass5;
...
sLowpass3 += ((aInputValue - sLowpass3) + (1 << 2)) >> 3; // 1.8 us, alpha = 0.125, cutoff frequency 22.7 Hz @1kHz
sLowpass5 += ((aInputValue - sLowpass5) + (1 << 4)) >> 5; // 2.5 us, alpha = 1/32 0.03125, cutoff frequency 5.13 Hz @1kHz

which takes 2.5 µs on a 16 MHz Arduino Uno.

The alpha's for the implemented ultra fast EMA filters are 1/2, 1/4, 1/8, 1/16, 1/32 and 1/256 corresponding to the shift values of 1, 2, 3, 4, 5 and 8.

For a 1 kHz sampling rate (1/1000s sampling interval) we get the following equivalent cutoff (-3db) frequencies:

Simplified formula for small alpha is CutoffFrequency = (SampleFrequency / (2π * ((1/alpha) - 1));

  • For alpha 1/2 (shift 1) -> (160 Hz / 1) With 1 = (1/alpha) - 1
  • 1/4 (shift 2) -> 53 Hz (160 Hz / 3) With 3 = (1/alpha) - 1
  • 1/8 -> 22.7 Hz (160 Hz / 7)
  • 1/16 -> 10.6 Hz (160 Hz / 15)
  • 1/32 -> 5.13 Hz (160 Hz / 31)
  • 1/256 -> 0.624 Hz (160 Hz / 255)

Using the more exact formula, alpha = 1 - e ^ -((2π * CutoffFrequency) / SampleFrequency)
=> CutoffFrequency = (-ln(1-alpha) * SampleFrequency) / 2π we get the values:

  • 110 Hz
  • 46 Hz
  • 21.2 Hz
  • 10.3 Hz
  • 5.05 Hz

The maximum output value for integer filters is: InputValue - ((1 << (ShiftValue -1)) -1), i.e 85 for InputValue 100 and ShiftValue 5 (Lowpass5).

The SimpleEMAFilters.hpp contains:

  • A set of ultrafast EMA (Exponential Moving Average) filters which require only 1 to 2 microseconds.
  • 3 highpass and bandpass filters, generated by just subtracting one lowpass from input (highpass) or from another lowpass (bandpass).
  • 16 and 32 bit Biquad filters.
  • Display routines for Arduino Plotter.

All implemented filters are applied at once to the input test signal calling doFiltersStep(int16_t aInput) and the results can in turn easily be displayed in the Arduino Plotter.

Plotter outputs representing e.g. a 42, 21, 10.5, 5.2 Hz square / sine wave at a sample rate of 1 ms (or 84, 42 ... Hz at a sample rate of 0.5 ms and so on)

Arduino Plotter output for a rectangle input signal with a amplitude of +/- 100. E.g. LowP3 is the Lowpass with alpha 1/8 implemented by >> 3 and the cutoff frequency of 21 Hz. ArduinoPlotter output SignificantFilters

Note the following facts:

  • Highpass is always Input - Lowpass.
  • Therefore highpass -here HighP3_16- can have an overshoot of 100%.
  • The output of DoubleLowpass3 and 4 is more and more resembling a delayed sine.
  • The output of LowP5_32 and DoubleLowP4_16 have almost the same amplitude but DoubleLowP4_16 is way more resembling a (delayed) sine.
  • The output of TripleLowP3_16 has a higher amplitude than DoubleLowP4_16 with both looking quite like a (delayed) sine.

Arduino Plotter output for a rectangle input signal with very low amplitude of +/- 20 where you see clipping effects due to the limited resolution of the used 16 bit math. ArduinoPlotter output LowPass_LowInput The Lowpass3_int32 and Lowpass5_int32 are 32 bit fixed point implementations for higher resolution which requires 4.2 µs instead of the 1.8 / 2.5 µs of 16 bit.

Arduino Plotter output for a sine input signal with a amplitude of +/- 100. ArduinoPlotter output for LowPass with sine input Note the different attenuations at different frequencies.

Arduino Plotter output for a triangle input signal with a amplitude of +/- 100. ArduinoPlotter output triangle input Note that higher order filters and low pass with high shifts are almost a perfect sine.

Arduino Plotter output for a sawtooth input signal with a amplitude of +/- 100. ArduinoPlotter output for sawtooth input

Arduino Plotter output for a random input signal with a amplitude of +/- 100. ArduinoPlotter output for lowpass on random input


Arduino Plotter output for a rectangle input signal with 4 different frequencies 42 Hz > 21 Hz > 10.5 Hz > 5.2 Hz

Click on the pictures to enlarge them.

All lowpass filters. Highpass filters.
Generated by subtracting lowpass from input.
AllLowPass HighPassFilters
Bandpass and reject filters.
Bandpass is generated by subtracting one lowpass from onother with different shift value.
Reject filter is input minus bandpass.
High order Lowpass.
BandPassAndReject HighOrderLowPass
LowPass1, 3, 5 and 8.
Lowpass8 behave like a integrator here, resulting in a triangle output.
LowPass3, 5 and 8.
Comparison of different implementations with 16 bit integer, 32 bit integer and 32 bit float.
LowPass1_3_5_8 LowPass16And32Bit
BiQuad filters with damping factor = 0.
You see the overshoot.
BiQuadFilters

The floating point implementation of the 1/32 EMA filter takes 24 to 34 µs.
There are convenience functions implemented for EMA filter, but normally it is better to implement it inline by e.g.

int16_t Lowpass3;
int16_t Lowpass5;
int32_t Lowpass5_int32;
..
Lowpass3 += ((InputValue - Lowpass3) + (1 << 2)) >> 3; // 1.8 us, alpha = 0.125, cutoff frequency 22.7 Hz @1kHz
Lowpass5 += ((InputValue - Lowpass5) + (1 << 4)) >> 5; // 2.5 us, alpha = 1/32 0.03125, cutoff frequency 5.13 Hz @1kHz
Lowpass5_int32 += ((((int32_t) InputValue) << 8) - Lowpass5_int32) >> 5; // Fixed point 4.2 us, value is Lowpass5_int32 >> 8
Lowpass8_int32 += ((((int32_t) InputValue) << 16) - Lowpass8_int32) >> 8; // Fixed point 2.0 us because of fast shift, value is Lowpass8_int32 >> 16

!Attention! The 16 bit implementations are limited to a maximum input value of +/- 16383 for rectangular input (which is the worst input case). The reason is, that the term InputValue - Lowpass3 must always fit into a 16 bit signed integer.

Related Links

ADCUtils

Fast and flexible ADC conversions. Intelligent handling of delays for reference and channel switching.

  • Functions for easy oversampling.
  • Function for easy getting the maximum value of measurements.
  • Functions for getting temperature and VCC voltage. For VCC, resolution is 20 millivolt!
  • Functions to check if voltage is too low for a given period, used especially for Li-ion batteries supply.

HCSR04

  • Blocking and non-blocking reading of HCSR04 US Sensors with timeouts and exact conversions.
  • Supports also 1 Pin mode available with the HY-SRF05 or Parallax PING modules.

You can modify the HCSR04 modules to 1 Pin mode:

  1. Old module with 3 16 pin chips:
    Connect Trigger and Echo direct or use a resistor < 4.7 kΩ. If you remove both 10 kΩ pullup resistor you can use a connecting resistor < 47 kΩ, but I suggest to use 10 kΩ which is more reliable.
  2. Old module with 3 16 pin chips but with no pullup resistors near the connector row:
    Connect Trigger and Echo with a resistor > 200 Ω. Use 10 kΩ.
  3. New module with 1 16 pin and 2 8 pin chips:
    Connect Trigger and Echo by a resistor > 200 Ω and < 22 kΩ.
  4. All modules:
    Connect Trigger and Echo by a resistor of 4.7 kΩ.

MeasureVoltageAndResistance

Measures voltage and resistance with 1 mV and 2 Ω resolution at the lower end.
First voltage is measured. If voltage is zero, then the unknown resistance to ground is measured using the 5 volt (VCC) supply with internal/series resistance of 10 kΩ or 100 kΩ.

Fritzing board

Fritzing board

Fritzing schematics

Fritzing schematics

BlinkLed

  • Class for blinking one ore more LED's in different fashions.

ShowInfo

  • Serial.print display of timer and other peripheral and system registers (to be extended :-)).

HexDump

  • Creates hex memory dumps on serial output.
0x0000:  0xFF 0x81 0x82 0x00 0x08 0x02 0x00 0x27 0xFF 0xFF 0x0E 0xB3 0x81 0xFC 0x9B 0x47   .. .. '  .....G
0x0010:  0x00 0x00 0x00 0x00 0x20 0x65 0x00 0x0F 0xBE 0xEB 0x9B 0x98 0x2C 0xF1 0x08 0x2C       e .....,..,
0x0020:  0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55  UUUUUUUUUUUUUUUU

AVRUtils

  • Sleep and delay/sleep with watchdog functions.
  • Computation and display of available Ram, Heap / Stack memory.
  • Display of watchdog reset reason in AVRUtilsDemo, which currently (3/2024) only works with optiboot 8.1 bootloader.

RAM starts of variables initialized with values != 0, followed by variables initialized with 0 and variables not initialized by using attribute __attribute__((section(".noinit"))). It ends with the heap and the stack.

Sample output if stack runs into data

Size of Data + BSS, Heap start, Stack end=2041
Stack used 20 of 7
Currently available Heap=0

Optiboot 8.1

The optiboot 8.1 bootloader is required to determine the (watchdog) reset reason e.g. used in AVRUtilsDemo..

MillisUtils

Unifies millis() timer handling for Digispark, AttinyCore and Arduino cores.

  • Start, stop and modify milliseconds timer and value.
  • Functions to compensate millis() after long running tasks in noIterrupt() context like NeoPixel output, ADC buffer reading etc.
  • Blocking delayMilliseconds() function for use in noInterrupts context like ISR.

DebugLevel.h

  • Propagating debug levels for development. Supports level TRACE, DEBUG, INFO, WARN and ERROR.
  • Includes an explanation of semantics of these levels.

ATtinyUtils

  • toneWithTimer1PWM().
  • noToneWithTimer1PWM().
  • isBODSFlagExistent() -> checking for ATtiny85 revision C.
  • fuse reading function.
  • changeDigisparkClock() to use Digispark boards with no Digispark core like e.g. ATTinyCore by Spence Konde. It changes Digispark Bootloader clock settings to get the right CPU frequency and resets Digispark OCCAL tweak. Consider to use new optimized Digispark core instead.

AvrTracing

Tracing an Arduino program by printing each program counter value after executing one instruction.

Utilities available as separate Arduino library

Minimal bit-bang send serial

  • 115200 baud for 1/8/16 MHz ATtiny clock. The utility for serial output for ATtinies is contained in the ATtinySerialOut library available as an Arduino library.
  • Arduino library for handling push buttons just connected between ground and INT0 and / or INT1 pin.
  • No external pullup, no polling needed. The utility for easy button handling for ATmegas or ATtinies is contained in the EasyButtonAtInt01 library available as an Arduino library.

The very useful digitalWriteFast.h file based on the version from Watterott electronic.

Changing include (*.h) files with Arduino IDE

First, use Sketch > Show Sketch Folder (Ctrl+K).
If you have not yet saved the example as your own sketch, then you are instantly in the right library folder.
Otherwise you have to navigate to the parallel libraries folder and select the library you want to access.
In both cases the library source and include files are located in the libraries src directory.
The modification must be renewed for each new library version!

Modifying compile options with Sloeber IDE

If you are using Sloeber as your IDE, you can easily define global symbols with Properties > Arduino > CompileOptions.
Sloeber settings

Revision History

Version 1.2.0

  • Updated AVRUtils, ADCUtils, HexDump and ShowInfo.
  • Added Optiboot 8.1.

Version 1.1.0

  • SimpleEMAFilters: Added "State Variable" / Biquad and double and triple Lowpass filters.
  • ADCUtils: Renamed *VoltageCheck*() to *VCCCheck*(), setADCMultiplexerAndReferenceForNextConversion() to setADCChannelAndReferenceForNextConversion() and changed signature of readUntil4ConsecutiveValuesAreEqual().
  • Renamed getTemperature* to getCPUTemperature*.
  • Miscellaneous renamings and improvements.
  • Added float to LongUnion.
  • Added printRAMInfo() in AVRUtils.
  • Fixed bug in HexDump.
  • Documentation.

Version 1.0.0

  • Added unions.
  • Changed distance return value for overflow to 0.
  • Miscellaneous improvements.

Version 0.8.0

  • Initial version

CI

The library examples are tested with GitHub Actions for the following boards:

  • Arduino Uno
  • Arduino Leonardo
  • Digispark (using ATTiny85 @1MHz)
  • Generic ATTiny85 @1MHz