examples: Update system examples README

The following commit updates the first half of the the system
example README files. Some other changes were also made:

* Updated base_mac_address example
* Moved contents in GCOV README to GCOV docs
* Some *main.c file names updated
* Updated example README template
This commit is contained in:
Darian Leung 2019-06-26 02:41:19 +08:00
parent 61442cdcbb
commit de682a13b1
23 changed files with 1111 additions and 537 deletions

View file

@ -57,3 +57,7 @@ _For examples where ESP32 is connected with some other hardware, include a tabl
## Troubleshooting
_If there are any likely problems or errors which many users might encounter, mention them here. Remove this section for very simple examples where nothing is likely to go wrong._
## Example Breakdown
_If the example source code is lengthy, complex, or cannot be easily understood, use this section to break down and explain the source code. This can be done by breaking down the execution path step by step, or explaining what each major function/task/source file does. Add sub titles if necessary. Remove this section for very simple examples where the source code is self explanatory._

View file

@ -12,6 +12,7 @@ Developers can use this library to send application specific state of execution
1. Collecting application specific data, see :ref:`app_trace-application-specific-tracing`
2. Lightweight logging to the host, see :ref:`app_trace-logging-to-host`
3. System behavior analysis, see :ref:`app_trace-system-behaviour-analysis-with-segger-systemview`
4. Source code coverage, see :ref:`app_trace-gcov-source-code-coverage`
Tracing components when working over JTAG interface are shown in the figure below.
@ -434,3 +435,169 @@ After installing Impulse and ensuring that it can successfully load trace files
.. note::
If you have problems with visualization (no data are shown or strange behavior of zoom action is observed) you can try to delete current signal hierarchy and double click on the necessary file or port. Eclipse will ask you to create new signal hierarchy.
.. _app_trace-gcov-source-code-coverage:
Gcov (Source Code Coverage)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Basics of Gcov and Lcov
"""""""""""""""""""""""
Source code coverage is data indicating the count and frequency of every program execution path that has been taken within a program's runtime. `Gcov <https://en.wikipedia.org/wiki/Gcov>`_ is a GCC tool that, when used in concert with the compiler, can generate log files indicating the execution count of each line of a source file. The Lcov tool is similar to Gcov but is a graphical front end for Gcov, and generates code coverage reports in HTML format.
Generally, using Gcov to compile and run programs on the Host will undergo these steps:
1. Compile the source code using GCC with the ``--coverage`` option enabled. This will cause the compiler to generate a ``.gcno`` notes files during compilation. The notes files contain information to reconstruct execution path block graphs and map each block to source code line numbers. Each source file compiled with the ``--coverage`` option should have their own ``.gcno`` file of the same name (e.g., a ``main.c`` will generate a ``main.gcno`` when compiled).
2. Execute the program. During execution, the program should generate ``.gcda`` data files. These data files contain the counts of the number of times an execution path was taken. The program will generate a ``.gcda`` file for each source file compiled with the ``--coverage`` option (e.g., ``main.c`` will generate a ``main.gcda``.
3. Gcov or Lcov can be used generate a code coverage based on the ``.gcno``, ``.gcda``, and source files. Gcov will generate a text based coverage report for each source file in the form of a ``.gcov`` file, whilst Lcov will generate a coverage report in HTML format.
Gcov and Lcov in ESP-IDF
""""""""""""""""""""""""
Using Gcov in ESP-IDF is complicated by the fact that the program is running remotely from the Host (i.e., on the target). The code coverage data (i.e., the ``.gcda`` files) is initially stored on the target itself. OpenOCD is then used to dump the code coverage data from the target to the host via JTAG during runtime. Using Gcov in ESP-IDF can be split into the following steps.
1. :ref:`app_trace-gcov-setup-project`
2. :ref:`app_trace-gcov-dumping-data`
3. :ref:`app_trace-gcov-generate-report`
.. _app_trace-gcov-setup-project:
Setting Up a Project for Gcov
"""""""""""""""""""""""""""""
Compiler Option
~~~~~~~~~~~~~~~
In order to obtain code coverage data in a project, one or more source files within the project must be compiled with the ``--coverage`` option. In ESP-IDF, this can be achieved at the component level or the individual source file level:
To cause all source files in a component to be compiled with the ``--coverage`` option.
- Add ``target_compile_options(${COMPONENT_LIB} PRIVATE --coverage)`` to the ``CMakeLists.txt`` file of the component if using CMake.
- Add ``CFLAGS += --coverage`` to the ``component.mk`` file of the component if using Make.
To cause a select number of source files (e.g. ``sourec1.c`` and ``source2.c``) in the same component to be compiled with the ``--coverage`` option.
- Add ``set_source_files_properties(source1.c source2.c PROPERTIES COMPILE_FLAGS --coverage)`` to the ``CMakeLists.txt`` file of the component if using CMake.
- Add ``source1.o: CFLAGS += --coverage`` and ``source2.o: CFLAGS += --coverage`` to the ``component.mk`` file of the component if using Make.
When a source file is compiled with the ``--coverage`` option (e.g. ``gcov_example.c``), the compiler will generate the ``gcov_example.gcno`` file in the project's build directory.
Project Configuration
~~~~~~~~~~~~~~~~~~~~~
Before building a project with source code coverage, ensure that the following project configuration options are enabled by running ``idf.py menuconfig`` (or ``make menuconfig`` if using the legacy Make build system).
- Enable the application tracing module by choosing *Trace Memory* for the :ref:`CONFIG_ESP32_APPTRACE_DESTINATION` option.
- Enable Gcov to host via the :ref:`CONFIG_ESP32_GCOV_ENABLE`
.. _app_trace-gcov-dumping-data:
Dumping Code Coverage Data
""""""""""""""""""""""""""
Once a project has been complied with the ``--coverage`` option and flashed onto the target, code coverage data will be stored internally on the target (i.e., in trace memory) whilst the application runs. The process of transferring code coverage data from the target to the Host is know as dumping.
The dumping of coverage data is done via OpenOCD (see :doc:`JTAG Debugging <../api-guides/jtag-debugging/index>` on how to setup and run OpenOCD). A dump is triggered by issuing commands to OpenOCD, therefore a telnet session to OpenOCD must be opened to issue such commands (run ``telnet localhost 4444``). Note that GDB could be used instead of telnet to issue commands to OpenOCD, however all commands issued from GDB will need to be prefixed as ``mon <oocd_command>``.
When the target dumps code coverage data, the ``.gcda`` files are stored in the project's build directory. For example, if ``gcov_example_main.c`` of the ``main`` component was compiled with the ``--coverage`` option, then dumping the code coverage data would generate a ``gcov_example_main.gcda`` in ``build/esp-idf/main/CMakeFiles/__idf_main.dir/gcov_example_main.c.gcda`` (or ``build/main/gcov_example_main.gcda`` if using the legacy Make build system). Note that the ``.gcno`` files produced during compilation are also placed in the same directory.
The dumping of code coverage data can be done multiple times throughout an application's life time. Each dump will simply update the ``.gcda`` file with the newest code coverage information. Code coverage data is accumulative, thus the newest data will contain the total execution count of each code path over the application's entire lifetime.
ESP-IDF supports two methods of dumping code coverage data form the target to the host:
* Instant Run-Time Dump
* Hard-coded Dump
Instant Run-Time Dump
~~~~~~~~~~~~~~~~~~~~~
An Instant Run-Time Dump is triggered by calling the ``esp32 gcov`` OpenOCD command (via a telnet session). Once called, OpenOCD will immediately preempt the ESP32's current state and execute a builtin IDF Gcov debug stub function. The debug stub function will handle the dumping of data to the Host. Upon completion, the ESP32 will resume it's current state.
.. note::
Due to the use of the debug stub function, the OpenOCD Debug Stub option must be enabled in project configuration. The option can be found under ``Component config -> ESP32-specific -> OpenOCD debug stubs``.
Hard-coded Dump
~~~~~~~~~~~~~~~
A Hard-coded Dump is triggered by the application itself by calling :cpp:func:`esp_gcov_dump` from somewhere within the application. When called, the application will halt and wait for OpenOCD to connect and retrieve the code coverage data. Once :cpp:func:`esp_gcov_dump` is called, the Host must execute the ``esp32 gcov dump`` OpenOCD command (via a telnet session). The ``esp32 gcov dump`` command will cause OpenOCD to connect to the ESP32, retrieve the code coverage data, then disconnect from the ESP32 thus allowing the application to resume. Hard-coded Dumps can also be triggered multiple times throughout an application's lifetime.
Hard-coded dumps are useful if code coverage data is required at certain points of an application's lifetime by placing :cpp:func:`esp_gcov_dump` where necessary (e.g., after application initialization, during each iteration of an application's main loop).
GDB can be used to set a breakpoint on :cpp:func:`esp_gcov_dump`, then call ``mon esp32 gcov dump`` automatically via the use a ``gdbinit`` script (see Using GDB from :ref:`jtag-debugging-using-debugger-command-line`).
The following GDB script is will add a breakpoint at :cpp:func:`esp_gcov_dump`, then call the ``mon esp32 gcov dump`` OpenOCD command.
.. code-block:: none
b esp_gcov_dump
commands
mon esp32 gcov dump
end
.. note::
Note that all OpenOCD commands should be invoked in GDB as: ``mon <oocd_command>``.
.. _app_trace-gcov-generate-report:
Generating Coverage Report
""""""""""""""""""""""""""
Once the code coverage data has been dumped, the ``.gcno``, ``.gcda`` and the source files can be used to generate a code coverage report. A code coverage report is simply a report indicating the number of times each line in a source file has been executed.
Both Gcov and Lcov (along with genhtml) can be used to generate code coverage reports. Gcov is provided along with the Xtensa toolchian, whilst Lcov may need to be installed separately. For details on how to use Gcov or Lcov, refer to `Gcov documentation <https://gcc.gnu.org/onlinedocs/gcc/Gcov.html>`_ and `Lcov documentation <https://linux.die.net/man/1/lcov>`_.
.. note::
There is currently no support for Lcov for the CMake build system on Windows.
Adding Lcov Build Target to Project
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To make report generation more convenient, users can define additional build targets in their projects such report generation can be done with a single build command.
CMake Build System
******************
For the CMake build systems, add the following lines to the ``CMakeLists.txt`` file of your project.
.. code-block:: none
include($ENV{IDF_PATH}/tools/cmake/gcov.cmake)
idf_create_coverage_report(${CMAKE_CURRENT_BINARY_DIR}/coverage_report)
idf_clean_coverage_report(${CMAKE_CURRENT_BINARY_DIR}/coverage_report)
The following commands can now be used:
* ``cmake --build build/ --target lcov-report`` will generate an HTML coverga report in ``$(BUILD_DIR_BASE)/coverage_report/html`` directory.
* ``cmake --build build/ --target cov-data-clean`` will remove all coverage data files and report.
Make Build System
*****************
For the Make build systems, add the following lines to the ``Makefile`` of your project.
.. code-block:: none
GCOV := $(call dequote,$(CONFIG_SDK_TOOLPREFIX))gcov
REPORT_DIR := $(BUILD_DIR_BASE)/coverage_report
lcov-report:
echo "Generating coverage report in: $(REPORT_DIR)"
echo "Using gcov: $(GCOV)"
mkdir -p $(REPORT_DIR)/html
lcov --gcov-tool $(GCOV) -c -d $(BUILD_DIR_BASE) -o $(REPORT_DIR)/$(PROJECT_NAME).info
genhtml -o $(REPORT_DIR)/html $(REPORT_DIR)/$(PROJECT_NAME).info
cov-data-clean:
echo "Remove coverage data files..."
find $(BUILD_DIR_BASE) -name "*.gcda" -exec rm {} +
rm -rf $(REPORT_DIR)
.PHONY: lcov-report cov-data-clean
The following commands can now be used:
* ``make lcov-report`` will generate an HTML coverga report in ``$(BUILD_DIR_BASE)/coverage_report/html`` directory.
* ``make cov-data-clean`` will remove all coverage data files and report.

View file

