11 Commits

Author SHA1 Message Date
9675548e2f Documentation, version updated
All checks were successful
PlatformIO flow / build (push) Successful in 28s
2025-03-07 21:02:11 +01:00
a653f0e3d6 Refactored code, unit tests added, GA activated. 2025-03-07 20:54:10 +01:00
1c870374cd Upravy na zaklade clang-tidy 2023-05-20 11:53:18 +02:00
581749b7f7 Poradi inicializace 2019-11-23 12:08:38 +01:00
73f8bd919a Odstraneni varovani z cppchecku 2019-11-23 11:56:50 +01:00
15be6b4674 Konstruktor pro periodic autostart 2019-10-09 09:41:11 +02:00
acffaeef87 Dokumentace 2019-08-26 16:52:04 +02:00
a343d97e3d Merge branch 'feature/oneshotperiodic' 2018-11-16 21:05:00 +01:00
1356d845b3 Fix pro preklad 2018-11-16 19:21:12 +01:00
f8bc7a8360 Zjednoduseni kodu a odstraneni zbytecne promenne 2018-11-11 08:12:36 +01:00
be8e0b341e Prvni verze pro otestovani oneshot a periodic rezimu 2018-11-11 08:02:18 +01:00
11 changed files with 407 additions and 96 deletions

31
.clang-format Normal file
View File

@ -0,0 +1,31 @@
BasedOnStyle: LLVM
AlignConsecutiveMacros: AcrossEmptyLines
AlignEscapedNewlines: DontAlign
AlignTrailingComments: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
ColumnLimit: 0
IndentCaseLabels: true
IndentPPDirectives: BeforeHash
IndentWidth: 4
MaxEmptyLinesToKeep: 2
PointerAlignment: Middle
SpaceAfterCStyleCast: true
NamespaceIndentation: All

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
tab_width = 4

View File

@ -0,0 +1,34 @@
name: PlatformIO flow
run-name: ${{ gitea.actor }} is testing
on:
push:
branches:
- '*'
tags:
- '*'
jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure caching
uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 'pypy3.9'
- name: Install PlatformIO Core
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Run native unit tests
run: |
pio test

43
.gitignore vendored
View File

@ -1,32 +1,11 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
dist
# C Lion
.idea
cmake-build-*
CMakeLists.txt
CMakeListsPrivate.txt

View File

