initial commit
This commit is contained in:
commit
b1b2686714
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/.pio
|
27
.project
Normal file
27
.project
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>cwkeyer</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||||
|
<triggers>clean,full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||||
|
<triggers>full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||||
|
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
38
.settings/PlatformIO Debugger.launch
Normal file
38
.settings/PlatformIO Debugger.launch
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<launchConfiguration type="org.eclipse.cdt.launch.applicationLaunchType">
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.dsf.gdb.AUTO_SOLIB" value="true"/>
|
||||||
|
<listAttribute key="org.eclipse.cdt.dsf.gdb.AUTO_SOLIB_LIST"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.dsf.gdb.DEBUG_NAME" value="piodebuggdb"/>
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.dsf.gdb.DEBUG_ON_FORK" value="false"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.dsf.gdb.GDB_INIT" value=".pioinit"/>
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.dsf.gdb.NON_STOP" value="false"/>
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.dsf.gdb.REVERSE" value="false"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.dsf.gdb.REVERSE_MODE" value="UseSoftTrace"/>
|
||||||
|
<listAttribute key="org.eclipse.cdt.dsf.gdb.SOLIB_PATH"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.dsf.gdb.TRACEPOINT_MODE" value="TP_NORMAL_ONLY"/>
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.dsf.gdb.UPDATE_THREADLIST_ON_SUSPEND" value="false"/>
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.dsf.gdb.internal.ui.launching.LocalApplicationCDebuggerTab.DEFAULTS_SET" value="true"/>
|
||||||
|
<intAttribute key="org.eclipse.cdt.launch.ATTR_BUILD_BEFORE_LAUNCH_ATTR" value="1"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.COREFILE_PATH" value=""/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_ID" value="gdb"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_REGISTER_GROUPS" value=""/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_START_MODE" value="run"/>
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN" value="false"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN_SYMBOL" value=""/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="/home/omega/Documents/foss/esp32projects/cwkeyer/.pio/build/esp32doit-devkit-v1/firmware.elf"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="cwkeyer"/>
|
||||||
|
<booleanAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_AUTO_ATTR" value="false"/>
|
||||||
|
<stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value="0.910961921.1363900502"/>
|
||||||
|
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||||
|
<listEntry value="/cwkeyer"/>
|
||||||
|
</listAttribute>
|
||||||
|
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||||
|
<listEntry value="4"/>
|
||||||
|
</listAttribute>
|
||||||
|
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||||
|
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||||
|
</listAttribute>
|
||||||
|
<stringAttribute key="org.eclipse.dsf.launch.MEMORY_BLOCKS" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <memoryBlockExpressionList context="reserved-for-future-use"/> "/>
|
||||||
|
<stringAttribute key="process_factory_id" value="org.eclipse.cdt.dsf.gdb.GdbProcessFactory"/>
|
||||||
|
<stringAttribute key="saved_expressions<seperator>Unknown" value="0x55f4"/>
|
||||||
|
</launchConfiguration>
|
25
.settings/language.settings.xml
Normal file
25
.settings/language.settings.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<project>
|
||||||
|
<configuration id="0.910961921" name="Default">
|
||||||
|
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||||
|
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="-736738477489841414" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${HOME}/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-g++ ${FLAGS} -std=gnu++11 -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||||
|
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||||
|
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||||
|
</provider>
|
||||||
|
</extension>
|
||||||
|
</configuration>
|
||||||
|
<configuration id="0.910961921.1363900502" name="Debug">
|
||||||
|
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||||
|
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="-736738477489841414" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${HOME}/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-g++ ${FLAGS} -std=gnu++11 -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||||
|
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||||
|
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||||
|
</provider>
|
||||||
|
</extension>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
11
.settings/org.eclipse.cdt.core.prefs
Normal file
11
.settings/org.eclipse.cdt.core.prefs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
environment/project/0.910961921/PATH/delimiter=\:
|
||||||
|
environment/project/0.910961921/PATH/operation=replace
|
||||||
|
environment/project/0.910961921/PATH/value=/usr/local/bin\:/usr/bin\:/bin\:/usr/local/sbin\:/usr/lib/jvm/default/bin\:/usr/bin/site_perl\:/usr/bin/vendor_perl\:/usr/bin/core_perl\:/usr/local/bin\:/usr/bin\:/bin\:/usr/local/sbin\:/usr/lib/jvm/default/bin\:/usr/bin/site_perl\:/usr/bin/vendor_perl\:/usr/bin/core_perl${PathDelimiter}${PATH}
|
||||||
|
environment/project/0.910961921/append=true
|
||||||
|
environment/project/0.910961921/appendContributed=true
|
||||||
|
environment/project/0.910961921.1363900502/PATH/delimiter=\:
|
||||||
|
environment/project/0.910961921.1363900502/PATH/operation=replace
|
||||||
|
environment/project/0.910961921.1363900502/PATH/value=/usr/local/bin\:/usr/bin\:/bin\:/usr/local/sbin\:/usr/lib/jvm/default/bin\:/usr/bin/site_perl\:/usr/bin/vendor_perl\:/usr/bin/core_perl\:/usr/local/bin\:/usr/bin\:/bin\:/usr/local/sbin\:/usr/lib/jvm/default/bin\:/usr/bin/site_perl\:/usr/bin/vendor_perl\:/usr/bin/core_perl${PathDelimiter}${PATH}
|
||||||
|
environment/project/0.910961921.1363900502/append=true
|
||||||
|
environment/project/0.910961921.1363900502/appendContributed=true
|
39
include/README
Normal file
39
include/README
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
lib/README
Normal file
46
lib/README
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
21
platformio.ini
Normal file
21
platformio.ini
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:esp32doit-devkit-v1]
|
||||||
|
platform = espressif32
|
||||||
|
framework = arduino
|
||||||
|
board = esp32doit-devkit-v1
|
||||||
|
upload_protocol = esptool
|
||||||
|
upload_speed = 921600
|
||||||
|
board_build.flash_mode = dio
|
||||||
|
build_flags = -DCORE_DEBUG_LEVEL=4
|
||||||
|
lib_deps =
|
||||||
|
yellobyte/DacESP32@^1.0.10
|
||||||
|
madhephaestus/ESP32Encoder@^0.10.1
|
159
src/main.cpp
Normal file
159
src/main.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <DacESP32.h>
|
||||||
|
#include <ESP32Encoder.h>
|
||||||
|
|
||||||
|
#define PIN_L 4 // pin GPIO T4 = 13
|
||||||
|
#define PIN_R 15 // pin GPIO T5 = 12
|
||||||
|
|
||||||
|
#define MODE 0 // set 1 for ESP32-S2 and -S3 (higher touchRead value = contact)
|
||||||
|
|
||||||
|
enum cw_touchmode_t {
|
||||||
|
TOUCH,
|
||||||
|
PADDLE
|
||||||
|
} cw_touchmode = PADDLE;
|
||||||
|
|
||||||
|
uint8_t thresh_L = 10;
|
||||||
|
uint8_t thresh_R = 10;
|
||||||
|
|
||||||
|
DacESP32 dac1(DAC_CHANNEL_1); // GPIO 25
|
||||||
|
|
||||||
|
enum cw_element_t {
|
||||||
|
IDLE,
|
||||||
|
DIT,
|
||||||
|
DAH,
|
||||||
|
DELAY,
|
||||||
|
};
|
||||||
|
|
||||||
|
cw_element_t prevEl = IDLE;
|
||||||
|
cw_element_t currEl = IDLE;
|
||||||
|
cw_element_t nextEl = IDLE;
|
||||||
|
int currElEndTime = 0;
|
||||||
|
|
||||||
|
int wpm = 20;
|
||||||
|
|
||||||
|
|
||||||
|
ESP32Encoder encoder;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
if (cw_touchmode == TOUCH) {
|
||||||
|
thresh_L = (int) ((float)touchRead(PIN_L) * 0.9f);
|
||||||
|
thresh_R = (int) ((float)touchRead(PIN_R) * 0.9f);
|
||||||
|
} else if (cw_touchmode == PADDLE) {
|
||||||
|
pinMode(PIN_L, INPUT_PULLUP);
|
||||||
|
pinMode(PIN_R, INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
dac1.outputCW(1000);
|
||||||
|
dac1.dacCwDeselect();
|
||||||
|
|
||||||
|
ESP32Encoder::useInternalWeakPullResistors=UP;
|
||||||
|
encoder.attachHalfQuad(16, 17);
|
||||||
|
encoder.setCount(wpm * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void toneStart() {
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
dac1.dacCwSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void toneStop() {
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
dac1.dacCwDeselect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
auto ditLength = 1200 / wpm;
|
||||||
|
auto time = millis();
|
||||||
|
|
||||||
|
long newWpm = encoder.getCount() / 2;
|
||||||
|
if (newWpm != wpm) {
|
||||||
|
if (newWpm > 3) {
|
||||||
|
wpm = newWpm;
|
||||||
|
log_d("Changing WPM to %d\n", wpm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool holding_L, holding_R;
|
||||||
|
if (cw_touchmode == TOUCH) {
|
||||||
|
auto val_L = touchRead(PIN_L);
|
||||||
|
auto val_R = touchRead(PIN_R);
|
||||||
|
|
||||||
|
holding_L = (val_L < thresh_L);
|
||||||
|
holding_R = (val_R < thresh_R);
|
||||||
|
} else if (cw_touchmode == PADDLE) {
|
||||||
|
holding_L = !digitalRead(PIN_L);
|
||||||
|
holding_R = !digitalRead(PIN_R);
|
||||||
|
}
|
||||||
|
holding_L ^= MODE;
|
||||||
|
holding_R ^= MODE;
|
||||||
|
// in MODE 0: L = dit, R = dah
|
||||||
|
|
||||||
|
// log_v("prev: %d, curr: %d, next: %d, L: %d (trig %d) => %d | R: %d (trig %d) => %d @ %d / %d\n", prevEl, currEl, nextEl, val_L, thresh_L, holding_L, val_R, thresh_R, holding_R, time, currElEndTime);
|
||||||
|
|
||||||
|
switch (currEl) {
|
||||||
|
case IDLE:
|
||||||
|
if (holding_L && !holding_R) {
|
||||||
|
currEl = DIT;
|
||||||
|
currElEndTime = time + ditLength;
|
||||||
|
} else if (!holding_L && holding_R) {
|
||||||
|
currEl = DAH;
|
||||||
|
currElEndTime = time + 3 * ditLength;
|
||||||
|
} else if (holding_L && holding_R && nextEl == IDLE) { // both pressed at the same time
|
||||||
|
currEl = DIT;
|
||||||
|
nextEl = DAH;
|
||||||
|
currElEndTime = time + 3 * ditLength;
|
||||||
|
}
|
||||||
|
toneStop();
|
||||||
|
break;
|
||||||
|
case DIT:
|
||||||
|
if (holding_R && nextEl == IDLE) {
|
||||||
|
log_v("holding R, queuing DAH\n");
|
||||||
|
nextEl = DAH;
|
||||||
|
}
|
||||||
|
if (time >= currElEndTime) {
|
||||||
|
currEl = DELAY;
|
||||||
|
currElEndTime = time + ditLength;
|
||||||
|
}
|
||||||
|
toneStart();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DAH:
|
||||||
|
if (holding_L && nextEl == IDLE) {
|
||||||
|
log_v("holding L, queuing DIT\n");
|
||||||
|
nextEl = DIT;
|
||||||
|
}
|
||||||
|
if (time >= currElEndTime) {
|
||||||
|
currEl = DELAY;
|
||||||
|
currElEndTime = time + ditLength;
|
||||||
|
}
|
||||||
|
toneStart();
|
||||||
|
break;
|
||||||
|
case DELAY:
|
||||||
|
if (time >= currElEndTime) { // delay ended
|
||||||
|
log_v("delay ended, curr: %d, next: %d\n", currEl, nextEl);
|
||||||
|
currEl = nextEl; // play the next element
|
||||||
|
if (currEl == DIT) {
|
||||||
|
log_v("playing DIT next\n");
|
||||||
|
currElEndTime = time + ditLength;
|
||||||
|
} else if (currEl == DAH) {
|
||||||
|
log_v("playing DAH next\n");
|
||||||
|
currElEndTime = time + 3 * ditLength;
|
||||||
|
}
|
||||||
|
nextEl = IDLE;
|
||||||
|
}
|
||||||
|
if (prevEl == DIT && holding_R && nextEl == IDLE) {
|
||||||
|
nextEl = DAH;
|
||||||
|
} else if (prevEl == DAH && holding_L && nextEl == IDLE) {
|
||||||
|
nextEl = DIT;
|
||||||
|
}
|
||||||
|
toneStop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevEl = currEl;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
11
test/README
Normal file
11
test/README
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
Loading…
Reference in New Issue
Block a user