5 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
11 changed files with 368 additions and 120 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 .pio
*.d .vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
# Compiled Object files .vscode/launch.json
*.slo .vscode/ipch
*.lo dist
*.o # C Lion
*.obj .idea
cmake-build-*
# Precompiled Headers CMakeLists.txt
*.gch CMakeListsPrivate.txt
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

View File

@ -1,36 +1,38 @@
# Interval - Arduino knihovna pro časování pomocí intervalů # Interval - Arduino Library for Interval Timing
Koncepce programové konstrukce aplikace pro Arduino spočívá ve dvou hlavních metodách **setup()** a **loop()**, ve které program neustále běží. ![Build](https://git.xpablo.cz/pablo2048/Interval/actions/workflows/pio_tests.yml/badge.svg)
Pro pohodlnější práci s obsluhou periodických procesů jsem napsal jednoduchou knihovnu, která tyto úkoly umožňuje realizovat velmi elegantním způsobem.
Knihovna **Interval** vytváří časovací objekty a umožňuje jejich ovládání pomocí následujících metod. 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** ## **set**
Metoda **set** se používá k nastavení timeoutu a definici začátku časování. The **set** method is used to set the timeout and define the start of the timing.
## **expired** ## **expired**
Metoda **expired** pak slouží k ověření, zda nastavený interval již vypršel. The **expired** method is used to check whether the set interval has already elapsed.
## **setOneshot** ## **setOneshot**
Metoda **setOneshot** nastaví jednorázový běh časovače. Po jeho vypršení je časovač zastavený. The **setOneshot** method configures the timer for a one-shot run. After it expires, the timer is stopped.
## **setPeriodic** ## **setPeriodic**
Metoda **setPeriodic** nastaví opakovaný běh časovače. Po jeho vypršení je časovač automaticky nastavený na zadaný čas. The **setPeriodic** method configures the timer for a periodic run. After expiration, the timer is automatically reset to the specified time.
## **reload** ## **reload**
Metoda **reload** způsobí nový start časovače s poslední zadanou časovou konstantou. The **reload** method restarts the timer with the last specified time constant.
## **elapsed** ## **elapsed**
Metoda **elapsed** vrátí čas, který už uplynul od začátku běhu časovače. The **elapsed** method returns the time that has elapsed since the timer started.
## **remains** ## **remains**
Metoda **remains** vrátí čas, který ještě zbývá do konce běhu časovače, nebo do startu nového cyklu. The **remains** method returns the time remaining until the timer finishes, or until a new cycle begins.
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í. 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": "repository":
{ {
"type": "git", "type": "git",
"url": "http://git.xpablo.cz/pablo2048/Interval.git" "url": "https://git.xpablo.cz/pablo2048/Interval.git"
}, },
"version": "0.0.4", "version": "2.0.0",
"license": "MIT", "license": "MIT",
"frameworks": "arduino", "frameworks": "arduino",
"platforms": "*", "platforms": "*",

View File

@ -1,5 +1,5 @@
name=Interval name=Interval
version=0.0.4 version=2.0.0
author=Pavel Brychta author=Pavel Brychta
maintainer=Pavel Brychta maintainer=Pavel Brychta
sentence=Make timing by using intervals instead of delay() 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,74 +1,82 @@
#include "interval.h" #include "interval.h"
// Public Methods ////////////////////////////////////////////////////////////// // Returns the remaining time until expiration, correctly handling overflow
uint32_t Interval::remains(void) uint32_t Interval::remains() const {
{ uint32_t currentTime = millis();
return _timeout - (currentTime - _timefrom);
return _timeout - (millis() - _timefrom);
} }
uint32_t Interval::elapsed(void) // Returns the elapsed time since the interval started
{ uint32_t Interval::elapsed() const {
return millis() - _timefrom; return millis() - _timefrom;
} }
bool Interval::expired(void) // Checks if the interval has expired based on the mode
{ bool Interval::expired() {
bool result = false; uint32_t currentTime = millis();
uint32_t elapsedTime = currentTime - _timefrom;
if (_done) { switch (_mode) {
if (1 == _mode) { case ONESHOT:
// oneshot mode // In oneshot mode, return true only once upon expiration
if ((millis() - _timefrom) >= _timeout) { if (elapsedTime >= _timeout) {
_done = 0; if (_active) {
result = true; _active = false;
return true;
}
} }
} else if (2 == _mode) { break;
// periodic mode case PERIODIC:
if ((millis() - _timefrom) >= _timeout) { // In periodic mode, return true and reset the timer on expiration
result = true; if (elapsedTime >= _timeout) {
_timefrom = millis(); _timefrom += _timeout;
if (currentTime - _timefrom >= _timeout) {
_timefrom = currentTime;
}
return true;
} }
} else { break;
// compatibility mode case COMPATIBILITY:
if ((millis() - _timefrom) >= _timeout) default:
result = true; // 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 result; return false;
} }
void Interval::set(uint32_t tmout) // Sets the interval in compatibility mode
{ void Interval::set(const uint32_t tmout) {
_timefrom = millis(); _timefrom = millis();
_timeout = tmout; _timeout = tmout;
_mode = 0; _mode = COMPATIBILITY;
_done = 0xff; _active = true;
} }
void Interval::setOneshot(uint32_t tmout) // Sets the interval in oneshot mode
{ void Interval::setOneshot(const uint32_t tmout) {
_timefrom = millis(); _timefrom = millis();
_timeout = tmout; _timeout = tmout;
_mode = 1; _mode = ONESHOT;
_done = 0xff; _active = true;
} }
void Interval::setPeriodic(uint32_t tmout) // Sets the interval in periodic mode
{ void Interval::setPeriodic(const uint32_t tmout) {
_timefrom = millis(); _timefrom = millis();
_timeout = tmout; _timeout = tmout;
_mode = 2; _mode = PERIODIC;
_done = 0xff;
} }
void Interval::reload(void) // Reloads the interval timer (resets the starting time)
{ void Interval::reload() {
_timefrom = millis(); _timefrom = millis();
_done = 0xff; _active = true;
} }

View File

@ -1,32 +1,52 @@
#ifndef _INTERVAL_H_ #pragma once
#define _INTERVAL_H_ /*
* Interval
/* Interval * Copyright (C) 2014, 2016, 2018, 2019, 2023, 2025 Pavel Brychta http://www.xpablo.cz
* Copyright (C) 2014, 2016, 2018, 2019 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 * This program is free software: you can redistribute it and/or modify it under the terms of the MIT License
*/ */
#include <Arduino.h> #include <Arduino.h>
class Interval class Interval {
{ public:
protected: // Enumeration for interval modes
uint32_t _timefrom; enum IntervalMode {
uint32_t _timeout; COMPATIBILITY = 0,
uint8_t _mode; // mode of actual operation (compatibility, oneshot, periodic) ONESHOT = 1,
uint8_t _done; PERIODIC = 2
public: };
Interval(): _mode(0), _done(0xff) {} // compatibility mode autostart
Interval(uint32_t tmout): _timeout(tmout), _mode(2), _done(0xff) {} // periodic mode autostart
bool expired(void);
void set(uint32_t tmout);
void setOneshot(uint32_t tmout);
void setPeriodic(uint32_t tmout);
void reload(void);
uint32_t elapsed(void);
uint32_t remains(void);
};
#endif protected:
// EOF 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();
}