@ -1,7 +1,38 @@
# Interval
Arduino knihovna pro časování pomocí intervalů
Koncepce programové konstrukce aplikace pro Arduino spočívá ve dvou hlavních metodách **setup()** a **loop()**, ve které program neustále běží.
Pro pohodlnější práci s obsluhou periodických procesů jsem napsal jednoduchou knihovnu, která tyto úkoly umožňuje napsat velmi elegantním způsobem.
Knihovna **Interval** vytváří časovací objekty a zpřístupňuje dvě metody metodu **set** a metodu **expired**.
Metoda **set** se používá k nastavení timeoutu a definici začátku časování, metoda **expired** pak slouží k ověření, zda nastavený interval již vypršel.
Zajímavostí je, že knihovna korektně ošetřuje přetečení vnitřního milisekundového čítače, takže nehrozí nebezpečí špatného časování i při velmi dlouhé době chodu zařízení.
# Interval - Arduino Library for Interval Timing
![Build](https://git.xpablo.cz/pablo2048/Interval/actions/workflows/pio_tests.yml/badge.svg)
The design concept of an Arduino application is based on two main methods **setup()** and **loop()**, in which the program runs continuously.
For more convenient handling of periodic processes, I have written a simple library that allows these tasks to be executed in a very elegant way.
The **Interval** library creates timer objects and allows you to control them using the following methods.
## **set**
The **set** method is used to set the timeout and define the start of the timing.
## **expired**
The **expired** method is used to check whether the set interval has already elapsed.
## **setOneshot**
The **setOneshot** method configures the timer for a one-shot run. After it expires, the timer is stopped.
## **setPeriodic**
The **setPeriodic** method configures the timer for a periodic run. After expiration, the timer is automatically reset to the specified time.
## **reload**
The **reload** method restarts the timer with the last specified time constant.
## **elapsed**
The **elapsed** method returns the time that has elapsed since the timer started.
## **remains**
The **remains** method returns the time remaining until the timer finishes, or until a new cycle begins.
An interesting feature is that the library correctly handles the overflow of the internal millisecond counter, so there is no risk of incorrect timing even during very long operation periods.

View File

@ -10,9 +10,9 @@
"repository":
{
"type": "git",
"url": "https://github.com/Pablo2048/Interval.git"
"url": "https://git.xpablo.cz/pablo2048/Interval.git"
},
"version": "0.0.2",
"version": "2.0.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=Interval
version=0.0.2
version=2.0.0
author=Pavel Brychta
maintainer=Pavel Brychta
sentence=Make timing by using intervals instead of delay()

17
platformio.ini Normal file
View File

@ -0,0 +1,17 @@
[platformio]
default_envs = native
[env:native]
; build for desktop, not embedded device
; expects GCC to be installed and available on the computer!
platform = native
; build source code in src/ too
test_build_src = yes
;test_framework = custom ; uncomment this if you need debugging messages to be shown (or else run with `pio test -v`)
debug_test = test_interval
lib_deps =
https://github.com/FabioBatSilva/ArduinoFake/archive/refs/heads/master.zip
build_flags =
-std=gnu++17
-DUNITY_OUTPUT_COLOR
build_type = debug

View File

@ -1,44 +1,82 @@
extern "C" {
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
}
#include <Arduino.h>
#include "interval.h"
// Public Methods //////////////////////////////////////////////////////////////
uint32_t Interval::remains(void)
{
return _timeout - (millis() - _timefrom);
// Returns the remaining time until expiration, correctly handling overflow
uint32_t Interval::remains() const {
uint32_t currentTime = millis();
return _timeout - (currentTime - _timefrom);
}
uint32_t Interval::elapsed(void)
{
// Returns the elapsed time since the interval started
uint32_t Interval::elapsed() const {
return millis() - _timefrom;
}
bool Interval::expired(void)
{
// Checks if the interval has expired based on the mode
bool Interval::expired() {
uint32_t currentTime = millis();
uint32_t elapsedTime = currentTime - _timefrom;
if ((millis() - _timefrom) >= _timeout)
switch (_mode) {
case ONESHOT:
// In oneshot mode, return true only once upon expiration
if (elapsedTime >= _timeout) {
if (_active) {
_active = false;
return true;
else
}
}
break;
case PERIODIC:
// In periodic mode, return true and reset the timer on expiration
if (elapsedTime >= _timeout) {
_timefrom += _timeout;
if (currentTime - _timefrom >= _timeout) {
_timefrom = currentTime;
}
return true;
}
break;
case COMPATIBILITY:
default:
// In compatibility mode, always return true if the interval has expired
if (_active) {
if (elapsedTime >= _timeout) {
_active = false;
return true;
}
} else {
return true; // already expired
}
break;
}
return false;
}
void Interval::set(uint32_t tmout)
{
_reload = tmout;
// Sets the interval in compatibility mode
void Interval::set(const uint32_t tmout) {
_timefrom = millis();
_timeout = _reload;
_timeout = tmout;
_mode = COMPATIBILITY;
_active = true;
}
void Interval::reload(void)
{
// Sets the interval in oneshot mode
void Interval::setOneshot(const uint32_t tmout) {
_timefrom = millis();
_timeout = _reload;
_timeout = tmout;
_mode = ONESHOT;
_active = true;
}
// Sets the interval in periodic mode
void Interval::setPeriodic(const uint32_t tmout) {
_timefrom = millis();
_timeout = tmout;
_mode = PERIODIC;
}
// Reloads the interval timer (resets the starting time)
void Interval::reload() {
_timefrom = millis();
_active = true;
}

View File

@ -1,28 +1,52 @@
#ifndef interval_h
#define interval_h
/* Interval
* Copyright (C) 2014, 2016, 2018 Pavel Brychta http://www.xpablo.cz
#pragma once
/*
* Interval
* Copyright (C) 2014, 2016, 2018, 2019, 2023, 2025 Pavel Brychta http://www.xpablo.cz
*
* This program is free software: you can redistribute it and/or modify it under the terms of the MIT License
*/
#include <Arduino.h>
#include <inttypes.h>
class Interval
{
protected:
uint32_t _timefrom;
uint32_t _timeout;
uint32_t _reload;
class Interval {
public:
bool expired(void);
void set(uint32_t tmout);
void reload(void);
uint32_t elapsed(void);
uint32_t remains(void);
// Enumeration for interval modes
enum IntervalMode {
COMPATIBILITY = 0,
ONESHOT = 1,
PERIODIC = 2
};
#endif
// EOF
protected:
uint32_t _timefrom = 0; // Start time of the interval
uint32_t _timeout = 0; // Timeout duration in milliseconds
IntervalMode _mode = COMPATIBILITY; // Current mode of operation
bool _active = true; // Active flag for oneshot and compatibilitymode
public:
Interval() = default;
// Constructor for periodic mode with autostart
explicit Interval(const uint32_t tmout)
: _timefrom(millis()), _timeout(tmout), _mode(PERIODIC) {
}
// Checks if the interval has expired based on the current mode
bool expired();
// Sets the interval in compatibility mode
void set(uint32_t tmout);
// Sets the interval in oneshot mode
void setOneshot(uint32_t tmout);
// Sets the interval in periodic mode
void setPeriodic(uint32_t tmout);
// Reloads the interval timer without changing mode or timeout
void reload();
// Returns the elapsed time since the interval started
[[nodiscard]] uint32_t elapsed() const;
// Returns the remaining time until the interval expires
[[nodiscard]] uint32_t remains() const;
};

148
test/test_interval/main.cpp Normal file
View File

@ -0,0 +1,148 @@
#include <ArduinoFake.h>
#include <unity.h>
#include "interval.h"
using namespace fakeit;
// Global variable to simulate time in milliseconds
static uint32_t fakeMillis = 0;
// Fake function to override millis()
uint32_t fakeMillisFunc() {
return fakeMillis;
}
// setUp() is called before each test
void setUp(void) {
// Reset fake time
fakeMillis = 0;
ArduinoFakeReset();
// Override millis() to use our fakeMillisFunc()
When(Method(ArduinoFake(), millis)).AlwaysDo(fakeMillisFunc);
}
// tearDown() is called after each test (if needed)
void tearDown(void) {
// No teardown actions required
}
// Test for Compatibility Mode (using set() method)
void test_CompatibilityMode(void) {
Interval interval;
interval.set(1000); // Set timeout to 1000ms in compatibility mode
fakeMillis = 500;
TEST_ASSERT_FALSE(interval.expired()); // Should not be expired at 500ms
fakeMillis = 1000;
TEST_ASSERT_TRUE(interval.expired()); // Should be expired at 1000ms
// In compatibility mode, subsequent calls should still return true once expired
TEST_ASSERT_TRUE(interval.expired());
}
// Test for One-shot Mode (using setOneshot() method)
void test_OneshotMode(void) {
Interval interval;
interval.setOneshot(1000); // Set timeout to 1000ms in oneshot mode
fakeMillis = 500;
TEST_ASSERT_FALSE(interval.expired()); // Not yet expired
fakeMillis = 1000;
TEST_ASSERT_TRUE(interval.expired()); // Expired at 1000ms
// Subsequent calls should return false because oneshot mode triggers only once
TEST_ASSERT_FALSE(interval.expired());
}
// Test for Periodic Mode (using setPeriodic() method)
void test_PeriodicMode(void) {
Interval interval;
interval.setPeriodic(1000); // Set periodic mode with 1000ms timeout
fakeMillis = 500;
TEST_ASSERT_FALSE(interval.expired()); // Not yet expired
fakeMillis = 1000;
TEST_ASSERT_TRUE(interval.expired()); // First period expired
// After expiration, timer is reset; simulate next period
fakeMillis = 1500;
TEST_ASSERT_FALSE(interval.expired()); // Still within new period
fakeMillis = 2000;
TEST_ASSERT_TRUE(interval.expired()); // Second period expired
}
// Test for elapsed() and remains() functions
void test_elapsed_and_remains(void) {
Interval interval;
interval.set(1000); // Using compatibility mode, timeout set to 1000ms
fakeMillis = 0;
TEST_ASSERT_EQUAL_UINT32(0, interval.elapsed());
TEST_ASSERT_EQUAL_UINT32(1000, interval.remains());
fakeMillis = 500;
TEST_ASSERT_EQUAL_UINT32(500, interval.elapsed());
TEST_ASSERT_EQUAL_UINT32(500, interval.remains());
fakeMillis = 1100;
TEST_ASSERT_EQUAL_UINT32(1100, interval.elapsed());
// remains() uses unsigned arithmetic, so it will underflow if elapsed > timeout
TEST_ASSERT_EQUAL_UINT32(1000 - 1100, interval.remains());
}
// Test for handling overflow of millis() value
void test_overflow(void) {
Interval interval;
// Set fakeMillis to a value near the maximum of 32-bit unsigned int
fakeMillis = 0xFFFFFFF0;
interval.set(100); // Set timeout to 100ms in compatibility mode; _timefrom = 0xFFFFFFF0
// Simulate time just before expiration after overflow.
// After overflow, fakeMillis is low. For example, fakeMillis = 0x00000050.
// Expected elapsed time = 0x00000050 + (0x100000000 - 0xFFFFFFF0) = 0x50 + 0x10 = 96ms.
fakeMillis = 0x00000050;
TEST_ASSERT_FALSE(interval.expired()); // Not yet expired
// Now simulate time after expiration:
// Set fakeMillis = 0x00000070. Expected elapsed time = 0x70 + 0x10 = 128ms, which is >= 100ms.
fakeMillis = 0x00000070;
TEST_ASSERT_TRUE(interval.expired()); // Should be expired due to overflow handling
}
// Test for reload() functionality in oneshot mode
void test_reload(void) {
Interval interval;
interval.setOneshot(1000); // Set oneshot mode with 1000ms timeout
fakeMillis = 0;
// Simulate expiration
fakeMillis = 1100;
TEST_ASSERT_TRUE(interval.expired()); // Expired at 1100ms
// Reload the interval to reset the timer and reactivate oneshot mode
fakeMillis = 1100;
interval.reload();
// Immediately after reload, should not be expired
fakeMillis = 1200;
TEST_ASSERT_FALSE(interval.expired());
// After passing the timeout from reload, it should expire again
fakeMillis = 2200;
TEST_ASSERT_TRUE(interval.expired());
}
int main(int argc, char **argv) {
UNITY_BEGIN();
RUN_TEST(test_CompatibilityMode);
RUN_TEST(test_OneshotMode);
RUN_TEST(test_PeriodicMode);
RUN_TEST(test_elapsed_and_remains);
RUN_TEST(test_overflow);
RUN_TEST(test_reload);
return UNITY_END();
}