@ -1,97 +1,45 @@
# Example: Application Level Tracing - Logging to Host (app_trace_to_host)
# Application Level Tracing Example (Logging to Host)
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates how to use the [Application Level Tracing Library](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#) (henceforth referred to as **App Trace**) to log messages to a host via JTAG instead of the normal method of logging via UART.
This example will show how to perform high speed logging by redirecting the log messages to a host instead of UART.
UART logs are time consuming and can significantly slow down the function that calls it. Therefore, it is generally a bad idea to use UART logs in time-critical functions. Logging to host via JTAG is significantly faster and can be used in time-critical functions. For more details regarding logging to host via JTAG, refer to the [Logging to Host Documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#app-trace-logging-to-host).
For more description of [logging to host](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#logging-to-host) please refer to **ESP-IDF Programming Guide**, section **Application Level Tracing library**. The following example provides practical implementation of this functionality.
This example demonstrates JTAG logging to host in the context of polling for a [zero crossing](https://en.wikipedia.org/wiki/Zero_crossing). The ESP32 will continuously sample a 50 to 60 Hz sinusoidal signal (using the ADC) and log the sampled values (via JTAG). Due to the higher speed of JTAG logging, the polling rate of the ADC should be high enough to detect a zero crossing.
### Use Case
Debugging of time critical functions may not work as desired if log messages are sent through the UART port. Printing out the logs may considerably slow down tested function to the point where it will not operate as expected.
Let's consider a case we are testing implementation of [zero level crossing](https://en.wikipedia.org/wiki/Zero_crossing) detection for a 50 Hz signal with ESP32's ADC.
We will start by checking if we can read ADC, what is the signal level and how many samples can be collected over 20 ms period by using a code snippet below:
```c
int sampling_period = 20;
int i = 0;
uint32_t sampling_start = esp_log_timestamp(); //this clock counts miliseconds
do {
ESP_LOGI(TAG, "Sample:%d, Value:%d", ++i, adc1_get_raw(ADC1_TEST_CHANNEL));
} while (esp_log_timestamp() - sampling_start < sampling_period);
```
Above code is sampling ADC in a loop continuously for 20 ms of time to collect one full period of 50 Hz signal (use 17 ms for 60 Hz signal), and printing out results. If you run this code, result will be likely as below:
```bash
I (4309) example: Sample:1, Value:2224
I (4309) example: Sample:2, Value:840
I (4309) example: Sample:3, Value:3503
I (4319) example: Sample:4, Value:27
I (4319) example: Sample:5, Value:4095
```
As you see we were able to collect only five samples. This seems rather not adequate for zero crossing detection.
We can remove `ESP_LOGI()` line and sample much faster, but then will not be able to see the values. To see the values we would need to save them in the memory and print out later.
Instead of saving samples to memory, a simple and compelling solution to this issue is sending logs to a host over a JTAG interface. This works much faster than UART. To do so, we need to redirect log messages to JTAG by calling the following function before `ESP_LOGx` line:
```c
esp_log_set_vprintf(esp_apptrace_vprintf);
```
Once time critical messages are sent out, we can redirect `ESP_LOGx` back back to the UART by adding extra two lines of code.
```c
esp_log_set_vprintf(vprintf);
esp_apptrace_flush(ESP_APPTRACE_DEST_TRAX, 100000);
```
Note command `esp_apptrace_flush()` used to empty buffer with pending messages to the host.
If checking the log received by the host, we will see over 400 samples collected!
```bash
...
I (59379) example: Sample:424, Value:298
I (59379) example: Sample:425, Value:345
I (59379) example: Sample:426, Value:386
I (59379) example: Sample:427, Value:432
I (61409) example: Sample:1, Value:2653
```
This example utilizes the following ESP-IDF features:
* [DAC driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/dac.html) to generate the 50 Hz sinusoidal signal.
* [ADC driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/adc.html) to sample the sinusoidal signal.
* [Application Level Tracing Library](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#) to log ADC samples to host.
* [OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#setup-of-openocd) to interface with the ESP32 and receive the log output over JTAG.
## How to use example
### Hardware Required
To run this example, you need an ESP32 dev board (e.g. [ESP-WROVER-KIT](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v4-1)) which integrates an on-board JTAG adapter. ESP32 core board (e.g. ESP32-DevKitC) can also work as long as you connect it to an external JTAG adapter (e.g. FT2232H, J-LINK). For convenience, here we will take the [ESP-WROVER-KIT](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v4-1) for example.
To run this example, you need an ESP32 dev board connected to a JTAG adapter, which can come in the following forms:
* [ESP-WROVER-KIT](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v4-1) which integrates an on-board JTAG adapter. Ensure that the [required jumpers to enable JTAG are connected](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/get-started-wrover-kit.html#setup-options) on the WROVER-KIT.
* ESP32 core board (e.g. ESP32-DevKitC) can also work as long as you connect it to an external JTAG adapter (e.g. FT2232H, J-LINK).
This example will assume that that an ESP-WROVER-KIT is used.
#### Pin Assignment:
**Note:** Connect 50 / 60 Hz sinusoidal signal to ADC1_CHANNEL_6 / GPIO34. The signal range should be from 0V to max 3.1V. Optionally you can bridge GPIO34 with DAC_CHANNEL_1 / GPIO25.
The sinusoidal signal of 50 to 60 Hz ranging from 0 V ~ 3.1 V should be input into `GPIO34` (`ADC1_CHANNEL_6`). Users may provide this signal themselves, our use the DAC generated signal by bridging GPIO34 with `GPIO25` (`DAC_CHANNEL_1`).
| DAC Output | ADC Input |
| ----------------- | ----------------- |
| Channel1 (GPIO25) | Channel6 (GPIO34) |
| DAC Output | ADC Input |
| ------------------ | ------------------ |
| Channel 1 (GPIO25) | Channel 6 (GPIO34) |
#### Extra Connections:
1. Connect JTAG interface to ESP32 board, power up both JTAG and ESP32. For details about how to set up JTAG interface, please see [JTAG Debugging](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html).
1. Connect the JTAG interface to ESP32 board, and power up both the JTAG and ESP32. For details about how to set up JTAG interface, please see [JTAG Debugging](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html).
2. After connecting JTAG interface, you need to [Run OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#run-openocd). If you are using the [Binary Distribution of OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#jtag-debugging-setup-openocd) and one of versions of [ESP-WROVER-KIT](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v4-1), respective command line will look as follows:
2. After connecting JTAG interface, you need to [Run OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#run-openocd).
```bash
cd ~/esp/openocd-esp32
bin/openocd -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg
```
3. Open a separate terminal window and run telnet by entering:
3. Open a separate terminal window and run telnet by entering the command below. The telnet terminal window is used to feed commands to OpenOCD:
```bash
telnet localhost 4444
@ -99,84 +47,39 @@ To run this example, you need an ESP32 dev board (e.g. [ESP-WROVER-KIT](https://
### Configure the project
Open the project configuration menu (`idf.py menuconfig`). Then go into `Example Configuration` menu.
```
idf.py menuconfig
```
- By default, the DAC will generate 130 Hz signal within 0V..3.1V. To get 50Hz, you need to set non-standard driver of RTC 8MHz clock to lower minimum CW (Cosine Waveform) generator's frequency in `Set custom RTC 8 MHz clock divider to lower CW frequency`.
* By default, the DAC will generate 130 Hz signal ranging from 0 V ~ 3.1 V. To generate a 50 Hz signal, the RTC 8 MHz clock will need to use a non-standard divider. This is achieved by enabling the `Example Configuration > Set custom RTC 8 MHz clock divider to lower CW frequency` configuration option.
**Note** To enable application tracing, go to `Component config > Application Level Tracing` and select `(X) Trace memory`. (By default, this option has been switched on in the sdkconfig.defaults).
* To enable application tracing, select the `(X) Trace memory` option under `Component config > Application Level Tracing`. This option should have been selected by default.
### Build, Flash and Run
### Build, Flash, and Run
1. Run `idf.py -p PORT flash monitor` to build and flash the project.. (To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
Build the project and flash it to the board, then run monitor tool to view serial output:
2. In the telnet session window, run the following command. This command will collect 9000 bytes of log data and save them to `adc.log` file in `~/esp/openocd-esp32` folder.
```
idf.py -p PORT flash monitor
```
```bash
esp32 apptrace start file://adc.log 0 9000 5 0 0
```
(Replace PORT with the name of the serial port to use.)
3. Decode and print out retrieved log file by executing:
**Start App Trace:** In the telnet session window, trigger OpenOCD to start App Trace on the ESP32 by entering the command below. This command will collect 9000 bytes of JTAG log data and save them to `adc.log` file in `~/esp/openocd-esp32` folder.
```bash
$IDF_PATH/tools/esp_app_trace/logtrace_proc.py ~/esp/openocd-esp32/adc.log ~/esp/app_trace_to_host/build/app_trace_to_host_test.elf
```
```bash
esp32 apptrace start file://adc.log 0 9000 5 0 0
```
This should provide a similar output:
**Note:** For more details on OpenOCD commands regarding App Trace, refer to the [OpenOCD Application Level Tracing Commands](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#openocd-application-level-tracing-commands)
```
Parse trace file '/user-home/esp/openocd-esp32/adc.log'...
Unprocessed 7 bytes of log record args!
Parsing completed.
====================================================================
I (59369) example: Sample:1, Value:3717
I (59369) example: Sample:2, Value:3647
I (59369) example: Sample:3, Value:3575
I (59369) example: Sample:4, Value:3491
...
(To exit the serial monitor, type ``Ctrl-]``.)
I (59379) example: Sample:398, Value:78
I (59379) example: Sample:399, Value:58
I (59379) example: Sample:400, Value:22
I (59379) example: Sample:401, Value:14
I (59379) example: Sample:402, Value:0
I (59379) example: Sample:403, Value:0
I (59379) example: Sample:404, Value:0
I (59379) example: Sample:405, Value:0
I (59379) example: Sample:406, Value:0
I (59379) example: Sample:407, Value:0
I (59379) example: Sample:408, Value:0
I (59379) example: Sample:409, Value:0
I (59379) example: Sample:410, Value:0
I (59379) example: Sample:411, Value:0
I (59379) example: Sample:412, Value:0
I (59379) example: Sample:413, Value:0
I (59379) example: Sample:414, Value:16
I (59379) example: Sample:415, Value:32
I (59379) example: Sample:416, Value:40
I (59379) example: Sample:417, Value:74
I (59379) example: Sample:418, Value:89
I (59379) example: Sample:419, Value:113
I (59379) example: Sample:420, Value:160
I (59379) example: Sample:421, Value:192
I (59379) example: Sample:422, Value:221
I (59379) example: Sample:423, Value:256
I (59379) example: Sample:424, Value:298
I (59379) example: Sample:425, Value:345
I (59379) example: Sample:426, Value:386
I (59379) example: Sample:427, Value:432
I (61409) example: Sample:1, Value:2653
====================================================================
Log records count: 428
```
This is the log we have been looking for, complete with timestamps as if printed to UART, but almost two orders of magnitude faster.
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
Check the full example code [app_trace_to_host](main/app_trace_to_host_test.c) that combines both tests above and runs them in a loop showing instantly the number of samples collected:
The example will continuously sample the ADC for 20ms per iteration, and will alternate between JTAG and UART logging per iteration. However, the JTAG logs should be captured by OpenOCD, thus will not appear in the monitor's output. Therefore, the monitor should only display the iterations where UART logging was used (i.e. every alternate iteration) such as the following:
```
I (4289) example: Sampling ADC and sending data to the host...
@ -191,15 +94,120 @@ I (4319) example: Sample:5, Value:4095
I (4329) example: Collected 5 samples in 20 ms.
```
**note** Above result is generated under CPU running at 240 MHz.
**Note:** The UART log above was produced with the CPU running at 240 MHz.
## Conclusion
To access the JTAG logs, the `adc.log` file should be decoded. This can be done by using the `logtrace_proc.py` script as such:
With this example code we have demonstrated powerful functionality of logging to host with JTAG interface. With standard UART communication baud rate setting of 115200, printing out a single line of a log message takes about 4 ms. This is also the maximum period we can sequentially execute other tasks in between. By providing the same logging over JTAG, we will be able to improve performance of this process over 80 times.
```bash
$IDF_PATH/tools/esp_app_trace/logtrace_proc.py ~/esp/openocd-esp32/adc.log ~/esp/app_trace_to_host/build/app_trace_to_host_test.elf
```
The `logtrace_proc.py` script should produce the following output when decoding:
```
Parse trace file '/user-home/esp/openocd-esp32/adc.log'...
Unprocessed 7 bytes of log record args!
Parsing completed.
====================================================================
I (59369) example: Sample:1, Value:3717
I (59369) example: Sample:2, Value:3647
I (59369) example: Sample:3, Value:3575
I (59369) example: Sample:4, Value:3491
...
I (59379) example: Sample:398, Value:78
I (59379) example: Sample:399, Value:58
I (59379) example: Sample:400, Value:22
I (59379) example: Sample:401, Value:14
I (59379) example: Sample:402, Value:0
I (59379) example: Sample:403, Value:0
I (59379) example: Sample:404, Value:0
I (59379) example: Sample:405, Value:0
I (59379) example: Sample:406, Value:0
I (59379) example: Sample:407, Value:0
I (59379) example: Sample:408, Value:0
I (59379) example: Sample:409, Value:0
I (59379) example: Sample:410, Value:0
I (59379) example: Sample:411, Value:0
I (59379) example: Sample:412, Value:0
I (59379) example: Sample:413, Value:0
I (59379) example: Sample:414, Value:16
I (59379) example: Sample:415, Value:32
I (59379) example: Sample:416, Value:40
I (59379) example: Sample:417, Value:74
I (59379) example: Sample:418, Value:89
I (59379) example: Sample:419, Value:113
I (59379) example: Sample:420, Value:160
I (59379) example: Sample:421, Value:192
I (59379) example: Sample:422, Value:221
I (59379) example: Sample:423, Value:256
I (59379) example: Sample:424, Value:298
I (59379) example: Sample:425, Value:345
I (59379) example: Sample:426, Value:386
I (59379) example: Sample:427, Value:432
I (61409) example: Sample:1, Value:2653
====================================================================
Log records count: 428
```
## Troubleshooting
1. I can not flash new firmware when OpenOCD is connected to ESP32.
* One likely cause would be that you set wrong SPI flash voltage when you start OpenOCD. Suppose you're working with an ESP32 board / module which has a 3.3V powered SPI flash, but you select
`board/esp32-wrover.cfg` configuration file when start OpenOCD. In this situation, you might not be able to flash ESP32 when OpenOCD is connected. So make sure what the working voltage of the SPI flash is. Currently, for 1.8V flash, we'd like to suggest using `board/esp32-wrover.cfg` and for 3.3V flash, using `board/esp-wroom-32.cfg`. For more information about it, please refer to [ESP32 Modules and Boards](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html) and [Set SPI Flash Voltage](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/tips-and-quirks.html#why-to-set-spi-flash-voltage-in-openocd-configuration).
### Unable to flash when OpenOCD is connected to ESP32
One likely cause would be an incorrect SPI flash voltage when starting OpenOCD. Suppose an ESP32 board/module with a 3.3 V powered SPI flash is being used, but the `board/esp32-wrover.cfg` configuration file is selected when starting OpenOCD which can set the SPI flash voltage to 1.8 V. In this situation, the SPI flash will not work after OpenOCD connects to the ESP32 as OpenOCD has changed the SPI flash voltage. Therefore, you might not be able to flash ESP32 when OpenOCD is connected.
To work around this issue, users are suggested to use `board/esp32-wrover.cfg` for ESP32 boards/modules operating with an SPI flash voltage of 1.8 V, and `board/esp-wroom-32.cfg` for 3.3 V. Refer to [ESP32 Modules and Boards](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html) and [Set SPI Flash Voltage](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/tips-and-quirks.html#why-to-set-spi-flash-voltage-in-openocd-configuration) for more details.
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
The log should be identical to those printed via UART (complete with timestamps), but almost two orders of magnitude faster.
## Example Breakdown
The following code snippet demonstrates a loop of the sampling and logging the ADC over a 20 ms period in order to capture one full period of a 50 Hz signal.
```c
int sampling_period = 20;
int i = 0;
uint32_t sampling_start = esp_log_timestamp(); //this clock counts milliseconds
do {
ESP_LOGI(TAG, "Sample:%d, Value:%d", ++i, adc1_get_raw(ADC1_TEST_CHANNEL));
} while (esp_log_timestamp() - sampling_start < sampling_period);
```
If `ESP_LOGI()` is routed via UART (occurs by default), the log output produced will likely resemble the output shown below. Notice that due to UART logging is time consuming, thus the ADC is only sampled five times, which is too infrequent to consistently detect a zero crossing (where the zero crossing is `4096/2 = 2048` i.e., the mid point of the 12-bit ADC).
```bash
I (4309) example: Sample:1, Value:2224
I (4309) example: Sample:2, Value:840
I (4309) example: Sample:3, Value:3503
I (4319) example: Sample:4, Value:27
I (4319) example: Sample:5, Value:4095
I (4329) example: Collected 5 samples in 20 ms.
```
However, by logging via JTAG, the logging is much quicker hence allows a much higher sampling frequency (over 400 times) as shown the the log output below thus would be able to detect a zero crossing more consistently.
```c
esp_log_set_vprintf(esp_apptrace_vprintf);
```
```bash
...
I (59379) example: Sample:423, Value:256
I (59379) example: Sample:424, Value:298
I (59379) example: Sample:425, Value:345
I (59379) example: Sample:426, Value:386
I (59379) example: Sample:427, Value:432
I (61409) example: Sample:1, Value:2653
====================================================================
Log records count: 428
```
This example has demonstrated powerful functionality of logging to host via JTAG interface. With standard UART communication at a baud rate of 115200, printing out a single line log message takes approximately 4 ms. This also means that logged tasks cannot run more frequently than every 4 ms. By providing the same logging over JTAG, logging performance is improved 80 fold.

View file

@ -1,4 +1,93 @@
# Example: base mac address
# Base MAC Address
This example illustrates how to get and set base MAC address.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
Each network interface on the ESP32 (e.g., WiFi Station/AP, BT, Ethernet) must be assigned a unique Medium Access Control (MAC) address. Each interface's MAC address is [derived from a base MAC address](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/system.html#mac-address) that must be set before any network interface's initialization is called.
This example demonstrates the following:
1. How to retrieve a base MAC address from non-volatile memory
2. How to set the base MAC address
3. How to obtain the derived MAC addresses of each network interface
This example utilizes the [MAC Address API](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/system.html#mac-address).
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP32 development board. However, if an external source of non-volatile memory is used to store the base MAC address (e.g. an I2C Serial EEPROM), the user should wire the connections to their chosen external storage as needed.
### Configure the project
```
idf.py menuconfig
```
* To select the storage source of the base MAC address, go to `Example Configuration > Storage location of the base MAC address` where the following options are available:
* `eFuse BLK0` will cause this example to read the base MAC address from eFuse Block 0 and is selected by default. The `eFuse BLK0` MAC address is factory programmed into each ESP32.
* `eFuse BLK3` will cause this example to read the base MAC address from words 1 & 2 of eFuse Block 3. `eFuse BLK3` allows users to use a custom eFuse, but must be burned into the ESP32 using the [espefuse tool](https://github.com/espressif/esptool/wiki/espefuse). Attempting to read `eFuse BLK3` without burning the eFuse will result in an error.
* `Other external storage` will call a `external_storage_mac_get()` which will merely retrieve an arbitrary base MAC address that is set in software. Users should re-implement `external_storage_mac_get()` to access their chosen external storage (e.g. an I2C Serial EEPROM)
* If `eFuse BLK3` is chosen, the `Behavior when retrieving BLK3 eFuse fails` option may also be selected. When retrieving the base MAC address from `eFuse BLK3` without burning eFuse Block 3, the example can either abort or default to retrieving from eFuse Block 0.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
Depending on the `Storage of the base MAC address` configuration option, the console should output one of the following logs to indicate the source of the base MAC address.
```
I (288) BASE_MAC: Base MAC Address read from EFUSE BLK0
...
I (288) BASE_MAC: Base MAC Address read from EFUSE BLK3
...
I (288) BASE_MAC: Base MAC Address read from external storage
```
The console will also output the retrieved 6 byte MAC address when it sets it as the system wide base MAC address.
```
I (298) BASE_MAC: Using "0xd8, 0xa0, 0x1d, 0x0, 0xb, 0x88" as base MAC address
```
The console will then output the derived MAC address of each network interface as such:
```
I (297) WIFI_STA MAC: 0x0, 0x11, 0x22, 0x33, 0x44, 0x55
I (307) SoftAP MAC: 0x0, 0x11, 0x22, 0x33, 0x44, 0x56
I (317) BT MAC: 0x0, 0x11, 0x22, 0x33, 0x44, 0x57
I (317) Ethernet MAC: 0x0, 0x11, 0x22, 0x33, 0x44, 0x58
```
## Troubleshooting
When attempting to retrieve the base MAC address from `eFuse BLK3` without first burning eFuse Block 3 will result in one of the following situations.
If the example has been configured to abort when retrieving from `eFuse BLK3` fails.
```
E (288) system_api: Base MAC address from BLK3 of EFUSE version error, version = 0
E (298) BASE_MAC: Failed to get base MAC address from EFUSE BLK3. (ESP_ERR_INVALID_VERSION)
E (308) BASE_MAC: Aborting
abort() was called at PC 0x400d237b on core 0
```
If not configured to abort, the example will default to `eFuse BLK0` and output the following instead.
```
E (288) system_api: Base MAC address from BLK3 of EFUSE version error, version = 0
E (298) BASE_MAC: Failed to get base MAC address from EFUSE BLK3. (ESP_ERR_INVALID_VERSION)
I (308) BASE_MAC: Defaulting to base MAC address in BLK0 of EFUSE
I (308) BASE_MAC: Base MAC Address read from EFUSE BLK0
```

View file

@ -1,19 +1,22 @@
menu "Example Configuration"
choice BASE_MAC_ADDRESS_STORAGE
prompt "Storage of the base MAC address"
prompt "Storage location of the base MAC address"
default BASE_MAC_STORED_EFUSE_BLK0
help
Select storage of the base MAC address which is used to generate MAC addresses of all network interfaces
when networking is initialized.
If "Default (Espressif factory) eFuse BLK0" is selected, esp32 will use default base MAC address which is
written into eFuse block 0 words 1, 2 when the chip is manufactured.
If "Custom eFuse BLK3" is selected, ESP32 will use customer-defined base MAC address which is written into
eFuse Block 3 words 0, 1. Users must call esp_efuse_mac_get_custom to get base MAC address and
esp_base_mac_addr_set to set the base MAC address before network interfaces are initialised.
If "Other external storage" is selected, esp32 will use customer-defined base MAC address from external
storage(flash, EEPROM, etc). Users must get the base MAC address first and call esp_base_mac_addr_set to
set the base MAC address before network interfaces are initialised.
Select the storage location of the base MAC addresses.
1. eFuse BLK0: The "Default (Espressif factory)" selection. The
default base MAC address is written to words 1 and 2 of eFuse block
0 when the chip was manufactured. Call esp_efuse_mac_get_default()
read retrieve the "eFuse BLK0" MAC address.
2. eFuse BLK3: A custom base MAC address is burned by the user into
eFuse word 0 of block 3. Call esp_efuse_mac_get_custom() to read
the "eFuse BLK3" MAC address.
3. Other External Storage: Selecting this option will cause the
example to call external_storage_mac_get() which is defined in this
example to simply return a MAC address preset in software. Users
should modify this function to access their desired storage mediums
(e.g. flash, EEPROM etc).
config BASE_MAC_STORED_EFUSE_BLK0
bool "Default (Espressif factory) eFuse BLK0"
@ -24,19 +27,20 @@ menu "Example Configuration"
endchoice
choice BASE_MAC_STORED_EFUSE_BLK3_ERROR_BEHAV
prompt "Read base MAC address from BLK3 of eFuse error behavior"
prompt "Behavior when retrieving eFuse BLK3 fails"
depends on BASE_MAC_STORED_EFUSE_BLK3
default BASE_MAC_STORED_EFUSE_BLK3_ERROR_DEFAULT
help
Select behavior when reading base MAC address from BLK3 of eFuse error.
If "Abort" is selected, esp32 will abort.
If "Use base MAC address from BLK3 of eFuse" is selected, esp32 will use the base MAC address which is
written into eFuse block 0 words 1, 2 when the chip is manufactured.
Select the behavior when reading base MAC address "eFuse BLK3" fails
(i.e. the retrieved result is all 0).
- If "Abort" is selected, the ESP32 will abort.
- If "Use the default base MAC address from BLK0 of eFuse" is
selected, the default "eFuse BLK0" will be used instead.
config BASE_MAC_STORED_EFUSE_BLK3_ERROR_ABORT
bool "Abort"
config BASE_MAC_STORED_EFUSE_BLK3_ERROR_DEFAULT
bool "Use base MAC address from BLK3 of eFuse"
bool "Use the default base MAC address eFuse BLK0"
endchoice
endmenu

View file

@ -15,11 +15,13 @@
#define TAG "BASE_MAC"
#ifdef CONFIG_BASE_MAC_STORED_OTHER_EXTERNAL_STORAGE
/* This is an example to show getting base MAC address from other external storage (flash, EEPROM, etc). */
static esp_err_t external_storage_mac_get(uint8_t *mac)
{
uint8_t external_storage_mac_addr[8] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
/* This function simulates getting a base MAC address from external storage
* by simply setting the base MAC to an arbitrary address. Users should
* re-implement this function to access external storage (e.g. flash, EEPROM) */
uint8_t external_storage_mac_addr[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
if (mac == NULL) {
ESP_LOGE(TAG, "The mac parameter is NULL");
abort();
@ -32,45 +34,75 @@ static esp_err_t external_storage_mac_get(uint8_t *mac)
void app_main(void)
{
#if defined(CONFIG_BASE_MAC_STORED_EFUSE_BLK3) || defined(CONFIG_BASE_MAC_STORED_OTHER_EXTERNAL_STORAGE)
uint8_t mac_addr[8] = {0};
//Get the base MAC address from different sources
uint8_t base_mac_addr[6] = {0};
esp_err_t ret = ESP_OK;
#ifdef CONFIG_BASE_MAC_STORED_EFUSE_BLK3
/* Get base MAC address from BLK3 of EFUSE */
ret = esp_efuse_mac_get_custom(mac_addr);
//Get base MAC address from EFUSE BLK3
ret = esp_efuse_mac_get_custom(base_mac_addr);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Get base MAC address from BLK3 of EFUSE error (%s)", esp_err_to_name(ret));
/* If get custom base MAC address error, the application developer can decide what to do:
* abort or use the default base MAC address which is stored in BLK0 of EFUSE by doing
* nothing.
*/
ESP_LOGE(TAG, "Failed to get base MAC address from EFUSE BLK3. (%s)", esp_err_to_name(ret));
#ifdef CONFIG_BASE_MAC_STORED_EFUSE_BLK3_ERROR_ABORT
ESP_LOGE(TAG, "Aborting");
abort();
#else
ESP_LOGI(TAG, "Use base MAC address which is stored in BLK0 of EFUSE");
ESP_LOGI(TAG, "Defaulting to base MAC address in BLK0 of EFUSE");
esp_efuse_mac_get_default(base_mac_addr);
ESP_LOGI(TAG, "Base MAC Address read from EFUSE BLK0");
#endif//CONFIG_BASE_MAC_STORED_EFUSE_BLK3_ERROR_ABORT
} else {
ESP_LOGI(TAG, "Base MAC Address read from EFUSE BLK3");
}
else {
esp_base_mac_addr_set(mac_addr);
ESP_LOGI(TAG, "Use base MAC address which is stored in BLK3 of EFUSE");
#elif defined(CONFIG_BASE_MAC_STORED_OTHER_EXTERNAL_STORAGE)
//Get base MAC address from other external storage, or set by software
ret = external_storage_mac_get(base_mac_addr);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get base MAC address from external storage. (%s)", esp_err_to_name(ret));
ESP_LOGE(TAG, "Aborting");
abort();
} else {
ESP_LOGI(TAG, "Base MAC Address read from external storage");
}
#endif//CONFIG_BASE_MAC_STORED_EFUSE_BLK3
#ifdef CONFIG_BASE_MAC_STORED_OTHER_EXTERNAL_STORAGE
/* the application developer can get base MAC address which is stored in other external
* storage (flash, EEPROM, etc) by calling some functions here.
*/
ret = external_storage_mac_get(mac_addr);
if (ret == ESP_OK) {
esp_base_mac_addr_set(mac_addr);
ESP_LOGI(TAG, "Use base MAC address which is stored in other external storage(flash, EEPROM, etc)");
}
else {
ESP_LOGI(TAG, "Use base MAC address which is stored in BLK0 of EFUSE (%s)", esp_err_to_name(ret));
}
#endif//CONFIG_BASE_MAC_STORED_OTHER_EXTERNAL_STORAGE
#else
ESP_LOGI(TAG, "Use base MAC address which is stored in BLK0 of EFUSE");
#endif//CONFIG_BASE_MAC_STORED_EFUSE_BLK3 || CONFIG_BASE_MAC_STORED_OTHER_EXTERNAL_STORAGE
//Get base MAC address from EFUSE BLK0(default option)
ret = esp_efuse_mac_get_default(base_mac_addr);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get base MAC address from EFUSE BLK0. (%s)", esp_err_to_name(ret));
ESP_LOGE(TAG, "Aborting");
abort();
} else {
ESP_LOGI(TAG, "Base MAC Address read from EFUSE BLK0");
}
#endif
//Set the base MAC address using the retrieved MAC address
ESP_LOGI(TAG, "Using \"0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\" as base MAC address",
base_mac_addr[0], base_mac_addr[1], base_mac_addr[2], base_mac_addr[3], base_mac_addr[4], base_mac_addr[5]);
esp_base_mac_addr_set(base_mac_addr);
//Get the derived MAC address for each network interface
uint8_t derived_mac_addr[6] = {0};
//Get MAC address for WiFi Station interface
ESP_ERROR_CHECK(esp_read_mac(derived_mac_addr, ESP_MAC_WIFI_STA));
ESP_LOGI("WIFI_STA MAC", "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x",
derived_mac_addr[0], derived_mac_addr[1], derived_mac_addr[2],
derived_mac_addr[3], derived_mac_addr[4], derived_mac_addr[5]);
//Get MAC address for SoftAp interface
ESP_ERROR_CHECK(esp_read_mac(derived_mac_addr, ESP_MAC_WIFI_SOFTAP));
ESP_LOGI("SoftAP MAC", "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x",
derived_mac_addr[0], derived_mac_addr[1], derived_mac_addr[2],
derived_mac_addr[3], derived_mac_addr[4], derived_mac_addr[5]);
//Get MAC address for Bluetooth
ESP_ERROR_CHECK(esp_read_mac(derived_mac_addr, ESP_MAC_BT));
ESP_LOGI("BT MAC", "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x",
derived_mac_addr[0], derived_mac_addr[1], derived_mac_addr[2],
derived_mac_addr[3], derived_mac_addr[4], derived_mac_addr[5]);
//Get MAC address for Ethernet
ESP_ERROR_CHECK(esp_read_mac(derived_mac_addr, ESP_MAC_ETH));
ESP_LOGI("Ethernet MAC", "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x",
derived_mac_addr[0], derived_mac_addr[1], derived_mac_addr[2],
derived_mac_addr[3], derived_mac_addr[4], derived_mac_addr[5]);
}

View file

@ -1,42 +1,42 @@
# Console example
# Console Example
This example illustrates usage of `console` component to create an interactive shell.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Configuring UART and VFS
This example illustrates the usage of the [Console Component](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html#console) to create an interactive shell on the ESP32. The interactive shell running on the ESP32 can then be controlled/interacted with over a serial port (UART).
``initialize_console`` function configures some aspects of UART and VFS relevant to the operation of console:
The interactive shell implemented in this example contains a wide variety of commands, and can act as a basis for applications that require a command-line interface (CLI).
- By default `stdin` and `stdout` are buffered streams. This means that the text written to `stdout` will not be immediately sent to UART. This is not the desirable behavior for the console, so buffering for `stdin` and `stdout` is disabled using `setvbuf` function.
## How to use example
- Line endings are configured to match those expected/generated by common serial monitor programs, such as `screen`, `minicom`, and the `idf_monitor.py` included in the SDK. The default behavior for these commands is:
- When 'enter' key is pressed on the keyboard, `CR` (0x13) code is sent to the serial device.
- To move the cursor to the beginning of the next line, serial device needs to send `CR LF` (0x13 0x10) sequence.
### Hardware Required
- UART driver is initialized, and VFS is configured to use UART driver's interrupt-driver read and write functions.
This example should be able to run on any commonly available ESP32 development board.
## Line editing
### Configure the project
The main source file of the example illustrates how to use `linenoise` library, including line completion, hints, and history.
```
idf.py menuconfig
```
## Commands
* Enable/disable `Example Configuration > Store command history in flash` as necessary
Several commands are registered using `esp_console_cmd_register` function. See `register_wifi` and `register_system` functions in `cmd_wifi.c` and `cmd_system.c` files.
### Build and Flash
## Command handling
Build the project and flash it to the board, then run monitor tool to view serial output:
Main loop inside `app_main` function illustrates how to use `linenoise` and `esp_console_run` to implement read/eval loop.
```
idf.py -p PORT flash monitor
```
## Argument parsing
(Replace PORT with the name of the serial port to use.)
Several commands implemented in `cmd_wifi.c` and `cmd_system.c` use Argtable3 library to parse and check the arguments.
(To exit the serial monitor, type ``Ctrl-]``.)
## Command history
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
Each time a new command line is obtained from `linenoise`, it is written into history and the history is saved into a file in flash memory. On reset, history is initialized from that file.
## Example Output
# Example output
Here is an sample session with the console example. GPIO15 is connected to GND to remove boot log output.
Enter the `help` command get a full list of all available commands. The following is a sample session of the Console Example where a variety of commands provided by the Console Example are used. Note that GPIO15 is connected to GND to remove the boot log output.
```
This is an example of ESP-IDF console component.
@ -103,6 +103,49 @@ Press TAB when typing command name to auto-complete.
```
---
## Troubleshooting
See the README.md file in the upper level 'examples' directory for more information about examples.
### Line Endings
The line endings in the Console Example are configured to match particular serial monitors. Therefore, if the following log output appears, consider using a different serial monitor (e.g. Putty for Windows) or modify the example's [UART configuration](#Configuring-UART-and-VFS).
```
This is an example of ESP-IDF console component.
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
Your terminal application does not support escape sequences.
Line editing and history features are disabled.
On Windows, try using Putty instead.
esp32>
```
## Example Breakdown
### Configuring UART
The ``initialize_console()`` function in the example configures some aspects of UART relevant to the operation of the console.
- **Line Endings**: The default line endings are configured to match those expected/generated by common serial monitor programs, such as `screen`, `minicom`, and the `idf_monitor.py` included in the SDK. The default behavior for these commands are:
- When 'enter' key is pressed on the keyboard, `CR` (0x13) code is sent to the serial device.
- To move the cursor to the beginning of the next line, serial device needs to send `CR LF` (0x13 0x10) sequence.
### Line editing
The main source file of the example illustrates how to use `linenoise` library, including line completion, hints, and history.
### Commands
Several commands are registered using `esp_console_cmd_register()` function. See the `register_wifi()` and `register_system()` functions in `cmd_wifi.c` and `cmd_system.c` files.
### Command handling
Main loop inside `app_main()` function illustrates how to use `linenoise` and `esp_console_run()` to implement read/eval loop.
### Argument parsing
Several commands implemented in `cmd_wifi.c` and `cmd_system.c` use the Argtable3 library to parse and check the arguments.
### Command history
Each time a new command line is obtained from `linenoise`, it is written into history and the history is saved into a file in flash memory. On reset, history is initialized from that file.

View file

@ -6,12 +6,24 @@ This example demonstrates usage of C++ exceptions in ESP-IDF.
By default, C++ exceptions support is disabled in ESP-IDF. It can be enabled using `CONFIG_COMPILER_CXX_EXCEPTIONS` configuration option.
In this example, `sdkconfig.defaults` file sets `CONFIG_COMPILER_CXX_EXCEPTIONS` option. This enables both compile time support (`-fexceptions` compiler flag) and run-time support for C++ exception handling.
In this example, the `sdkconfig.defaults` file sets the `CONFIG_COMPILER_CXX_EXCEPTIONS` option. This enables both compile time support (`-fexceptions` compiler flag) and run-time support for C++ exception handling.
Example source code declares a class which can throw exception from the constructor, depending on the argument. It illustrates that exceptions can be thrown and caught using standard C++ facilities.
The example source code declares a class which can throw exception from the constructor if the argument provided is equal to `0`. This is used to demonstrate that exceptions can be thrown and caught using standard C++ facilities.
**Note: Due to the use of the C++ exceptions, this example is written in C++ instead of C.**
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP32 development board.
### Configure the project
```
idf.py menuconfig
```
### Build and Flash
```

View file

@ -1,5 +1,48 @@
# pthread examples
# C++ pthread Example
This example shows how to use the pthread API to create std::threads with different stack sizes, names, priorities and pinned to certain cores.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example is in C++, contrary to the the normal standard of pure C.
Support for the [C++ threads](http://www.cplusplus.com/reference/thread/thread/) in ESP-IDF is implemented on top of the [ESP-pthread](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/esp_pthread.html#overview) component. Thus, C++ threads created using the standard thread class constructor will automatically inherit the current ESP-pthread configuration. This example demonstrates how to leverage the thread configuration functions provided by ESP-pthread (e.g., `esp_pthread_get_default_config()` and `esp_pthread_set_cfg()`) to modify the stack sizes, priorities, names, and core affinities of the C++ threads.
**Note: Due to the use of the C++ threads, this example is written in C++ instead of C.**
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP32 development board.
### Configure the project
```
idf.py menuconfig
```
* The default ESP-pthread configuration may also be modified under `Component config > PThreads`
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The following log output should appear when the example runs (note that the bootloader log has been omitted).
```
...
I (380) Thread 1: Core id: 0, prio: 5, minimum free stack: 2068 bytes.
I (0) pthread: This thread (with the default name) may run on any core.Core id: 1, prio: 5, minimum free stack: 2056 bytes.
I (390) Thread 1: This is the INHERITING thread with the same parameters as our parent, including name. Core id: 0, prio: 5, minimum free stack: 2092 bytes.
I (410) Thread 2: Core id: 1, prio: 5, minimum free stack: 2088 bytes.
I (410) main: core id: 0, prio: 1, minimum free stack: 2928 bytes.
```

View file

@ -1,12 +1,87 @@
# Example: deep sleep
# Deep Sleep Example
This example illustrates usage of deep sleep mode and various wakeup sources.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The following wake up sources are configured:
The [deep sleep mode](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/sleep_modes.html#sleep-modes) of the ESP32 is a power saving mode that causes the CPU, majority of RAM, and digital peripherals that are clocked from APB_CLK to be powered off. Deep sleep mode can be exited using one of multiple [wake up sources](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/sleep_modes.html#wakeup-sources). This example demonstrates how to use the [`esp_sleep.h`](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/sleep_modes.html#api-reference) API to enter deep sleep mode, then wake up form different sources.
- Timer: wake up the chip in 20 seconds
- EXT1: wake up the chip if any of the two buttons are pressed (GPIO25, GPIO26)
- Touch: wake up the chip if any of the touch pads are pressed (GPIO32, GPIO33)
- ULP: wake up when the chip temperature changes by more than ~5 degrees Celsius (this value hasn't been characterized exactly yet).
The following wake up sources are demonstrated in this example (refer to the [Wakeup Sources documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/sleep_modes.html#wakeup-sources) for more details regarding wake up sources):
In this example, the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot.
1. **Timer:** An RTC timer that can be programmed to trigger a wake up after a preset time. This example will trigger a wake up every 20 seconds.
2. **EXT1:** External wake up 1 which is tied to multiple RTC GPIOs. This example use GPIO25 and GPIO26 to trigger a wake up with any one of the two pins are HIGH.
3. **Touch:** Touch pad sensor interrupt. This example uses touch pads connected to GPIO32 and GPIO33 to trigger a wake up when any of the pads are pressed.
4. **ULP:** Ultra Low Power Coprocessor which can continue to run during deep sleep. This example utilizes the ULP and constantly sample the chip's temperature and trigger a wake up if the chips temperature exceeds ~5 degrees Celsius.
Note: Some wake up sources can be disabled via configuration (see section on [project configuration](#Configure-the-project))
In this example, the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot.
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP32 development board without any extra hardware if only **Timer** and **ULP** wake up sources are used. However, the following extra connections will be required for the remaining wake up sources.
- **EXT1:** GPIO25 and GPIO26 should be connected to LOW to avoid floating pins. When triggering a wake up, connect one or both of then pins to HIGH. Note that floating pins may trigger an wake up.
- **Touch:** GPIO32 and GPIO33 should be connected to touch sensors (see [Touch Sensor Application Note](https://github.com/espressif/esp-iot-solution/blob/master/documents/touch_pad_solution/touch_sensor_design_en.md)).
### Configure the project
```
idf.py menuconfig
```
* **Touch wake up** can be enabled/disabled via `Example configuration > Enable touch wake up`
* **ULT wake up** can be enabled/disabled via `Example configuration > Enable temperature monitoring by ULP`
Wake up sources that are unused or unconnected should be disabled in configuration to prevent inadvertent triggering of wake up as a result of floating pins.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
On initial startup, this example will detect that this is the first boot and output the following low:
```
...
I (304) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Not a deep sleep reset
Enabling timer wakeup, 20s
Enabling EXT1 wakeup on pins GPIO25, GPIO26
Touch pad #8 average: 2148, wakeup threshold set to 2048.
Touch pad #9 average: 2148, wakeup threshold set to 2048.
Enabling touch pad wakeup
Enabling ULP wakeup
Entering deep sleep
```
The ESP32 will then enter deep sleep. When a wake up occurs, the ESP32 must undergo the entire boot process again. However the example will detect that this boot is due to a wake up and indicate the wake up source in the output log such as the following:
```
...
I (304) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Wake up from timer. Time spent in deep sleep: 20313ms
ULP did 110 temperature measurements in 20313 ms
Initial T=87, latest T=87
Enabling timer wakeup, 20s
Enabling EXT1 wakeup on pins GPIO25, GPIO26
Touch pad #8 average: 2149, wakeup threshold set to 2049.
Touch pad #9 average: 2146, wakeup threshold set to 2046.
Enabling touch pad wakeup
Enabling ULP wakeup
Entering deep sleep
```

View file

@ -1,36 +1,58 @@
# Event Loop Library ('esp_event') Default Event Loop Example
# Default Event Loop Example
This example illustrates the basics of the event loop library. To keep the example simple, demonstration is limited to the use of the default event loop.
The default event loop is the event loop the system uses for its events. For some use cases, this could be sufficient.
However, should the user need to create their own event loops, the example `user_event_loops` should be able to provide guidance.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
Here are the things this example illustrates:
Note: Should users need to create their own event loops, refer to the **user event loops** [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/esp_event.html#using-esp-event-apis) and [example](https://github.com/espressif/esp-idf/tree/master/examples/system/esp_event/user_event_loops).
The [**default event loop**](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/esp_event.html#default-event-loop) is an [event loop](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/esp_event.html#) the system uses to post and handle events (e.g. Wi-Fi events). This example demonstrates the usage of the following default event loop features:
### Declaring and Defining Events
This example shows the typical setup of having the event base and event IDs declared in a header
file and then having the definitions in a source file.
Declaration of the event base makes use of the macro `ESP_EVENT_DECLARE_BASE`, while the event IDs are declared as an `enum`; see
``event_source.h``. The source file ``main.c`` holds the definition of the event base using the macro `ESP_EVENT_DEFINE_BASE`.
This example shows the typical setup of having the event base and event IDs declared in a header file, and having the definitions in a source file. Declaration of the event base makes use of the macro `ESP_EVENT_DECLARE_BASE()`, whilst the event IDs are declared as an `enum` (see `event_source.h`). The source file `main.c` holds the definition of the event base using the `ESP_EVENT_DEFINE_BASE()` macro.
### Creating the Default Event Loop
This example illustrates the creation of the default event loop using the API function `esp_event_loop_create_default`.
This example illustrates the creation of the default event loop using the API function `esp_event_loop_create_default()`.
### Posting Events to the Default Event Loop
Simply put, posting an event to a loop is the act of queueing its handlers for execution. For the default loop, this is done using the API `esp_event_post`. The ability to pass event-specific data to the handler is also illustrated.
Simply put, posting an event to a loop is the act of queueing its handlers for execution. For the default loop, this is done using the API `esp_event_post()`. The ability to pass event-specific data to the handler is also demonstrated.
### Handler Registration/Unregistration
This example illustrates handler registration to the default event loop using `esp_event_handler_register` for (1) specific events, (2) *any* event under a certain base, and (3) *any* event. This also shows the possbility of registering multiple handlers to the same event.
This example demonstrates handler registration to the default event loop using `esp_event_handler_register` for (1) specific events, (2) **any** event under a certain base, and (3) **any** event. This also shows the possbility of registering multiple handlers to the same event.
Unregistering a handler is done using `esp_event_handler_register`. Unregistering a handler means that it no longer executes even when the event it was previously registered to gets posted to the loop.
Unregistering a handler is done using `esp_event_handler_unregister()`. Unregistering a handler means that it no longer executes even when the event it was previously registered to gets posted to the loop.
## Example Flow Explained
## How to use example
The example flow is best explained using a sample log output.
### Hardware Required
This example should be able to run on any commonly available ESP32 development board.
### Configure the project
```
idf.py menuconfig
```
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The example should have the following log output:
```
I (276) default_event_loop: setting up
@ -71,80 +93,83 @@ I (3326) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: deleted timer eve
I (3326) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_any_handler
I (3336) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: all_event_handler
I (3346) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: deleting task event source
```
### Setting
## Example Breakdown
This example uses two event sources: a periodic timer and a task with a loop inside. Events are raised for the periodic timer when (1) the timer is started (2) the timer period expires and (3) the timer is stopped. Events are raised for the when (1) the loop iterates.
### Setting of Event Sources
All of the events mentioned above has their own specific handler. There are additional handlers, however. One handler executes when *any* event under the periodic timer event is posted; while the other executes if *any* event is posted.
This example uses two event sources:
The number of periodic timer expiries and loop iterations are limited. When the limit for the timer expiry is reached,
the timer is stopped. When the limit for the iterarations is reached, the task is deleted. In the case of the loop iteration, there is another limit: the number of iterations for when its handler will be unregistered.
- A periodic timer. An event is raised when (1) the timer is started (2) the timer period expires and (3) the timer is stopped
- A task with a loop inside. An event is raised for the when (1) the loop iterates.
All of the events mentioned above have their own specific handler, however there are the following additional handlers.
- One handler executes when **any** event under the periodic timer event is posted
- The other handler executes if **any** event is posted.
The number of periodic timer expiries and loop iterations are limited. When the limit for the number of timer expiries is reached, the timer is stopped. When the limit for the number of iterations is reached, the task is deleted. In the case of the loop iteration, there is another limit: the number of iterations for when its handler will be unregistered.
### Step-by-Step Explanation
The following text explains the important points of the sample log output.
The following text explains the important points of the [sample log output](#Example-Output).
a.
#### 1. Setting up of default event loop and event handlers
```
I (297) default_event_loop: setting up
```
At this stage the default event loop is created, as well as the handlers for the different events registered.
At this stage the default event loop is created, and the handlers for the different events are registered.
#### 2. Posting to the event loop
b.
```
I (276) default_event_loop: starting event sources
I (276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: posting to default loop
I (276) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 1 out of 5
```
The two event sources are started. The respective events are posted to the default event loop.
c.
#### 3. Execution of handlers
```
I (296) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_started_handler
I (306) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_any_handler
I (316) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: all_event_handler
```
```
...
I (326) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 1 times
I (336) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
```
The handlers are executed for the events posted in **(b)**. In addition to event-specific handlers `timer_started_handler`
and `task_iteration_handler`, the handlers `timer_any_handler` and `all_event_handler` also executed.
The handlers are executed for the events that were posted in **(2)**. In addition to the event-specific handlers `timer_started_handler()` and `task_iteration_handler()`, the `timer_any_handler()` and `all_event_handler()` are also executed.
The handler `timer_any_handler` executes for *any* timer event. It can be seen executing for the timer expiry and timer stopped events in the upcoming parts of the log.
The `timer_any_handler()` executes for **any** timer event. It can be seen executing for the timer expiry and timer stopped events in the subsequent parts of the log.
On the other hand, `all_event_handler` executes for *any* event. This is the reason why it executes for both ``TIMER_EVENTS:TIMER_EVENT_STARTED`` and ``TASK_EVENTS:TASK_ITERATION_EVENT``.
On the other hand, `all_event_handler()` executes for **any** event, hence why it executes for both ``TIMER_EVENTS:TIMER_EVENT_STARTED`` and ``TASK_EVENTS:TASK_ITERATION_EVENT``.
For both the timer and task events, notice that the handlers are executed in the same order they are registered in the code. This is
a guarantee that the `esp_event` library provides.
For both the timer and task events, notice that the handlers are executed in the same order they are registered in the code. This is a guarantee that the `esp_event` library provides.
The proceeding lines of the log follows the same pattern: the event is posted to the loop and the handlers are executed.
The subsequent lines of the log follows the same pattern: the event is posted to the loop and the handlers are executed.
d.
#### 4. Unregistering the `task_iteration_handler()`
```
...
I (1316) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: unregistering task_iteration_handler
```
At this point in the execution the handler `task_iteration_handler` is unregistered, therefore it no longer executes
when the event ``TASK_EVENTS:TASK_ITERATION_EVENT`` is posted.
At this point in the execution the handler `task_iteration_handler()` is unregistered, therefore it no longer executes when the event ``TASK_EVENTS:TASK_ITERATION_EVENT`` is posted.
```
I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 4 out of 5
I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
...
I (1846) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 4 out of 5
I (1846) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handle
```
The iteration event continues to get posted, but only `all_event_handler` gets executed.
The iteration event continues to get posted, but only the `all_event_handler()` gets executed.
#### 5. Iteration Limit
e.
```
...
I (3276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
@ -155,15 +180,11 @@ I (3306) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (3316) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_stopped_handler
I (3326) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: deleted timer event source
```
When the periodic timer expiry limit is reached, the event ``TIMER_EVENTS:TIMER_EVENT_STOPPED`` is posted to the loop. The periodic timer is consequently deleted in the handler `timer_stopped_handler`.
When the periodic timer expiry limit is reached, the event ``TIMER_EVENTS:TIMER_EVENT_STOPPED`` is posted to the loop. The periodic timer is consequently deleted in the handler `timer_stopped_handler()`.
```
...
I (3346) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: deleting task event source
...
```
The task containing the loop that posts iteration events also gets deleted. The example ends at this point.
---
See the README.md file in the upper level 'examples' directory for more information about examples.
The task containing the loop that posts iteration events also gets deleted. The example ends at this point.

View file

@ -1,29 +1,59 @@
# Event Loop Library ('esp_event') User Event Loops Example
# User Event Loops Example
This example demonstrates the creation and use of user event loops. This example is a supplement
to the `default_event_loop` example, if the default event loop is not sufficient for the user's use case.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
Here are the things this example illustrates:
This example demonstrates the creation and use of [**user event loops**](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/esp_event.html#). This example is a supplement to the [**default event loop** example](https://github.com/espressif/esp-idf/tree/master/examples/system/esp_event/default_event_loop), if the default event loop is not sufficient for the user's use case.
This example demonstrates the following things regarding user event loops:
### Creating Event Loops
Creating a loop entails populating the structure `esp_event_loop_args_t` with the desired parameters and calling `esp_event_loop_create`. The call to `esp_event_loop_create` produces a handle to the loop, which is used to perform actions on that loop such as handler registration/unregistration and posting events.
Creating a loop entails populating the structure `esp_event_loop_args_t` with the desired parameters and calling `esp_event_loop_create()`. The call to `esp_event_loop_create()` produces a handle to the loop, which is used to perform actions on that loop such as handler registration/unregistration and posting events.
### Running Event Loops
Depending on the parameters, the user can create either a loop with a dedicated task or one without. The purpose of the dedicated task is to unqueue events from the loop and execute its handlers. For loops without the dedicated task, the user should make a call to `esp_event_loop_run` in an application task.
Depending on the parameters, the user can create either a loop with a dedicated task or one without. The purpose of the dedicated task is to unqueue events from the loop and execute its handlers. For loops without the dedicated task, the user should make a call to `esp_event_loop_run()` in an application task.
### Handler Registration/Unregistration,
Handler registration and unregistration works the same way as the default event loop, just with a different API, `esp_event_handler_register_with` and `esp_event_handler_register_with` respectively. There are two things this example highlights: (1) the possibility of registering the same handler for different loops and (2) the ability to pass static data to handlers.
Handler registration and unregistration works the same way as the default event loop, just with a different API, `esp_event_handler_register_with()` and `esp_event_handler_register_with()` respectively. There are two things this example highlights:
1. The possibility of registering the same handler for different loops
2. The ability to pass static data to handlers.
### Posting Events to the Default Event Loop
Posting events also works the same way as the default event loop, except with a different API, `esp_event_post_to`.
Posting events also works the same way as the default event loop, except with a different API, `esp_event_post_to()`.
## Example Flow Explained
## How to use example
The example flow is best explained using the sample log output.
### Hardware Required
This example should be able to run on any commonly available ESP32 development board.
### Configure the project
```
idf.py menuconfig
```
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The example should have the following log output:
```
I (296) user_event_loops: setting up
@ -62,17 +92,17 @@ I (9126) user_event_loops: application_task: running application task
...
```
### Setting
## Example Breakdown
### Setting of Event Sources
This example has a single event source: a task with a loop inside. Events are raised for the task event source when the loop iterates.
Two loops are created, one with a dedicated task and one without. Events are posted to either loops,depending on whether the iteration is odd or even. For the loop with a dedicated task, event handlers are automatically executed. However, for the loop without the dedicated task, a call to run the loop is made in one of the application tasks. As a result, the execution of the event handlers for this loop is interspersed with the execution of application task code.
Two user event loops are created, one with a dedicated task and one without. Events are posted to either loops, depending on whether the iteration is odd or even. For the loop with a dedicated task, event handlers are automatically executed. However, for the loop without the dedicated task, a call to run the loop is made in one of the application tasks. As a result, the execution of the event handlers for this loop is interspersed with the execution of application task code.
### Step-by-Step Explanation
The following text explains the important points of this example's sample log output.
a.
#### 1.Setting up user event loop and event handlers
```
I (296) user_event_loops: setting up
@ -81,7 +111,7 @@ I (296) user_event_loops: starting application task
```
At this stage the two event loops are created, as well as the handlers for the iteration event registered. The event source is started, which will post the event to the appropriate loop. The application task which makes the call to run the loop without dedicated task, is also created and started.
b.
#### 2. Posting to the event loop
```
I (296) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_without_task, iteration 1 out of 10
I (316) user_event_loops: application_task: running application task
@ -96,7 +126,7 @@ I (1826) user_event_loops: posting TASK_EVENTS:TASK_ITERATION_EVENT to loop_with
```
In this section of the log we see the odd iterations posted to the loop without dedicated task, and the even iterations to the loop with a dedicated task. For the event with dedicated task, event handlers are executed automatically. The loop without a dedicated task, on the other hand, runs in the context of the application task.
c.
#### 3. Iteration Limit
```
...
@ -110,8 +140,4 @@ I (9126) user_event_loops: application_task: running application task
...
```
The last of the iteration event is posted, and the event source is deleted. Because the loop without the task no longer receive events to execute, only the application task code executes.
---
See the README.md file in the upper level 'examples' directory for more information about examples.
The last of the iteration event is posted, and the event source is deleted. Because the loop without the task no longer receive events to execute, only the application task code executes.

View file

@ -1,79 +1,156 @@
# High resolution timer (`esp_timer`) example
# High Resolution Timer Example (`esp_timer`)
This example illustrates usage of `esp_timer` APIs to create one-shot and periodic software timers.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
`esp_timer` APIs allow application to create multiple timers using single hardware timer, and hides complexity associated with managing multiple timers, invoking callbacks, accounting for APB frequency changes (if dynamic frequency scaling is enabled), and maintaining correct time after light sleep.
The [High Resolution Timer (`esp_timer`)](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/esp_timer.html) APIs allow an application to create multiple timers using a single hardware timer, and hides complexity associated with managing multiple timers, invoking callbacks, accounting for APB frequency changes (if dynamic frequency scaling is enabled), and maintaining correct time after light sleep.
`esp_timer` API also provides `esp_timer_get_time` function which returns time since boot, in microseconds. This can be useful for fine-grained timing in tasks and ISRs.
This example illustrates the usage of the [`esp_timer` API](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/esp_timer.html#api-reference) to create one-shot and periodic software timers.
The `esp_timer` API also provides the `esp_timer_get_time()` function which returns the time since boot in microseconds. This can be useful for fine-grained timing in tasks and ISRs thus is also demonstrated in this example.
## Example flow explained
## How to use example
1. Example starts and timers are created.
### Hardware Required
```
I (265) example: Starting timers, time since boot: 2479 us
```
This example should be able to run on any commonly available ESP32 development board.
2. These two repeating lines are the output of `esp_timer_dump` function. There is one line for each of the timers created. This function can be useful for debugging purposes. Note that such debugging information is available because the example sets `CONFIG_ESP_TIMER_PROFILING` option in sdkconfig. Without this option, less information will be available. See documentation of `esp_timer_dump` in ESP-IDF programming guide for more details.
### Configure the project
```
timer period next time times times callback
name to fire started fired run time (us)
------------------------------------------------------------------------------------
```
idf.py menuconfig
```
Under `Component config > Common ESP-related` are the following `esp_timer` related configurations
* `High-resolution timer task stack size` can be increased if timer callbacks require a larger stack
* `Enable esp_timer profiling features` will cause `esp_timer_dump()` to include more information.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The example should have the following log output:
```
...
I (294) example: Started timers, time since boot: 9662 us
periodic 500000 509644 1 0 0
one-shot 0 5009654 1 0 0
I (794) example: Periodic timer called, time since boot: 509694 us
I (1294) example: Periodic timer called, time since boot: 1009671 us
I (1794) example: Periodic timer called, time since boot: 1509671 us
I (2294) example: Periodic timer called, time since boot: 2009671 us
periodic 500000 2509644 1 4 542
one-shot 0 5009654 1 0 0
I (2794) example: Periodic timer called, time since boot: 2509671 us
I (3294) example: Periodic timer called, time since boot: 3009671 us
I (3794) example: Periodic timer called, time since boot: 3509671 us
I (4294) example: Periodic timer called, time since boot: 4009671 us
periodic 500000 4509644 1 8 1026
one-shot 0 5009654 1 0 0
I (4794) example: Periodic timer called, time since boot: 4509671 us
I (5294) example: Periodic timer called, time since boot: 5009669 us
I (5294) example: One-shot timer called, time since boot: 5009788 us
I (5294) example: Restarted periodic timer with 1s period, time since boot: 5012675 us
I (6294) example: Periodic timer called, time since boot: 6012692 us
periodic 1000000 7012666 2 11 1391
one-shot 0 0 1 1 11472
I (7294) example: Periodic timer called, time since boot: 7012692 us
I (8294) example: Periodic timer called, time since boot: 8012692 us
periodic 1000000 9012666 2 13 1639
one-shot 0 0 1 1 11472
I (9294) example: Periodic timer called, time since boot: 9012692 us
I (10294) example: Periodic timer called, time since boot: 10012692 us
I (10314) example: Entering light sleep for 0.5s, time since boot: 10024351 us
I (10314) example: Woke up from light sleep, time since boot: 10525143 us
...
```
## Example Breakdown
### 1. Creating and starting timers
The example starts by creating a periodic and a one shot timer using the `esp_timer_create()` function. Once created, the two timers are started using the `esp_timer_start_periodic()` and `esp_timer_start_once()` functions.
```
I (265) example: Starting timers, time since boot: 2479 us
```
### 2. Getting initial timer dump
These two repeating lines are the output of `esp_timer_dump()` function. There is one line for each of the timers created. This function can be useful for debugging purposes. Note that such debugging information is available because the example sets `CONFIG_ESP_TIMER_PROFILING` option in sdkconfig. Without this option, less information will be available. See documentation of `esp_timer_dump()` in ESP-IDF programming guide for more details.
```
timer period next time times times callback
name to fire started fired run time (us)
------------------------------------------------------------------------------------
periodic 500000 502455 1 0 0
one-shot 0 5002469 1 0 0
```
### 3. Periodic timer keeps running at 500ms period:
```
I (765) example: Periodic timer called, time since boot: 502506 us
I (1265) example: Periodic timer called, time since boot: 1002478 us
I (1765) example: Periodic timer called, time since boot: 1502478 us
I (2265) example: Periodic timer called, time since boot: 2002478 us
periodic 500000 2502455 1 4 511
one-shot 0 5002469 1 0 0
I (2765) example: Periodic timer called, time since boot: 2502478 us
I (3265) example: Periodic timer called, time since boot: 3002478 us
I (3765) example: Periodic timer called, time since boot: 3502478 us
I (4265) example: Periodic timer called, time since boot: 4002478 us
periodic 500000 4502455 1 8 971
one-shot 0 5002469 1 0 0
I (4765) example: Periodic timer called, time since boot: 4502478 us
I (5265) example: Periodic timer called, time since boot: 5002476 us
```
### 4. One-shot timer runs
The one-shot timer runs and changes the period of the periodic timer. Now the periodic timer runs with a period of 1 second:
```
I (5265) example: One-shot timer called, time since boot: 5002586 us
I (5265) example: Restarted periodic timer with 1s period, time since boot: 5005475 us
I (6265) example: Periodic timer called, time since boot: 6005492 us
periodic 1000000 7005469 2 11 1316
one-shot 0 0 1 1 11474
I (7265) example: Periodic timer called, time since boot: 7005492 us
I (8265) example: Periodic timer called, time since boot: 8005492 us
periodic 1000000 9005469 2 13 1550
one-shot 0 0 1 1 11474
I (9265) example: Periodic timer called, time since boot: 9005492 us
I (10265) example: Periodic timer called, time since boot: 10005492 us
```
### 5. Continuation through light sleep
To illustrate that timekeeping continues correctly after light sleep, the example enters light sleep for 0.5 seconds. This sleep does not impact timer period, and the timer is executed 1 second after the previous iteration. Note that the timers can not execute during light sleep, since the CPU is not running at that time. Such timers would execute immediately after light sleep, and then continue running with their normal period.
periodic 500000 502455 1 0 0
one-shot 0 5002469 1 0 0
```
```
I (10275) example: Entering light sleep for 0.5s, time since boot: 10011559 us
I (10275) example: Woke up from light sleep, time since boot: 10512007 us
I (10765) example: Periodic timer called, time since boot: 11005492 us
I (11765) example: Periodic timer called, time since boot: 12005492 us
```
3. Periodic timer keeps running at 500ms period:
### 6. Finally, timers are deleted.
```
I (765) example: Periodic timer called, time since boot: 502506 us
I (1265) example: Periodic timer called, time since boot: 1002478 us
I (1765) example: Periodic timer called, time since boot: 1502478 us
I (2265) example: Periodic timer called, time since boot: 2002478 us
periodic 500000 2502455 1 4 511
one-shot 0 5002469 1 0 0
I (2765) example: Periodic timer called, time since boot: 2502478 us
I (3265) example: Periodic timer called, time since boot: 3002478 us
I (3765) example: Periodic timer called, time since boot: 3502478 us
I (4265) example: Periodic timer called, time since boot: 4002478 us
periodic 500000 4502455 1 8 971
one-shot 0 5002469 1 0 0
I (4765) example: Periodic timer called, time since boot: 4502478 us
I (5265) example: Periodic timer called, time since boot: 5002476 us
```
4. One-shot timer runs, and changes the period of the periodic timer. Now the periodic timer runs with 1 second period:
```
I (5265) example: One-shot timer called, time since boot: 5002586 us
I (5265) example: Restarted periodic timer with 1s period, time since boot: 5005475 us
I (6265) example: Periodic timer called, time since boot: 6005492 us
periodic 1000000 7005469 2 11 1316
one-shot 0 0 1 1 11474
I (7265) example: Periodic timer called, time since boot: 7005492 us
I (8265) example: Periodic timer called, time since boot: 8005492 us
periodic 1000000 9005469 2 13 1550
one-shot 0 0 1 1 11474
I (9265) example: Periodic timer called, time since boot: 9005492 us
I (10265) example: Periodic timer called, time since boot: 10005492 us
```
5. To illustrate that timekeeping continues correctly after light sleep, the example enters light sleep for 0.5s. This sleep does not impact timer period, and the timer is executed 1 second after the previous iteration. Note that the timers can not execute during light sleep, since the CPU is not running at that time. Such timers would execute immediately after light sleep, and then continue running with their normal period.
```
I (10275) example: Entering light sleep for 0.5s, time since boot: 10011559 us
I (10275) example: Woke up from light sleep, time since boot: 10512007 us
I (10765) example: Periodic timer called, time since boot: 11005492 us
I (11765) example: Periodic timer called, time since boot: 12005492 us
```
6. Finally, timers are deleted.
```
I (12275) example: Stopped and deleted timers
```
---
See the README.md file in the upper level 'examples' directory for more information about examples.
```
I (12275) example: Stopped and deleted timers
```

View file

@ -18,11 +18,9 @@ This example should be able to run on any commonly available ESP32 development b
idf.py menuconfig
```
* Set serial port under Serial Flasher Options.
* Select `Enable FreeRTOS to collect run time stats` under `Component Config > FreeRTOS` (this should be enabled in the example by default)
* Enable FreeRTOS to collect run time stats under `Component Config->FreeRTOS`
* The clock source of reference timer used for FreeRTOS statistics can be configured under `Component Config->FreeRTOS`
* `Choose the clock source for run time stats` configured under `Component Config > FreeRTOS`. The `esp_timer` should be selected be default. This option will affect the time unit resolution in which the statistics are measured with respect to.
### Build and Flash
@ -32,13 +30,18 @@ Build the project and flash it to the board, then run monitor tool to view seria
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The example should have the following log output:
```
...
Getting real time stats over 100 ticks
| Task | Run Time | Percentage
| stats | 1304 | 0%
@ -55,17 +58,15 @@ Getting real time stats over 100 ticks
| ipc1 | 0 | 0%
| ipc0 | 0 | 0%
Real time stats obtained
...
```
- When compiled in dual core mode, the percentage is with respect to the combined run time of both CPUs. Thus, `50%` would indicate full utilization of a single CPU.
- In single core mode, the percentage is with respect to a single CPU. Thus, `100%` would indicate full utilization of the CPU.
## Example Breakdown
The unit of `Run Time` is the period of the timer clock source used for FreeRTOS statistics.
### Spin tasks
## Troubleshooting
During the examples initialization process, multiple `spin` tasks are created. These tasks will simply spin a certain number of CPU cycles to consume CPU time, then block for a predetermined period.
```
Getting real time stats over 100 ticks
Error getting real time stats
```
If the above is output when running the example, users should check the return value of `print_real_time_stats()` to determine the reason for failure.
### Understanding the stats
From the log output, it can be seen that the spin tasks consume nearly an equal amount of time over the specified stats collection period of `print_real_time_stats()`. The real time stats also display the CPU time consumption of other tasks created by default in ESP-IDF (e.g. `IDLE` and `ipc` tasks).

View file

@ -1,2 +1,2 @@
idf_component_register(SRCS "main.c"
idf_component_register(SRCS "real_time_stats_example_main.c"
INCLUDE_DIRS ".")

View file

@ -1,150 +1,102 @@
# Blink Example with Coverage Info
# Blink Example With Coverage Info (Gcov)
See the README.md file in the upper level 'examples' directory for more information about examples.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
GCC has useful feature which allows to generate code coverage information. Generated data show how many times every program execution paths has been taken.
Basing on coverage data developers can detect untested pieces of code and also it gives valuable information about critical (frequently used) execution paths.
In general case when coverage option is enabled GCC generates additional code to accumulate necessary data and save them into files. File system is not always available in ESP32 based projects or size of the file storage can be very limited to keep all the coverage data. To overcome those limitations IDF provides functionality to transfer the data to host and save them on its file system. Data transfer is done via JTAG.
This example shows how to generate coverage information for the program.
The following example demonstrates how to compile an ESP-IDF project to generate code coverage data, and how generate a code coverage report using Gcov or Lcov. Refer to the [Gcov Guide](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#gcov-source-code-coverage) for more details on the code coverage features supported in ESP-IDF.
## How To Gather Coverage Info
This example implements a simple blink application but with code coverage enabled. The example will demonstrate the following features:
* How to compile a project with the `--coverage` flag
* Various methods of dumping code coverage data (e.g. Instant Run-Time Dump and Hard-coded Dump).
* How to generate a code coverage report.
There are two ways to collect gcov data:
* Hard-coded call to `esp_gcov_dump`.
* Instant run-time dumping w/o changes in your code via IDF's gcov debug stub.
## How to use example
### Generic Steps
### Hardware Required
Below are generic steps which should be performed to obtain coverage info. The steps are already done for this example project.
To run this example, you need an ESP32 dev board connected to a JTAG adapter, which can come in the following forms:
1. Enable application tracing module in menuconfig. Choose `Trace memory` in `Component config -> Application Level Tracing -> Data Destination`.
2. Enable GCOV to host interface in menuconfig `Component config -> Application Level Tracing -> GCOV to Host Enable`.
3. Enable coverage info generation for necessary source files. To do this add the following line to the 'component.mk' files of your project:
`CFLAGS += --coverage`
It will enable coverage info for all source files of your component. If you need to enable the option only for certain files the following line should be added for every file of interest:
`gcov_example.o: CFLAGS += --coverage`
Replace `gcov_example.o` with path to your file.
* [ESP-WROVER-KIT](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v4-1) which integrates an on-board JTAG adapter. Ensure that the [required jumpers to enable JTAG are connected](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/get-started-wrover-kit.html#setup-options) on the WROVER-KIT.
* ESP32 core board (e.g. ESP32-DevKitC) can also work as long as you connect it to an external JTAG adapter (e.g. FT2232H, J-LINK).
For CMake-based build system, use `target_compile_options(${COMPONENT_LIB} PRIVATE --coverage)` or: ` set_source_files_properties(gcov_example.c PROPERTIES COMPILE_FLAGS --coverage`
This example will assume that that an ESP-WROVER-KIT is used.
1. Connect the JTAG interface to ESP32 board, and power up both the JTAG and ESP32. For details about how to set up JTAG interface, please see [JTAG Debugging](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html).
### Hard-coded Dump Call
2. After connecting JTAG interface, you need to [Run OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#run-openocd).
This method requires `esp_gcov_dump` to be called from your application's code. Below are additional steps which should be performed after the generic ones to obtain coverage info via hard-coded call. Step 1 is already done for this example project.
3. Open a separate terminal window and run telnet by entering the command below. The telnet terminal window is used to feed commands to OpenOCD:
1. Add call to `esp_gcov_dump` function in your program. This function will wait for command from host and dump coverage data. The exact place where to put the call depends on the program.
Usually it should be placed at the end of the program execution (at exit). But if you need to generate GCOV data incrementally `esp_gcov_dump` can be called multiple times. See `gcov_example.c` for example.
2. Build, flash and run program.
3. Wait until `esp_gcov_dump` is called. To detect this a call to `printf` can be used (see `gcov_example.c`) or, for example, you can use a LED to indicate the readiness to dump data.
Another way to detect call to `esp_gcov_dump` is to set breakpoint on that function, start target execution and wait for the target to be stopped. See the next section for respective GDB example.
4. Connect OpenOCD to the target and start telnet session with it.
5. Run the following OpenOCD command: `esp32 gcov dump`
Example of the command output:
```
> esp32 gcov dump
Total trace memory: 16384 bytes
Connect targets...
Target halted. PRO_CPU: PC=0x40088BC3 (active) APP_CPU: PC=0x400D14E6
Targets connected.
Open file 0x1 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example.gcda'
Open file 0x1 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example.gcda'
Open file 0x2 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example_func.gcda'
Open file 0x2 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example_func.gcda'
Disconnect targets...
Target halted. PRO_CPU: PC=0x400D14E6 (active) APP_CPU: PC=0x400D14E6
Targets disconnected.
```bash
telnet localhost 4444
```
#### Dump Using GDB
As it was said above breakpoint can be used to detect when `esp_gcov_dump` is called.
The following GDB commands can be used to dump data upon call to `esp_gcov_dump` automatically (you can put them into `gdbinit` file):
```
b esp_gcov_dump
commands
mon esp32 gcov dump
end
```
Note that all OpenOCD commands should be invoked in gdb as: `mon <oocd_command>`.
### Instant Run-Time Dump
Instant dump does not require to call `esp_gcov_dump`, so your application's code does not need to be modified. This method stops target at its current state and executes builtin IDF gcov debug stub function.
Having data dumped target resumes its execution. Below are the steps which should be performed to do instant dump. Step 1 is already done for this example project.
1. Enable OpenOCD debug stubs in menuconfig `Component config -> ESP32-specific -> OpenOCD debug stubs`.
2. Build, flash and run program.
3. Connect OpenOCD to the target and start telnet session with it.
4. Run the following OpenOCD command: `esp32 gcov`
Example of the command output:
```
> esp32 gcov
Total trace memory: 16384 bytes
Connect targets...
Target halted. PRO_CPU: PC=0x400D14DA (active) APP_CPU: PC=0x400D14DA
Targets connected.
Open file 0x1 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example.gcda'
Open file 0x1 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example.gcda'
Open file 0x2 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example_func.gcda'
Open file 0x2 '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example_func.gcda'
Target halted. PRO_CPU: PC=0x400844CE (active) APP_CPU: PC=0x400855E3
Disconnect targets...
Targets disconnected.
>
```
### Coverage Data Accumulation
Coverage data from several dumps are automatically accumulated. So the resulting gcov data files contain statistics since the board reset. Every data dump updates files accordingly.
New data collection is started if target has been reset.
## How To Process Coverage Info
There are several ways to process collected data. Two of the most common are:
1. Using `gcov` tool supplied along with xtensa toolchain. See [GCOV documentation](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html) for details.
2. Using `lcov` and `genhtml` tools. This way allows to generate pretty looking coverage reports in html. This example shows how to add build target to generate such kind of reports.
Add the following lines to you project's `Makefile` after the line including `project.mk`:
### Configure the project
```
GCOV := $(call dequote,$(CONFIG_SDK_TOOLPREFIX))gcov
REPORT_DIR := $(BUILD_DIR_BASE)/coverage_report
lcov-report:
echo "Generating coverage report in: $(REPORT_DIR)"
echo "Using gcov: $(GCOV)"
mkdir -p $(REPORT_DIR)/html
lcov --gcov-tool $(GCOV) -c -d $(BUILD_DIR_BASE) -o $(REPORT_DIR)/$(PROJECT_NAME).info
genhtml -o $(REPORT_DIR)/html $(REPORT_DIR)/$(PROJECT_NAME).info
cov-data-clean:
echo "Remove coverage data files..."
find $(BUILD_DIR_BASE) -name "*.gcda" -exec rm {} +
rm -rf $(REPORT_DIR)
.PHONY: lcov-report cov-data-clean
idf.py menuconfig
```
For CMake-based build system, add the following lines to you project's `CMakeLists.txt` after the line including `project.cmake`:
The example will enable the following options by default:
* Enable the Application Tracing Module under `Component config -> Application Level Tracing -> Data Destination` by choosing `Trace memory`.
* Enable GCOV to host interface under `Component config -> Application Level Tracing -> GCOV to Host Enable`.
* Enable OpenOCD Debug Stubs under `Component config -> ESP32-specific -> OpenOCD debug stubs`
### Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
include($ENV{IDF_PATH}/tools/cmake/gcov.cmake)
idf_create_coverage_report(${CMAKE_CURRENT_BINARY_DIR}/coverage_report)
idf_clean_coverage_report(${CMAKE_CURRENT_BINARY_DIR}/coverage_report)
idf.py -p PORT flash monitor
```
Those lines add two build targets:
* `lcov-report` - generates html coverage report in `$(BUILD_DIR_BASE)/coverage_report/html` directory.
* `cov-data-clean` - removes all coverage data files and reports.
(Replace PORT with the name of the serial port to use.)
To generate report type the following command:
`make lcov-report`
(To exit the serial monitor, type ``Ctrl-]``.)
For CMake-based build system, type `cmake --build build/ --target lcov-report`
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
The sample output of the command is below:
## Example Output
### 1. Hard-coded Dump
The example will initially execute two hard-coded dumps. Therefore, when the application outputs `Ready to dump GCOV data...`, users should execute the `esp32 gcov dump` OpenOCD command. The example should output the following:
```
Counter = 0
Ready to dump GCOV data...
GCOV data have been dumped.
Counter = 1
Ready to dump GCOV data...
GCOV data have been dumped.
```
### 2. Instant Run-Time Dump
After the two hard-coded dumps, the example will continue looping through it's main blink function. Users can call `esp32 gcov` OpenOCD command to trigger an instant run-time dump. The output should resemble the following:
```
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 6
Counter = 7
Counter = 8
Counter = 9
Counter = 10
...
```
### Generating Lcov Report
After dumping one or more times, a coverage report can be generated by calling `cmake --build build/ --target lcov-report`. This should result in an HTML code coverage report being generated in the build directory.
To clean Gcov and report related data from the build directory, call `cmake --build build/ --target cov-data-clean`
**Note:** Currently, the CMake build system on Windows does not support Lcov. However, Lcov is available in MINGW (installed through package manager). Therefore, if the project is built using the legacy GNU Make build system, call `make lcov-report` or `make cov-data-clean` instead.
The following log should be output when generating the coverage report:
```
Generating coverage report in: /home/alexey/projects/esp/esp-idf/examples/system/gcov/build/coverage_report
@ -169,5 +121,29 @@ Overall coverage rate:
functions..: 100.0% (3 of 3 functions)
```
NOTE: Since `lcov` tool is not part of GCC bundle it can happen that format of GCOV binary data has been changed and your local version of `lcov` fails to understand it.
So it always better to use the latest `lcov` version from [LCOV repo](https://github.com/linux-test-project/lcov).
## Troubleshooting
### OpenOCD Out of Sync
If the following log is output when issuing an OpenOCD command via telnet, it could indicate that OpenOCD and the ESP32 are out of sync. This occurs when the ESP32 is externally reset whilst connected to OpenOCD (e.g., by pressing the EN button).
```
Open On-Chip Debugger
> esp32 gcov dump
Target halted. PRO_CPU: PC=0x4008AFF4 (active) APP_CPU: PC=0x400E396E
Total trace memory: 16384 bytes
Connect targets...
Target halted. PRO_CPU: PC=0x400D5D74 (active) APP_CPU: PC=0x400E396E
timed out while waiting for target halted / 1 - 2
Failed to wait halt on bp target (-4)!
Failed to halt targets (-4)!
Failed to connect to targets (-4)!
```
This issue can be resolved in the following ways:
* Reset the board by issuing the `reset` command via telnet
* Restart OpenOCD
### Outdated Lcov
Due to `lcov` not being part of the GCC bundle, it is possible that the format of the GCOV binary data can change resulting in `lcov` failing to understand it. Therefore, it is always better to use the latest `lcov` version from the [Lcov repo](https://github.com/linux-test-project/lcov).

View file

@ -1,7 +1,8 @@
idf_component_register(SRCS "gcov_example.c"
idf_component_register(SRCS "gcov_example_main.c"
"gcov_example_func.c"
INCLUDE_DIRS ".")
set_source_files_properties(gcov_example.c
set_source_files_properties(gcov_example_main.c
gcov_example_func.c
PROPERTIES COMPILE_FLAGS
--coverage)

View file

@ -1,30 +1,22 @@
# _Himem API example_
# Himem API Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example uses the Himem API to run a memory test of the upper 4MiB of an 8MiB PSRAM chip.
This example uses the [Himem API](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/himem.html) to run a memory test of the upper 4 MiB of an 8 MiB PSRAM chip.
The ESP32 has the ability to access external SPI RAM in the same way as internal memory can be accessed, that is, if
enabled in menuconfig, you can allocate memory in external RAM using standard C allocation APIs like `malloc()`, `calloc()`, etc.
The ESP32 has the ability to access external SPI RAM in the same way internal memory can be accessed. In other words, if enabled in project configuration, you can allocate memory in external RAM using standard C allocation APIs like `malloc()`, `calloc()`, etc.
However, because of hardware limitations, this only works for up to 4MiB of external memory. If you have, for instance,
an ESP32-WROVER module with 8MiB of PSRAM, you cannot use the upper 4MiB of PSRAM this way. However, this memory is not wasted,
using the Himem API (which essentially is a bank switching scheme for the upper memory regions), it is still usable.
However, because of hardware limitations, this only works for up to 4 MiB of external memory. If you have, for instance, an ESP32-WROVER module with 8 MiB of PSRAM, you cannot use the upper 4 MiB of PSRAM this way. However, this memory is not wasted, using the Himem API (which essentially is a bank switching scheme for the upper memory regions), the upper 4 MiB is still usable.
The Himem subsystem does this by reserving some amount of address space, then allowing applications to swap in and out normally
unreachable ranges of physical SPI RAM. While this does not allow transparent access in the way memory allocated with `malloc()` does,
it does provide an usable way to store data for e.g. large audio or video buffers in the upper 4MiB.
The Himem subsystem does this by reserving some amount of address space, then allowing applications to swap in and out normally unreachable ranges of physical SPI RAM. While this does not allow transparent access in the way memory allocated with `malloc()` does, it does provide an usable way to store data for e.g., large audio or video buffers in the upper 4 MiB.
This example uses the Himem API to run a simple memory test of the entire range of upper memory. It illustrates how to allocate
address space to bankswitch the physical memory in, allocate the physical memory, and switch it in or out of the allocated address space.
This example uses the Himem API to run a simple memory test of the entire range of upper memory. It illustrates how to allocate address space to bankswitch the physical memory in, allocate the physical memory, and switch it in or out of the allocated address space.
## How to use example
### Hardware Required
This example requires an ESP32 with external SPI RAM connected, for instance an ESP32-WROVER module. The example is intended to run on
an ESP32 with 8MiB external RAM connected. It will still run on an ESP32 with 4MiB external RAM, but in practice using Himem with such
a setup does not make much sense.
This example requires an ESP32 with external SPI RAM connected, for instance an ESP32-WROVER module. The example is intended to run on an ESP32 with 8 MiB external RAM connected. It will still run on an ESP32 with 4 MiB external RAM, but in practice using Himem with such a setup does not make much sense.
### Configure the project
@ -34,9 +26,7 @@ idf.py menuconfig
* Set serial port under Serial Flasher Options.
* Make sure that SPI RAM bank switching is enabled. (Compiling the example with default values will automatically enable this.) It can be found under
Component config -> ESP32-specific -> Support for external, SPI-connected RAM -> SPI RAM config .
* Make sure that SPI RAM bank switching is enabled. (Compiling the example with default values will automatically enable this.) It can be found under `Component config > ESP32-specific > Support for external, SPI-connected RAM > SPI RAM config`.
### Build and Flash
@ -107,7 +97,6 @@ I (121) esp_himem: Initialized. Using last 62 32KB address blocks for bank switc
I (122) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (132) spiram: Reserving pool of 32K of internal memory for DMA/internal allocations
Himem has 6080KiB of memory, 6080KiB of which is free. Testing the free memory...
Himem has 6080 KiB of memory, 6080 KiB of which is free. Testing the free memory...
Done!
```
```

View file

@ -1,2 +1,2 @@
idf_component_register(SRCS "himem_test_main.c"
idf_component_register(SRCS "himem_example_main.c"
INCLUDE_DIRS ".")

View file

@ -2,7 +2,7 @@
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example illustrates usage of light sleep mode. Unlike deep sleep mode, light sleep preserves the state of the memory, CPU, and peripherals. Execution of code on both CPUs is stopped when `esp_light_sleep_start` function is called. When the chip exits light sleep mode, execution continues at the point where it was stopped, and `esp_light_sleep_start` function returns.
This example illustrates usage of light sleep mode. Unlike deep sleep mode, light sleep preserves the state of the memory, CPU, and peripherals. Execution of code on both CPUs is stopped when `esp_light_sleep_start()` function is called. When the chip exits light sleep mode, execution continues at the point where it was stopped, and `esp_light_sleep_start()` function returns.
The example enables the following wakeup sources:
@ -15,7 +15,13 @@ The example also prints time spent in light sleep mode to illustrate that timeke
### Hardware Required
This example can be used with any ESP32 development board. Most boards have a button attached to GPIO0, often labelled "BOOT". If the board does not have such button, an external button can be connected, along with a 10k pull-up resistor, and a 100nF capacitor to ground for debouncing.
This example can be used with any ESP32 development board. Most boards have a button attached to GPIO0, often labelled `BOOT`. If the board does not have such button, an external button can be connected, along with a 10k pull-up resistor, and a 100nF capacitor to ground for debouncing.
### Configure the project
```
idf.py menuconfig
```
### Build and Flash