# Get started with ESP-IDF and ESP32-DevKitC: debugging, unit testing, project analysis

The goal of this tutorial is to demonstrate how simple it is to use VSCode to develop, run and debug a simple Wi-Fi project with the ESP-IDF framework for the `ESP32-DevKitC` board.

- Level: Intermediate

- Platforms: Windows, Mac OS X, Linux

Requirements:

  - Downloaded and installed VSCode

  - ESP32-DevKitC development board

  - An external debug adapter (e.g. Olimex ARM-USB-OCD)

Contents

- Setting Up the Project

- Adding Code to the Generated Project

- Debugging the Firmware

  - Setting Up the Hardware

- Writing Unit Tests

- Project Inspection

- Conclusion

## Setting Up the Project

1. Click on “PlatformIO Home” button on the bottom PlatformIO Toolbar:

2. Click on “New Project”, select `ESP32-DevKitC` as the development board, ESP-IDF as the framework and a path to the project location (or use the default one):

## Adding Code to the Generated Project

1. Create a new file `main.c` in src_dir folder and add the following code:

   >     /* WiFi softAP Example

   >        This example code is in the Public Domain (or CC0 licensed, at your option.)

   >        Unless required by applicable law or agreed to in writing, this
   >        software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   >        CONDITIONS OF ANY KIND, either express or implied.
   >     */
   >     #include <string.h>
   >     #include "freertos/FreeRTOS.h"
   >     #include "freertos/task.h"
   >     #include "esp_system.h"
   >     #include "esp_wifi.h"
   >     #include "esp_event.h"
   >     #include "esp_log.h"
   >     #include "nvs_flash.h"

   >     #include "lwip/err.h"
   >     #include "lwip/sys.h"

   >     #define EXAMPLE_ESP_WIFI_SSID      "mywifissid"
   >     #define EXAMPLE_ESP_WIFI_PASS      "mywifipass"
   >     #define EXAMPLE_MAX_STA_CONN       (3)

   >     static const char *TAG = "wifi softAP";

   >     static void wifi_event_handler(void* arg, esp_event_base_t event_base,
   >                                         int32_t event_id, void* event_data)
   >     {
   >         if (event_id == WIFI_EVENT_AP_STACONNECTED) {
   >             wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
   >             ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
   >                      MAC2STR(event->mac), event->aid);
   >         } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
   >             wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
   >             ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
   >                      MAC2STR(event->mac), event->aid);
   >         }
   >     }

   >     void wifi_init_softap()
   >     {
   >         tcpip_adapter_init();
   >         ESP_ERROR_CHECK(esp_event_loop_create_default());

   >         wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
   >         ESP_ERROR_CHECK(esp_wifi_init(&cfg));

   >         ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));

   >         wifi_config_t wifi_config = {
   >             .ap = {
   >                 .ssid = EXAMPLE_ESP_WIFI_SSID,
   >                 .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
   >                 .password = EXAMPLE_ESP_WIFI_PASS,
   >                 .max_connection = EXAMPLE_MAX_STA_CONN,
   >                 .authmode = WIFI_AUTH_WPA_WPA2_PSK
   >             },
   >         };
   >         if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
   >             wifi_config.ap.authmode = WIFI_AUTH_OPEN;
   >         }

   >         ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
   >         ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
   >         ESP_ERROR_CHECK(esp_wifi_start());

   >         ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s",
   >                  EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
   >     }

   >     void app_main()
   >     {
   >         //Initialize NVS
   >         esp_err_t ret = nvs_flash_init();
   >         if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
   >           ESP_ERROR_CHECK(nvs_flash_erase());
   >           ret = nvs_flash_init();
   >         }
   >         ESP_ERROR_CHECK(ret);

   >         ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
   >         wifi_init_softap();
   >     }

   > Warning

   > Make sure this new file `main.c` is registered as source file using `idf_component_register` function in `src/CMakeLists.txt` file:

   >     idf_component_register(SRCS "main.c")

2. To compile the project use one of the following options:

   > - Build option from the `Project Tasks` menu

   > - Build button in PlatformIO Toolbar

   > - Task Menu `Tasks: Run Task... > PlatformIO: Build` or in PlatformIO Toolbar

   > - Command Palette `View: Command Palette > PlatformIO: Build`

   > - Hotkeys `cmd-alt-b / ctrl-alt-b`:

3. If everything went well, we should see a successful result message in the terminal window:

4. To upload the firmware to the board we can use the following options:

   > - Upload option from the `Project Tasks` menu

   > - Upload button in PlatformIO Toolbar

   > - Command Palette `View: Command Palette > PlatformIO: Upload`

   > - Task Menu `Tasks: Run Task... > PlatformIO: Upload`

   > - Hotkeys `cmd-alt-u / ctrl-alt-u`:

5. Connect the board to your computer and update the default monitor speed to `115200` in `platformio.ini` file:

   >     [env:esp32dev]
   >     platform = espressif32
   >     board = esp32dev
   >     framework = espidf
   >     monitor_speed = 115200

6. Open Serial Monitor to observe the output from the board:

7. If everything went well, the board should be visible as a WiFi access point:

## Debugging the Firmware

### Setting Up the Hardware

In order to use PIO Unified Debugger, we need to connect an external JTAG probe and the board using the following pins:

ESP32 pin | JTAG probe pin
--- | ---
`3.3V` | `Pin 1(VTref)`
`GPIO 9 (EN)` | `Pin 3 (nTRST)`
`GND` | `Pin 4 (GND)`
`GPIO 12 (TDI)` | `Pin 5 (TDI)`
`GPIO 14 (TMS)` | `Pin 7 (TMS)`
`GPIO 13 (TCK)` | `Pin 9 (TCK)`
`GPIO 15 (TDO)` | `Pin 13 (TDO)`

1. Specify debug_tool in “platformio.ini” (Project Configuration File). In this tutorial, Olimex ARM-USB-OCD-H debug probe is used:

   >     [env:esp32dev]
   >     platform = espressif32
   >     board = esp32dev
   >     framework = espidf
   >     monitor_speed = 115200
   >     debug_tool = olimex-arm-usb-ocd-h

2. To start the debug session we can use the following methods:

   > - `Debug: Start debugging` in the top menu

   > - `Start Debugging` option in the Quick Access menu

   > - Hotkey button `F5`:

3. Walk through the code using control buttons, set breakpoints, and add variables to the `Watch window`:

## Writing Unit Tests

Note

Functions `setUp` and `tearDown` are used to initialize and finalize test conditions. Implementations of these functions are not required for running tests but if you need to initialize some variables before you run a test, you use the `setUp` function and if you need to clean up variables you use `tearDown` function.

For the sake of simplicity, let’s create a small library called `calculator`, implement several basic functions `addition`, `subtraction`, `multiplication`, `division` and test them using PIO Unit Testing engine.

1. Create a new folder `calculator` in the lib_dir folder and add two new files `calculator.h` and `calculator.c` with the following contents:

   > `calculator.h`:

   >     #ifndef _CALCULATOR_H_
   >     #define _CALCULATOR_H_

   >     #ifdef __cplusplus
   >     extern "C" {
   >     #endif

   >     int addition (int a, int b);
   >     int subtraction (int a, int b);
   >     int multiplication (int a, int b);
   >     int division (int a, int b);

   >     #ifdef __cplusplus
   >     }
   >     #endif

   >     #endif // _CALCULATOR_H_

   > `calculator.c`:

   >     #include "calculator.h"

   >     int addition(int a, int b)
   >     {
   >         return a + b;
   >     }

   >     int subtraction(int a, int b)
   >     {
   >         return a - b;
   >     }

   >     int multiplication(int a, int b)
   >     {
   >         return a * b;
   >     }

   >     int division(int a, int b)
   >     {
   >         return a / b;
   >     }

2. Create a new file `test_calc.c` to the folder test_dir and add basic tests for the `calculator` library:

   >     #include <calculator.h>
   >     #include <unity.h>

   >     void test_function_calculator_addition(void) {
   >         TEST_ASSERT_EQUAL(32, addition(25, 7));
   >     }

   >     void test_function_calculator_subtraction(void) {
   >         TEST_ASSERT_EQUAL(20, subtraction(23, 3));
   >     }

   >     void test_function_calculator_multiplication(void) {
   >         TEST_ASSERT_EQUAL(50, multiplication(25, 2));
   >     }

   >     void test_function_calculator_division(void) {
   >         TEST_ASSERT_EQUAL(32, division(100, 3));
   >     }

   >     void main() {
   >         UNITY_BEGIN();

   >         RUN_TEST(test_function_calculator_addition);
   >         RUN_TEST(test_function_calculator_subtraction);
   >         RUN_TEST(test_function_calculator_multiplication);
   >         RUN_TEST(test_function_calculator_division);

   >         UNITY_END();
   >     }

3. Let’s run tests on the board and check the results. There should be a problem with `test_function_calculator_division` test:

4. Let’s fix the incorrect expected value and run tests again. After processing the results should be correct:

## Project Inspection

For illustrative purposes, let’s imagine we need to find a function with the biggest memory footprint. Also, let’s introduce a bug to our project so PIO Check can report it.

1. Open `PlatformIO Home` and navigate to `Inspect` section, select the current project and press `Inspect` button:

2. Project statistics:

3. The biggest function:

4. Possible bugs:

## Conclusion

Now we have a project template for the `ESP32-DevKitC` board that we can use as boilerplate for later projects.