diff --git a/.github/workflows/cpp-linter.yml b/.github/workflows/cpp-linter.yml
new file mode 100644
index 0000000..6fca937
--- /dev/null
+++ b/.github/workflows/cpp-linter.yml
@@ -0,0 +1,26 @@
+name: cpp-linter
+
+on:
+ push:
+ paths-ignore: "doc/**"
+ pull_request:
+ paths-ignore: "doc/**"
+
+jobs:
+ cpp-linter:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: cpp-linter/cpp-linter-action@master
+ id: linter
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ style: file
+
+ - name: Fail fast?!
+ if: steps.linter.outputs.checks-failed > 0
+ run: |
+ echo "Some files failed the linting checks!"
+ # for actual deployment
+ # run: exit 1
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c7a8068
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+# Build results
+build/
+
+# VSCode
+.vscode/
+
+# Project
+sdkconfig
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..2418b0b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(esp32_wifi_onewire_mqtt)
diff --git a/README.md b/README.md
index 0e952d2..ee500ed 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,74 @@
-# ESP32_WiFi_OneWire_MQTT
\ No newline at end of file
+[![GitHub](https://img.shields.io/github/license/VPavlusha/ESP32_WiFi_OneWire_MQTT?color=blue&label=License&logo=github)](LICENSE)
+[![cpp-linter](https://github.com/VPavlusha/ESP32_WiFi_OneWire_MQTT/actions/workflows/cpp-linter.yml/badge.svg)](https://github.com/VPavlusha/ESP32_WiFi_OneWire_MQTT/actions/workflows/cpp-linter.yml)
+[![GitHub release (latest by date)](https://img.shields.io/github/v/release/VPavlusha/ESP32_WiFi_OneWire_MQTT?label=Release&logo=github)](https://github.com/VPavlusha/ESP32_WiFi_OneWire_MQTT/releases)
+[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
+[![Made in Ukraine](https://img.shields.io/badge/Made_in-Ukraine-ffd700.svg?labelColor=0057b7)](https://stand-with-ukraine.pp.ua)
+---
+
+# ESP32 Wi-Fi OneWire MQTT
+The ESP32 WiFi OneWire MQTT project is a simple way to read the data from DS18B20 temperature sensors connected to an ESP32 microcontroller, and send the data to an MQTT broker over Wi-Fi.
+Based on: https://github.com/espressif/esp-idf
+
+#### Table of Contents
+ [1. Features](#1-features)
+ [2. Monitor And Control Your Project](#2-monitor-and-control-your-project)
+ [3. Getting Started](#3-getting-started)
+ [4. Contributing](#4-contributing)
+ [5. License](#5-license)
+
+## 1. Features
+ - ESP-IDF v5.0.2
+ - Reading temperature data from a DS18B20 sensor using the OneWire protocol.
+ - Sending temperature data to an MQTT broker over Wi-Fi.
+ - Easy-to-use API for customizing the firmware to meet your specific needs.
+ - Used Wi-Fi, OneWire, DS18B20, MQTT technology.
+ - Written in C language.
+ - MIT License.
+
+## 2. Monitor And Control Your Project
+
+
+You have the ability to monitor and control your Internet of Things (IoT) projects with some app like [IoT OnOff](https://www.iot-onoff.com/).
+
+## 3. Getting Started
+To get started with the ESP32 WiFi OneWire MQTT project, you'll need an ESP32 microcontroller, a DS18B20 temperature sensor, and access to an MQTT broker. You'll also need to install the ESP-IDF development framework.
+
+### 3.1 Clone the project repository:
+```C
+ git clone https://github.com/VPavlusha/ESP32_WiFi_OneWire_MQTT.git
+```
+### 3.2 Customize the firmware to match your Wi-Fi, DS18B20 and MQTT settings:
+ - Edit the **main/Kconfig.projbuild** file with your Wi-Fi, DS18B20 and MQTT settings.
+
+The ESP32 WiFi OneWire MQTT firmware is designed to be easily customizable. You can use the provided API to change the MQTT topic, adjust the temperature DS18B20 port connections, Wi-Fi settings and more.
+
+### 3.3 Build the project:
+```C
+ cd ESP32_WiFi_OneWire_MQTT
+ idf.py build
+```
+### 3.4 Flash onto your ESP32 microcontroller:
+```C
+ idf.py -p PORT [-b BAUD] flash
+```
+Replace PORT with your ESP32 board’s serial port name.
+You can also change the flasher baud rate by replacing BAUD with the baud rate you need. The default baud rate is 460800.
+### 3.5 Monitor the output:
+```C
+ idf.py -p monitor
+```
+Do not forget to replace PORT with your serial port name.
+
+### 3.6 Check Wi-Fi network and MQTT broker:
+ - Check the output on the serial monitor to verify that the ESP32 is connecting to your Wi-Fi network and MQTT broker.
+ - Check your MQTT broker to verify that temperature data is being sent.
+
+More information how to build project: [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/v5.0.2/esp32/get-started/start-project.html).
+
+## 4. Contributing
+Contributions to the ESP32 WiFi OneWire MQTT project are welcome. If you find a bug or have a feature request, please submit an issue on the project's GitHub page. If you'd like to contribute code, please submit a pull request.
+
+## 5. License
+The ESP32_WiFi_OneWire_MQTT project is licensed under the MIT License. See the [MIT license] file for more information.
+
+ [MIT license]: http://www.opensource.org/licenses/mit-license.html
diff --git a/components/onewire_bus/CMakeLists.txt b/components/onewire_bus/CMakeLists.txt
new file mode 100644
index 0000000..05df568
--- /dev/null
+++ b/components/onewire_bus/CMakeLists.txt
@@ -0,0 +1,3 @@
+idf_component_register(SRCS "onewire_bus_rmt.c" "onewire_bus.c"
+ INCLUDE_DIRS "."
+ PRIV_REQUIRES driver)
diff --git a/components/onewire_bus/README.md b/components/onewire_bus/README.md
new file mode 100644
index 0000000..cb9eb18
--- /dev/null
+++ b/components/onewire_bus/README.md
@@ -0,0 +1,90 @@
+| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
+
+# RMT Transmit & Receive Example -- 1-Wire bus
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+RMT peripheral has both transmit and receive channels. Connecting one transmit channel and one receive channel to the same GPIO and put the GPIO in open-drain mode can simulate bi-directional single wire protocols, such as [1-Wire protocol](https://www.maximintegrated.com/en/design/technical-documents/tutorials/1/1796.html).
+
+This example demonstrates how to use RMT to simulate 1-Wire bus and read temperatrue from [DS18B20](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf).
+
+## How to Use Example
+
+### Hardware Required
+
+* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
+* Several DS18B20 temperature sensors and a 4.7kohm pullup resistor
+
+Connection :
+
+```
+┌──────────────────────────┐
+│ 3.3V├───────┬─────────────┬──────────────────────┐
+│ │ ┌┴┐ │VDD │VDD
+│ ESP32 Board │ 4.7k│ │ ┌──────┴──────┐ ┌──────┴──────┐
+│ │ └┬┘ DQ│ │ DQ│ │
+│ ONEWIRE_GPIO_PIN├───────┴──┬───┤ DS18B20 │ ┌───┤ DS18B20 │ ......
+│ │ └───│-------------│────┴───│-------------│──
+│ │ └──────┬──────┘ └──────┬──────┘
+│ │ │GND │GND
+│ GND├─────────────────────┴──────────────────────┘
+└──────────────────────────┘
+```
+
+The GPIO number used in this example can be changed according to your board, by the macro `EXAMPLE_ONEWIRE_GPIO_PIN` defined in [onewire_ds18b20_example_main.c](main/onewire_ds18b20_example_main.c).
+
+*Note*: Parasite power mode is not supported currently by this example, you have to connect VDD pin to make DS18B20 functional.
+
+### Build and Flash
+
+Run `idf.py -p PORT flash monitor` to build, flash and monitor 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.
+
+## Console Output
+
+If there are some DS18B20s on the bus:
+
+```
+I (327) cpu_start: Starting scheduler on PRO CPU.
+I (0) cpu_start: Starting scheduler on APP CPU.
+I (338) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
+I (348) onewire_rmt: RMT Tx channel created for 1-wire bus
+I (358) gpio: GPIO[5]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
+I (358) onewire_rmt: RMT Rx channel created for 1-wire bus
+I (368) example: 1-wire bus installed
+I (418) example: found device with rom id 28FF30BE21170317
+I (458) example: found device with rom id 28FF297E211703A1
+I (498) example: found device with rom id 28FF6F7921170352
+I (508) example: 3 devices found on 1-wire bus
+I (2518) example: temperature of device 28FF30BE21170317: 27.00C
+I (2528) example: temperature of device 28FF297E211703A1: 26.81C
+I (2538) example: temperature of device 28FF6F7921170352: 26.50C
+I (3548) example: temperature of device 28FF30BE21170317: 26.94C
+I (3558) example: temperature of device 28FF297E211703A1: 26.75C
+I (3568) example: temperature of device 28FF6F7921170352: 26.44C
+```
+
+If there is no DS18B20 on the bus:
+
+```
+I (327) cpu_start: Starting scheduler on PRO CPU.
+I (0) cpu_start: Starting scheduler on APP CPU.
+I (337) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
+I (347) onewire_rmt: RMT Tx channel created for 1-wire bus
+I (357) gpio: GPIO[5]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
+I (357) onewire_rmt: RMT Rx channel created for 1-wire bus
+I (367) example: 1-wire bus installed
+E (377) onewire_rmt: no device present on 1-wire bus
+I (377) example: 0 device found on 1-wire bus
+I (387) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
+I (397) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
+I (397) example: 1-wire bus deleted
+```
+
+## Troubleshooting
+
+For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
diff --git a/components/onewire_bus/onewire_bus.c b/components/onewire_bus/onewire_bus.c
new file mode 100644
index 0000000..8bfa126
--- /dev/null
+++ b/components/onewire_bus/onewire_bus.c
@@ -0,0 +1,158 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include
+#include "esp_check.h"
+#include "esp_log.h"
+#include "onewire_bus.h"
+
+static const char *TAG = "onewire";
+
+struct onewire_rom_search_context_t {
+ onewire_bus_handle_t bus_handle;
+
+ uint8_t last_device_flag;
+ uint16_t last_discrepancy;
+ uint8_t rom_number[8];
+};
+
+// Algorithm inspired by https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/187.html
+
+static const uint8_t dscrc_table[] = {
+ 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
+ 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
+ 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
+ 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
+ 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
+ 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
+ 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
+ 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
+ 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
+ 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
+ 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
+ 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
+ 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
+ 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
+ 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
+ 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53
+};
+
+uint8_t onewire_check_crc8(uint8_t *input, size_t input_size)
+{
+ uint8_t crc8 = 0;
+
+ for (size_t i = 0; i < input_size; i ++) {
+ crc8 = dscrc_table[crc8 ^ input[i]];
+ }
+
+ return crc8;
+}
+
+esp_err_t onewire_rom_search_context_create(onewire_bus_handle_t handle, onewire_rom_search_context_handler_t *context_out)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+ ESP_RETURN_ON_FALSE(context_out, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
+
+ struct onewire_rom_search_context_t *context = calloc(1, sizeof(struct onewire_rom_search_context_t));
+ if (!context) {
+ return ESP_ERR_NO_MEM;
+ }
+
+ context->bus_handle = handle;
+ *context_out = context;
+
+ return ESP_OK;
+}
+
+esp_err_t onewire_rom_search_context_delete(onewire_rom_search_context_handler_t context)
+{
+ ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
+
+ free(context);
+
+ return ESP_OK;
+}
+
+esp_err_t onewire_rom_search(onewire_rom_search_context_handler_t context)
+{
+ ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
+
+ uint8_t last_zero = 0;
+
+ if (!context->last_device_flag) {
+ if (onewire_bus_reset(context->bus_handle) != ESP_OK) { // no device present
+ return ESP_ERR_NOT_FOUND;
+ }
+
+ // send rom search command and start search algorithm
+ ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(context->bus_handle, (uint8_t[]){ONEWIRE_CMD_SEARCH_ROM}, 1),
+ TAG, "error while sending search rom command");
+
+ for (uint16_t rom_bit_index = 0; rom_bit_index < 64; rom_bit_index ++) {
+ uint8_t rom_byte_index = rom_bit_index / 8;
+ uint8_t rom_bit_mask = 1 << (rom_bit_index % 8); // calculate byte index and bit mask in advance for convenience
+
+ uint8_t rom_bit, rom_bit_complement;
+ ESP_RETURN_ON_ERROR(onewire_bus_read_bit(context->bus_handle, &rom_bit), TAG, "error while reading rom bit"); // write 1 bit to read from the bus
+ ESP_RETURN_ON_ERROR(onewire_bus_read_bit(context->bus_handle, &rom_bit_complement),
+ TAG, "error while reading rom bit"); // read a bit and its complement
+
+ uint8_t search_direction;
+ if (rom_bit && rom_bit_complement) { // No devices participating in search.
+ ESP_LOGE(TAG, "no devices participating in search");
+ return ESP_ERR_NOT_FOUND;
+ } else {
+ if (rom_bit != rom_bit_complement) { // There are only 0s or 1s in the bit of the participating ROM numbers.
+ search_direction = rom_bit; // just go ahead
+ } else { // There are both 0s and 1s in the current bit position of the participating ROM numbers. This is a discrepancy.
+ if (rom_bit_index < context->last_discrepancy) { // current id bit is before the last discrepancy bit
+ search_direction = (context->rom_number[rom_byte_index] & rom_bit_mask) ? 0x01 : 0x00; // follow previous way
+ } else {
+ search_direction = (rom_bit_index == context->last_discrepancy) ? 0x01 : 0x00; // search for 0 bit first
+ }
+
+ if (search_direction == 0) { // record zero's position in last zero
+ last_zero = rom_bit_index;
+ }
+ }
+
+ if (search_direction == 1) { // set corrsponding rom bit by serach direction
+ context->rom_number[rom_byte_index] |= rom_bit_mask;
+ } else {
+ context->rom_number[rom_byte_index] &= ~rom_bit_mask;
+ }
+
+ ESP_RETURN_ON_ERROR(onewire_bus_write_bit(context->bus_handle, search_direction),
+ TAG, "error while writing direction bit"); // set search direction
+ }
+ }
+ } else {
+ ESP_LOGD(TAG, "1-wire rom search finished");
+ return ESP_FAIL;
+ }
+
+ // if the search was successful
+ context->last_discrepancy = last_zero;
+ if (context->last_discrepancy == 0) { // last zero loops back to the first bit
+ context->last_device_flag = true;
+ }
+
+ if (onewire_check_crc8(context->rom_number, 7) != context->rom_number[7]) { // check crc
+ ESP_LOGE(TAG, "bad crc checksum of device with id " ONEWIRE_ROM_ID_STR, ONEWIRE_ROM_ID(context->rom_number));
+ return ESP_ERR_INVALID_CRC;
+ }
+
+ return ESP_OK;
+}
+
+esp_err_t onewire_rom_get_number(onewire_rom_search_context_handler_t context, uint8_t *rom_number_out)
+{
+ ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context pointer");
+ ESP_RETURN_ON_FALSE(rom_number_out, ESP_ERR_INVALID_ARG, TAG, "invalid rom_number pointer");
+
+ memcpy(rom_number_out, context->rom_number, sizeof(context->rom_number));
+
+ return ESP_OK;
+}
diff --git a/components/onewire_bus/onewire_bus.h b/components/onewire_bus/onewire_bus.h
new file mode 100644
index 0000000..3cb0ea5
--- /dev/null
+++ b/components/onewire_bus/onewire_bus.h
@@ -0,0 +1,77 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include "esp_err.h"
+#include "onewire_bus_rmt.h"
+
+#define ONEWIRE_CMD_SEARCH_ROM 0xF0
+#define ONEWIRE_CMD_READ_ROM 0x33
+#define ONEWIRE_CMD_MATCH_ROM 0x55
+#define ONEWIRE_CMD_SKIP_ROM 0xCC
+#define ONEWIRE_CMD_ALARM_SEARCH_ROM 0xEC
+
+#define ONEWIRE_ROM_ID(id) (id)[0], (id)[1], (id)[2], (id)[3], (id)[4], (id)[5], (id)[6], (id)[7]
+#define ONEWIRE_ROM_ID_STR "%02X%02X%02X%02X%02X%02X%02X%02X"
+
+/**
+ * @brief Type of 1-wire ROM search algorithm context
+ *
+ */
+typedef struct onewire_rom_search_context_t *onewire_rom_search_context_handler_t;
+
+/**
+ * @brief Calculate Dallas CRC value of given buffer
+ *
+ * @param[in] input Input buffer to calculate CRC value
+ * @param[in] input_size Size of input buffer
+ * @return CRC result of input buffer
+ */
+uint8_t onewire_check_crc8(uint8_t *input, size_t input_size);
+
+/**
+ * @brief Create context for 1-wire ROM search algorithm
+ *
+ * @param[in] handle 1-wire handle used for ROM search
+ * @param[out] context_out Created context for ROM search algorithm
+ * @return
+ * - ESP_OK 1-wire ROM search context is created successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_rom_search_context_create(onewire_bus_handle_t handle, onewire_rom_search_context_handler_t *context_out);
+
+/**
+ * @brief Delete context for 1-wire ROM search algorithm
+ *
+ * @param[in] context Context for ROM search algorithm
+ * @return
+ * - ESP_OK 1-wire ROM search context is deleted successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_rom_search_context_delete(onewire_rom_search_context_handler_t context);
+
+/**
+ * @brief Search next device on 1-wire bus
+ *
+ * @param[in] context Context for ROM search algorithm
+ * @return
+ * - ESP_OK Successfully found a device
+ * - ESP_ERR_NOT_FOUND There are no device on the bus
+ * - ESP_ERR_INVALID_CRC Bad CRC value of found device
+ * - ESP_FAIL Reached last device on the bus, search algorighm finishes
+ */
+esp_err_t onewire_rom_search(onewire_rom_search_context_handler_t context);
+
+/**
+ * @brief Get device ROM number from ROM search context
+ *
+ * @param[in] context Context for ROM search algorithm
+ * @param[out] rom_number_out Device ROM number
+ * @return
+ * - ESP_OK Get ROM numbuer from 1-wire ROM search context success.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_rom_get_number(onewire_rom_search_context_handler_t context, uint8_t *rom_number_out);
diff --git a/components/onewire_bus/onewire_bus_rmt.c b/components/onewire_bus/onewire_bus_rmt.c
new file mode 100644
index 0000000..af0e6a2
--- /dev/null
+++ b/components/onewire_bus/onewire_bus_rmt.c
@@ -0,0 +1,438 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "esp_check.h"
+#include "driver/rmt_tx.h"
+#include "driver/rmt_rx.h"
+#include "driver/rmt_types.h"
+#include "driver/rmt_encoder.h"
+#include "onewire_bus_rmt.h"
+
+static const char *TAG = "onewire_rmt";
+
+/**
+ * @brief RMT resolution for 1-wire bus, in Hz
+ *
+ */
+#define ONEWIRE_RMT_RESOLUTION_HZ 1000000
+
+/**
+ * @brief 1-wire bus timing parameters, in us (1/ONEWIRE_RMT_RESOLUTION_HZ)
+ *
+ */
+#define ONEWIRE_RESET_PULSE_DURATION 500 // duration of reset bit
+#define ONEWIRE_RESET_WAIT_DURATION 200 // how long should master wait for device to show its presence
+#define ONEWIRE_RESET_PRESENSE_WAIT_DURATION_MIN 15 // minimum duration for master to wait device to show its presence
+#define ONEWIRE_RESET_PRESENSE_DURATION_MIN 60 // minimum duration for master to recognize device as present
+
+#define ONEWIRE_SLOT_START_DURATION 2 // bit start pulse duration
+#define ONEWIRE_SLOT_BIT_DURATION 60 // duration for each bit to transmit
+// refer to https://www.maximintegrated.com/en/design/technical-documents/app-notes/3/3829.html for more information
+#define ONEWIRE_SLOT_RECOVERY_DURATION 2 // recovery time between each bit, should be longer in parasite power mode
+#define ONEWIRE_SLOT_BIT_SAMPLE_TIME 15 // how long after bit start pulse should the master sample from the bus
+
+/*
+Reset Pulse:
+
+ | RESET_PULSE | RESET_WAIT_DURATION |
+ | _DURATION | |
+ | | | | RESET | |
+ | | * | | _PRESENSE | |
+ | | | | _DURATION | |
+------────┐ ┌─────┐ ┌───────-------
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ └─────────────┘ └───────────┘
+*: RESET_PRESENSE_WAIT_DURATION
+
+Write 1 bit:
+
+ | SLOT_START | SLOT_BIT | SLOT_RECOVERY | NEXT
+ | _DURATION | _DURATION | _DURATION | SLOT
+ | | | |
+------────┐ ┌───────────────────────────────------
+ │ │
+ │ │
+ │ │
+ └────────────┘
+
+Write 0 bit:
+
+ | SLOT_START | SLOT_BIT | SLOT_RECOVERY | NEXT
+ | _DURATION | _DURATION | _DURATION | SLOT
+ | | | |
+------────┐ ┌───────────────────------
+ │ │
+ │ │
+ │ │
+ └────────────────────────┘
+
+Read 1 bit:
+
+
+ | SLOT_START | SLOT_BIT_DURATION | SLOT_RECOVERY | NEXT
+ | _DURATION | | _DURATION | SLOT
+ | | SLOT_BIT_ | | |
+ | | SAMPLE_TIME | | |
+------────┐ ┌────────────────────────────────────────------
+ │ │
+ │ │
+ │ │
+ └────────────┘
+
+Read 0 bit:
+
+ | SLOT_START | SLOT_BIT_DURATION | SLOT_RECOVERY | NEXT
+ | _DURATION | | _DURATION | SLOT
+ | | SLOT_BIT_ | | |
+ | | SAMPLE_TIME | | |
+------────┐ | | ┌───────────────────────------
+ │ | │
+ │ | PULLED DOWN │
+ │ | BY DEVICE │
+ └─────────────────────────────┘
+*/
+
+struct onewire_bus_t {
+ rmt_channel_handle_t tx_channel; /*!< rmt tx channel handler */
+ rmt_encoder_handle_t tx_bytes_encoder; /*!< used to encode commands and data */
+ rmt_encoder_handle_t tx_copy_encoder; /*!< used to encode reset pulse and bits */
+
+ rmt_channel_handle_t rx_channel; /*!< rmt rx channel handler */
+ rmt_symbol_word_t *rx_symbols; /*!< hold rmt raw symbols */
+
+ size_t max_rx_bytes; /*!< buffer size in byte for single receive transaction */
+
+ QueueHandle_t receive_queue;
+};
+
+const static rmt_symbol_word_t onewire_bit0_symbol = {
+ .level0 = 0,
+ .duration0 = ONEWIRE_SLOT_START_DURATION + ONEWIRE_SLOT_BIT_DURATION,
+ .level1 = 1,
+ .duration1 = ONEWIRE_SLOT_RECOVERY_DURATION
+};
+
+const static rmt_symbol_word_t onewire_bit1_symbol = {
+ .level0 = 0,
+ .duration0 = ONEWIRE_SLOT_START_DURATION,
+ .level1 = 1,
+ .duration1 = ONEWIRE_SLOT_BIT_DURATION + ONEWIRE_SLOT_RECOVERY_DURATION
+};
+
+const static rmt_symbol_word_t onewire_reset_pulse_symbol = {
+ .level0 = 0,
+ .duration0 = ONEWIRE_RESET_PULSE_DURATION,
+ .level1 = 1,
+ .duration1 = ONEWIRE_RESET_WAIT_DURATION
+};
+
+const static rmt_transmit_config_t onewire_rmt_tx_config = {
+ .loop_count = 0, // no transfer loop
+ .flags.eot_level = 1 // onewire bus should be released in IDLE
+};
+
+const static rmt_receive_config_t onewire_rmt_rx_config = {
+ .signal_range_min_ns = 1000000000 / ONEWIRE_RMT_RESOLUTION_HZ,
+ .signal_range_max_ns = (ONEWIRE_RESET_PULSE_DURATION + ONEWIRE_RESET_WAIT_DURATION) * 1000
+};
+
+static bool onewire_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
+{
+ BaseType_t task_woken = pdFALSE;
+ struct onewire_bus_t *handle = (struct onewire_bus_t *)user_data;
+
+ xQueueSendFromISR(handle->receive_queue, edata, &task_woken);
+
+ return task_woken;
+}
+
+/*
+[0].0 means symbol[0].duration0
+
+First reset pulse after rmt channel init:
+
+Bus is low | Reset | Wait | Device | Bus Idle
+after init | Pulse | | Presense |
+ ┌──────┐ ┌─────------
+ │ │ │
+ │ │ │
+ │ │ │
+------─────────────┘ └──────────┘
+ 1 2 3
+
+ [0].1 [0].0 [1].1 [1].0
+
+
+Following reset pulses:
+
+Bus is high | Reset | Wait | Device | Bus Idle
+after init | Pulse | | Presense |
+------──────┐ ┌──────┐ ┌─────------
+ │ │ │ │
+ │ │ │ │
+ │ │ │ │
+ └───────┘ └──────────┘
+ 1 2 3 4
+
+ [0].0 [0].1 [1].0 [1].1
+*/
+
+static bool onewire_rmt_check_presence_pulse(rmt_symbol_word_t *rmt_symbols, size_t symbol_num)
+{
+ if (symbol_num >= 2) { // there should be at lease 2 symbols(3 or 4 edges)
+ if (rmt_symbols[0].level1 == 1) { // bus is high before reset pulse
+ if (rmt_symbols[0].duration1 > ONEWIRE_RESET_PRESENSE_WAIT_DURATION_MIN &&
+ rmt_symbols[1].duration0 > ONEWIRE_RESET_PRESENSE_DURATION_MIN) {
+ return true;
+ }
+ } else { // bus is low before reset pulse(first pulse after rmt channel init)
+ if (rmt_symbols[0].duration0 > ONEWIRE_RESET_PRESENSE_WAIT_DURATION_MIN &&
+ rmt_symbols[1].duration1 > ONEWIRE_RESET_PRESENSE_DURATION_MIN) {
+ return true;
+ }
+ }
+ }
+
+ ESP_LOGE(TAG, "no device present on 1-wire bus");
+ return false;
+}
+
+static void onewire_rmt_decode_data(rmt_symbol_word_t *rmt_symbols, size_t symbol_num, uint8_t *decoded_bytes)
+{
+ size_t byte_pos = 0, bit_pos = 0;
+ for (size_t i = 0; i < symbol_num; i ++) {
+ if (rmt_symbols[i].duration0 > ONEWIRE_SLOT_BIT_SAMPLE_TIME) { // 0 bit
+ decoded_bytes[byte_pos] &= ~(1 << bit_pos); // LSB first
+ } else { // 1 bit
+ decoded_bytes[byte_pos] |= 1 << bit_pos;
+ }
+
+ bit_pos ++;
+ if (bit_pos >= 8) {
+ bit_pos = 0;
+ byte_pos ++;
+ }
+ }
+}
+
+esp_err_t onewire_new_bus_rmt(onewire_rmt_config_t *config, onewire_bus_handle_t *handle_out)
+{
+ ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid config pointer");
+ ESP_RETURN_ON_FALSE(handle_out, ESP_ERR_INVALID_ARG, TAG, "invalid handle pointer");
+
+ esp_err_t ret = ESP_OK;
+
+ struct onewire_bus_t *handle = calloc(1, sizeof(struct onewire_bus_t));
+ ESP_GOTO_ON_FALSE(handle, ESP_ERR_NO_MEM, err, TAG, "memory allocation for 1-wire bus handler failed");
+
+ // create rmt bytes encoder to transmit 1-wire commands and data
+ rmt_bytes_encoder_config_t bytes_encoder_config = {
+ .bit0 = onewire_bit0_symbol,
+ .bit1 = onewire_bit1_symbol,
+ .flags.msb_first = 0
+ };
+ ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &handle->tx_bytes_encoder),
+ err, TAG, "create data tx encoder failed");
+
+ // create rmt copy encoder to transmit 1-wire reset pulse or bits
+ rmt_copy_encoder_config_t copy_encoder_config = {};
+ ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &handle->tx_copy_encoder),
+ err, TAG, "create reset pulse tx encoder failed");
+
+ // create rmt rx channel
+ rmt_rx_channel_config_t onewire_rx_channel_cfg = {
+ .clk_src = RMT_CLK_SRC_DEFAULT,
+ .gpio_num = config->gpio_pin,
+#if SOC_RMT_SUPPORT_RX_PINGPONG
+ .mem_block_symbols = 64, // when the chip is ping-pong capable, we can use less rx memory blocks
+#else
+ .mem_block_symbols = config->max_rx_bytes * 8,
+#endif
+ .resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ, // in us
+ };
+ ESP_GOTO_ON_ERROR(rmt_new_rx_channel(&onewire_rx_channel_cfg, &handle->rx_channel),
+ err, TAG, "create rmt rx channel failed");
+ ESP_LOGI(TAG, "RMT Tx channel created for 1-wire bus");
+
+ // create rmt tx channel after rx channel
+ rmt_tx_channel_config_t onewire_tx_channel_cfg = {
+ .clk_src = RMT_CLK_SRC_DEFAULT,
+ .gpio_num = config->gpio_pin,
+ .mem_block_symbols = 64, // ping-pong is always avaliable on tx channel, save hardware memory blocks
+ .resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ, // in us
+ .trans_queue_depth = 4,
+ .flags.io_loop_back = true, // make tx channel coexist with rx channel on the same gpio pin
+ .flags.io_od_mode = true // enable open-drain mode for 1-wire bus
+ };
+ ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&onewire_tx_channel_cfg, &handle->tx_channel),
+ err, TAG, "create rmt tx channel failed");
+ ESP_LOGI(TAG, "RMT Rx channel created for 1-wire bus");
+
+ // allocate rmt rx symbol buffer
+ handle->rx_symbols = malloc(config->max_rx_bytes * sizeof(rmt_symbol_word_t) * 8);
+ ESP_GOTO_ON_FALSE(handle->rx_symbols, ESP_ERR_NO_MEM, err, TAG, "memory allocation for rx symbol buffer failed");
+ handle->max_rx_bytes = config->max_rx_bytes;
+
+ handle->receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));
+ ESP_GOTO_ON_FALSE(handle->receive_queue, ESP_ERR_NO_MEM, err, TAG, "receive queue creation failed");
+
+ // register rmt rx done callback
+ rmt_rx_event_callbacks_t cbs = {
+ .on_recv_done = onewire_rmt_rx_done_callback
+ };
+ ESP_GOTO_ON_ERROR(rmt_rx_register_event_callbacks(handle->rx_channel, &cbs, handle),
+ err, TAG, "enable rmt rx channel failed");
+
+ // enable rmt channels
+ ESP_GOTO_ON_ERROR(rmt_enable(handle->rx_channel), err, TAG, "enable rmt rx channel failed");
+ ESP_GOTO_ON_ERROR(rmt_enable(handle->tx_channel), err, TAG, "enable rmt tx channel failed");
+
+ *handle_out = handle;
+ return ESP_OK;
+
+err:
+ if (handle) {
+ onewire_del_bus(handle);
+ }
+
+ return ret;
+}
+
+esp_err_t onewire_del_bus(onewire_bus_handle_t handle)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+
+ if (handle->tx_bytes_encoder) {
+ rmt_del_encoder(handle->tx_bytes_encoder);
+ }
+ if (handle->tx_copy_encoder) {
+ rmt_del_encoder(handle->tx_copy_encoder);
+ }
+ if (handle->rx_channel) {
+ rmt_disable(handle->rx_channel);
+ rmt_del_channel(handle->rx_channel);
+ }
+ if (handle->tx_channel) {
+ rmt_disable(handle->tx_channel);
+ rmt_del_channel(handle->tx_channel);
+ }
+ if (handle->receive_queue) {
+ vQueueDelete(handle->receive_queue);
+ }
+ if (handle->rx_symbols) {
+ free(handle->rx_symbols);
+ }
+ free(handle);
+
+ return ESP_OK;
+}
+
+esp_err_t onewire_bus_reset(onewire_bus_handle_t handle)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+
+ // send reset pulse while receive presence pulse
+ ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, sizeof(rmt_symbol_word_t) * 2, &onewire_rmt_rx_config),
+ TAG, "1-wire reset pulse receive failed");
+ ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, &onewire_reset_pulse_symbol, sizeof(onewire_reset_pulse_symbol), &onewire_rmt_tx_config),
+ TAG, "1-wire reset pulse transmit failed");
+
+ // wait and check presence pulse
+ bool is_present = false;
+ rmt_rx_done_event_data_t rmt_rx_evt_data;
+ if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
+ is_present = onewire_rmt_check_presence_pulse(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols);
+ }
+
+ return is_present ? ESP_OK : ESP_ERR_NOT_FOUND;
+}
+
+esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t handle, const uint8_t *tx_data, uint8_t tx_data_size)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+ ESP_RETURN_ON_FALSE(tx_data && tx_data_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid tx buffer or buffer size");
+
+ // transmit data
+ ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_bytes_encoder, tx_data, tx_data_size, &onewire_rmt_tx_config),
+ TAG, "1-wire data transmit failed");
+
+ // wait the transmission to complete
+ ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(handle->tx_channel, 50), TAG, "wait for 1-wire data transmit failed");
+
+ return ESP_OK;
+}
+
+esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t handle, uint8_t *rx_data, size_t rx_data_size)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+ ESP_RETURN_ON_FALSE(rx_data && rx_data_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid rx buffer or buffer size");
+ ESP_RETURN_ON_FALSE(!(rx_data_size > handle->max_rx_bytes), ESP_ERR_INVALID_ARG,
+ TAG, "rx_data_size too large for buffer to hold");
+
+ uint8_t tx_buffer[rx_data_size];
+ memset(tx_buffer, 0xFF, rx_data_size); // transmit one bits to generate read clock
+
+ // transmit 1 bits while receiving
+ ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, rx_data_size * 8 * sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
+ TAG, "1-wire data receive failed");
+ ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_bytes_encoder, tx_buffer, sizeof(tx_buffer), &onewire_rmt_tx_config),
+ TAG, "1-wire data transmit failed");
+
+ // wait the transmission finishes and decode data
+ rmt_rx_done_event_data_t rmt_rx_evt_data;
+ if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
+ onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, rx_data);
+ } else {
+ return ESP_ERR_TIMEOUT;
+ }
+
+ return ESP_OK;
+}
+
+esp_err_t onewire_bus_write_bit(onewire_bus_handle_t handle, uint8_t tx_bit)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+
+ const rmt_symbol_word_t *symbol_to_transmit = tx_bit ? &onewire_bit1_symbol : &onewire_bit0_symbol;
+
+ // transmit bit
+ ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, symbol_to_transmit, sizeof(onewire_bit1_symbol), &onewire_rmt_tx_config),
+ TAG, "1-wire bit transmit failed");
+
+ // wait the transmission to complete
+ ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(handle->tx_channel, 50), TAG, "wait for 1-wire bit transmit failed");
+
+ return ESP_OK;
+}
+
+esp_err_t onewire_bus_read_bit(onewire_bus_handle_t handle, uint8_t *rx_bit)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+ ESP_RETURN_ON_FALSE(rx_bit, ESP_ERR_INVALID_ARG, TAG, "invalid rx_bit pointer");
+
+ // transmit 1 bit while receiving
+ ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
+ TAG, "1-wire bit receive failed");
+ ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, &onewire_bit1_symbol, sizeof(onewire_bit1_symbol), &onewire_rmt_tx_config),
+ TAG, "1-wire bit transmit failed");
+
+ // wait the transmission finishes and decode data
+ rmt_rx_done_event_data_t rmt_rx_evt_data;
+ if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
+ uint8_t rx_buffer[1];
+ onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, rx_buffer);
+ *rx_bit = rx_buffer[0] & 0x01;
+ } else {
+ return ESP_ERR_TIMEOUT;
+ }
+
+ return ESP_OK;
+}
diff --git a/components/onewire_bus/onewire_bus_rmt.h b/components/onewire_bus/onewire_bus_rmt.h
new file mode 100644
index 0000000..5661364
--- /dev/null
+++ b/components/onewire_bus/onewire_bus_rmt.h
@@ -0,0 +1,106 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include
+#include "esp_err.h"
+#include "driver/gpio.h"
+
+/**
+ * @brief 1-wire bus config
+ *
+ */
+typedef struct {
+ gpio_num_t gpio_pin; /*!< gpio used for 1-wire bus */
+ uint8_t max_rx_bytes; /*!< should be larger than the largest possible single receive size */
+} onewire_rmt_config_t;
+
+/**
+ * @brief Type of 1-wire bus handle
+ *
+ */
+typedef struct onewire_bus_t *onewire_bus_handle_t;
+
+/**
+ * @brief Install new 1-wire bus
+ *
+ * @param[in] config 1-wire bus configurations
+ * @param[out] handle_out Installed new 1-wire bus' handle
+ * @return
+ * - ESP_OK 1-wire bus is installed successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ * - ESP_ERR_NO_MEM Memory allocation failed.
+ */
+esp_err_t onewire_new_bus_rmt(onewire_rmt_config_t *config, onewire_bus_handle_t *handle_out);
+
+/**
+ * @brief Delete existing 1-wire bus
+ *
+ * @param[in] handle 1-wire bus handle to be deleted
+ * @return
+ * - ESP_OK 1-wire bus is deleted successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_del_bus(onewire_bus_handle_t handle);
+
+/**
+ * @brief Send reset pulse on 1-wire bus, and detect if there are devices on the bus
+ *
+ * @param[in] handle 1-wire bus handle
+ * @return
+ * - ESP_OK There are devices present on 1-wire bus.
+ * - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
+ */
+esp_err_t onewire_bus_reset(onewire_bus_handle_t handle);
+
+/**
+ * @brief Write bytes to 1-wire bus, this is a blocking function
+ *
+ * @param[in] handle 1-wire bus handle
+ * @param[in] tx_data pointer to data to be sent
+ * @param[in] tx_data_count number of data to be sent
+ * @return
+ * - ESP_OK Write bytes to 1-wire bus successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t handle, const uint8_t *tx_data, uint8_t tx_data_size);
+
+/**
+ * @brief Read bytes from 1-wire bus
+ *
+ * @note While receiving data, we use rmt transmit channel to send 0xFF to generate read pulse,
+ * at the same time, receive channel is used to record weather the bus is pulled down by device.
+ *
+ * @param[in] handle 1-wire bus handle
+ * @param[out] rx_data pointer to received data
+ * @param[in] rx_data_count number of received data
+ * @return
+ * - ESP_OK Read bytes from 1-wire bus successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t handle, uint8_t *rx_data, size_t rx_data_size);
+
+/**
+ * @brief Write a bit to 1-wire bus, this is a blocking function
+ *
+ * @param[in] handle 1-wire bus handle
+ * @param[in] tx_bit bit to transmit, 0 for zero bit, other for one bit
+ * @return
+ * - ESP_OK Write bit to 1-wire bus successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_bus_write_bit(onewire_bus_handle_t handle, uint8_t tx_bit);
+
+/**
+ * @brief Read a bit from 1-wire bus
+ *
+ * @param[in] handle 1-wire bus handle
+ * @param[out] rx_bit received bit, 0 for zero bit, 1 for one bit
+ * @return
+ * - ESP_OK Read bit from 1-wire bus successfully.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ */
+esp_err_t onewire_bus_read_bit(onewire_bus_handle_t handle, uint8_t *rx_bit);
diff --git a/doc/img/iot_onoff.png b/doc/img/iot_onoff.png
new file mode 100644
index 0000000..e6012bc
Binary files /dev/null and b/doc/img/iot_onoff.png differ
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
new file mode 100644
index 0000000..a86a892
--- /dev/null
+++ b/main/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(SOURCES
+ "app_main.c"
+ "non_volatile_storage.c"
+ "led.c"
+ "wifi.c"
+ "ds18b20.c"
+ "temperature.c"
+ "mqtt.c"
+ "task_monitor.c"
+)
+
+set(INCLUDES "." "include")
+
+idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS ${INCLUDES})
+
+set_source_files_properties(${SOURCES} PROPERTIES COMPILE_FLAGS "-Wall -Wextra -Werror")
+target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild
new file mode 100644
index 0000000..7e2ee0e
--- /dev/null
+++ b/main/Kconfig.projbuild
@@ -0,0 +1,94 @@
+menu "ESP32_WIFI_ONEWIRE_MQTT Project Configuration"
+
+ config ESP_WIFI_SSID
+ string "WiFi SSID"
+ default "Wi-Fi_name"
+ help
+ SSID (network name) for the example to connect to.
+
+ config ESP_WIFI_PASSWORD
+ string "WiFi Password"
+ default "Wi-Fi_password"
+ help
+ WiFi password (WPA or WPA2) for the example to use.
+
+ choice ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD
+ prompt "WiFi Scan auth mode threshold"
+ default ESP_WIFI_AUTH_WPA2_PSK
+ help
+ The weakest authmode to accept in the scan mode.
+ This value defaults to ESP_WIFI_AUTH_WPA2_PSK incase password is present and ESP_WIFI_AUTH_OPEN is used.
+ Please select ESP_WIFI_AUTH_WEP/ESP_WIFI_AUTH_WPA_PSK incase AP is operating in WEP/WPA mode.
+
+ config ESP_WIFI_AUTH_OPEN
+ bool "OPEN"
+ config ESP_WIFI_AUTH_WEP
+ bool "WEP"
+ config ESP_WIFI_AUTH_WPA_PSK
+ bool "WPA PSK"
+ config ESP_WIFI_AUTH_WPA2_PSK
+ bool "WPA2 PSK"
+ config ESP_WIFI_AUTH_WPA_WPA2_PSK
+ bool "WPA/WPA2 PSK"
+ config ESP_WIFI_AUTH_WPA3_PSK
+ bool "WPA3 PSK"
+ config ESP_WIFI_AUTH_WPA2_WPA3_PSK
+ bool "WPA2/WPA3 PSK"
+ config ESP_WIFI_AUTH_WAPI_PSK
+ bool "WAPI PSK"
+ endchoice
+
+ config BROKER_URL
+ string "Broker URL"
+ default "mqtt://mqtt.eclipseprojects.io"
+ help
+ URL of the broker to connect to.
+
+ config BROKER_PORT
+ int "Broker Server Port"
+ default 1883
+ help
+ Server Port of the broker to connect to.
+
+ config BROKER_TOPIC_PREFIX
+ string "Broker Topic Prefix"
+ default "ESP32_WIFI_ONEWIRE_MQTT"
+ help
+ The MQTT topic name starting with prefix.
+
+ config ONEWIRE_DATA_GPIO_PIN
+ int "GPIO pin for DS18B20 device DATA bus"
+ range 0 39
+ default 5
+ help
+ Select the GPIO pin that is connected to the DS18B20 device DATA bus.
+ This pin is used for data communication between the device and the microcontroller.
+
+ config ONEWIRE_NUMBER_OF_DEVICES
+ int "Number of DS18B20 devices"
+ range 1 128
+ default 1
+ help
+ Specify the number of DS18B20 temperature devices that are connected to the DATA bus.
+
+ config ONEWIRE_TEMPERATURE_UPDATE_TIME
+ int "Update time for DS18B20 devices in seconds"
+ default 2
+ help
+ Specify the update time for DS18B20 temperature devices in seconds.
+
+ config FREERTOS_USE_TRACE_FACILITY
+ bool "Enable trace facility"
+ default y
+ help
+ Enables additional structure members and functions to assist with execution visualization and tracing
+ (see configUSE_TRACE_FACILITY documentation for more details).
+
+ config FREERTOS_GENERATE_RUN_TIME_STATS
+ bool "Enable trace facility"
+ default y
+ help
+ Enables collection of run time statistics for each task
+ (see configGENERATE_RUN_TIME_STATS documentation for more details).
+
+endmenu
diff --git a/main/app_main.c b/main/app_main.c
new file mode 100644
index 0000000..15f22e4
--- /dev/null
+++ b/main/app_main.c
@@ -0,0 +1,22 @@
+#include "esp_check.h"
+
+#include "led.h"
+#include "mqtt.h"
+#include "non_volatile_storage.h"
+#include "task_monitor.h"
+#include "temperature.h"
+#include "wifi.h"
+
+void app_main(void)
+{
+ ESP_ERROR_CHECK(nvs_init());
+
+ ESP_ERROR_CHECK(led_init());
+ ESP_ERROR_CHECK(wifi_init());
+ ESP_ERROR_CHECK(ds18b20_init());
+ ESP_ERROR_CHECK(mqtt_init());
+
+ // NOTE: USE_TRACE_FACILITY and GENERATE_RUN_TIME_STATS must be defined as 1
+ // in FreeRTOSConfig.h for this API function task_monitor() to be available.
+ ESP_ERROR_CHECK(task_monitor());
+}
diff --git a/main/ds18b20.c b/main/ds18b20.c
new file mode 100644
index 0000000..6801c4b
--- /dev/null
+++ b/main/ds18b20.c
@@ -0,0 +1,106 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include
+#include "ds18b20.h"
+#include "esp_check.h"
+
+static const char *TAG = "ds18b20";
+
+esp_err_t ds18b20_trigger_temperature_conversion(onewire_bus_handle_t handle, const uint8_t *rom_number)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+
+ ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
+
+ uint8_t tx_buffer[10];
+ uint8_t tx_buffer_size;
+
+ if (rom_number) { // specify rom id
+ tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
+ tx_buffer[9] = DS18B20_CMD_CONVERT_TEMP;
+ memcpy(&tx_buffer[1], rom_number, 8);
+ tx_buffer_size = 10;
+ } else { // skip rom id
+ tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
+ tx_buffer[1] = DS18B20_CMD_CONVERT_TEMP;
+ tx_buffer_size = 2;
+ }
+
+ ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
+ TAG, "error while triggering temperature convert");
+
+ return ESP_OK;
+}
+
+esp_err_t ds18b20_get_temperature(onewire_bus_handle_t handle, const uint8_t *rom_number, float *temperature)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+ ESP_RETURN_ON_FALSE(temperature, ESP_ERR_INVALID_ARG, TAG, "invalid temperature pointer");
+
+ ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
+
+ ds18b20_scratchpad_t scratchpad;
+
+ uint8_t tx_buffer[10];
+ uint8_t tx_buffer_size;
+
+ if (rom_number) { // specify rom id
+ tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
+ tx_buffer[9] = DS18B20_CMD_READ_SCRATCHPAD;
+ memcpy(&tx_buffer[1], rom_number, 8);
+ tx_buffer_size = 10;
+ } else {
+ tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
+ tx_buffer[1] = DS18B20_CMD_READ_SCRATCHPAD;
+ tx_buffer_size = 2;
+ }
+
+ ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
+ TAG, "error while sending read scratchpad command");
+ ESP_RETURN_ON_ERROR(onewire_bus_read_bytes(handle, (uint8_t *)&scratchpad, sizeof(scratchpad)),
+ TAG, "error while reading scratchpad command");
+
+ ESP_RETURN_ON_FALSE(onewire_check_crc8((uint8_t *)&scratchpad, 8) == scratchpad.crc_value, ESP_ERR_INVALID_CRC,
+ TAG, "crc error");
+
+ static const uint8_t lsb_mask[4] = { 0x07, 0x03, 0x01, 0x00 };
+ uint8_t lsb_masked = scratchpad.temp_lsb & (~lsb_mask[scratchpad.configuration >> 5]); // mask bits not used in low resolution
+ *temperature = (((int16_t)scratchpad.temp_msb << 8) | lsb_masked) / 16.0f;
+
+ return ESP_OK;
+}
+
+esp_err_t ds18b20_set_resolution(onewire_bus_handle_t handle, const uint8_t *rom_number, ds18b20_resolution_t resolution)
+{
+ ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
+
+ ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
+
+ uint8_t tx_buffer[10];
+ uint8_t tx_buffer_size;
+
+ if (rom_number) { // specify rom id
+ tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
+ tx_buffer[9] = DS18B20_CMD_WRITE_SCRATCHPAD;
+ memcpy(&tx_buffer[1], rom_number, 8);
+ tx_buffer_size = 10;
+ } else {
+ tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
+ tx_buffer[1] = DS18B20_CMD_WRITE_SCRATCHPAD;
+ tx_buffer_size = 2;
+ }
+
+ ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
+ TAG, "error while sending read scratchpad command");
+
+ tx_buffer[0] = 0;
+ tx_buffer[1] = 0;
+ tx_buffer[2] = resolution;
+ ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, 3),
+ TAG, "error while sending write scratchpad command");
+
+ return ESP_OK;
+}
diff --git a/main/include/ds18b20.h b/main/include/ds18b20.h
new file mode 100644
index 0000000..7c10646
--- /dev/null
+++ b/main/include/ds18b20.h
@@ -0,0 +1,78 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#pragma once
+
+#include "onewire_bus.h"
+
+#define DS18B20_CMD_CONVERT_TEMP 0x44
+#define DS18B20_CMD_WRITE_SCRATCHPAD 0x4E
+#define DS18B20_CMD_READ_SCRATCHPAD 0xBE
+
+/**
+ * @brief Structure of DS18B20's scratchpad
+ *
+ */
+typedef struct {
+ uint8_t temp_lsb; /*!< lsb of temperature */
+ uint8_t temp_msb; /*!< msb of temperature */
+ uint8_t th_user1; /*!< th register or user byte 1 */
+ uint8_t tl_user2; /*!< tl register or user byte 2 */
+ uint8_t configuration; /*!< configuration register */
+ uint8_t _reserved1;
+ uint8_t _reserved2;
+ uint8_t _reserved3;
+ uint8_t crc_value; /*!< crc value of scratchpad data */
+} ds18b20_scratchpad_t;
+
+/**
+ * @brief Enumeration of DS18B20's resolution config
+ *
+ */
+typedef enum {
+ DS18B20_RESOLUTION_12B = 0x7F, /*!< 750ms convert time */
+ DS18B20_RESOLUTION_11B = 0x5F, /*!< 375ms convert time */
+ DS18B20_RESOLUTION_10B = 0x3F, /*!< 187.5ms convert time */
+ DS18B20_RESOLUTION_9B = 0x1F, /*!< 93.75ms convert time */
+} ds18b20_resolution_t;
+
+/**
+ * @brief Trigger temperature conversion of DS18B20
+ *
+ * @param[in] handle 1-wire handle with DS18B20 on
+ * @param[in] rom_number ROM number to specify which DS18B20 to send command, NULL to skip ROM
+ * @return
+ * - ESP_OK Trigger tempreture convertsion success.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ * - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
+ */
+esp_err_t ds18b20_trigger_temperature_conversion(onewire_bus_handle_t handle, const uint8_t *rom_number);
+
+/**
+ * @brief Get temperature from DS18B20
+ *
+ * @param[in] handle 1-wire handle with DS18B20 on
+ * @param[in] rom_number ROM number to specify which DS18B20 to read from, NULL to skip ROM
+ * @param[out] temperature result from DS18B20
+ * @return
+ * - ESP_OK Get tempreture from DS18B20 success.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ * - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
+ * - ESP_ERR_INVALID_CRC CRC check failed.
+ */
+esp_err_t ds18b20_get_temperature(onewire_bus_handle_t handle, const uint8_t *rom_number, float *temperature);
+
+/**
+ * @brief Set DS18B20's temperation conversion resolution
+ *
+ * @param[in] handle 1-wire handle with DS18B20 on
+ * @param[in] rom_number ROM number to specify which DS18B20 to read from, NULL to skip ROM
+ * @param[in] resolution resolution of DS18B20's temperation conversion
+ * @return
+ * - ESP_OK Set DS18B20 resolution success.
+ * - ESP_ERR_INVALID_ARG Invalid argument.
+ * - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
+ */
+esp_err_t ds18b20_set_resolution(onewire_bus_handle_t handle, const uint8_t *rom_number, ds18b20_resolution_t resolution);
diff --git a/main/include/led.h b/main/include/led.h
new file mode 100644
index 0000000..eb021b1
--- /dev/null
+++ b/main/include/led.h
@@ -0,0 +1,20 @@
+#ifndef ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_LED_H_
+#define ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_LED_H_
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/event_groups.h" // NOTE: #include "FreeRTOS.h" must appear before #include "event_groups.h"
+
+#include "esp_err.h"
+#include "esp_bit_defs.h"
+
+esp_err_t led_init(void);
+
+extern EventGroupHandle_t led_event_group;
+
+typedef enum {
+ LED_EVENT_ON = BIT0,
+ LED_EVENT_OFF = BIT1,
+ LED_EVENT_BLINK = BIT2,
+} led_event_t;
+
+#endif // ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_LED_H_
diff --git a/main/include/mqtt.h b/main/include/mqtt.h
new file mode 100644
index 0000000..c64d667
--- /dev/null
+++ b/main/include/mqtt.h
@@ -0,0 +1,8 @@
+#ifndef ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_MQTT_H_
+#define ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_MQTT_H_
+
+#include "esp_err.h"
+
+esp_err_t mqtt_init(void);
+
+#endif // ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_MQTT_H_
diff --git a/main/include/non_volatile_storage.h b/main/include/non_volatile_storage.h
new file mode 100644
index 0000000..932b41d
--- /dev/null
+++ b/main/include/non_volatile_storage.h
@@ -0,0 +1,8 @@
+#ifndef ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_NON_VOLATILE_STORAGE_H_
+#define ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_NON_VOLATILE_STORAGE_H_
+
+#include "esp_err.h"
+
+esp_err_t nvs_init(void);
+
+#endif // ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_NON_VOLATILE_STORAGE_H_
diff --git a/main/include/task_monitor.h b/main/include/task_monitor.h
new file mode 100644
index 0000000..6b89e2e
--- /dev/null
+++ b/main/include/task_monitor.h
@@ -0,0 +1,26 @@
+#ifndef ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TASK_MONITOR_H_
+#define ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TASK_MONITOR_H_
+
+#include "esp_err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// NOTE: USE_TRACE_FACILITY and GENERATE_RUN_TIME_STATS must be defined as 1
+// in FreeRTOSConfig.h for this API function task_monitor() to be available.
+
+/**
+ * @brief Create a new task to monitor the status and activity of other FreeRTOS tasks in the system
+ *
+ * @return
+ * - ESP_OK Success.
+ * - ESP_ERR_NO_MEM Out of memory.
+ */
+esp_err_t task_monitor(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TASK_MONITOR_H_
diff --git a/main/include/temperature.h b/main/include/temperature.h
new file mode 100644
index 0000000..53d5285
--- /dev/null
+++ b/main/include/temperature.h
@@ -0,0 +1,18 @@
+#ifndef ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TEMPERATURE_H_
+#define ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TEMPERATURE_H_
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h" // NOTE: #include "FreeRTOS.h" must appear in source files before #include "queue.h"
+
+#include "esp_err.h"
+
+typedef struct {
+ uint8_t device;
+ float temperature;
+} temperature_device_t;
+
+esp_err_t ds18b20_init(void);
+
+extern QueueHandle_t temperature_queue;
+
+#endif // ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TEMPERATURE_H_
diff --git a/main/include/types.h b/main/include/types.h
new file mode 100644
index 0000000..3b8fe5b
--- /dev/null
+++ b/main/include/types.h
@@ -0,0 +1,13 @@
+#ifndef ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TYPES_H_
+#define ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TYPES_H_
+
+typedef enum {
+ PRIORITY_IDLE = 0,
+ PRIORITY_LOWEST,
+ PRIORITY_LOW,
+ PRIORITY_MIDDLE,
+ PRIORITY_HIGH,
+ PRIORITY_MAX,
+} task_priority_t;
+
+#endif // ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_TYPES_H_
diff --git a/main/include/wifi.h b/main/include/wifi.h
new file mode 100644
index 0000000..059df02
--- /dev/null
+++ b/main/include/wifi.h
@@ -0,0 +1,8 @@
+#ifndef ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_WIFI_H_
+#define ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_WIFI_H_
+
+#include "esp_err.h"
+
+esp_err_t wifi_init(void);
+
+#endif // ESP32_WIFI_ONEWIRE_MQTT_MAIN_INCLUDE_WIFI_H_
diff --git a/main/led.c b/main/led.c
new file mode 100644
index 0000000..328c924
--- /dev/null
+++ b/main/led.c
@@ -0,0 +1,113 @@
+#include "led.h"
+
+#include
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_event.h"
+#include "freertos/event_groups.h"
+
+#include "driver/gpio.h"
+#include "esp_err.h"
+#include "esp_timer.h"
+#include "esp_log.h"
+
+#include "types.h"
+
+static const char *TAG = "led";
+
+#define LED_1 GPIO_NUM_2
+
+typedef enum {
+ LED_OFF = 0,
+ LED_ON = 1,
+} led_state_t;
+
+EventGroupHandle_t led_event_group = NULL;
+static esp_timer_handle_t led_blink_timer = NULL;
+
+static void led_blink_timer_callback(void *arg);
+
+static void led_blink_timer_start(uint64_t timeout_us)
+{
+ if (led_blink_timer == NULL) {
+ const esp_timer_create_args_t timer_args = {
+ .callback = led_blink_timer_callback,
+ .arg = NULL,
+ .name = "Led timer"
+ };
+ ESP_ERROR_CHECK(esp_timer_create(&timer_args, &led_blink_timer));
+ }
+ ESP_ERROR_CHECK(esp_timer_start_once(led_blink_timer, timeout_us));
+}
+
+static void led_blink_timer_stop(void)
+{
+ if (led_blink_timer != NULL) {
+ if (esp_timer_is_active(led_blink_timer)) {
+ ESP_ERROR_CHECK(esp_timer_stop(led_blink_timer));
+ }
+ ESP_ERROR_CHECK(esp_timer_delete(led_blink_timer));
+ led_blink_timer = NULL;
+ }
+}
+
+static void led_blink_timer_callback(void *arg)
+{
+ static bool is_led_on = true;
+ is_led_on = !is_led_on;
+
+ if (is_led_on) {
+ gpio_set_level(LED_1, LED_OFF);
+ led_blink_timer_start(9 * 100000); // 900 ms
+ } else {
+ gpio_set_level(LED_1, LED_ON);
+ led_blink_timer_start(1 * 100000); // 100 ms
+ }
+}
+
+static void led_task(void *params)
+{
+ while (true) {
+ EventBits_t event_bits = xEventGroupWaitBits(led_event_group, LED_EVENT_ON | LED_EVENT_OFF | LED_EVENT_BLINK,
+ pdTRUE, pdFALSE, portMAX_DELAY);
+
+ if (event_bits & LED_EVENT_ON) {
+ led_blink_timer_stop();
+ gpio_set_level(LED_1, LED_ON);
+ } else if (event_bits & LED_EVENT_OFF) {
+ led_blink_timer_stop();
+ gpio_set_level(LED_1, LED_OFF);
+ } else if (event_bits & LED_EVENT_BLINK) {
+ led_blink_timer_start(0);
+ }
+ }
+}
+
+esp_err_t led_init(void)
+{
+ const gpio_config_t io_config = {
+ .pin_bit_mask = (1ULL << LED_1), // ((1ULL << LED_1) | (1ULL << LED_2) ...)
+ .mode = GPIO_MODE_OUTPUT,
+ .pull_up_en = GPIO_PULLUP_DISABLE,
+ .pull_down_en = GPIO_PULLDOWN_DISABLE,
+ .intr_type = GPIO_INTR_DISABLE,
+ };
+ gpio_config(&io_config);
+ gpio_set_level(LED_1, LED_OFF);
+
+ led_event_group = xEventGroupCreate();
+ if (led_event_group == NULL) {
+ ESP_LOGE(TAG, "led_event_group: Event Group was not created. Could not allocate required memory");
+ return ESP_ERR_NO_MEM;
+ }
+
+ BaseType_t status = xTaskCreate(led_task, "led_task", configMINIMAL_STACK_SIZE * 4, NULL, PRIORITY_MIDDLE, NULL);
+ if (status != pdPASS) {
+ ESP_LOGE(TAG, "led_task(): Task was not created. Could not allocate required memory");
+ return ESP_ERR_NO_MEM;
+ }
+
+ ESP_LOGI(TAG, "led_init() finished successfully");
+ return ESP_OK;
+}
diff --git a/main/mqtt.c b/main/mqtt.c
new file mode 100644
index 0000000..a51b2c1
--- /dev/null
+++ b/main/mqtt.c
@@ -0,0 +1,194 @@
+#include "mqtt.h"
+
+#include
+#include
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/event_groups.h"
+
+#include "esp_err.h"
+#include "esp_log.h"
+
+#include "mqtt_client.h"
+
+#include "led.h"
+#include "temperature.h"
+#include "types.h"
+
+static const char *TAG = "mqtt";
+
+typedef enum {
+ MQTT_RETAIN_TRUE = true,
+ MQTT_RETAIN_FALSE = false,
+} mqtt_retain_t;
+
+static const char TOPIC_LED_SWITCH[] = CONFIG_BROKER_TOPIC_PREFIX "/led_switch";
+static const char TOPIC_LED_STATUS[] = CONFIG_BROKER_TOPIC_PREFIX "/led_status";
+static const char TOPIC_TEMPERATURE[] = CONFIG_BROKER_TOPIC_PREFIX "/temperature/device_";
+
+static TaskHandle_t mqtt_task_handle = NULL;
+
+static void log_error_if_nonzero(const char *message, int error_code)
+{
+ if (error_code != 0) {
+ ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
+ }
+}
+
+static inline bool is_event_group_created(EventGroupHandle_t event)
+{
+ return event != NULL ? true : false;
+}
+
+static void handle_data(void *event_data)
+{
+ esp_mqtt_event_handle_t event = event_data;
+ esp_mqtt_client_handle_t client = event->client;
+
+ if (*(event->topic) == *TOPIC_LED_SWITCH) {
+ //xEventGroupSetBits(led_event_group, LED_EVENT_BLINK);
+ if (*(event->data) == '1') {
+ if (is_event_group_created(led_event_group)) {
+ xEventGroupSetBits(led_event_group, LED_EVENT_ON);
+ esp_mqtt_client_publish(client, TOPIC_LED_STATUS, "1", 0, 1, MQTT_RETAIN_TRUE);
+ ESP_LOGI(TAG, "led_event_group - LED_EVENT_ON");
+ }
+ } else if (*(event->data) == '0') {
+ if (is_event_group_created(led_event_group)) {
+ xEventGroupSetBits(led_event_group, LED_EVENT_OFF);
+ esp_mqtt_client_publish(client, TOPIC_LED_STATUS, "0", 0, 1, MQTT_RETAIN_TRUE);
+ ESP_LOGI(TAG, "led_event_group - LED_EVENT_OFF");
+ }
+ }
+ }
+}
+
+/*
+ * @brief Event handler registered to receive MQTT events
+ *
+ * This function is called by the MQTT client event loop.
+ *
+ * @param handler_args user data registered to the event.
+ * @param base Event base for the handler(always MQTT Base in this example).
+ * @param event_id The id for the received event.
+ * @param event_data The data for the event, esp_mqtt_event_handle_t.
+ */
+static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
+{
+ ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%lu", base, event_id);
+ esp_mqtt_event_handle_t event = event_data;
+ esp_mqtt_client_handle_t client = event->client;
+ int msg_id;
+ switch ((esp_mqtt_event_id_t)event_id) {
+ case MQTT_EVENT_CONNECTED:
+ vTaskResume(mqtt_task_handle);
+ ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
+
+ msg_id = esp_mqtt_client_subscribe(client, TOPIC_LED_SWITCH, 0);
+ ESP_LOGI(TAG, "Sent subscribe successful, msg_id=%d", msg_id);
+ break;
+ case MQTT_EVENT_DISCONNECTED:
+ vTaskSuspend(mqtt_task_handle);
+ ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
+ break;
+
+ case MQTT_EVENT_SUBSCRIBED:
+ ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
+ break;
+ case MQTT_EVENT_UNSUBSCRIBED:
+ ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
+ break;
+ case MQTT_EVENT_PUBLISHED:
+ ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
+ break;
+ case MQTT_EVENT_DATA:
+ ESP_LOGI(TAG, "MQTT_EVENT_DATA");
+ ESP_LOGI(TAG, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
+ ESP_LOGI(TAG, "DATA=%.*s\r\n", event->data_len, event->data);
+
+ handle_data(event_data);
+ break;
+ case MQTT_EVENT_ERROR:
+ ESP_LOGE(TAG, "MQTT_EVENT_ERROR");
+ if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
+ log_error_if_nonzero("Reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
+ log_error_if_nonzero("Reported from tls stack", event->error_handle->esp_tls_stack_err);
+ log_error_if_nonzero("Captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
+ ESP_LOGE(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
+ }
+ break;
+ default:
+ ESP_LOGW(TAG, "Other event id:%d", event->event_id);
+ break;
+ }
+}
+
+static const char* float_to_string(float number, char *string)
+{
+ sprintf(string, "%.1f", number); // Convert float to string with one decimal place
+ return string;
+}
+
+static const char* get_topic(char *topic, int device)
+{
+ sprintf(topic, "%s%d", TOPIC_TEMPERATURE, device);
+ return topic;
+}
+
+static inline bool is_queue_created(QueueHandle_t queue)
+{
+ return queue != NULL ? true : false;
+}
+
+static void mqtt_task(void *params)
+{
+ while (true) {
+ if (is_queue_created(temperature_queue)) {
+ temperature_device_t received_value;
+ BaseType_t status = xQueueReceive(temperature_queue, &received_value, portMAX_DELAY);
+
+ if (status == pdPASS) {
+ char topic[sizeof(TOPIC_TEMPERATURE) + 3 * sizeof(char)]; // 3 chars for number 128 (max devices)
+ char string[20]; // 20 - maximum number of characters for a float: -[sign][d].[d...]e[sign]d
+ const esp_mqtt_client_handle_t client = *(esp_mqtt_client_handle_t*)params;
+
+ esp_mqtt_client_publish(client, get_topic(topic, received_value.device),
+ float_to_string(received_value.temperature, string), 0, 1, MQTT_RETAIN_TRUE);
+ } else {
+ ESP_LOGE(TAG, "mqtt_task(): Failed to receive the message from the temperature_queue");
+ }
+ } else {
+ ESP_LOGE(TAG, "The temperature_queue has not been created yet");
+ vTaskDelay(pdMS_TO_TICKS(1000));
+ }
+ }
+}
+
+esp_err_t mqtt_init(void)
+{
+ const esp_mqtt_client_config_t mqtt_cfg = {
+ .broker.address.uri = CONFIG_BROKER_URL,
+ .broker.address.port = CONFIG_BROKER_PORT,
+ };
+
+ // NOTE: The parameter "client" must still exist when the created task executes. It must be static.
+ static esp_mqtt_client_handle_t client;
+ client = esp_mqtt_client_init(&mqtt_cfg);
+
+ ESP_ERROR_CHECK(esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL));
+ ESP_ERROR_CHECK(esp_mqtt_client_start(client));
+
+ BaseType_t status = xTaskCreate(mqtt_task, "mqtt_task", configMINIMAL_STACK_SIZE * 4, &client, PRIORITY_MIDDLE,
+ &mqtt_task_handle);
+ if (status != pdPASS) {
+ ESP_LOGE(TAG, "mqtt_task(): Task was not created. Could not allocate required memory");
+ return ESP_ERR_NO_MEM;
+ }
+ vTaskSuspend(mqtt_task_handle);
+
+ ESP_LOGI(TAG, "mqtt_init() finished successfully");
+
+ return ESP_OK;
+}
diff --git a/main/non_volatile_storage.c b/main/non_volatile_storage.c
new file mode 100644
index 0000000..82e4a54
--- /dev/null
+++ b/main/non_volatile_storage.c
@@ -0,0 +1,16 @@
+#include "non_volatile_storage.h"
+
+#include "nvs_flash.h"
+
+#include "esp_check.h"
+#include "esp_err.h"
+
+esp_err_t nvs_init(void)
+{
+ esp_err_t err = nvs_flash_init();
+ if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+ ESP_ERROR_CHECK(nvs_flash_erase());
+ err = nvs_flash_init();
+ }
+ return err;
+}
diff --git a/main/task_monitor.c b/main/task_monitor.c
new file mode 100644
index 0000000..ab4affb
--- /dev/null
+++ b/main/task_monitor.c
@@ -0,0 +1,180 @@
+#include "task_monitor.h"
+
+#include
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "esp_err.h"
+#include "esp_heap_caps.h"
+#include "esp_system.h"
+#include "esp_timer.h"
+#include "sdkconfig.h"
+
+#if !defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) || !defined(CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS)
+ #error "USE_TRACE_FACILITY and GENERATE_RUN_TIME_STATS must be defined!"
+#endif
+
+#define COLOR_BLACK "30"
+#define COLOR_RED "31"
+#define COLOR_GREEN "32"
+#define COLOR_YELLOW "33"
+#define COLOR_BLUE "34"
+#define COLOR_PURPLE "35"
+#define COLOR_CYAN "36"
+#define COLOR_WHITE "37"
+
+#define COLOR(COLOR) "\033[0;" COLOR "m"
+#define RESET_COLOR "\033[0m"
+#define UNDERLINE "\033[4m" // TODO: FIX. Does not work!
+#define BOLD "\033[1m" // TODO: FIX. Does not work!
+
+#define BLACK COLOR(COLOR_BLACK)
+#define RED COLOR(COLOR_RED)
+#define GREEN COLOR(COLOR_GREEN)
+#define YELLOW COLOR(COLOR_YELLOW)
+#define BLUE COLOR(COLOR_BLUE)
+#define PURPLE COLOR(COLOR_PURPLE)
+#define CYAN COLOR(COLOR_CYAN)
+#define WHITE COLOR(COLOR_WHITE)
+
+static uint32_t get_current_time_ms(void)
+{
+ return xTaskGetTickCount() * 1000 / configTICK_RATE_HZ;
+}
+
+
+static float get_current_heap_free_percent(void)
+{
+ size_t current_size = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
+ size_t total_size = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
+ return ((float) current_size / total_size) * 100.0;
+}
+
+static float get_minimum_heap_free_percent(void)
+{
+ size_t minimum_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
+ size_t total_size = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
+ return ((float) minimum_size / total_size) * 100.0;
+}
+
+static const char* int_to_string(int number, char *string)
+{
+ sprintf(string, "%d", number);
+ return string;
+}
+
+static const char* task_state_to_string(eTaskState state) {
+ switch (state) {
+ case eRunning:
+ return "Running";
+ case eReady:
+ return "Ready";
+ case eBlocked:
+ return "Blocked";
+ case eSuspended:
+ return "Suspended";
+ case eDeleted:
+ return "Deleted";
+ case eInvalid:
+ return "Invalid";
+ default:
+ return "Unknown state";
+ }
+}
+
+static void sort_tasks_by_runtime(TaskStatus_t *tasks_status_array, size_t number_of_tasks)
+{
+ for (size_t i = 0; i < number_of_tasks - 1; ++i) {
+ for (size_t k = i + 1; k < number_of_tasks; ++k) {
+ if (tasks_status_array[k].ulRunTimeCounter > tasks_status_array[i].ulRunTimeCounter) {
+ TaskStatus_t temp = tasks_status_array[i];
+ tasks_status_array[i] = tasks_status_array[k];
+ tasks_status_array[k] = temp;
+ }
+ }
+ }
+}
+
+static void task_status_monitor_task(void *params)
+{
+ while (true) {
+ UBaseType_t number_of_tasks = uxTaskGetNumberOfTasks();
+ TaskStatus_t *p_tasks_status_array = pvPortMalloc(number_of_tasks * sizeof(TaskStatus_t));
+
+ if (p_tasks_status_array != NULL) {
+ uint32_t total_run_time;
+ number_of_tasks = uxTaskGetSystemState(p_tasks_status_array, number_of_tasks, &total_run_time);
+
+ if (total_run_time > 0) { // Avoid divide by zero error
+ sort_tasks_by_runtime(p_tasks_status_array, number_of_tasks);
+
+ printf("I (%lu) tm: " CYAN "%-18.16s %-11.10s %-7.6s %-8.7s %-11.10s %-12.10s %-15.15s %-s"
+ RESET_COLOR,
+ get_current_time_ms(),
+ "TASK NAME:",
+ "STATE:",
+ "CORE:",
+ "NUMBER:",
+ "PRIORITY:",
+ "STACK_MIN:",
+ "RUNTIME, µs:",
+ "RUNTIME, %:\n"
+ );
+
+ for (size_t i = 0; i < number_of_tasks; ++i) {
+ char string[10]; // 10 - maximum number of characters for int
+ printf("I (%lu) tm: " YELLOW "%-18.16s %-11.10s %-7.6s %-8d %-11d %-12lu %-14lu %-10.3f\n"
+ RESET_COLOR,
+ get_current_time_ms(),
+ p_tasks_status_array[i].pcTaskName,
+ task_state_to_string(p_tasks_status_array[i].eCurrentState),
+
+ xTaskGetAffinity(p_tasks_status_array[i].xHandle) == tskNO_AFFINITY ?
+ "Any" : int_to_string((int)xTaskGetAffinity(p_tasks_status_array[i].xHandle), string),
+
+ p_tasks_status_array[i].xTaskNumber,
+ p_tasks_status_array[i].uxCurrentPriority,
+ p_tasks_status_array[i].usStackHighWaterMark,
+ p_tasks_status_array[i].ulRunTimeCounter,
+ (p_tasks_status_array[i].ulRunTimeCounter * 100.0) / total_run_time);
+ }
+
+ printf("I (%lu) tm: " YELLOW "Total heap free size: " GREEN "%d" YELLOW " bytes\n" RESET_COLOR,
+ get_current_time_ms(), heap_caps_get_total_size(MALLOC_CAP_DEFAULT));
+
+ printf("I (%lu) tm: " YELLOW "Current heap free size: " GREEN "%d" YELLOW " bytes (" GREEN "%.2f"
+ YELLOW " %%)\n" RESET_COLOR, get_current_time_ms(), heap_caps_get_free_size(MALLOC_CAP_DEFAULT),
+ get_current_heap_free_percent());
+
+ printf("I (%lu) tm: " YELLOW "Minimum heap free size: " GREEN "%d" YELLOW " bytes (" GREEN "%.2f"
+ YELLOW " %%)\n" RESET_COLOR, get_current_time_ms(),
+ heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT), get_minimum_heap_free_percent());
+
+ printf("I (%lu) tm: " YELLOW "Total RunTime: " GREEN "%lu" YELLOW " µs (" GREEN "%lu" YELLOW
+ " seconds)\n" RESET_COLOR, get_current_time_ms(), total_run_time, total_run_time / 1000000);
+
+ uint64_t current_time = esp_timer_get_time();
+ printf("I (%lu) tm: " YELLOW "System UpTime: " GREEN "%llu" YELLOW " µs (" GREEN "%llu" YELLOW
+ " seconds)\n\n" RESET_COLOR, get_current_time_ms(), current_time, current_time / 1000000);
+ }
+ vPortFree(p_tasks_status_array);
+ } else {
+ printf("I (%lu) tm: " RED "Could not allocate required memory\n" RESET_COLOR, get_current_time_ms());
+ }
+ vTaskDelay(pdMS_TO_TICKS(30 * 1000));
+ }
+}
+
+esp_err_t task_monitor(void)
+{
+ BaseType_t status = xTaskCreatePinnedToCore(task_status_monitor_task, "monitor_task", configMINIMAL_STACK_SIZE * 4,
+ NULL, tskIDLE_PRIORITY + 1, NULL, tskNO_AFFINITY);
+ if (status != pdPASS) {
+ printf("I (%lu) tm: task_status_monitor_task(): Task was not created. Could not allocate required memory\n",
+ get_current_time_ms());
+ return ESP_ERR_NO_MEM;
+ }
+ printf("I (%lu) tm: task_status_monitor_task() started successfully\n", get_current_time_ms());
+ return ESP_OK;
+}
diff --git a/main/temperature.c b/main/temperature.c
new file mode 100644
index 0000000..5d3d90e
--- /dev/null
+++ b/main/temperature.c
@@ -0,0 +1,183 @@
+#include "temperature.h"
+
+#include
+#include
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+
+#include "sdkconfig.h"
+#include "esp_err.h"
+#include "esp_log.h"
+
+#include "onewire_bus.h"
+#include "ds18b20.h"
+
+#include "types.h"
+
+static const char *TAG = "temperature";
+
+typedef struct {
+ uint8_t device_num;
+ onewire_bus_handle_t handle;
+ uint8_t device_rom_id[CONFIG_ONEWIRE_NUMBER_OF_DEVICES][8];
+} ds18b20_task_params_t;
+
+QueueHandle_t temperature_queue = NULL;
+
+#define AVERAGE_ARRAY_SIZE 3
+static float get_average_temperature(size_t device, float temperature)
+{
+ static float average_array[CONFIG_ONEWIRE_NUMBER_OF_DEVICES][AVERAGE_ARRAY_SIZE];
+ static bool is_average_array_init_with_nan = false;
+
+ if (!is_average_array_init_with_nan) {
+ for (size_t i = 0; i < CONFIG_ONEWIRE_NUMBER_OF_DEVICES; ++i) {
+ for (size_t k = 0; k < AVERAGE_ARRAY_SIZE; ++k) {
+ average_array[i][k] = NAN;
+ }
+ }
+ is_average_array_init_with_nan = true;
+ }
+
+ static size_t index[CONFIG_ONEWIRE_NUMBER_OF_DEVICES] = {0};
+ average_array[device][index[device]] = temperature;
+ index[device] = (index[device] + 1) % AVERAGE_ARRAY_SIZE;
+
+ size_t count = 0;
+ float average = 0.0;
+ for (size_t i = 0; i < AVERAGE_ARRAY_SIZE; ++i) {
+ if (!isnan(average_array[device][i])) {
+ average += average_array[device][i];
+ count++;
+ }
+ }
+ return count > 0 ? average / (float)count : NAN;
+}
+
+#define CHANGE_THRESHOLD 0.09 // °C
+static bool is_temperature_changed(size_t device, float current_temperature)
+{
+ static float previous_temperature[CONFIG_ONEWIRE_NUMBER_OF_DEVICES] = {0.0};
+
+ bool is_changed = fabs(current_temperature - previous_temperature[device]) > (float)CHANGE_THRESHOLD;
+ if (is_changed) {
+ previous_temperature[device] = current_temperature;
+ }
+ return is_changed;
+}
+
+static void ds18b20_task(void *params)
+{
+ // convert and read temperature
+ while (true) {
+ esp_err_t err;
+ ds18b20_task_params_t task_params = *(ds18b20_task_params_t*)params;
+
+ vTaskDelay(pdMS_TO_TICKS(200));
+
+ // set all sensors' temperature conversion resolution
+ err = ds18b20_set_resolution(task_params.handle, NULL, DS18B20_RESOLUTION_12B);
+ if (err != ESP_OK) {
+ continue;
+ }
+
+ // trigger all sensors to start temperature conversion
+ err = ds18b20_trigger_temperature_conversion(task_params.handle, NULL); // skip rom to send command to all devices on the bus
+ if (err != ESP_OK) {
+ continue;
+ }
+
+ vTaskDelay(pdMS_TO_TICKS(800)); // 12-bit resolution needs 750ms to convert
+
+ // get temperature from sensors
+ for (size_t device = 0; device < task_params.device_num; ++device) {
+ float temperature;
+ err = ds18b20_get_temperature(task_params.handle, task_params.device_rom_id[device], &temperature); // read scratchpad and get temperature
+ if (err != ESP_OK) {
+ continue;
+ }
+ ESP_LOGI(TAG, "Temperature of device " ONEWIRE_ROM_ID_STR ": %.2f°C",
+ ONEWIRE_ROM_ID(task_params.device_rom_id[device]), temperature);
+
+ temperature = get_average_temperature(device, temperature);
+
+ if (is_temperature_changed(device, temperature)) {
+ temperature_device_t temperature_device_to_send = {
+ .device = device,
+ .temperature = temperature,
+ };
+
+ BaseType_t status = xQueueSend(temperature_queue, &temperature_device_to_send, 0);
+ if (status != pdPASS) {
+ ESP_LOGW(TAG, "ds18b20_task(): Failed to send the message");
+ }
+ }
+ }
+ vTaskDelay(pdMS_TO_TICKS(CONFIG_ONEWIRE_TEMPERATURE_UPDATE_TIME * 1000));
+ }
+}
+
+esp_err_t ds18b20_init(void)
+{
+ onewire_rmt_config_t config = {
+ .gpio_pin = CONFIG_ONEWIRE_DATA_GPIO_PIN,
+ .max_rx_bytes = 10, // 10 tx bytes (1byte ROM command + 8byte ROM number + 1byte device command)
+ };
+
+ // install new 1-wire bus
+
+ // NOTE: The parameter "task_params" must still exist when the created task executes. It must be static.
+ static ds18b20_task_params_t task_params = {
+ .device_num = 0,
+ };
+
+ ESP_ERROR_CHECK(onewire_new_bus_rmt(&config, &task_params.handle));
+ ESP_LOGI(TAG, "1-wire bus installed");
+
+ // create 1-wire rom search context
+ onewire_rom_search_context_handler_t context_handler;
+ ESP_ERROR_CHECK(onewire_rom_search_context_create(task_params.handle, &context_handler));
+
+ // search for devices on the bus
+ do {
+ esp_err_t search_result = onewire_rom_search(context_handler);
+
+ if (search_result == ESP_ERR_INVALID_CRC) {
+ continue; // continue on crc error
+ } else if (search_result == ESP_FAIL || search_result == ESP_ERR_NOT_FOUND) {
+ break; // break on finish or no device
+ }
+
+ ESP_ERROR_CHECK(onewire_rom_get_number(context_handler, task_params.device_rom_id[task_params.device_num]));
+ ESP_LOGI(TAG, "found device with rom id " ONEWIRE_ROM_ID_STR,
+ ONEWIRE_ROM_ID(task_params.device_rom_id[task_params.device_num]));
+ task_params.device_num++;
+ } while (task_params.device_num < CONFIG_ONEWIRE_NUMBER_OF_DEVICES);
+
+ // delete 1-wire rom search context
+ ESP_ERROR_CHECK(onewire_rom_search_context_delete(context_handler));
+ ESP_LOGI(TAG, "%d device%s found on 1-wire bus", task_params.device_num, task_params.device_num > 1 ? "s" : "");
+
+ if (task_params.device_num > 0) {
+ temperature_queue = xQueueCreate(task_params.device_num, sizeof(temperature_device_t));
+ if (temperature_queue == NULL) {
+ ESP_LOGE(TAG, "temperature_queue: Queue was not created. Could not allocate required memory");
+ return ESP_ERR_NO_MEM;
+ }
+
+ BaseType_t status = xTaskCreate(ds18b20_task, "ds18b20_task", configMINIMAL_STACK_SIZE * 4, &task_params,
+ PRIORITY_HIGH, NULL);
+ if (status != pdPASS) {
+ ESP_LOGE(TAG, "ds18b20_task(): Task was not created. Could not allocate required memory");
+ return ESP_ERR_NO_MEM;
+ }
+ } else {
+ ESP_ERROR_CHECK(onewire_del_bus(task_params.handle));
+ ESP_LOGI(TAG, "1-wire bus deleted");
+ }
+
+ ESP_LOGI(TAG, "ds18b20_init() finished");
+ return ESP_OK;
+}
diff --git a/main/wifi.c b/main/wifi.c
new file mode 100644
index 0000000..67d73e5
--- /dev/null
+++ b/main/wifi.c
@@ -0,0 +1,178 @@
+#include "wifi.h"
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+
+#include "esp_system.h"
+#include "esp_timer.h"
+#include
+#include "esp_event.h"
+#include "esp_check.h"
+#include "esp_log.h"
+
+static const char *TAG = "wifi";
+
+#if CONFIG_ESP_WIFI_AUTH_OPEN
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
+#elif CONFIG_ESP_WIFI_AUTH_WEP
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
+#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
+#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
+#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
+#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
+#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
+#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
+#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
+#endif
+
+// FreeRTOS event group to signal when we are connected
+static EventGroupHandle_t wifi_event_group = NULL;
+
+// The event group allows multiple bits for each event, but we only care about two events:
+// - we are connected to the AP with an IP
+// - we failed to connect after the maximum amount of retries
+#define WIFI_CONNECTED_BIT BIT0
+#define WIFI_FAIL_BIT BIT1
+
+static esp_timer_handle_t wifi_reconnect_timer = NULL;
+
+static void wifi_reconnect_timer_callback(void* arg)
+{
+ esp_wifi_connect();
+ ESP_LOGW(TAG, "Retry to connect to the Wi-Fi");
+}
+
+static void wifi_reconnect_timer_start(void)
+{
+ if (wifi_reconnect_timer == NULL) {
+ const esp_timer_create_args_t timer_args = {
+ .callback = wifi_reconnect_timer_callback,
+ .arg = NULL,
+ .name = "Wi-Fi reconnect timer"
+ };
+ ESP_ERROR_CHECK(esp_timer_create(&timer_args, &wifi_reconnect_timer));
+ }
+ ESP_ERROR_CHECK(esp_timer_start_once(wifi_reconnect_timer, 5 * 1000000)); // 5 seconds
+}
+
+static void wifi_reconnect_timer_stop(void)
+{
+ if (wifi_reconnect_timer != NULL) {
+ if (esp_timer_is_active(wifi_reconnect_timer)) {
+ ESP_ERROR_CHECK(esp_timer_stop(wifi_reconnect_timer));
+ }
+ ESP_ERROR_CHECK(esp_timer_delete(wifi_reconnect_timer));
+ wifi_reconnect_timer = NULL;
+ }
+}
+
+static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
+{
+ if (event_base == WIFI_EVENT) {
+ switch (event_id) {
+ case WIFI_EVENT_STA_START:
+ esp_wifi_connect();
+ ESP_LOGI(TAG, "Trying to connect to the Wi-Fi");
+ break;
+ case WIFI_EVENT_STA_DISCONNECTED:
+ if (wifi_event_group != NULL) {
+ xEventGroupSetBits(wifi_event_group, WIFI_FAIL_BIT);
+ }
+ wifi_reconnect_timer_start();
+ break;
+ default:
+ break;
+ }
+ } else if (event_base == IP_EVENT) {
+ switch (event_id) {
+ case IP_EVENT_STA_GOT_IP:
+ ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;
+ ESP_LOGI(TAG, "Connected to the Wi-Fi. Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
+ if (wifi_event_group != NULL) {
+ xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
+ } else {
+ wifi_reconnect_timer_stop();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+esp_err_t wifi_init(void)
+{
+ wifi_event_group = xEventGroupCreate();
+ if (wifi_event_group == NULL) {
+ ESP_LOGE(TAG, "wifi_event_group: Event Group was not created. Could not allocate required memory");
+ return ESP_ERR_NO_MEM;
+ }
+
+ ESP_ERROR_CHECK(esp_netif_init());
+
+ ESP_ERROR_CHECK(esp_event_loop_create_default());
+ esp_netif_create_default_wifi_sta();
+
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+ ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+
+ esp_event_handler_instance_t instance_any_id;
+ esp_event_handler_instance_t instance_got_ip;
+ ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
+ ESP_EVENT_ANY_ID,
+ &event_handler,
+ NULL,
+ &instance_any_id));
+ ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
+ IP_EVENT_STA_GOT_IP,
+ &event_handler,
+ NULL,
+ &instance_got_ip));
+
+ wifi_config_t wifi_config = {
+ .sta = {
+ .ssid = CONFIG_ESP_WIFI_SSID,
+ .password = CONFIG_ESP_WIFI_PASSWORD,
+ // Authmode threshold resets to WPA2 as default if password matches WPA2 standards (pasword len => 8).
+ // If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
+ // to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
+ // WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
+ .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
+ .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
+ },
+ };
+ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+ ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
+ ESP_ERROR_CHECK(esp_wifi_start());
+
+ // Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
+ // number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above).
+ EventBits_t bits = xEventGroupWaitBits(wifi_event_group,
+ WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
+ pdFALSE,
+ pdFALSE,
+ portMAX_DELAY);
+
+ // xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event
+ // actually happened.
+ if (bits & WIFI_CONNECTED_BIT) {
+ //ESP_LOGI(TAG, "Connected to AP. SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
+ ESP_LOGI(TAG, "Connected to the Wi-Fi. SSID:%s, password:%s", wifi_config.sta.ssid, wifi_config.sta.password);
+ } else if (bits & WIFI_FAIL_BIT) {
+ //ESP_LOGE(TAG, "Failed to connect to AP. SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
+ ESP_LOGE(TAG, "Failed to connect to the Wi-Fi. SSID:%s, password:%s", wifi_config.sta.ssid, wifi_config.sta.password);
+ } else {
+ ESP_LOGE(TAG, "Unexpected event");
+ }
+ vEventGroupDelete(wifi_event_group);
+ wifi_event_group = NULL;
+
+ ESP_LOGI(TAG, "wifi_init() finished");
+ return ESP_OK;
+}