Compare commits

..

10 Commits

Author SHA1 Message Date
Thaddeus Hughes
53bea4eb04 fw versioning, hotkeys 2026-01-05 12:14:42 -06:00
Thaddeus Hughes
5c55d8da9b versioning, fixed calibration sequence 2026-01-05 11:58:12 -06:00
Thaddeus Hughes
ffb56936f1 logtool. and lots of things in the main firmware.
better integration into main
fixed scheduler and timestamping
simplified the api there
ditched integer 64-bit storage types (not needed. 32 bits is plenty for everything except current time - but that's handled in RTC, everything else is deltas)
web remote!
2026-01-03 22:38:52 -06:00
Thaddeus Hughes
a0601c16fa log tool (SC-TF003) 2025-12-30 20:47:25 -06:00
Thaddeus Hughes
40a2b3765c tidier. minified more. hard reset. 2025-12-30 20:02:44 -06:00
Thaddeus Hughes
d46cb252fb DNS, web ui nearly done, great log streaming, attempted https (abandoned that though) 2025-12-30 18:51:11 -06:00
Thaddeus Hughes
012d28ae14 Ironed out tons of stuff on the webserver
Logging, time sync, collapsible menus, oh my!
2025-12-29 22:21:43 -06:00
Thaddeus Hughes
2ac5d30490 integrate tim's changes 2025-12-29 16:03:18 -06:00
Thaddeus Hughes
095a52fea7 better logging 2025-12-29 15:49:45 -06:00
Thaddeus Hughes
039c29a39d OTA works, log download works, integrated OTA build sys 2025-12-27 11:52:57 -06:00
156 changed files with 22578 additions and 1403 deletions

View File

@@ -13,4 +13,6 @@
<storageModule moduleId="org.eclipse.cdt.core.pathentry">
<pathentry excluding="**/CMakeFiles/**" kind="out" path="build"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
</cproject>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/SC-F001/ota_deploy.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/SC-F001}"/>
</launchConfiguration>

View File

@@ -11,6 +11,16 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/OTA SC-F001.launch</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>

View File

@@ -0,0 +1,6 @@
doxygen/doxygen_new_line_after_brief=true
doxygen/doxygen_use_brief_tag=false
doxygen/doxygen_use_javadoc_tags=true
doxygen/doxygen_use_pre_tag=false
doxygen/doxygen_use_structural_commands=false
eclipse.preferences.version=1

View File

@@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(app-template)
project(SC-F001)

View File

@@ -1,11 +1,4 @@
ESP-IDF template app
====================
SC-F001
=======
This is a template application to be used with [Espressif IoT Development Framework](https://github.com/espressif/esp-idf).
Please check [ESP-IDF docs](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for getting started instructions.
*Code in this repository 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.*
Firmware for SC-B001

View File

@@ -1,6 +1,6 @@
dependencies:
esp-idf-lib/esp_idf_lib_helpers:
component_hash: a8049b1e609679fb54b2d57b0399dd29c4d1fda09a797edac9926f7810aa5703
component_hash: 689853bb8993434f9556af0f2816e808bf77b5d22100144b21f3519993daf237
dependencies: []
source:
registry_url: https://components.espressif.com
@@ -16,9 +16,9 @@ dependencies:
- esp32p4
- esp32s2
- esp32s3
version: 1.3.10
version: 1.4.0
esp-idf-lib/i2cdev:
component_hash: 11c08f9e1a7d346b5dd763196dc2567cf2209ae49042402c2c2d296624601c14
component_hash: 4f3838b2e68ab2b77fd43737139fa97dd0243b46af7b4a04588c67ff6b275ba1
dependencies:
- name: esp-idf-lib/esp_idf_lib_helpers
registry_url: https://components.espressif.com
@@ -38,7 +38,7 @@ dependencies:
- esp32p4
- esp32s2
- esp32s3
version: 2.0.8
version: 2.1.0
esp-idf-lib/tca95x5:
component_hash: 4bbdbd82828cf1fd5c03fd07e3ea2cb0f36daf16cb3ac7219d1e5decb9ec04ee
dependencies:
@@ -65,6 +65,16 @@ dependencies:
- esp32s2
- esp32s3
version: 1.0.7
espressif/mdns:
component_hash: 29e47564b1a7ee778135e17fbbf2a2773f71c97ebabfe626c8eda7c958a7ad16
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.9.1
idf:
source:
type: idf
@@ -81,8 +91,9 @@ dependencies:
version: 1.20.3
direct_dependencies:
- esp-idf-lib/tca95x5
- espressif/mdns
- idf
- joltwallet/littlefs
manifest_hash: b32a1e2b2eb19ff5cd4984c921bf3a1bcd9c1546566ee977021d2d4bf4b3de31
manifest_hash: c3a20310a8ecc5e8e0221a7589abf8d2e372eb48f06d6b6fbb3fbf5f48a61aaf
target: esp32
version: 2.0.0

View File

@@ -1,12 +1,17 @@
# See the build system documentation in IDF programming guide
# for more information about component CMakeLists.txt files.
include(${CMAKE_CURRENT_LIST_DIR}/version.cmake)
idf_component_register(
SRCS main.c i2c.c rtc.c storage.c uart_comms.c control_fsm.c power_mgmt.c rf_433.c rtc.c sensors.c solar.c webserver.c # list the source files of this component
INCLUDE_DIRS # optional, add here public include directories
SRCS main.c i2c.c rtc.c storage.c uart_comms.c control_fsm.c power_mgmt.c rf_433.c rtc.c sensors.c solar.c webserver.c simple_dns_server.c # list the source files of this component
INCLUDE_DIRS "." "${CMAKE_BINARY_DIR}"
PRIV_INCLUDE_DIRS # optional, add here private include directories
REQUIRES # optional, list the public requirements (component names)
PRIV_REQUIRES # optional, list the private requirements
REQUIRES driver esp_http_server esp_netif lwip json esp_timer esp_adc app_update esp_wifi nvs_flash mdns # optional, list the public requirements (component names)
# esp_https_server
PRIV_REQUIRES # optional, list the private requirements
#EMBED_TXTFILES servercert.pem prvtkey.pem
)

View File

@@ -8,6 +8,7 @@
#include "control_fsm.h"
#include "esp_task_wdt.h"
#include "esp_timer.h"
#include "i2c.h"
#include "power_mgmt.h"
#include "rtc_wdt.h"
#include "driver/gpio.h"
@@ -18,6 +19,9 @@
#define TRANSITION_DELAY_US 1000000
#define CALIBRATE_JACK_MAX_TIME 3000000
#define CALIBRATE_DRIVE_MAX_TIME 6000000
#define TAG "FSM"
static QueueHandle_t fsm_cmd_queue = NULL;
@@ -35,10 +39,19 @@ bridge_t bridge_map[] = {
bool relay_states[8] = {false};
int64_t override_times[8] = {-1};
int64_t override_cooldown[8] = {-1};
bool enabled = false;
RTC_DATA_ATTR float remaining_distance = 0.0f;
float fsm_get_remaining_distance(void) { return remaining_distance; }
void fsm_set_remaining_distance(float x) { remaining_distance = x;}
// Track the starting encoder count for the current move
static int32_t move_start_encoder = 0;
// Track total jack up time to use for jack down duration
static int64_t jack_up_total_time = 0;
volatile fsm_state_t current_state = STATE_IDLE;
volatile int64_t current_time = 0;
@@ -48,9 +61,16 @@ void setRelay(int8_t relay, bool state) {
relay_states[relay] = state;
}
bool isRunning() {
for (int i=0;i<8;i++) {
if (relay_states[i]) return true;
}
return false;
}
void driveRelays() {
uint8_t state = 0x00;
relay_states[0] = (current_time / 1000000) % 2;
//relay_states[0] = (current_time / 1000000) % 2; // for testing purposes
for (uint8_t i=0; i<8; i++) {
// if we command and efuse permits it set the relay
@@ -60,7 +80,7 @@ void driveRelays() {
}
}
ESP_LOGI(TAG, "RELAY STATE: %x", state);
//ESP_LOGI(TAG, "RELAY STATE: %x", state);
i2c_set_relays(state);
}
@@ -70,8 +90,8 @@ fsm_state_t fsm_get_state() {
return current_state;
}
static uint64_t timer_end = 0;
static uint64_t timer_start = 0;
static int64_t timer_end = 0;
static int64_t timer_start = 0;
static inline void set_timer(uint64_t us) {
timer_end = current_time + us;
timer_start = current_time;
@@ -79,8 +99,14 @@ static inline void set_timer(uint64_t us) {
static inline bool timer_done() { return current_time >= timer_end; }
void pulseOverride(relay_t relay) {
if (current_state == STATE_IDLE)
override_times[relay] = current_time + get_param_value_t(PARAM_RF_PULSE_LENGTH).u64;
if (current_state == STATE_IDLE) {
// Check if this relay is in cooldown
if (override_cooldown[relay] > current_time) {
// Still cooling down, ignore the command
return;
}
override_times[relay] = current_time + get_param_value_t(PARAM_RF_PULSE_LENGTH).u32;
}
}
/*void fsm_begin_auto_move() {
@@ -89,6 +115,12 @@ void pulseOverride(relay_t relay) {
set_timer(TRANSITION_DELAY_US);
}*/
int64_t fsm_cal_t, fsm_cal_e;
float fsm_cal_val;
void fsm_set_cal_val(float v) {fsm_cal_val = v;}
int64_t fsm_get_cal_t(){return fsm_cal_t;}
int64_t fsm_get_cal_e(){return fsm_cal_e;}
void fsm_request(fsm_cmd_t cmd)
{
if (fsm_cmd_queue != NULL)
@@ -99,6 +131,7 @@ int8_t fsm_get_current_progress(int8_t denominator) {
int8_t x = 0;
switch (current_state) {
case STATE_DRIVE:
case STATE_JACK_UP_START:
case STATE_JACK_UP:
case STATE_JACK_DOWN:
case STATE_MOVE_START_DELAY:
@@ -120,15 +153,15 @@ int8_t fsm_get_current_progress(int8_t denominator) {
}
#define JACK_TIME get_param_value_t(PARAM_JACK_MSPI ).u32 * 1000 * get_param_value_t(PARAM_JACK_DIST ).u8
#define DRIVE_TIME get_param_value_t(PARAM_DRIVE_MSPF).u32 * 1000 * get_param_value_t(PARAM_DRIVE_DIST).u8
#define DRIVE_DIST get_param_value_t(PARAM_DRIVE_TPDF).u32 / 10 * get_param_value_t(PARAM_DRIVE_DIST).u8
#define JACK_TIME get_param_value_t(PARAM_JACK_KT).f32 * get_param_value_t(PARAM_JACK_DIST ).f32
#define DRIVE_TIME get_param_value_t(PARAM_DRIVE_KT).f32 * get_param_value_t(PARAM_DRIVE_DIST).f32
#define DRIVE_DIST get_param_value_t(PARAM_DRIVE_KE).f32 * get_param_value_t(PARAM_DRIVE_DIST).f32
void control_task(void *param) {
esp_task_wdt_add(NULL);
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(50);
const TickType_t xFrequency = pdMS_TO_TICKS(20);
enabled = true;
@@ -141,8 +174,16 @@ void control_task(void *param) {
switch (cmd) {
case FSM_CMD_START:
if (current_state == STATE_IDLE) {
current_state = STATE_MOVE_START_DELAY;
set_timer(TRANSITION_DELAY_US);
// Check if we have remaining distance before starting
if (remaining_distance > 0.0f
&& !efuse_is_tripped(BRIDGE_DRIVE)
&& !efuse_is_tripped(BRIDGE_JACK)
&& !efuse_is_tripped(BRIDGE_AUX)) {
current_state = STATE_MOVE_START_DELAY;
set_timer(TRANSITION_DELAY_US);
} else {
ESP_LOGW(TAG, "Cannot start move: no remaining distance (%.2f)", remaining_distance);
}
}
break;
case FSM_CMD_STOP:
@@ -158,6 +199,68 @@ void control_task(void *param) {
case FSM_CMD_SHUTDOWN:
enabled = false;
break;
case FSM_CMD_CALIBRATE_JACK_PREP:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_PREP");
if (current_state == STATE_IDLE) {
current_state = STATE_CALIBRATE_JACK_DELAY;
}
break;
case FSM_CMD_CALIBRATE_JACK_START:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_START");
if (current_state == STATE_CALIBRATE_JACK_DELAY) {
current_state = STATE_CALIBRATE_JACK_MOVE;
set_timer(CALIBRATE_JACK_MAX_TIME);
}
break;
case FSM_CMD_CALIBRATE_JACK_END:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_END");
if (current_state == STATE_CALIBRATE_JACK_MOVE) {
fsm_cal_t = current_time - timer_start;
current_state = STATE_IDLE;
}
break;
case FSM_CMD_CALIBRATE_JACK_FINISH:
set_param_value_t(PARAM_JACK_KT,
(param_value_t){.f32 = fsm_cal_t / fsm_cal_val});
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_JACK_FINISH -> %f", get_param_value_t(PARAM_JACK_KT).f32);
break;
case FSM_CMD_CALIBRATE_DRIVE_PREP:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_PREP");
if (current_state == STATE_IDLE) {
current_state = STATE_CALIBRATE_DRIVE_DELAY;
}
break;
case FSM_CMD_CALIBRATE_DRIVE_START:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_START");
if (current_state == STATE_CALIBRATE_DRIVE_DELAY) {
current_state = STATE_CALIBRATE_DRIVE_MOVE;
set_timer(CALIBRATE_DRIVE_MAX_TIME);
set_sensor_counter(SENSOR_DRIVE, 0);
}
break;
case FSM_CMD_CALIBRATE_DRIVE_END:
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_END");
if (current_state == STATE_CALIBRATE_DRIVE_MOVE) {
fsm_cal_t = current_time - timer_start;
fsm_cal_e = get_sensor_counter(SENSOR_DRIVE);
current_state = STATE_IDLE;
}
break;
case FSM_CMD_CALIBRATE_DRIVE_FINISH:
set_param_value_t(PARAM_DRIVE_KT,
(param_value_t){.f32 = fsm_cal_t / fsm_cal_val});
set_param_value_t(PARAM_DRIVE_KE,
(param_value_t){.f32 = fsm_cal_e / fsm_cal_val});
ESP_LOGI(TAG, "FSM_CMD_CALIBRATE_DRIVE_FINISH -> %f / %f",
get_param_value_t(PARAM_DRIVE_KT).f32,
get_param_value_t(PARAM_DRIVE_KE).f32);
break;
}
}
@@ -168,51 +271,181 @@ void control_task(void *param) {
// State transitions
switch (current_state) {
case STATE_IDLE:
break;
//ESP_LOGI("FSM", "IDLE @ %lld", current_time);
for (uint8_t i = 0; i < N_RELAYS; ++i) {
//ESP_LOGI("FSM", "t[%d] %lld", i, override_times[i]);
bool active = override_times[i] > current_time;
if (active) rtc_reset_shutdown_timer();
// Current limiting for manual jack down override (RELAY_B2)
if (i == RELAY_B2 && active) {
int64_t elapsed = current_time - (override_times[i] - get_param_value_t(PARAM_RF_PULSE_LENGTH).u32);
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32;
// After inrush delay, check for current spike
if (elapsed > delay) {
float current = get_bridge_A(BRIDGE_JACK);
float threshold = get_param_value_t(PARAM_JACK_I_DOWN).f32;
if (current > threshold) {
// Current spike detected - stop jacking down and start cooldown
override_times[i] = -1;
override_cooldown[i] = current_time + get_param_value_t(PARAM_EFUSE_TCOOL).u32;
active = false;
}
}
}
// prohibit movement past jack limit switch
//if (i == BRIDGE_JACK*2+(bridge_polarities[BRIDGE_JACK]>0?0:1) && get_sensor(SENSOR_JACK))
// setRelay(i, false);
//else
setRelay(i, active);
//if (active) ESP_LOGI("FSM", "RUN CHANNEL %d (%lld %c %lld)", i, (long long) override_times[i], active ? '>':'<', (long long) current_time);
}
break;
case STATE_MOVE_START_DELAY:
if (timer_done()) {
current_state = STATE_JACK_UP;
set_timer(JACK_TIME);
current_state = STATE_JACK_UP_START;
set_timer(JACK_TIME / 2); // First phase is half of total jack time
jack_up_total_time = 0; // Reset jack up time tracker
}
break;
case STATE_JACK_UP_START:
{
// Track elapsed time
int64_t elapsed = current_time - timer_start;
jack_up_total_time = elapsed;
// Get current sensing parameters
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32;
float current = get_bridge_A(BRIDGE_JACK);
float threshold = get_param_value_t(PARAM_JACK_I_UP).f32;
// After inrush delay, check for current spike OR half-time timeout
if (elapsed > delay) {
if (current > threshold || timer_done()) {
ESP_LOGI(TAG, "START->UP BY CURRENT");
current_state = STATE_JACK_UP;
set_timer(JACK_TIME); // Second phase is also half of total jack time
}
}
// E-fuse trip should still cause undo
if (efuse_is_tripped(BRIDGE_JACK)) {
ESP_LOGI(TAG, "START->UP BY TIME");
current_state = STATE_UNDO_JACK_START;
}
}
break;
case STATE_JACK_UP:
if (timer_done()) {
current_state = STATE_DRIVE_START_DELAY;
set_timer(TRANSITION_DELAY_US);
}
if (efuse_is_tripped(BRIDGE_JACK)) {
current_state = STATE_UNDO_JACK_START;
{
if (timer_done() || efuse_is_tripped(BRIDGE_JACK)) {
// Track total time including first phase
jack_up_total_time += current_time - timer_start;
current_state = STATE_DRIVE_START_DELAY;
set_timer(TRANSITION_DELAY_US);
}
if (efuse_is_tripped(BRIDGE_JACK)) {
ESP_LOGE(TAG, "JACK TRIPPED EFUSE");
current_state = STATE_UNDO_JACK_START;
}
}
break;
case STATE_DRIVE_START_DELAY:
if (timer_done()) {
current_state = STATE_DRIVE;
set_timer(DRIVE_TIME);
// Set the encoder counter to track remaining distance in this move
set_sensor_counter(SENSOR_DRIVE, -DRIVE_DIST);
// Record starting encoder position AFTER setting it
move_start_encoder = get_sensor_counter(SENSOR_DRIVE);
ESP_LOGI(TAG, "STATE_DRIVE starting: encoder=%ld, remaining_distance=%.2f, DRIVE_DIST=%.2f",
(long)move_start_encoder, remaining_distance, DRIVE_DIST);
}
break;
case STATE_DRIVE:
if (timer_done() || get_sensor_counter(SENSOR_DRIVE) > 0) {
current_state = STATE_DRIVE_END_DELAY;
set_timer(TRANSITION_DELAY_US);
}
if (efuse_is_tripped(BRIDGE_DRIVE)) {
current_state = STATE_UNDO_JACK_START;
{
int32_t current_encoder = get_sensor_counter(SENSOR_DRIVE);
int32_t ticks_traveled = current_encoder - move_start_encoder;
float ke = get_param_value_t(PARAM_DRIVE_KE).f32;
float distance_traveled = ticks_traveled / ke;
ESP_LOGI(TAG, "STATE_DRIVE: current_encoder=%ld, move_start=%ld, ticks=%ld, ke=%.2f, dist_traveled=%.2f, remaining=%.2f",
(long)current_encoder, (long)move_start_encoder, (long)ticks_traveled,
ke, distance_traveled, remaining_distance);
// Check if we'll exceed remaining distance with a full move
bool will_exceed = distance_traveled >= remaining_distance;
// Stop if timer expires OR encoder target reached OR we've used up remaining distance
if (timer_done() || current_encoder > 0 || will_exceed) {
ESP_LOGI(TAG, "Drive stopping: timer_done=%d, encoder>0=%d, will_exceed=%d",
timer_done(), current_encoder > 0, will_exceed);
// Update remaining distance based on actual travel
float old_remaining = remaining_distance;
if (will_exceed) {
ESP_LOGI(TAG, "Move stopped early - reached remaining distance limit (%.2f)", remaining_distance);
remaining_distance = 0.0f;
} else {
remaining_distance -= distance_traveled;
if (remaining_distance < 0.0f) remaining_distance = 0.0f;
}
ESP_LOGI(TAG, "Drive complete: traveled %.2f, old_remaining %.2f, new_remaining %.2f",
distance_traveled, old_remaining, remaining_distance);
current_state = STATE_DRIVE_END_DELAY;
set_timer(TRANSITION_DELAY_US);
}
if (efuse_is_tripped(BRIDGE_DRIVE)) {
float old_remaining = remaining_distance;
// Update remaining distance even on fault
remaining_distance -= distance_traveled;
if (remaining_distance < 0.0f) remaining_distance = 0.0f;
ESP_LOGW(TAG, "Drive fault: traveled %.2f, old_remaining %.2f, new_remaining %.2f",
distance_traveled, old_remaining, remaining_distance);
current_state = STATE_UNDO_JACK_START;
}
}
break;
case STATE_DRIVE_END_DELAY:
if (timer_done()) {
current_state = STATE_JACK_DOWN;
set_timer(JACK_TIME);
set_timer(jack_up_total_time); // Use the tracked jack up time
}
break;
case STATE_JACK_DOWN:
if (timer_done() || get_sensor(SENSOR_JACK)) {
current_state = STATE_IDLE;
}
// assume we hit something hard and should stop
if (efuse_is_tripped(BRIDGE_JACK)) {
current_state = STATE_IDLE;
{
// Get current sensing parameters
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32;
int64_t elapsed = current_time - timer_start;
// After inrush delay, check for current spike
if (elapsed > delay) {
float current = get_bridge_A(BRIDGE_JACK);
float threshold = get_param_value_t(PARAM_JACK_I_DOWN).f32;
if (current > threshold) {
ESP_LOGI(TAG, "DOWN->IDLE BY CURRENT");
// Current spike detected - we've hit the ground
current_state = STATE_IDLE;
break;
}
}
// Timeout - finished jacking down
if (timer_done()) {
ESP_LOGI(TAG, "DOWN->IDLE BY TIME");
current_state = STATE_IDLE;
}
// E-fuse trip - assume we hit something hard
if (efuse_is_tripped(BRIDGE_JACK)) {
current_state = STATE_IDLE;
}
}
break;
@@ -225,7 +458,7 @@ void control_task(void *param) {
}
break;
case STATE_UNDO_JACK:
if (timer_done() || get_sensor(SENSOR_JACK)) {
if (timer_done()){ // || get_sensor(SENSOR_JACK)) {
current_state = STATE_IDLE;
}
@@ -234,33 +467,79 @@ void control_task(void *param) {
current_state = STATE_IDLE;
}
break;
case STATE_CALIBRATE_JACK_DELAY:
// no way out of this except a command
break;
case STATE_CALIBRATE_JACK_MOVE:
if (timer_done()) {
ESP_LOGI(TAG, "STATE_CALIBRATE_JACK_END");
current_state = STATE_IDLE;
fsm_cal_t = current_time - timer_start;
}
break;
case STATE_CALIBRATE_DRIVE_DELAY:
// no way out of this except a command
break;
case STATE_CALIBRATE_DRIVE_MOVE:
if (timer_done()) {
ESP_LOGI(TAG, "STATE_CALIBRATE_DRIVE_END");
current_state = STATE_IDLE;
fsm_cal_t = current_time - timer_start;
fsm_cal_e = get_sensor_counter(SENSOR_DRIVE);
}
break;
default: break;
}
int64_t elapsed_t = (current_time-timer_start);
int64_t total_t = (timer_end-timer_start);
int32_t ticks = get_sensor_counter(SENSOR_DRIVE);
//int64_t elapsed_t = (current_time-timer_start);
//int64_t total_t = (timer_end-timer_start);
//int32_t ticks = get_sensor_counter(SENSOR_DRIVE);
//ESP_LOGI("FSM", "[%d] %lld / %lld ms, %ld ticks", current_state, (long long) elapsed_t, (long long) total_t, (long) ticks);
// Output control
switch (current_state) {
case STATE_IDLE:
//ESP_LOGI("FSM", "IDLE @ %lld", current_time);
for (uint8_t i = 0; i < N_RELAYS; ++i) {
//ESP_LOGI("FSM", "t[%d] %lld", i, override_times[i]);
bool active = override_times[i] > current_time;
if (active) reset_shutdown_timer();
// prohibit movement past jack limit switch
//if (i == BRIDGE_JACK*2+(bridge_polarities[BRIDGE_JACK]>0?0:1) && get_sensor(SENSOR_JACK))
// setRelay(i, false);
//else
setRelay(i, active);
//if (active) ESP_LOGI("FSM", "RUN CHANNEL %d (%lld %c %lld)", i, (long long) override_times[i], active ? '>':'<', (long long) current_time);
}
break;
//ESP_LOGI("FSM", "IDLE @ %lld", current_time);
for (uint8_t i = 0; i < N_RELAYS; ++i) {
//ESP_LOGI("FSM", "t[%d] %lld", i, override_times[i]);
bool active = override_times[i] > current_time;
if (active) rtc_reset_shutdown_timer();
// Current limiting for manual jack down override (RELAY_B2)
if (i == RELAY_B2 && active) {
int64_t elapsed = current_time - (override_times[i] - get_param_value_t(PARAM_RF_PULSE_LENGTH).u32);
int64_t delay = get_param_value_t(PARAM_EFUSE_INRUSH_US).u32;
// After inrush delay, check for current spike
if (elapsed > delay) {
float current = get_bridge_A(BRIDGE_JACK);
float threshold = get_param_value_t(PARAM_JACK_I_DOWN).f32;
if (current > threshold) {
// Current spike detected - stop jacking down
override_times[i] = -1;
active = false;
}
}
}
// prohibit movement past jack limit switch
//if (i == BRIDGE_JACK*2+(bridge_polarities[BRIDGE_JACK]>0?0:1) && get_sensor(SENSOR_JACK))
// setRelay(i, false);
//else
setRelay(i, active);
//if (active) ESP_LOGI("FSM", "RUN CHANNEL %d (%lld %c %lld)", i, (long long) override_times[i], active ? '>':'<', (long long) current_time);
}
break;
case STATE_CALIBRATE_JACK_MOVE:
case STATE_JACK_UP_START:
case STATE_JACK_UP:
// jack up and fluff
setRelay(RELAY_A1, false);
@@ -270,8 +549,9 @@ void control_task(void *param) {
setRelay(RELAY_B2, false);
setRelay(RELAY_A3, true);
reset_shutdown_timer();
rtc_reset_shutdown_timer();
break;
case STATE_CALIBRATE_DRIVE_MOVE:
case STATE_DRIVE:
// drive and fluff
setRelay(RELAY_A1, true);
@@ -281,7 +561,7 @@ void control_task(void *param) {
setRelay(RELAY_B2, false);
setRelay(RELAY_A3, true);
reset_shutdown_timer();
rtc_reset_shutdown_timer();
break;
case STATE_UNDO_JACK:
case STATE_JACK_DOWN:
@@ -293,7 +573,7 @@ void control_task(void *param) {
setRelay(RELAY_B2, true);
setRelay(RELAY_A3, true);
reset_shutdown_timer();
rtc_reset_shutdown_timer();
break;
case STATE_UNDO_JACK_START:
case STATE_DRIVE_START_DELAY:
@@ -306,8 +586,9 @@ void control_task(void *param) {
setRelay(RELAY_B2, false);
setRelay(RELAY_A3, true);
reset_shutdown_timer();
rtc_reset_shutdown_timer();
break;
case STATE_CALIBRATE_JACK_DELAY:
default:
// invalid state; turn all relays off
setRelay(RELAY_A1, false);

View File

@@ -7,18 +7,40 @@
#include "freertos/queue.h"
typedef enum { FSM_CMD_START, FSM_CMD_STOP, FSM_CMD_UNDO, FSM_CMD_SHUTDOWN} fsm_cmd_t;
typedef enum {
FSM_CMD_START,
FSM_CMD_STOP,
FSM_CMD_UNDO,
FSM_CMD_SHUTDOWN,
FSM_CMD_CALIBRATE_JACK_PREP,
FSM_CMD_CALIBRATE_JACK_START,
FSM_CMD_CALIBRATE_JACK_END,
FSM_CMD_CALIBRATE_JACK_FINISH,
FSM_CMD_CALIBRATE_DRIVE_PREP,
FSM_CMD_CALIBRATE_DRIVE_START,
FSM_CMD_CALIBRATE_DRIVE_END,
FSM_CMD_CALIBRATE_DRIVE_FINISH
} fsm_cmd_t;
typedef enum {
STATE_IDLE = 0,
STATE_MOVE_START_DELAY = 1,
STATE_JACK_UP = 2,
STATE_DRIVE_START_DELAY = 3,
STATE_DRIVE = 4,
STATE_DRIVE_END_DELAY = 5,
STATE_JACK_DOWN = 6,
STATE_UNDO_JACK = 7,
STATE_UNDO_JACK_START = 8,
STATE_MOVE_START_DELAY,
STATE_JACK_UP_START,
STATE_JACK_UP,
STATE_DRIVE_START_DELAY,
STATE_DRIVE,
STATE_DRIVE_END_DELAY,
STATE_JACK_DOWN,
STATE_UNDO_JACK,
STATE_UNDO_JACK_START,
STATE_CALIBRATE_JACK_DELAY,
STATE_CALIBRATE_JACK_MOVE,
STATE_CALIBRATE_DRIVE_DELAY,
STATE_CALIBRATE_DRIVE_MOVE
} fsm_state_t;
typedef enum {
@@ -46,8 +68,17 @@ void pulseOverride(relay_t relay/*, int64_t pulse*/);
esp_err_t fsm_init();
esp_err_t fsm_stop();
bool isRunning();
void fsm_set_cal_val(float v);
int64_t fsm_get_cal_t();
int64_t fsm_get_cal_e();
void fsm_request(fsm_cmd_t cmd);
float fsm_get_remaining_distance(void);
void fsm_set_remaining_distance(float x);
//void fsm_begin_auto_move();
int8_t fsm_get_current_progress(int8_t remainder);

View File

@@ -169,4 +169,7 @@ int64_t i2c_get_button_ms(uint8_t btn) {
uint64_t now = esp_timer_get_time() / 1000;
return now - last_change_time[btn];
}
int64_t i2c_get_button_us(uint8_t btn) {
return i2c_get_button_ms(btn)*1000;
}

View File

@@ -19,6 +19,7 @@ bool i2c_get_button_released(uint8_t button);
bool i2c_get_button_state(uint8_t button);
bool i2c_get_button_repeat(uint8_t btn);
int8_t i2c_get_button_repeats(uint8_t btn);
int64_t i2c_get_button_us(uint8_t btn);
int64_t i2c_get_button_ms(uint8_t btn);
#endif // I2C_H_

View File

@@ -1,5 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
espressif/mdns: "*"
joltwallet/littlefs: "==1.20.3"
esp-idf-lib/tca95x5: "*"
## Required IDF version

File diff suppressed because it is too large Load Diff

View File

@@ -12,35 +12,35 @@
#include "solar.h"
#include "rf_433.h"
#include "webserver.h"
#include "version.h"
#define TAG "MAIN"
int64_t last_log_time = 0;
esp_err_t send_log() {
// >Hqfffflccc
char entry[LOG_ENTRY_SIZE] = {0};
entry[0] = LOG_ENTRY_SIZE;
// Pack 64-bit timestamp into bytes 1-8
uint64_t be_timestamp = htobe64(rtc_time_ms());
uint64_t be_timestamp = rtc_get_ms();
memcpy(&entry[1], &be_timestamp, 8);
// Pack 32-bit voltages/currents into bytes 9-24
float be_voltage = htobe32(get_battery_V());
float be_voltage = get_battery_V();
memcpy(&entry[9], &be_voltage, 4);
float be_current1 = htobe32(get_bridge_A(BRIDGE_DRIVE));
float be_current1 = get_bridge_A(BRIDGE_DRIVE);
memcpy(&entry[13], &be_current1, 4);
float be_current2 = htobe32(get_bridge_A(BRIDGE_JACK));
float be_current2 = get_bridge_A(BRIDGE_JACK);
memcpy(&entry[17], &be_current2, 4);
float be_current3 = htobe32(get_bridge_A(BRIDGE_AUX));
float be_current3 = get_bridge_A(BRIDGE_AUX);
memcpy(&entry[21], &be_current3, 4);
int32_t be_counter = htobe32(get_sensor_counter(SENSOR_DRIVE));
int32_t be_counter = get_sensor_counter(SENSOR_DRIVE);
memcpy(&entry[25], &be_counter, 4);
entry[29] = get_sensor(SENSOR_DRIVE);
entry[30] = get_sensor(SENSOR_JACK);
entry[29] = get_sensor(SENSOR_SAFETY);
entry[30] = get_sensor(SENSOR_DRIVE);
entry[31] = fsm_get_state();
last_log_time = esp_timer_get_time();
@@ -54,6 +54,7 @@ typedef enum {
LED_STATE_ERROR,
LED_STATE_AWAKE,
LED_STATE_CANCELLING,
LED_STATE_ERRORED,
LED_STATE_START1,
LED_STATE_START2,
LED_STATE_START3,
@@ -62,17 +63,19 @@ typedef enum {
} led_state_t;
void driveLEDs(led_state_t state) {
uint8_t patterns[4][12] = {
uint8_t patterns[5][12] = {
{1,3,7,6,4,0},
{7,0},
{0b101,0b001},
{1,1,1,1,1,1, 1,1,1,3},
{4,2}
{4,2},
{0b001, 0b101},
};
switch(state) {
case LED_STATE_DRIVING:
i2c_set_led1(patterns[state][(esp_timer_get_time()/100000) % 6]);
break;
case LED_STATE_ERROR:
//ESP_LOGE(TAG, "SOME SORT OF ERROR");
i2c_set_led1(patterns[state][(esp_timer_get_time()/1000000) % 2]);
break;
case LED_STATE_AWAKE:
@@ -82,44 +85,106 @@ void driveLEDs(led_state_t state) {
i2c_set_led1(patterns[state][(esp_timer_get_time()/200000) % 2]);
break;
case LED_STATE_ERRORED:
i2c_set_led1(patterns[state][(esp_timer_get_time()/200000) % 2]);
break;
case LED_STATE_BOOTING:
i2c_set_led1(1);
i2c_set_led1(0b001);
break;
case LED_STATE_START1:
i2c_set_led1(0);
i2c_set_led1(0b000);
break;
case LED_STATE_START2:
i2c_set_led1(1);
i2c_set_led1(0b001);
break;
case LED_STATE_START3:
i2c_set_led1(3);
i2c_set_led1(0b011);
break;
case LED_STATE_START4:
i2c_set_led1(7);
i2c_set_led1(0b111);
break;
}
}
RTC_DATA_ATTR bool first_boot = true;
void app_main(void) {
esp_task_wdt_add(NULL);
// Say hello; turn on the lights
esp_sleep_wakeup_cause_t cause = rtc_wakeup_cause();
if (rtc_xtal_init() != ESP_OK) ESP_LOGE(TAG, "RTC FAILED");
// Say hello; turn on the lights
esp_sleep_wakeup_cause_t cause = rtc_wakeup_cause();
if (i2c_init() != ESP_OK) ESP_LOGE(TAG, "I2C FAILED");
i2c_set_relays(0);
driveLEDs(LED_STATE_BOOTING);
// Every boot we load parameters and monitor solar, no matter what
ESP_LOGI(TAG, "Firmware: %s", FIRMWARE_STRING);
ESP_LOGI(TAG, "Version: %s", FIRMWARE_VERSION);
ESP_LOGI(TAG, "Branch: %s", FIRMWARE_BRANCH);
ESP_LOGI(TAG, "Built: %s", BUILD_DATE);
// Check for factory reset condition: Cold boot + button held
// This is a cold boot (power-on or hard reset)
// Check if button is being held (pin is LOW)
if (first_boot && gpio_get_level(GPIO_NUM_13) == 0) {
ESP_LOGW(TAG, "FACTORY RESET TRIGGERED - Button held on cold boot");
// Flash LED pattern to indicate factory reset
for (int i = 0; i < 10; i++) {
i2c_set_led1(0b111);
vTaskDelay(pdMS_TO_TICKS(100));
i2c_set_led1(0b000);
vTaskDelay(pdMS_TO_TICKS(100));
}
// Initialize minimal components needed for factory reset
if (storage_init() != ESP_OK) ESP_LOGE(TAG, "STORAGE FAILED");
// Perform factory reset
esp_err_t reset_err = factory_reset();
if (reset_err == ESP_OK) {
ESP_LOGI(TAG, "Factory reset completed successfully");
// Flash success pattern
for (int i = 0; i < 5; i++) {
i2c_set_led1(0b010);
vTaskDelay(pdMS_TO_TICKS(200));
i2c_set_led1(0b000);
vTaskDelay(pdMS_TO_TICKS(200));
}
} else {
ESP_LOGE(TAG, "Factory reset failed!");
// Flash error pattern
for (int i = 0; i < 5; i++) {
i2c_set_led1(0b100);
vTaskDelay(pdMS_TO_TICKS(200));
i2c_set_led1(0b000);
vTaskDelay(pdMS_TO_TICKS(200));
}
}
// Reboot the system
ESP_LOGI(TAG, "Rebooting system...");
vTaskDelay(pdMS_TO_TICKS(1000));
esp_restart();
}
first_boot = false;
// Every boot we load parameters and monitor solar, no matter what
if (adc_init() != ESP_OK) ESP_LOGE(TAG, "ADC FAILED");
if (rtc_xtal_init() != ESP_OK) ESP_LOGE(TAG, "RTC FAILED");
if (storage_init() != ESP_OK) ESP_LOGE(TAG, "STORAGE FAILED");
if (log_init() != ESP_OK) ESP_LOGE(TAG, "LOG FAILED");
if (run_solar_fsm() != ESP_OK) ESP_LOGE(TAG, "SOLAR FAILED");
if (solar_run_fsm() != ESP_OK) ESP_LOGE(TAG, "SOLAR FAILED");
// TODO: Do a 12V check and enter deep sleep if there's a problem
send_log();
//write_dummy_log_1();
// Check wake reasons
// If button held, we stay #woke
@@ -133,13 +198,13 @@ void app_main(void) {
} else */if (cause == ESP_SLEEP_WAKEUP_EXT0) {
ESP_LOGI("MAIN", "Woke from button press");
} else {
if (!alarm_tripped()) {
if (!rtc_alarm_tripped()) {
//enter_deep_sleep();
}
}
/*** FULL BOOT ***/
if (uart_init() != ESP_OK) ESP_LOGE(TAG, "UART FAILED");
//if (uart_init() != ESP_OK) ESP_LOGE(TAG, "UART FAILED");
if (power_init() != ESP_OK) ESP_LOGE(TAG, "POWER FAILED");
if (rf_433_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED");
if (fsm_init() != ESP_OK) ESP_LOGE(TAG, "FSM FAILED");
@@ -147,9 +212,14 @@ void app_main(void) {
if (webserver_init() != ESP_OK) ESP_LOGE(TAG, "WEBSERVER FAILED");
/*** MAIN LOOP ***/
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(100);
const TickType_t xFrequency = pdMS_TO_TICKS(50);
/*while(true) {
ESP_LOGI(TAG, "TICK");
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));
esp_task_wdt_reset();
}*/
while(true) {
vTaskDelayUntil(&xLastWakeTime, xFrequency);
@@ -157,12 +227,12 @@ void app_main(void) {
i2c_poll_buttons();
if (i2c_get_button_state(0))
reset_shutdown_timer();
rtc_reset_shutdown_timer();
switch (fsm_get_state()) {
case STATE_IDLE:
// LED cue for user
if (i2c_get_button_ms(0) > 1600){
if (i2c_get_button_ms(0) > 1600){
driveLEDs(LED_STATE_START4);
} else if (i2c_get_button_ms(0) > 1100){
driveLEDs(LED_STATE_START3);
@@ -171,11 +241,20 @@ void app_main(void) {
} else if (i2c_get_button_ms(0) > 100){
driveLEDs(LED_STATE_START1);
} else{
driveLEDs(LED_STATE_AWAKE);
if (
rtc_is_set() &&
!efuse_is_tripped(BRIDGE_JACK) &&
!efuse_is_tripped(BRIDGE_AUX) &&
!efuse_is_tripped(BRIDGE_DRIVE)
) {
driveLEDs(LED_STATE_AWAKE);
} else {
driveLEDs(LED_STATE_ERROR);
}
}
// when not actively moving we log at a low frequency
if (esp_timer_get_time() > last_log_time + DEEP_SLEEP_US)
if (isRunning() || (esp_timer_get_time() > last_log_time + DEEP_SLEEP_US))
send_log();
if(i2c_get_button_ms(0) > 2100)
@@ -183,7 +262,7 @@ void app_main(void) {
break;
case STATE_UNDO_JACK:
case STATE_UNDO_JACK_START:
// assume it's running
// it's running the jack, but undoing
send_log();
driveLEDs(LED_STATE_CANCELLING);
if (i2c_get_button_tripped(0)) {
@@ -192,12 +271,34 @@ void app_main(void) {
}
break;
case STATE_CALIBRATE_JACK_DELAY:
send_log();
if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_JACK_START);
break;
case STATE_CALIBRATE_JACK_MOVE:
send_log();
if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_JACK_END);
break;
case STATE_CALIBRATE_DRIVE_DELAY:
send_log();
if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_DRIVE_START);
break;
case STATE_CALIBRATE_DRIVE_MOVE:
send_log();
if (i2c_get_button_tripped(0))
fsm_request(FSM_CMD_CALIBRATE_DRIVE_END);
break;
default:
// assume it's running in every other case
// it's running in every other case
send_log();
driveLEDs(LED_STATE_DRIVING);
if (i2c_get_button_tripped(0)) {
ESP_LOGI(TAG, "CTRL + Z PLZ!");
fsm_request(FSM_CMD_UNDO);
}
break;
@@ -206,24 +307,14 @@ void app_main(void) {
if (alarm_tripped()) {
if (rtc_alarm_tripped()) {
fsm_request(FSM_CMD_START);
set_next_alarm();
rtc_schedule_next_alarm();
}
/*ESP_LOGI(TAG, "VOLTAGE: %2.3f | CURRENTS: %+2.8f %+2.8f %+2.8f",
get_battery_V(),
get_bridge_A(BRIDGE_DRIVE),
get_bridge_A(BRIDGE_JACK),
get_bridge_A(BRIDGE_AUX));*/
run_solar_fsm();
solar_run_fsm();
//check_shutdown_timer();
rtc_check_shutdown_timer();
esp_task_wdt_reset();
}
}

View File

@@ -63,6 +63,9 @@ typedef struct {
float heat;
bool tripped;
int64_t trip_time;
// Inrush tolerance tracking
int64_t inrush_start_time; // When instantaneous overcurrent first detected (0 = not in overcurrent)
} isens_channel_t;
static isens_channel_t isens[N_BRIDGES] = {0};
@@ -82,7 +85,7 @@ esp_err_t adc_init() {
// Configure all channels
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = ADC_ATTEN_DB_11,
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_12,
};
@@ -94,7 +97,7 @@ esp_err_t adc_init() {
// Line fitting calibration (modern scheme)
adc_cali_line_fitting_config_t cali_cfg = {
.unit_id = ADC_UNIT_1,
.atten = ADC_ATTEN_DB_11,
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_12,
};
ESP_ERROR_CHECK(adc_cali_create_scheme_line_fitting(&cali_cfg, &adc_cali_handle));
@@ -112,7 +115,7 @@ float get_raw_battery_voltage(void) {
!= ESP_OK) { return NAN; }
// Voltage divider: 150kohm to 1Mohm -> gain = 1.15 -> scale = 1150/150
return voltage_mv * 0.00766666666; // same as / 1000.0 * 1150.0 / 150.0;
return voltage_mv * get_param_value_t(PARAM_V_SENS_K).f32 + get_param_value_t(PARAM_V_SENS_OFFSET).f32; // same as / 1000.0 * 1150.0 / 150.0;
}
esp_err_t process_battery_voltage(void)
@@ -125,12 +128,13 @@ esp_err_t process_battery_voltage(void)
} else {
float alpha = get_param_value_t(PARAM_ADC_ALPHA_BATTERY).f32;
if (isnan(raw)) {
ESP_LOGI(TAG, "RAW BATTERY IS NAN");
//ESP_LOGI(TAG, "RAW BATTERY IS NAN");
} else {
if (isnan(ema_battery) || isnan(alpha))
if (isnan(ema_battery) || isnan(alpha)) {
ema_battery = raw;
else
} else {
ema_battery = alpha * (float)raw + (1.0f - alpha) * ema_battery;
}
}
}
@@ -174,7 +178,7 @@ esp_err_t process_bridge_current(bridge_t bridge) {
break;
case BRIDGE_DRIVE:
// ACS37220LEZATR-100B3 is 100A capable and 13.2 mV/A
raw_a = (voltage_mv - 1650.0f) / 13.2f;
raw_a = -(voltage_mv - 1650.0f) / 13.2f;
break;
}
@@ -184,13 +188,14 @@ esp_err_t process_bridge_current(bridge_t bridge) {
} else {
float alpha = get_param_value_t(PARAM_ADC_ALPHA_ISENS).f32;
if (isnan(raw_a)) {
ESP_LOGI(TAG, "RAW BATTERY IS NAN");
//ESP_LOGI(TAG, "RAW BATTERY IS NAN");
channel->ema_current = NAN;
} else {
if (isnan(ema_battery) || isnan(alpha))
if (isnan(ema_battery) || isnan(alpha)) {
channel->ema_current = raw_a;
else
} else {
channel->ema_current = alpha * raw_a + (1.0f - alpha) * channel->ema_current;
}
}
}
@@ -206,13 +211,14 @@ esp_err_t process_bridge_current(bridge_t bridge) {
} else {
float alpha = get_param_value_t(PARAM_ADC_ALPHA_IAZ).f32;
if (isnan(raw_a)) {
ESP_LOGI(TAG, "RAW BATTERY IS NAN");
//ESP_LOGI(TAG, "RAW BATTERY IS NAN");
} else {
if (isnan(ema_battery) || isnan(alpha))
if (isnan(ema_battery) || isnan(alpha)) {
channel->az_offset = channel->ema_current;
else
} else {
channel->az_offset = alpha * channel->ema_current +
(1.0f - alpha) * channel->az_offset;
}
}
}
}
@@ -244,12 +250,26 @@ esp_err_t process_bridge_current(bridge_t bridge) {
// Normalize the current as a fraction of rated current
float I_norm = fabsf(channel->current / I_nominal);
// Instant trip on extreme overcurrent
// Instant trip on extreme overcurrent - but with inrush tolerance
if (I_norm >= get_param_value_t(PARAM_EFUSE_KINST).f32) {
channel->tripped = true;
channel->trip_time = now;
ESP_LOGI(TAG, "FUSE TRIP: Inom: %+.5f HEAT:%+2.5f", I_norm, channel->heat);
return ESP_OK; // no more processing, if we're over, we're over
// Start tracking if this is the first time we've seen overcurrent
if (channel->inrush_start_time == 0) {
channel->inrush_start_time = now;
}
// Check if overcurrent has persisted long enough
int64_t inrush_duration = now - channel->inrush_start_time;
if (inrush_duration >= get_param_value_t(PARAM_EFUSE_INRUSH_US).u32) {
channel->tripped = true;
channel->trip_time = now;
channel->inrush_start_time = 0; // Reset for next time
//ESP_LOGI(TAG, "FUSE TRIP: Inom: %+.5f HEAT:%+2.5f", I_norm, channel->heat);
return ESP_OK; // no more processing, if we're over, we're over
}
// Still in overcurrent but within inrush tolerance window - don't trip yet
} else {
// Current dropped below threshold - reset inrush timer
channel->inrush_start_time = 0;
}
// Accumulate heat
@@ -275,13 +295,13 @@ esp_err_t process_bridge_current(bridge_t bridge) {
// And enough time has passed
// Go ahead and reset the e-fuse
} else if (channel->tripped &&
(now - channel->trip_time) > get_param_value_t(PARAM_EFUSE_TCOOL).i64) {
(now - channel->trip_time) > get_param_value_t(PARAM_EFUSE_TCOOL).u32) {
channel->tripped = false;
// channel.heat = 0.0f // I think we should wait for the e-fuse to catch up
}
if (bridge == BRIDGE_DRIVE)
ESP_LOGI(TAG, "FUSE: Inom: %+.5f HEAT:%+2.5f", I_norm, channel->heat);
//if (bridge == BRIDGE_JACK)
//ESP_LOGI(TAG, "FUSE: raw_a: %+.4f cur: %+.4f Inorm: %+.5f HEAT:%+2.5f", raw_a, channel->current, I_norm, channel->heat);
return ESP_OK;
}

View File

@@ -1,546 +0,0 @@
/*
* power_mgmt.c
*
* 1 kHz power-management task:
* • Samples all three H-bridge current sensors (DRIVE, AUX, JACK)
* • Samples battery voltage (BAT)
* • Applies EMA filtering on every channel
* • Updates shared volatile globals for the control FSM
* • Handles over-current spike protection
*
* Updated to modern ESP-IDF ADC API (line fitting)
* All variables now defined locally
*
* Created on: Nov 10, 2025
*/
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
#include "driver/rtc_io.h"
#include "esp_log.h"
#include "esp_task_wdt.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_timer.h"
#include "driver/gpio.h"
#include "control_fsm.h"
#include "soc/rtc_io_reg.h"
#include "power_mgmt.h"
#include "storage.h"
#include "rtc.h"
// === GPIO Pin Definitions ===
#define PIN_V_ISENS1 ADC_CHANNEL_0 // GPIO36 / VP
#define PIN_V_ISENS2 ADC_CHANNEL_6 // GPIO34
#define PIN_V_ISENS3 ADC_CHANNEL_7 // GPIO35
#define PIN_V_BATTERY ADC_CHANNEL_3 // GPIO39 / VN
#define PIN_V_SENS_BAT PIN_V_BATTERY
#define PIN_CHG_BULK GPIO_NUM_26
#define AUTOZERO_THRESH 2000.0f // mA
typedef enum {
CHG_T_LOWBAT = 0,
CHG_T_BULK = 1,
CHG_T_STEADY = 2,
} charge_timer_t;
#define N_CHG_TIMERS 3
RTC_DATA_ATTR charge_state_t current_charge_state = CHG_STATE_BULK;
RTC_DATA_ATTR int64_t charge_timers[N_CHG_TIMERS] = {-1};
int64_t now;
charge_state_t get_charging_state() { return current_charge_state; }
void setTimerN(charge_timer_t i, int64_t sec) {
// set the timer for <sec> in the future if it's currently less than now
if (charge_timers[i] < now) {
charge_timers[i] = now + sec;
ESP_LOGI("BAT", "Set timer[%d] +%lld", i, (long long)sec);
}
}
void resetTimerN(charge_timer_t i) {
charge_timers[i] = -1;
}
void resetBatTimers() {
for (uint8_t i=0; i<N_CHG_TIMERS; i++)
resetTimerN(i);
}
bool getTimerN(charge_timer_t i) {
if (charge_timers[i] < 0) return false;
return system_rtc_get_raw_time() > charge_timers[i];
}
#define BULK_CHARGE_S 20 //2*60*60
#define FLOAT_STEADY_S 10 //30*60
#define LOW_DETECT_S 10 //5*60
#define STEADY_MV 13000
#define LOW_MV 12800
void run_charge_fsm() {
now = system_rtc_get_raw_time();
//ESP_LOGI("BAT", "FSM STATE %d", current_charge_state);
if (rtc_is_set()) {
switch(current_charge_state) {
case CHG_STATE_BULK:
// turn off bulk charging and go to float when time is up
if (getTimerN(CHG_T_BULK)) {
ESP_LOGI("BAT", "BULK -> FLOAT");
current_charge_state = CHG_STATE_FLOAT;
}
break;
case CHG_STATE_FLOAT:
if (getTimerN(CHG_T_STEADY)) {
ESP_LOGI("BAT", "FLOAT -> OFF");
current_charge_state = CHG_STATE_OFF;
}
if (get_battery_mV() > STEADY_MV) {
setTimerN(CHG_T_STEADY, FLOAT_STEADY_S);
} else {
resetTimerN(CHG_T_STEADY);
}
// NO break; !! float should also kick into bulk with same triggers
case CHG_STATE_OFF:
// after 5 minutes of low-ish battery go into bulk charge
if (getTimerN(CHG_T_LOWBAT)) {
ESP_LOGI("BAT", " -> BULK");
current_charge_state = CHG_STATE_BULK;
setTimerN(CHG_T_BULK, BULK_CHARGE_S);
}
if (get_battery_mV() < LOW_MV) {
setTimerN(CHG_T_LOWBAT, LOW_DETECT_S);
} else {
resetTimerN(CHG_T_LOWBAT);
}
break;
}
} else {
//ESP_LOGI("BAT", " -> BULK");
current_charge_state = CHG_STATE_BULK;
}
//rtc_gpio_hold_dis(PIN_CHG_BULK);
//rtc_gpio_hold_dis(PIN_CHG_DISABLE);
switch(current_charge_state) {
case CHG_STATE_BULK:
gpio_set_level(PIN_CHG_BULK, 1);
//ESP_LOGI("BAT", "BULK");
break;
case CHG_STATE_FLOAT:
gpio_set_level(PIN_CHG_BULK, 0);
//ESP_LOGI("BAT", "FLOAT");
break;
case CHG_STATE_OFF:
gpio_set_level(PIN_CHG_BULK, 0);
//ESP_LOGI("BAT", "OFF");
break;
}
//rtc_gpio_hold_en(PIN_CHG_BULK);
//rtc_gpio_hold_en(PIN_CHG_DISABLE);
}
typedef struct {
bool enabled; // Auto-zero active for this channel
float threshold_ma; // Max current to consider "zero" (mA)
float learned_offset_mv; // Accumulated zero offset (mV)
bool initialized; // First valid zero established
} autozero_t;
static autozero_t autozero[N_BRIDGES] = {0};
// === E-Fuse (Software Breaker) Configuration ===
static const char* currentLimits_A[N_BRIDGES] = {
[BRIDGE_DRIVE] = "efuse_drive_A", //40000,
[BRIDGE_AUX] = "efuse_aux_A", // 5000,
[BRIDGE_JACK] = "efuse_jack_A" // 10000
};
static const float i2t_thresholds[N_BRIDGES] = { // A^2*s (tunable per bridge if needed)
[BRIDGE_DRIVE] = 6.0f,
[BRIDGE_AUX] = 6.0f,
[BRIDGE_JACK] = 6.0f
};
static const float i_instant[N_BRIDGES] = { // Instant trip multiplier of I_rated
[BRIDGE_DRIVE] = 15.0f,
[BRIDGE_AUX] = 15.0f,
[BRIDGE_JACK] = 15.0f
};
static const float cool_rate[N_BRIDGES] = { // Cooling constant (1/s)
[BRIDGE_DRIVE] = 0.008f,
[BRIDGE_AUX] = 0.008f,
[BRIDGE_JACK] = 0.008f
};
static const int32_t cooldown_ms[N_BRIDGES] = { // Auto-reset delay after trip
[BRIDGE_DRIVE] = 5000,
[BRIDGE_AUX] = 5000,
[BRIDGE_JACK] = 5000
};
static float efuse_heat[N_BRIDGES] = {0};
static uint64_t efuse_trip_time[N_BRIDGES] = {0}; // Timestamp when tripped
static bool efuse_tripped[N_BRIDGES] = {false};
// === ADC Handles ===
static adc_oneshot_unit_handle_t adc1_handle = NULL;
static adc_cali_handle_t adc_cali_handle = NULL;
// === EMA Filter State ===
#define EMA_ALPHA_CURRENT 0.5f
#define EMA_ALPHA_BATTERY 0.05f
static float ema_current[N_BRIDGES] = {0};
static bool ema_init[N_BRIDGES] = {false};
static float ema_battery = 0.0f;
static bool ema_battery_init = false;
// === Shared Volatile Outputs ===
volatile int32_t bridgeCurrents_mA[N_BRIDGES] = {0};
volatile int32_t batteryVoltage_mV = 0;
// === ADC Initialization ===
static esp_err_t adc_init(void) {
if (adc1_handle != NULL) {
return ESP_OK; // Already initialized
}
// ADC1 oneshot mode
adc_oneshot_unit_init_cfg_t init_cfg = {
.unit_id = ADC_UNIT_1,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_cfg, &adc1_handle));
// Configure all channels
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = ADC_ATTEN_DB_11,
.bitwidth = ADC_BITWIDTH_12,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, PIN_V_ISENS1, &chan_cfg));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, PIN_V_ISENS2, &chan_cfg));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, PIN_V_ISENS3, &chan_cfg));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, PIN_V_SENS_BAT, &chan_cfg));
// Line fitting calibration (modern scheme)
adc_cali_line_fitting_config_t cali_cfg = {
.unit_id = ADC_UNIT_1,
.atten = ADC_ATTEN_DB_11,
.bitwidth = ADC_BITWIDTH_12,
};
ESP_ERROR_CHECK(adc_cali_create_scheme_line_fitting(&cali_cfg, &adc_cali_handle));
return ESP_OK;
}
void autozero_enable(bridge_t bridge, bool enable) {
if (bridge >= N_BRIDGES) return;
autozero[bridge].enabled = enable;
if (!enable) {
autozero[bridge].learned_offset_mv = 0.0f;
autozero[bridge].initialized = false;
}
}
void autozero_set_threshold(bridge_t bridge, float threshold_ma) {
if (bridge >= N_BRIDGES) return;
autozero[bridge].threshold_ma = fmaxf(0.0f, threshold_ma);
}
float autozero_get_offset_mv(bridge_t bridge) {
if (bridge >= N_BRIDGES) return 0.0f;
return autozero[bridge].learned_offset_mv;
}
void autozero_reset(bridge_t bridge) {
if (bridge >= N_BRIDGES) return;
autozero[bridge].learned_offset_mv = 0.0f;
autozero[bridge].initialized = false;
}
void autozero_reset_all(void) {
for (uint8_t i = 0; i < N_BRIDGES; i++) {
autozero_reset((bridge_t)i);
}
}
// === Raw Current Reading (mA) ===
static int32_t read_bridge_current_raw(bridge_t bridge) {
int adc_raw = 0;
int voltage_mv = 0;
adc_channel_t pin;
switch(bridge) {
case BRIDGE_DRIVE: pin = PIN_V_ISENS1; break;
case BRIDGE_AUX: pin = PIN_V_ISENS3; break;
case BRIDGE_JACK: pin = PIN_V_ISENS2; break;
default: return -42069; // lol
}
if (adc_oneshot_read(adc1_handle, pin, &adc_raw) != ESP_OK) {
return 0;
}
if (adc_cali_raw_to_voltage(adc_cali_handle, adc_raw, &voltage_mv) != ESP_OK) {
return 0;
}
float current_sense_mv = (float)voltage_mv;
autozero_t *az = &autozero[bridge];
// === AUTO-ZERO LEARNING PHASE ===
if (az->enabled && get_bridge_state(bridge)==0) {
float raw_current_ma = 0.0f;
switch (bridge) {
case BRIDGE_JACK:
case BRIDGE_AUX:
// ACS37042KLHBLT-030B3 is 30A capable and 44 mV/A
raw_current_ma = (current_sense_mv - 1650.0f) * 1000.0f / 44.0f;
break;
case BRIDGE_DRIVE:
// ACS37220LEZATR-100B3 is 100A capable and 13.2 mV/A
raw_current_ma = (current_sense_mv - 1650.0f) * 1000.0f / 13.20f;
break;
}
if (fabsf(raw_current_ma) <= az->threshold_ma) {
// Valid zero sample
if (!az->initialized) {
az->learned_offset_mv = current_sense_mv - 1650.0f;
az->initialized = true;
} else {
// EMA on offset (slow adaptation)
float alpha = 0.1f;
az->learned_offset_mv = alpha * (current_sense_mv - 1650.0f) +
(1.0f - alpha) * az->learned_offset_mv;
}
}
}
// === APPLY AUTO-ZERO OFFSET ===
float corrected_mv = current_sense_mv - az->learned_offset_mv;
int32_t offset_mv = (int32_t)(corrected_mv - 1650.0f);
int32_t current_ma = 0;
switch (bridge) {
case BRIDGE_JACK:
case BRIDGE_AUX:
current_ma = offset_mv * 1000 / 44; // 44 mV/A
break;
case BRIDGE_DRIVE:
current_ma = offset_mv * 10000 / 132; // 13.2 mV/A
break;
}
return current_ma;
}
// === Raw Battery Voltage Reading (mV) ===
static int32_t read_battery_voltage_raw(void)
{
int adc_raw = 0;
int voltage_mv = 0;
if (adc_oneshot_read(adc1_handle, PIN_V_SENS_BAT, &adc_raw) != ESP_OK) {
return 0;
}
if (adc_cali_raw_to_voltage(adc_cali_handle, adc_raw, &voltage_mv) != ESP_OK) {
return 0;
}
// Voltage divider: 150kΩ to 1MΩ → gain = 1.15 → scale = 1150/150
return (int32_t)voltage_mv * 1150 / 150;
}
// === EMA Filter Update ===
static void apply_ema(float *state, bool *init, float alpha, int32_t raw, volatile int32_t *out)
{
if (!*init) {
*state = (float)raw;
*init = true;
} else {
*state = alpha * (float)raw + (1.0f - alpha) * *state;
}
*out = (int32_t)(*state + 0.5f);
}
// === Public Accessors ===
int32_t get_bridge_mA(uint8_t bridge)
{
if (bridge >= N_BRIDGES) return -1;
return (int32_t)bridgeCurrents_mA[bridge];
}
int32_t get_battery_mV(void)
{
return (int32_t)batteryVoltage_mV;
}
// === E-Fuse: Trip Logic (called every cycle) ===
static void efuse_update(uint8_t bridge, float I, float dt, uint64_t now)
{
float I_rated = (float)get_param_i8(currentLimits_A[bridge]);
float I_norm = I / I_rated;
// Instant trip on extreme overcurrent
if (I_norm >= i_instant[bridge]) {
efuse_tripped[bridge] = true;
efuse_trip_time[bridge] = now;
return;
}
// Cooling when below threshold
if (I_norm < 1.1f) {
efuse_heat[bridge] -= efuse_heat[bridge] * cool_rate[bridge] * dt;
efuse_heat[bridge] = fmaxf(0.0f, efuse_heat[bridge]);
efuse_tripped[bridge] = false; // Auto-clear if cooled
return;
}
// Accumulate heat (I²t)
efuse_heat[bridge] += (I_norm * I_norm) * dt;
if (efuse_heat[bridge] >= i2t_thresholds[bridge]) {
efuse_tripped[bridge] = true;
efuse_trip_time[bridge] = now;
}
}
// === E-Fuse: Auto-Reset After Cooldown ===
static void efuse_cooldown_check(uint64_t now)
{
for (uint8_t i = 0; i < N_BRIDGES; i++) {
if (efuse_tripped[i] &&
(now - efuse_trip_time[i]) >= (cooldown_ms[i] * 1000ULL)) {
efuse_heat[i] = 0.0f;
efuse_tripped[i] = false;
}
}
}
// === Public E-Fuse Controls ===
void efuse_reset_all(void)
{
for (uint8_t i = 0; i < N_BRIDGES; i++) {
efuse_heat[i] = 0.0f;
efuse_tripped[i] = false;
}
}
bool efuse_is_tripped(uint8_t bridge)
{
if (bridge >= N_BRIDGES) return false;
return efuse_tripped[bridge];
}
// === Power Management Task ===
void power_mgmt_task(void *param) {
esp_task_wdt_add(NULL);
/*gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << PIN_CHG_DISABLE) | (1ULL << PIN_CHG_BULK),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io_conf);*/
/*// Enable RTC GPIO domain (required for hold)
rtc_gpio_init(PIN_CHG_DISABLE);
rtc_gpio_init(PIN_CHG_BULK);
// Set as output
rtc_gpio_set_direction(PIN_CHG_DISABLE, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_direction(PIN_CHG_BULK, RTC_GPIO_MODE_OUTPUT_ONLY);
// Optional: set initial level (will be held)
//rtc_gpio_set_level(PIN_CHG_DISABLE, 1); // e.g., start disabled
//rtc_gpio_set_level(PIN_CHG_BULK, 0);
// **Critical: Enable hold function**
rtc_gpio_hold_en(PIN_CHG_DISABLE);
rtc_gpio_hold_en(PIN_CHG_BULK);*/
ESP_ERROR_CHECK(adc_init());
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(20);
// Optional: Enable auto-zero with default threshold
autozero_enable(BRIDGE_DRIVE, true);
autozero_enable(BRIDGE_AUX, true);
autozero_enable(BRIDGE_JACK, true);
autozero_set_threshold(BRIDGE_DRIVE, AUTOZERO_THRESH);
autozero_set_threshold(BRIDGE_AUX, AUTOZERO_THRESH);
autozero_set_threshold(BRIDGE_JACK, AUTOZERO_THRESH);
//uint64_t last_wake_time = esp_timer_get_time();
//const uint64_t period = 5000; // 100 us => 10kHz
while (1) {
vTaskDelayUntil(&xLastWakeTime, xFrequency);
uint64_t now_us = esp_timer_get_time();
/*if (now - last_wake_time < period) {
uint32_t delay_us = (period - (now - last_wake_time)) / 1000;
if (delay_us > 0) vTaskDelay(pdMS_TO_TICKS(delay_us));
continue;
}
last_wake_time = now;*/
// Sample currents
for (uint8_t i = 0; i < N_BRIDGES; i++) {
int32_t raw_ma = read_bridge_current_raw((bridge_t)i);
apply_ema(&ema_current[i], &ema_init[i], EMA_ALPHA_CURRENT,
raw_ma, &bridgeCurrents_mA[i]);
// Reset spike timer if under limit
/*if (bridgeCurrents_mA[i] < currentLimits_mA[i]) {
currentSpikeSafeTimes[i] = now + CURRENT_SPIKE_TIME_US;
}*/
// === E-FUSE UPDATE ===
float I = (float)bridgeCurrents_mA[i] / 1000.0f;
float dt = 0.020f; // 20 ms task period
efuse_update(i, I, dt, now_us);
}
/*ESP_LOGI("PWR", "[ %6ld | %6ld | %6ld mA ] { %6ld mV }",
(long)bridgeCurrents_mA[BRIDGE_DRIVE],
(long)bridgeCurrents_mA[BRIDGE_JACK],
(long)bridgeCurrents_mA[BRIDGE_AUX],
(long)batteryVoltage_mV);*/
// Sample battery
int32_t raw_bat = read_battery_voltage_raw();
apply_ema(&ema_battery, &ema_battery_init, EMA_ALPHA_BATTERY,
raw_bat, &batteryVoltage_mV);
//run_charge_fsm();
efuse_cooldown_check(now_us);
esp_task_wdt_reset();
}
}
void start_power() {
xTaskCreate(power_mgmt_task, "PWR", 4096, NULL, 5, NULL);
}
void shutdown_power() {
}

28
main/prvtkey.pem Normal file
View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCc4kQulPmDvj52
HrvgQPMrGrMsSQdtOKFOFBT0o7uTXXWK2y48TMdy3RKKNVaIu0nhKpsFSBfekyAe
iUJH4N/1ih+byJtUZJ3iMyfDrPoElmAPBQJYkjwvM8V94ug648GV5YF20C9hNOyi
7Z9BIdRvd3DiNexuYz+3C2vRGW/lHu9r7HdK1u4nv9SF10jZhjfa0DidotKaDrul
558QoHskrQkxOQa5hKRcB4KmceRihs5bU9qngrgdSlax0LUBhpBBBsgUxsKdvht3
C4R55MmAkDWuYYvqa8cAX8+XWylILRjlvktZhDrWFOlXbTxShD9ax3zUfuJRj8Jx
t0/SLamZAgMBAAECggEAA/dwk+DuYhdYSvJB+8yImWlmaFM8XdWVtnypfvn4zyQI
ycb650llrZDLXDU3B+P8XTYPj1WgTN9Za4w64chcwT+Jxw2OZ9bXaxWyBq+D7sPC
j/6nbYfc/7CGaMVo67xAc8LGwDNJT2LgLMpnQWVSkrLpZr7ISI432S/vvOywuJFC
gQVdslNvVDAwC0KU+0Y0P4CTq2gQYeThODMMstRm0Ui/L/nENUNWCNmNd+WQYx4E
8DQfCCLh2u9W7whpYFbaOSQxUzDMRcgZMTzVjU48/oZTagNMEFfYmv5pSQN6Lhf2
PvBbYRip3h0sdj1gmFuyGOEl0Mw4AzZyeBohxMNSUQKBgQDXy5A+Cd/8dPM8mL88
RtKrV3+Dy+otML1bK7eDN1jJ6XDCmvOAtWIwH2v1quls7uWX54lDDYLs6F2w5+Hz
dRnfkMO/JtWInYjLqh1pu5iVPbP+6HVSX6wslMvg8K+M5DOsG/q6J62j6e643o9G
fCiNR8rX8V/BdaCvtCBiCtNPjQKBgQC6HOd3302D6PKQSWLJss7QYdEQ5sYesoIu
GW6KfJEh6FafJ/fwJoDOH+9klvN9EBJLlXZa/9NM23DqovI/Ca+ABfFjjzea70+U
f9UbzaAMl6ENKU7vmAyM2WwHdZPJ4PCQ9J7nBkODzKAm++eQh12iOglcIMKdUPiL
72Ygx1bJPQKBgA9Ae/lmiUY2ndpykVGZT9p8XK7cArke8MM428eSadwbe7TFbuBx
8XalQeQjKExitid+Xd03X0GPSs/uE7I5XJLIkI06GW2GdNywMVP/xlEGA2rI00H3
MRwViDNlz4KNvnkzoQz3MPac2hqG4GmC7PrPUC7jCHmL7sd8W62SRk0hAoGAEwEI
kbD3lVSgECOuNrJPc+/JDVTDPjc0G8j1BKcbmr7CuZW3N4p29JVGOJtBWa/ebmFg
qIIe7WYq7Yqd+dnfVc9FiskBAI0XLy6ucBxbD24cP9/L86MvBOLeqRRUdvTFG8ge
wbBeDINEhzaJurRX10zdz854kN/HwWI8p3QzZHECgYBeDTRyYbi9c7eo6Uyo+pjz
bFWyVtAjzjChHa9ifI6GCgs1qakaNBrwb4yOlheEkuakscZY1myzHwHRH1QTQK2c
4du0I3/JFkk1JmYXw1VLo/BKs2S6RGBx+4jE5bkSXrxnQmT0EtbexkfIJHEAlIqU
DMmi7yrQo4dydJ20IHEF6A==
-----END PRIVATE KEY-----

View File

@@ -37,10 +37,11 @@ typedef struct {
size_t num_symbols;
} rf_code_t;
// Global queue for passing decoded codes between tasks
static QueueHandle_t g_code_queue = NULL;
int learn_flag = -1;
bool controls_enabled = true;
// Temporary storage for learned keycodes (not committed to params yet)
static int64_t temp_keycodes[NUM_RF_BUTTONS] = {0};
// For rmt_rx_register_event_callbacks
static bool rfrx_done(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *udata) {
@@ -52,8 +53,8 @@ static bool rfrx_done(rmt_channel_handle_t channel, const rmt_rx_done_event_data
// Task that receives and decodes RF signals
static void rf_433_receiver_task(void* param) {
esp_task_wdt_add(NULL);
esp_log_level_set("rmt", ESP_LOG_NONE); // disable rmt messages about hw buffer too small
esp_task_wdt_add(NULL);
esp_log_level_set("rmt", ESP_LOG_NONE); // disable rmt messages about hw buffer too small
const uint16_t tlow = (P_HIGH - P_LOW - (2 * P_MARGIN));
const uint16_t thigh = (P_HIGH - P_LOW + (2 * P_MARGIN));
@@ -130,45 +131,38 @@ static void rf_433_receiver_task(void* param) {
}
}
// If we got a valid code, send it to processing task
// If we got a valid code, process it
if (code) {
int64_t encoded = ((int64_t)len << 56) | code;
ESP_LOGI(TAG, "GOT KEYCODE 0x%lx [%d]", (long) code, len);
if (learn_flag >= 0) {
set_param_value_t(PARAM_KEYCODE_0 + learn_flag,
(param_value_t){.i64 = encoded});
ESP_LOGI(TAG, "LEARNED KEYCODE");
learn_flag = -1;
} else {
rf_code_t rf_msg = {
.code = code,
.high_avg = high / 24,
.low_avg = low / 24,
.errors = err,
.num_symbols = len
};
// Don't do this anymore. No need to pass data between threads. Just act on it.
// Non-blocking send - if queue is full, just drop it
//xQueueSend(g_code_queue, &rf_msg, 0);
for (uint8_t i = 0; i < NUM_RF_BUTTONS; i++) {
int64_t match = get_param_value_t(PARAM_KEYCODE_0+i).i64;
if (encoded == match) {
switch (i) {
case 0: pulseOverride(RELAY_A1); pulseOverride(RELAY_A3); break;
case 1: pulseOverride(RELAY_B1); pulseOverride(RELAY_A3); break;
case 2: pulseOverride(RELAY_A2); break;
case 3: pulseOverride(RELAY_B2); break;
default: break;
}
}
}
ESP_LOGI(TAG, "GOT KEYCODE 0x%lx [%d]", (long) code, len);
if (learn_flag >= 0) {
// Store to temporary storage, not to params yet
temp_keycodes[learn_flag] = (uint32_t)code;
ESP_LOGI(TAG, "LEARNED KEYCODE (temp storage)");
learn_flag = -1;
} else if (controls_enabled) {
// Only process RF commands if controls are enabled
rf_code_t rf_msg = {
.code = code,
.high_avg = high / 24,
.low_avg = low / 24,
.errors = err,
.num_symbols = len
};
for (uint8_t i = 0; i < NUM_RF_BUTTONS; i++) {
uint32_t match = get_param_value_t(PARAM_KEYCODE_0+i).u32;
// Compare just the code (lower 32 bits)
if ((uint32_t)match == code && code!=0) {
switch (i) {
case 0: pulseOverride(RELAY_A1); pulseOverride(RELAY_A3); break;
case 1: pulseOverride(RELAY_B1); pulseOverride(RELAY_A3); break;
case 2: pulseOverride(RELAY_A2); break;
case 3: pulseOverride(RELAY_B2); break;
default: break;
}
}
}
}
}
@@ -203,11 +197,10 @@ static void rf_433_receiver_task(void* param) {
}
// Start next receive
rmt_receive(rx_channel, symbols, sizeof(symbols), &rx_config);
esp_task_wdt_reset();
}
esp_task_wdt_reset();
}
// Cleanup (never reached in this case)
@@ -216,53 +209,46 @@ static void rf_433_receiver_task(void* param) {
vTaskDelete(NULL);
}
esp_err_t rf_433_init() {
g_code_queue = xQueueCreate(5, sizeof(rf_code_t));
assert(g_code_queue);
esp_err_t rf_433_init() {
xTaskCreate(rf_433_receiver_task, TAG, 4096, NULL, 10, NULL);
return ESP_OK;
return ESP_OK;
}
esp_err_t rf_433_stop() { return ESP_OK; }
void rf_433_set_keycode(uint8_t index, int64_t code) {
set_param_value_t(PARAM_KEYCODE_0+index, (param_value_t){.i64=code});
void rf_433_set_keycode(uint8_t index, uint32_t code) {
set_param_value_t(PARAM_KEYCODE_0+index, (param_value_t){.u32=code});
}
void rf_433_learn_keycode(uint8_t index) {
if (index >= 8) return;
learn_flag = index;
if (index >= 8) return;
learn_flag = index;
}
void rf_433_cancel_learn_keycode() {
learn_flag = -1;
learn_flag = -1;
}
/*int8_t rf_433_get_keycode() {
rf_code_t received_code;
if (xQueueReceive(g_code_queue, &received_code, 0) == pdPASS) {
int64_t newcode = ((int64_t)received_code.num_symbols << 56) | received_code.code;
for (uint8_t i = 0; i < NUM_RF_BUTTONS; i++) {
if (newcode == get_param_value_t(PARAM_KEYCODE_0+i).i64)
return i;
}
ESP_LOGI("RF", "Received unknown code 0x%08lx (%d) [0x%16llx]", (unsigned long)received_code.code, received_code.num_symbols, (unsigned long long) newcode);
}
return -1;
void rf_433_disable_controls() {
controls_enabled = false;
}
int64_t rf_433_get_raw_keycode() {
rf_code_t received_code;
int64_t code = -1;
if (xQueueReceive(g_code_queue, &received_code, 0) == pdPASS) {
code = ((int64_t)received_code.num_symbols << 56) | received_code.code;
//ESP_LOGI("RF", "Raw Code 0x%08lx (%d) [0x%16llx]", (unsigned long)received_code.code, received_code.num_symbols, (unsigned long long) code);
void rf_433_enable_controls() {
controls_enabled = true;
}
int32_t rf_433_get_temp_keycode(uint8_t index) {
if (index >= NUM_RF_BUTTONS) return 0;
return temp_keycodes[index];
}
void rf_433_set_temp_keycode(uint8_t index, uint32_t code) {
if (index >= NUM_RF_BUTTONS) return;
temp_keycodes[index] = code;
}
void rf_433_clear_temp_keycodes() {
for (uint8_t i = 0; i < NUM_RF_BUTTONS; i++) {
temp_keycodes[i] = 0;
}
return code;
}*/
void rf_433_clear_queue() {
xQueueReset(g_code_queue);
}

View File

@@ -1,4 +1,3 @@
#ifndef RF_H
#define RF_H
@@ -17,7 +16,7 @@ int64_t recieveKeycode();
esp_err_t rf_433_init();
esp_err_t rf_433_stop();
void rf_433_set_keycode(uint8_t index, int64_t code);
void rf_433_set_keycode(uint8_t index, uint32_t code);
/*
int8_t rf_433_get_keycode();
@@ -27,4 +26,11 @@ int64_t rf_433_get_raw_keycode();
void rf_433_learn_keycode(uint8_t index);
void rf_433_cancel_learn_keycode();
void rf_433_disable_controls();
void rf_433_enable_controls();
int32_t rf_433_get_temp_keycode(uint8_t index);
void rf_433_set_temp_keycode(uint8_t index, uint32_t code);
void rf_433_clear_temp_keycodes();
#endif

View File

@@ -67,13 +67,13 @@ esp_err_t rtc_xtal_init(void) {
return ESP_OK;
}
void reset_shutdown_timer(void)
void rtc_reset_shutdown_timer(void)
{
last_activity_tick = xTaskGetTickCount();
rtc_wdt_feed();
}
void enter_deep_sleep(void)
void rtc_enter_deep_sleep(void)
{
//close_current_log();
fsm_request(FSM_CMD_STOP);
@@ -82,37 +82,32 @@ void enter_deep_sleep(void)
esp_deep_sleep_start();
}
void rtc_set_time(struct tm *tm) {
rtc_set = true;
struct timeval tv = { .tv_sec = mktime(tm), .tv_usec = 0 };
settimeofday(&tv, NULL);
reset_solar_fsm();
}
void rtc_get_time(struct tm *tm)
{
time_t raw;
time(&raw);
localtime_r(&raw, tm);
}
int64_t system_rtc_get_raw_time(void)
int64_t rtc_get_s(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec;
}
uint64_t rtc_time_ms(void)
void rtc_set_s(int64_t tv_sec)
{
rtc_set = true;
settimeofday(&(struct timeval){.tv_sec = tv_sec, .tv_usec=0}, NULL);
solar_reset_fsm();
rtc_schedule_next_alarm();
}
int64_t rtc_get_ms(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
return (int64_t)tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
}
int64_t system_rtc_seconds_into_day(void)
int64_t rtc_get_s_in_day(void)
{
return system_rtc_get_raw_time() % 86400UL;
return rtc_get_s() % 86400UL;
}
esp_sleep_wakeup_cause_t rtc_wakeup_cause(void)
@@ -129,18 +124,18 @@ esp_sleep_wakeup_cause_t rtc_wakeup_cause(void)
/* -------------------------------------------------------------------------- */
/* Unified periodic update */
/* -------------------------------------------------------------------------- */
void check_shutdown_timer(void)
void rtc_check_shutdown_timer(void)
{
TickType_t elapsed = xTaskGetTickCount() - last_activity_tick;
if (elapsed * portTICK_PERIOD_MS >= POWER_INACTIVITY_TIMEOUT_MS)
enter_deep_sleep();
rtc_enter_deep_sleep();
}
/* -------------------------------------------------------------------------- */
/* Time adjustment helpers */
/* -------------------------------------------------------------------------- */
void adjust_rtc_hour(char *key, int8_t dir)
/*void adjust_rtc_hour(char *key, int8_t dir)
{
struct tm t;
rtc_get_time(&t);
@@ -162,13 +157,13 @@ void adjust_rtc_min(char *key, int8_t dir)
if (t.tm_min < 0) t.tm_min = 59;
rtc_set_time(&t);
set_next_alarm();
}
}*/
void set_next_alarm(void) {
int8_t start_h = 0; //get_param_i8("sched_start");
int8_t end_h = 23; //get_param_i8("sched_end");
int8_t num = 0; //get_param_i8("sched_num");
void rtc_schedule_next_alarm(void) {
int64_t start_sec = get_param_value_t(PARAM_MOVE_START).u32;
int64_t end_sec = get_param_value_t(PARAM_MOVE_END).u32;
int16_t num = get_param_value_t(PARAM_NUM_MOVES).i16;
if (num <= 0) {
next_alarm_time_s = -1;
@@ -176,15 +171,12 @@ void set_next_alarm(void) {
}
// Current time info
uint32_t s_into_day = system_rtc_seconds_into_day();
time_t current_time = system_rtc_get_raw_time();
int64_t s_into_day = rtc_get_s_in_day();
time_t current_time = rtc_get_s();
time_t today_midnight = current_time - s_into_day;
int start_sec = start_h * 3600;
int end_sec = end_h * 3600;
bool overnight = (start_h > end_h);
int total_duration = overnight ? (86400 - start_sec) + end_sec : end_sec - start_sec;
bool overnight = (start_sec > end_sec);
int64_t total_duration = overnight ? (86400 - start_sec) + end_sec : end_sec - start_sec;
// Determine period start
time_t period_start;
@@ -209,7 +201,7 @@ void set_next_alarm(void) {
int64_t spacing = total_duration / (num - 1);
time_t next_alarm = -1;
for (int8_t i = 0; i < num; i++) {
for (int16_t i = 0; i < num; i++) {
time_t alarm_time = period_start + spacing * i;
if (alarm_time > current_time) {
next_alarm = alarm_time;
@@ -227,12 +219,16 @@ void set_next_alarm(void) {
ESP_LOGI("ALARM", "SET FOR %lld (in %lld s)", next_alarm_time_s, next_alarm_time_s - current_time);
}
bool alarm_tripped() {
int64_t rtc_get_next_alarm_s() {
return next_alarm_time_s;
}
bool rtc_alarm_tripped() {
if (!rtc_is_set())
return false;
if (next_alarm_time_s < 0) {
set_next_alarm();
rtc_schedule_next_alarm();
return false;
}
return system_rtc_get_raw_time() > next_alarm_time_s;
return rtc_get_s() > next_alarm_time_s;
}

View File

@@ -16,6 +16,7 @@
#include "esp_sleep.h"
#include "esp_timer.h"
#include "esp_err.h"
#include <time.h>
#define POWER_INACTIVITY_TIMEOUT_MS 180000
@@ -29,23 +30,23 @@ esp_err_t rtc_xtal_init();
bool rtc_is_set();
void set_next_alarm(void);
void reset_shutdown_timer(); // reset shutoff timer
void enter_deep_sleep();
void check_shutdown_timer();
void rtc_check_shutdown_timer();
void rtc_reset_shutdown_timer(); // reset shutoff timer
void rtc_enter_deep_sleep();
esp_sleep_wakeup_cause_t rtc_wakeup_cause();
void adjust_rtc_hour(char *key, int8_t dir);
void adjust_rtc_min(char *key, int8_t dir);
/*void adjust_rtc_hour(char *key, int8_t dir);
void adjust_rtc_min(char *key, int8_t dir);*/
void rtc_get_time(struct tm * timeinfo);
int64_t rtc_get_s (void);
int64_t rtc_get_ms(void);
void rtc_set_s(int64_t);
int64_t system_rtc_get_raw_time(void);
void rtc_schedule_next_alarm(void);
int64_t rtc_get_next_alarm_s();
bool alarm_tripped();
uint64_t rtc_time_ms();
bool rtc_alarm_tripped();
/* -------------------------------------------------------------------------- */
#endif /* MAIN_RTC_H_ */

View File

@@ -58,7 +58,7 @@ static void sensor_debounce_task(void* param) {
uint8_t i = 0;
int64_t now = -1;
//int64_t now = -1;
while (1) {
if (xQueueReceive(sensor_event_queue, &evt, pdMS_TO_TICKS(100)) == pdTRUE) {
@@ -84,7 +84,7 @@ static void sensor_debounce_task(void* param) {
}
now = esp_timer_get_time();
//now = esp_timer_get_time();
/*// Wait for debounce period since last ISR
if (now - sensor_last_isr_time[i] >= (DEBOUNCE_TIME_US)) {

View File

@@ -16,9 +16,9 @@
#define SENSOR_DEBOUNCE_US 500 // Reduced to 0.5 ms for responsiveness
typedef enum {
SENSOR_DRIVE = 0,
SENSOR_JACK = 1,
N_SENSORS = 2
SENSOR_SAFETY = 0,
SENSOR_DRIVE = 1,
N_SENSORS = 2
} sensor_t;
void reset_sensor_counter(sensor_t i);

19
main/servercert.pem Normal file
View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIjCCAgqgAwIBAgIUDpXEqSZ2cdhTBSkwUpC6uSIsfvEwDQYJKoZIhvcNAQEL
BQAwEzERMA8GA1UEAwwIc2MubG9jYWwwHhcNMjUxMjMwMTcyMDMwWhcNMzUxMjI4
MTcyMDMwWjATMREwDwYDVQQDDAhzYy5sb2NhbDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAJziRC6U+YO+PnYeu+BA8ysasyxJB204oU4UFPSju5NddYrb
LjxMx3LdEoo1Voi7SeEqmwVIF96TIB6JQkfg3/WKH5vIm1RkneIzJ8Os+gSWYA8F
AliSPC8zxX3i6DrjwZXlgXbQL2E07KLtn0Eh1G93cOI17G5jP7cLa9EZb+Ue72vs
d0rW7ie/1IXXSNmGN9rQOJ2i0poOu6XnnxCgeyStCTE5BrmEpFwHgqZx5GKGzltT
2qeCuB1KVrHQtQGGkEEGyBTGwp2+G3cLhHnkyYCQNa5hi+prxwBfz5dbKUgtGOW+
S1mEOtYU6VdtPFKEP1rHfNR+4lGPwnG3T9ItqZkCAwEAAaNuMGwwHQYDVR0OBBYE
FICBmIRVzS0CQHS5OZmXSLQcTOz6MB8GA1UdIwQYMBaAFICBmIRVzS0CQHS5OZmX
SLQcTOz6MA8GA1UdEwEB/wQFMAMBAf8wGQYDVR0RBBIwEIIIc2MubG9jYWyHBMCo
BAEwDQYJKoZIhvcNAQELBQADggEBAIn1zeotOH72CcRUq5e+nsKLRCZSUZV1Gvip
nTADfFI8MpMGe0ikPJqetSYmP1HVIzGWQlWamqGhXckBeR9uq1e2k1Jsjf80w6o6
ild+fr1oga7n1xwXIMumDbSFjtsWe0fO9xI5NVGP3h6ikj1SN0o6T5EtTyjZ+vGw
u73tFmD7c/YBKkvLlP1UxVbAvJFEHL+O7e3QiIzeyryWFDEWRjOHPuPLCV84J5Q6
55mzfuUFkf7piLpLUXZ6lJ3MYa+iylll7Z9jQ8W5LWPpZhbTwyK4j7L6IiJoluzL
eV0ZuqoHda9Igd+Gigih1YOH4raMc98KVrNIUjNFoWlOx4rvWkY=
-----END CERTIFICATE-----

143
main/simple_dns_server.c Normal file
View File

@@ -0,0 +1,143 @@
#include "simple_dns_server.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <string.h>
static const char *TAG = "DNS_SERVER";
static int dns_socket = -1;
static TaskHandle_t dns_task_handle = NULL;
static char dns_ip[16] = {0};
// DNS header structure
typedef struct {
uint16_t id;
uint16_t flags;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
} dns_header_t;
static void dns_server_task(void *pvParameters) {
char rx_buffer[512];
char tx_buffer[512];
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(53);
dns_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (dns_socket < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
vTaskDelete(NULL);
return;
}
int err = bind(dns_socket, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(TAG, "Socket bind failed: errno %d", errno);
close(dns_socket);
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "DNS server started on port 53");
while (1) {
struct sockaddr_in source_addr;
socklen_t socklen = sizeof(source_addr);
int len = recvfrom(dns_socket, rx_buffer, sizeof(rx_buffer) - 1, 0,
(struct sockaddr *)&source_addr, &socklen);
if (len < 0) {
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
break;
}
if (len < sizeof(dns_header_t)) {
continue;
}
// Parse DNS query
dns_header_t *header = (dns_header_t *)rx_buffer;
// Build DNS response
memcpy(tx_buffer, rx_buffer, len);
dns_header_t *response = (dns_header_t *)tx_buffer;
// Set response flags
response->flags = htons(0x8180); // Standard query response, no error
response->ancount = htons(1); // One answer
// Add answer section (Type A record)
int pos = len;
// Name pointer to question
tx_buffer[pos++] = 0xC0;
tx_buffer[pos++] = 0x0C;
// Type A
tx_buffer[pos++] = 0x00;
tx_buffer[pos++] = 0x01;
// Class IN
tx_buffer[pos++] = 0x00;
tx_buffer[pos++] = 0x01;
// TTL (60 seconds)
tx_buffer[pos++] = 0x00;
tx_buffer[pos++] = 0x00;
tx_buffer[pos++] = 0x00;
tx_buffer[pos++] = 0x3C;
// Data length (4 bytes for IPv4)
tx_buffer[pos++] = 0x00;
tx_buffer[pos++] = 0x04;
// IP address
int a, b, c, d;
sscanf(dns_ip, "%d.%d.%d.%d", &a, &b, &c, &d);
tx_buffer[pos++] = a;
tx_buffer[pos++] = b;
tx_buffer[pos++] = c;
tx_buffer[pos++] = d;
// Send response
sendto(dns_socket, tx_buffer, pos, 0,
(struct sockaddr *)&source_addr, sizeof(source_addr));
}
close(dns_socket);
dns_socket = -1;
vTaskDelete(NULL);
}
esp_err_t simple_dns_server_start(const char *ap_ip) {
if (dns_task_handle != NULL) {
ESP_LOGW(TAG, "DNS server already running");
return ESP_ERR_INVALID_STATE;
}
strncpy(dns_ip, ap_ip, sizeof(dns_ip) - 1);
xTaskCreate(dns_server_task, "dns_server", 4096, NULL, 5, &dns_task_handle);
return ESP_OK;
}
void simple_dns_server_stop(void) {
if (dns_task_handle != NULL) {
if (dns_socket >= 0) {
close(dns_socket);
dns_socket = -1;
}
vTaskDelete(dns_task_handle);
dns_task_handle = NULL;
ESP_LOGI(TAG, "DNS server stopped");
}
}

19
main/simple_dns_server.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef SIMPLE_DNS_SERVER_H
#define SIMPLE_DNS_SERVER_H
#include "esp_err.h"
/**
* @brief Start a simple DNS server that redirects all queries to the AP IP
*
* @param ap_ip The IP address to return for all DNS queries (e.g., "192.168.4.1")
* @return esp_err_t ESP_OK on success
*/
esp_err_t simple_dns_server_start(const char *ap_ip);
/**
* @brief Stop the DNS server
*/
void simple_dns_server_stop(void);
#endif // SIMPLE_DNS_SERVER_H

View File

@@ -16,7 +16,7 @@ typedef enum {
RTC_DATA_ATTR charge_state_t current_charge_state = CHG_STATE_FLOAT;
RTC_DATA_ATTR int64_t timer;
esp_err_t reset_solar_fsm() {
esp_err_t solar_reset_fsm() {
timer = -1;
current_charge_state = CHG_STATE_FLOAT;
return ESP_OK;
@@ -32,10 +32,10 @@ esp_err_t init_solar_gpio() {
return ESP_OK;
}
esp_err_t run_solar_fsm() {
esp_err_t solar_run_fsm() {
init_solar_gpio();
int64_t now = system_rtc_get_raw_time();
int64_t now = rtc_get_ms();
//ESP_LOGI("BAT", "FSM STATE %d", current_charge_state);
@@ -50,7 +50,7 @@ esp_err_t run_solar_fsm() {
//if (rtc_is_set()) {
switch(current_charge_state) {
case CHG_STATE_BULK:
if (now > timer+get_param_value_t(PARAM_CHG_BULK_S).i64) {
if (now > timer+get_param_value_t(PARAM_CHG_BULK_S).u32) {
current_charge_state = CHG_STATE_FLOAT;
}
@@ -61,7 +61,7 @@ esp_err_t run_solar_fsm() {
timer = now;
}
if (now > timer+get_param_value_t(PARAM_CHG_LOW_S).i64) {
if (now > timer+get_param_value_t(PARAM_CHG_LOW_S).u32) {
timer = now;
current_charge_state = CHG_STATE_BULK;
}

View File

@@ -10,7 +10,7 @@
#include "esp_err.h"
esp_err_t run_solar_fsm();
esp_err_t reset_solar_fsm();
esp_err_t solar_run_fsm();
esp_err_t solar_reset_fsm();
#endif /* MAIN_SOLAR_H_ */

View File

@@ -1,9 +1,13 @@
#include <math.h>
#include <string.h>
#include "esp_partition.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_crc.h"
#include "storage.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "version.h"
#define TAG "STORAGE"
@@ -17,43 +21,68 @@
#define PARAM_NAME_STR(name) #name
// Generate parameter table with live values (initialized to defaults)
#define PARAM_DEF(name, type, default_val) PARAM_VALUE_INIT(type, default_val),
#define PARAM_DEF(name, type, default_val, unit) PARAM_VALUE_INIT(type, default_val),
param_value_t parameter_table[NUM_PARAMS] = {
PARAM_LIST
};
#undef PARAM_DEF
// Generate default values array
#define PARAM_DEF(name, type, default_val) PARAM_VALUE_INIT(type, default_val),
#define PARAM_DEF(name, type, default_val, unit) PARAM_VALUE_INIT(type, default_val),
const param_value_t parameter_defaults[NUM_PARAMS] = {
PARAM_LIST
};
#undef PARAM_DEF
// Generate parameter types array
#define PARAM_DEF(name, type, default_val) PARAM_TYPE_ENUM(type),
#define PARAM_DEF(name, type, default_val, unit) PARAM_TYPE_ENUM(type),
const param_type_e parameter_types[NUM_PARAMS] = {
PARAM_LIST
};
#undef PARAM_DEF
// Generate parameter names array
#define PARAM_DEF(name, type, default_val) PARAM_NAME_STR(name),
#define PARAM_DEF(name, type, default_val, unit) PARAM_NAME_STR(name),
const char* parameter_names[NUM_PARAMS] = {
PARAM_LIST
};
#undef PARAM_DEF
// Generate parameter units array (8 chars max per unit)
#define PARAM_DEF(name, type, default_val, unit) unit,
const char parameter_units[NUM_PARAMS][8] = {
PARAM_LIST
};
#undef PARAM_DEF
// Partition pointer
static const esp_partition_t *storage_partition = NULL;
// Log head tracking
// Log head tracking with mutex protection
static uint32_t log_head_index = 0;
static uint32_t log_tail_index = 0;
static SemaphoreHandle_t log_mutex = NULL;
static bool log_initialized = false;
// Calculate offset for log area (after parameters sector)
#define LOG_START_OFFSET FLASH_SECTOR_SIZE
uint32_t get_log_head(void) {
uint32_t head;
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
head = LOG_START_OFFSET + (log_head_index * LOG_ENTRY_SIZE);
if (log_mutex) xSemaphoreGive(log_mutex);
return head;
}
uint32_t get_log_tail(void) {
uint32_t tail;
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
tail = LOG_START_OFFSET + (log_tail_index * LOG_ENTRY_SIZE);
if (log_mutex) xSemaphoreGive(log_mutex);
return tail;
}
uint32_t get_log_offset(void) {
return LOG_START_OFFSET;
}
// ============================================================================
// PARAMETER FUNCTIONS
@@ -78,13 +107,101 @@ esp_err_t set_param_value_t(param_idx_t id, param_value_t val) {
return ESP_OK;
}
esp_err_t set_param_string(param_idx_t id, const char* str) {
if (id >= NUM_PARAMS) {
ESP_LOGE(TAG, "Invalid parameter ID: %d", id);
return ESP_ERR_INVALID_ARG;
}
if (parameter_types[id] != PARAM_TYPE_str) {
ESP_LOGE(TAG, "Parameter %d (%s) is not a string type", id, parameter_names[id]);
return ESP_ERR_INVALID_ARG;
}
if (str == NULL) {
parameter_table[id].str[0] = '\0';
} else {
strncpy(parameter_table[id].str, str, 15);
parameter_table[id].str[15] = '\0'; // Ensure null termination
}
ESP_LOGI(TAG, "String parameter %d (%s) set to '%s' (not committed)",
id, parameter_names[id], parameter_table[id].str);
return ESP_OK;
}
char* get_param_string(param_idx_t id) {
if (id >= NUM_PARAMS) {
ESP_LOGE(TAG, "Invalid parameter ID: %d", id);
return "";
}
if (parameter_types[id] != PARAM_TYPE_str) {
ESP_LOGE(TAG, "Parameter %d (%s) is not a string type", id, parameter_names[id]);
return "";
}
return parameter_table[id].str;
}
param_type_e get_param_type(param_idx_t id) {
if (id >= NUM_PARAMS) {
return PARAM_TYPE_u64; // Default fallback
return PARAM_TYPE_f64; // Default fallback
}
return parameter_types[id];
}
// ============================================================================
// JSON-FRIENDLY STRING CONVERSION
// ============================================================================
const char* get_param_json_string(param_idx_t id, char* buffer, size_t buf_size) {
if (id >= NUM_PARAMS || buffer == NULL || buf_size == 0) {
if (buffer && buf_size > 0) buffer[0] = '\0';
return "";
}
param_type_e type = parameter_types[id];
param_value_t val = parameter_table[id];
switch(type) {
case PARAM_TYPE_u16:
snprintf(buffer, buf_size, "%u", val.u16);
break;
case PARAM_TYPE_i16:
snprintf(buffer, buf_size, "%d", val.i16);
break;
case PARAM_TYPE_u32:
snprintf(buffer, buf_size, "%lu", (unsigned long)val.u32);
break;
case PARAM_TYPE_i32:
snprintf(buffer, buf_size, "%ld", (long)val.i32);
break;
case PARAM_TYPE_f32:
if (isnan(val.f32) || isinf(val.f32)) {
snprintf(buffer, buf_size, "null");
} else {
snprintf(buffer, buf_size, "%.6g", val.f32);
}
break;
case PARAM_TYPE_f64:
if (isnan(val.f64) || isinf(val.f64)) {
snprintf(buffer, buf_size, "null");
} else {
snprintf(buffer, buf_size, "%.15g", val.f64);
}
break;
case PARAM_TYPE_str:
// Escape quotes and backslashes for JSON string
snprintf(buffer, buf_size, "\"%s\"", val.str);
break;
default:
snprintf(buffer, buf_size, "null");
break;
}
return buffer;
}
const char* get_param_name(param_idx_t id) {
if (id >= NUM_PARAMS) {
return "INVALID";
@@ -100,39 +217,164 @@ param_value_t get_param_default(param_idx_t id) {
return parameter_defaults[id];
}
esp_err_t commit_params() {
const char* get_param_unit(param_idx_t id) {
if (id >= NUM_PARAMS) {
return "";
}
return parameter_units[id];
}
// ============================================================================
// STORAGE HELPER: Pack parameter value into buffer
// ============================================================================
static void pack_param(uint8_t *dest, param_idx_t id) {
param_type_e type = parameter_types[id];
switch(type) {
case PARAM_TYPE_u16:
memcpy(dest, &parameter_table[id].u16, 2);
break;
case PARAM_TYPE_i16:
memcpy(dest, &parameter_table[id].i16, 2);
break;
case PARAM_TYPE_u32:
memcpy(dest, &parameter_table[id].u32, 4);
break;
case PARAM_TYPE_i32:
memcpy(dest, &parameter_table[id].i32, 4);
break;
case PARAM_TYPE_f32:
memcpy(dest, &parameter_table[id].f32, 4);
break;
case PARAM_TYPE_f64:
memcpy(dest, &parameter_table[id].f64, 8);
break;
case PARAM_TYPE_str:
memcpy(dest, parameter_table[id].str, 16);
break;
}
}
// ============================================================================
// STORAGE HELPER: Unpack parameter value from buffer
// ============================================================================
static void unpack_param(const uint8_t *src, param_idx_t id) {
param_type_e type = parameter_types[id];
switch(type) {
case PARAM_TYPE_u16:
memcpy(&parameter_table[id].u16, src, 2);
break;
case PARAM_TYPE_i16:
memcpy(&parameter_table[id].i16, src, 2);
break;
case PARAM_TYPE_u32:
memcpy(&parameter_table[id].u32, src, 4);
break;
case PARAM_TYPE_i32:
memcpy(&parameter_table[id].i32, src, 4);
break;
case PARAM_TYPE_f32:
memcpy(&parameter_table[id].f32, src, 4);
break;
case PARAM_TYPE_f64:
memcpy(&parameter_table[id].f64, src, 8);
break;
case PARAM_TYPE_str:
memcpy(parameter_table[id].str, src, 16);
parameter_table[id].str[15] = '\0'; // Ensure null termination
break;
}
}
// ============================================================================
// COMMIT PARAMETERS TO FLASH
// ============================================================================
esp_err_t commit_params(void) {
if (storage_partition == NULL) {
ESP_LOGE(TAG, "Storage partition not initialized");
return ESP_FAIL;
}
// Prepare storage buffer with parameters and CRCs
param_stored_t params_to_store[NUM_PARAMS];
set_param_string(PARAM_BUILD_VERSION, FIRMWARE_VERSION);
// Calculate flash offset for each parameter
uint32_t flash_offset = PARAMS_OFFSET;
// Erase the parameter sectors
esp_err_t err = esp_partition_erase_range(storage_partition, PARAMS_OFFSET,
LOG_START_OFFSET);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase parameter sectors: %s", esp_err_to_name(err));
return ESP_FAIL;
}
// Write each parameter with its CRC
for (int i = 0; i < NUM_PARAMS; i++) {
params_to_store[i].val = parameter_table[i];
// Calculate CRC32 for each parameter value
params_to_store[i].crc = esp_crc32_le(0, (uint8_t*)&parameter_table[i], sizeof(param_value_t));
}
// Erase the first sector (4096 bytes)
esp_err_t err = esp_partition_erase_range(storage_partition, PARAMS_OFFSET, FLASH_SECTOR_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase parameter sector: %s", esp_err_to_name(err));
return ESP_FAIL;
}
// Write parameters to flash
err = esp_partition_write(storage_partition, PARAMS_OFFSET, params_to_store, PARAMS_TOTAL_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write parameters: %s", esp_err_to_name(err));
return ESP_FAIL;
param_stored_t stored;
memset(&stored, 0, sizeof(stored));
// Pack the parameter value
uint8_t size = param_type_size(parameter_types[i]);
pack_param(stored.data, i);
// Calculate CRC over the actual data size used
uint32_t crc_input = PARAM_CRC_SALT;
//uint32_t crc = esp_crc32_le(0, (uint8_t*)&crc_input, sizeof(crc_input));
stored.crc = esp_crc32_le(crc_input, stored.data, size);
// Write to flash
err = esp_partition_write(storage_partition, flash_offset,
&stored, sizeof(param_stored_t));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write parameter %d (%s): %s",
i, parameter_names[i], esp_err_to_name(err));
return ESP_FAIL;
}
flash_offset += sizeof(param_stored_t);
}
ESP_LOGI(TAG, "Parameters committed to flash successfully");
return ESP_OK;
}
// ============================================================================
// FACTORY RESET
// ============================================================================
esp_err_t factory_reset(void) {
if (storage_partition == NULL) {
ESP_LOGE(TAG, "Storage partition not initialized");
return ESP_ERR_INVALID_STATE;
}
ESP_LOGW(TAG, "FACTORY RESET: Erasing entire storage partition...");
// Erase the entire storage partition
esp_err_t err = esp_partition_erase_range(storage_partition, 0, storage_partition->size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase storage partition: %s", esp_err_to_name(err));
return err;
}
ESP_LOGI(TAG, "Storage partition erased successfully");
// Reset all parameters to defaults in RAM
for (int i = 0; i < NUM_PARAMS; i++) {
memcpy(&parameter_table[i], &parameter_defaults[i], sizeof(param_value_t));
}
// Reset log indices
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
log_head_index = 0;
log_tail_index = 0;
if (log_mutex) xSemaphoreGive(log_mutex);
ESP_LOGI(TAG, "Factory reset complete - all data erased");
return ESP_OK;
}
// ============================================================================
// INITIALIZATION FUNCTIONS
// ============================================================================
@@ -148,31 +390,44 @@ esp_err_t storage_init(void) {
return ESP_ERR_NOT_FOUND;
}
ESP_LOGI(TAG, "Storage partition found: size=%lu bytes", (unsigned long)storage_partition->size);
ESP_LOGI(TAG, "Storage partition found: size=%lu bytes",
(unsigned long)storage_partition->size);
// Load parameters from flash
param_stored_t params_stored[NUM_PARAMS];
esp_err_t err = esp_partition_read(storage_partition, PARAMS_OFFSET,
params_stored, PARAMS_TOTAL_SIZE);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to read parameters, using defaults");
return err;
}
// Validate and load each parameter
uint32_t flash_offset = PARAMS_OFFSET;
bool all_valid = true;
for (int i = 0; i < NUM_PARAMS; i++) {
uint32_t calculated_crc = esp_crc32_le(0, (uint8_t*)&params_stored[i].val,
sizeof(param_value_t));
param_stored_t stored;
if (calculated_crc == params_stored[i].crc) {
parameter_table[i] = params_stored[i].val;
esp_err_t err = esp_partition_read(storage_partition, flash_offset,
&stored, sizeof(param_stored_t));
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to read parameter %d (%s), using default",
i, parameter_names[i]);
memcpy(&parameter_table[i], &parameter_defaults[i], sizeof(param_value_t)); // SET DEFAULT HERE
all_valid = false;
flash_offset += sizeof(param_stored_t);
continue;
}
// Validate CRC over actual data size
uint8_t size = param_type_size(parameter_types[i]);
uint32_t crc_input = PARAM_CRC_SALT;
//uint32_t crc = esp_crc32_le(0, (uint8_t*)&crc_input, sizeof(crc_input));
uint32_t calculated_crc = esp_crc32_le(crc_input, stored.data, size);
if (calculated_crc == stored.crc) {
unpack_param(stored.data, i);
} else {
ESP_LOGW(TAG, "Parameter %d (%s) failed CRC check, using default",
i, parameter_names[i]);
memcpy(&parameter_table[i], &parameter_defaults[i], sizeof(param_value_t)); // SET DEFAULT HERE
all_valid = false;
}
flash_offset += sizeof(param_stored_t);
}
if (all_valid) {
@@ -185,7 +440,7 @@ esp_err_t storage_init(void) {
}
// ============================================================================
// LOGGING FUNCTIONS
// LOGGING FUNCTIONS (unchanged from original)
// ============================================================================
static esp_err_t find_log_head(void) {
@@ -193,16 +448,13 @@ static esp_err_t find_log_head(void) {
return ESP_ERR_INVALID_STATE;
}
// Calculate total log area size
uint32_t log_area_size = storage_partition->size - LOG_START_OFFSET;
uint32_t max_entries = log_area_size / LOG_ENTRY_SIZE;
// Read through entries to find first uninitialized (all 0xFF)
uint8_t entry[LOG_ENTRY_SIZE];
uint8_t empty_entry[LOG_ENTRY_SIZE];
memset(empty_entry, 0xFF, LOG_ENTRY_SIZE);
// Binary search would be faster, but linear is safer for circular buffer
for (uint32_t i = 0; i < max_entries; i++) {
uint32_t offset = LOG_START_OFFSET + (i * LOG_ENTRY_SIZE);
@@ -212,7 +464,6 @@ static esp_err_t find_log_head(void) {
return err;
}
// Check if this entry is uninitialized
if (memcmp(entry, empty_entry, LOG_ENTRY_SIZE) == 0) {
log_head_index = i;
ESP_LOGI(TAG, "Log head found at index %lu", (unsigned long)log_head_index);
@@ -220,11 +471,9 @@ static esp_err_t find_log_head(void) {
}
}
// If we get here, all entries are full - wrap to beginning
log_head_index = 0;
ESP_LOGI(TAG, "Log is full, wrapping to beginning");
// Erase the first log sector to start fresh
esp_err_t err = esp_partition_erase_range(storage_partition, LOG_START_OFFSET,
FLASH_SECTOR_SIZE);
if (err != ESP_OK) {
@@ -241,8 +490,16 @@ esp_err_t log_init(void) {
return ESP_ERR_INVALID_STATE;
}
log_mutex = xSemaphoreCreateMutex();
if (log_mutex == NULL) {
ESP_LOGE(TAG, "Failed to create log mutex");
return ESP_ERR_NO_MEM;
}
esp_err_t err = find_log_head();
if (err != ESP_OK) {
vSemaphoreDelete(log_mutex);
log_mutex = NULL;
return err;
}
@@ -256,64 +513,121 @@ esp_err_t write_log(char* entry) {
return ESP_FAIL;
}
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
uint32_t log_area_end = storage_partition->size;
uint32_t max_entries = (log_area_end - LOG_START_OFFSET) / LOG_ENTRY_SIZE;
//uint32_t max_sectors = max_entries / FLASH_SECTOR_SIZE;
// Calculate current offset
uint32_t current_offset = LOG_START_OFFSET + (log_head_index * LOG_ENTRY_SIZE);
// Check if we need to erase the next sector
uint32_t current_sector = current_offset / FLASH_SECTOR_SIZE;
uint32_t next_offset = current_offset + LOG_ENTRY_SIZE;
if (next_offset >= log_area_end)
next_offset = LOG_START_OFFSET;
if (next_offset >= log_area_end) {
next_offset = LOG_START_OFFSET;
}
uint32_t current_sector = current_offset / FLASH_SECTOR_SIZE;
uint32_t next_sector = next_offset / FLASH_SECTOR_SIZE;
// If we're crossing into a new sector, check if it needs erasing
if (next_sector != current_sector) {
// Check if next sector is uninitialized
uint8_t check_byte;
esp_err_t err = esp_partition_read(storage_partition, next_sector * FLASH_SECTOR_SIZE,
&check_byte, 1);
if (err == ESP_OK && check_byte != 0xFF) {
// Sector needs erasing
ESP_LOGI(TAG, "Erasing sector %lu for log", (unsigned long)next_sector);
err = esp_partition_erase_range(storage_partition,
next_sector * FLASH_SECTOR_SIZE,
FLASH_SECTOR_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase sector: %s", esp_err_to_name(err));
if (log_mutex) xSemaphoreGive(log_mutex);
return ESP_FAIL;
}
} else if (err == ESP_OK) {
ESP_LOGI(TAG, "Next sector %ld clear, no erasing needed", (unsigned long)next_sector);
} else {
ESP_LOGE(TAG, "Error checking byte (sector %ld)", (unsigned long)next_sector);
}
uint32_t tail_offset = next_sector * FLASH_SECTOR_SIZE;
if (tail_offset < LOG_START_OFFSET) {
tail_offset = LOG_START_OFFSET;
}
log_tail_index = (tail_offset - LOG_START_OFFSET) / LOG_ENTRY_SIZE;
if (log_tail_index >= max_entries) {
log_tail_index = 0;
}
ESP_LOGI(TAG, "Tail/Head are now %ld/%ld", (long)log_tail_index, (long)log_head_index);
}
}
// Write the log entry
esp_err_t err = esp_partition_write(storage_partition, current_offset, entry, LOG_ENTRY_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write log entry: %s", esp_err_to_name(err));
if (log_mutex) xSemaphoreGive(log_mutex);
return ESP_FAIL;
}
//ESP_LOGI(TAG, "Log @ sector %lu / index %lu / offset %lu", (unsigned long) current_sector, (unsigned long) log_head_index, (unsigned long)current_offset);
log_head_index++;
if (log_head_index >= max_entries) {
log_head_index = 0;
ESP_LOGI(TAG, "Log wrapped to beginning");
}
if (log_mutex) xSemaphoreGive(log_mutex);
return ESP_OK;
}
esp_err_t write_dummy_log_1(void) {
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
log_head_index = 0;
log_tail_index = 0;
if (log_mutex) xSemaphoreGive(log_mutex);
uint32_t log_area_end = storage_partition->size;
uint32_t max_entries = (log_area_end - LOG_START_OFFSET) / LOG_ENTRY_SIZE;
for (uint32_t i=0; i<max_entries*3/2; i++) {
ESP_LOGI(TAG, "log[%ld]", (long)i);
char entry[32] = {32, i>>24,i>>16,i>>8,i>>0};
write_log(entry);
}
return ESP_OK;
}
esp_err_t write_dummy_log_2(void) {
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
log_head_index = 56;
log_tail_index = 105;
if (log_mutex) xSemaphoreGive(log_mutex);
uint32_t log_area_end = storage_partition->size;
uint32_t max_entries = (log_area_end - LOG_START_OFFSET) / LOG_ENTRY_SIZE;
for (uint32_t i=0; i<max_entries*3/2; i++) {
ESP_LOGI(TAG, "log[%ld]", (long)i);
char entry[32] = {32, i>>24,i>>16,i>>8,i>>0};
write_log(entry);
}
return ESP_OK;
}
esp_err_t write_dummy_log_3(void) {
if (log_mutex) xSemaphoreTake(log_mutex, portMAX_DELAY);
log_head_index = 105;
log_tail_index = 34;
if (log_mutex) xSemaphoreGive(log_mutex);
uint32_t log_area_end = storage_partition->size;
uint32_t max_entries = (log_area_end - LOG_START_OFFSET) / LOG_ENTRY_SIZE;
for (uint32_t i=0; i<max_entries*3/2; i++) {
ESP_LOGI(TAG, "log[%ld]", (long)i);
char entry[32] = {32, i>>24,i>>16,i>>8,i>>0};
write_log(entry);
}
return ESP_OK;
}
void storage_deinit(void) {
storage_partition = NULL;
log_initialized = false;
if (log_mutex) {
vSemaphoreDelete(log_mutex);
log_mutex = NULL;
}
}

View File

@@ -1,5 +1,5 @@
/*
* storage.h
* storage.h - Simple variable-size parameter storage with per-param CRC
*
* Created on: Nov 5, 2025
* Author: Thad
@@ -17,127 +17,164 @@
#include <stdarg.h>
#include "i2c.h"
#define PARAM_CRC_SALT 0xDEADBEEF // Salt to prevent all-zero CRC collision
// Union for parameter values - now sized appropriately
typedef union {
uint8_t u8;
int8_t i8;
uint16_t u16;
int16_t i16;
uint32_t u32;
int32_t i32;
uint64_t u64;
int64_t i64;
float f32;
double f64;
char str[16]; // 15 chars + null terminator
} param_value_t;
typedef struct {
param_value_t val;
uint32_t crc;
// Enum for parameter types
typedef enum {
PARAM_TYPE_u16 = 0,
PARAM_TYPE_i16 = 1,
PARAM_TYPE_u32 = 2,
PARAM_TYPE_i32 = 3,
PARAM_TYPE_f32 = 6,
PARAM_TYPE_f64 = 7,
PARAM_TYPE_str = 8
} param_type_e;
// Storage format: each param stored as [data][crc32]
typedef struct __attribute__((packed)) {
uint8_t data[16]; // Max size needed (for strings)
uint32_t crc; // CRC of actual data bytes used
} param_stored_t;
typedef enum {
PARAM_TYPE_u8,
PARAM_TYPE_i8,
PARAM_TYPE_u16,
PARAM_TYPE_i16,
PARAM_TYPE_u32,
PARAM_TYPE_i32,
PARAM_TYPE_u64,
PARAM_TYPE_i64,
PARAM_TYPE_f32,
PARAM_TYPE_f64
} param_type_e;
// Get storage size for a given type (data only, not including CRC)
static inline uint8_t param_type_size(param_type_e type) {
switch(type) {
case PARAM_TYPE_u16:
case PARAM_TYPE_i16: return 2;
case PARAM_TYPE_u32:
case PARAM_TYPE_i32:
case PARAM_TYPE_f32: return 4;
case PARAM_TYPE_f64: return 8;
case PARAM_TYPE_str: return 16;
default: return 8; // Fallback
}
}
// ============================================================================
// PARAMETER DEFINITION MACRO
// ============================================================================
// Usage: PARAM_DEF(NAME, TYPE, DEFAULT_VALUE)
// Usage: PARAM_DEF(NAME, TYPE, DEFAULT_VALUE, UNIT)
//
// Examples:
// PARAM_DEF(NUM_MOVES, u32, 0)
// PARAM_DEF(EFUSE_1_AS, u16, 2400)
// PARAM_DEF(JACK_DIST, u8, 5)
// PARAM_DEF(KEYCODE_0, i64, -1)
// PARAM_DEF(TEMPERATURE, f32, 25.5)
// PARAM_DEF(NUM_MOVES, u32, 0, "")
// PARAM_DEF(EFUSE_1_AS, u16, 2400, "mA")
// PARAM_DEF(KEYCODE_0, i64, -1, "")
// PARAM_DEF(TEMPERATURE, f32, 25.5, "C")
// PARAM_DEF(DEVICE_NAME, str, "ESP32", "")
// ============================================================================
// REMEMBER: ORDER IS IMPERATIVE! PARAMETERS ARE ENTERED IN THE TABLE BY INDEX!
// ============================================================================
#define PARAM_LIST \
PARAM_DEF(NUM_MOVES, u32, 0) \
PARAM_DEF(MOVE_START, u32, 0) \
PARAM_DEF(MOVE_END, u32, 0) \
PARAM_DEF(DRIVE_DIST, u16, 10) /*3*/\
PARAM_DEF(JACK_DIST, u8, 5) \
PARAM_DEF(DRIVE_TPDF, u16, 4000) \
PARAM_DEF(DRIVE_MSPF, u16, 600) \
PARAM_DEF(JACK_MSPI, u16, 600) /*7*/\
PARAM_DEF(KEYCODE_0, i64, 0x19000000005D0C61) \
PARAM_DEF(KEYCODE_1, i64, 0x19000000005D0C62) \
PARAM_DEF(KEYCODE_2, i64, 0x19000000005D0C64) \
PARAM_DEF(KEYCODE_3, i64, 0x19000000005D0C68) /*11*/\
PARAM_DEF(KEYCODE_4, i64, -1) \
PARAM_DEF(KEYCODE_5, i64, -1) \
PARAM_DEF(KEYCODE_6, i64, -1) \
PARAM_DEF(KEYCODE_7, i64, -1) /*15*/\
PARAM_DEF(ADC_ALPHA_BATTERY, f32, 0.02) \
PARAM_DEF(ADC_ALPHA_ISENS, f32, 0.02) \
PARAM_DEF(ADC_ALPHA_IAZ, f32, 0.005) \
PARAM_DEF(ADC_DB_IAZ, f32, 5.0) /*19*/\
PARAM_DEF(EFUSE_INOM_1, f32, 40.0) \
PARAM_DEF(EFUSE_INOM_2, f32, 6.0) \
PARAM_DEF(EFUSE_INOM_3, f32, 2.0) \
PARAM_DEF(EFUSE_HEAT_THRESH, f32, 60.0) /*23*/\
PARAM_DEF(EFUSE_KINST, f32, 4.0) \
PARAM_DEF(EFUSE_TAUCOOL, f32, 0.2) \
PARAM_DEF(EFUSE_TCOOL, i64, 5000000) \
PARAM_DEF(LOW_PROTECTION_V, f32, 10.0) /*27*/\
PARAM_DEF(LOW_PROTECTION_S, i64, 10) \
PARAM_DEF(CHG_LOW_V, f32, 5.0) \
PARAM_DEF(CHG_LOW_S, i64, 5.0) \
PARAM_DEF(CHG_BULK_S, i64, 20) /*31*/\
PARAM_DEF(RF_PULSE_LENGTH, u64, 350000) \
PARAM_DEF(BOOT_TIME, i32, 0, "us") \
PARAM_DEF(NUM_MOVES, u32, 0, "") \
PARAM_DEF(MOVE_START, u32, 0, "s") \
PARAM_DEF(MOVE_END, u32, 0, "s") \
PARAM_DEF(DRIVE_DIST, f32, 10, "ft") \
PARAM_DEF(JACK_DIST, f32, 5, "in") \
PARAM_DEF(DRIVE_KE, f32, 29.2, "n/ft") \
PARAM_DEF(DRIVE_KT, f32, 2880000, "us/ft") \
PARAM_DEF(JACK_KT, f32, 1428571, "ms/in") \
PARAM_DEF(KEYCODE_0, u32, 0, "") \
PARAM_DEF(KEYCODE_1, u32, 0, "") \
PARAM_DEF(KEYCODE_2, u32, 0, "") \
PARAM_DEF(KEYCODE_3, u32, 0, "") \
PARAM_DEF(KEYCODE_4, u32, 0, "") \
PARAM_DEF(KEYCODE_5, u32, 0, "") \
PARAM_DEF(KEYCODE_6, u32, 0, "") \
PARAM_DEF(KEYCODE_7, u32, 0, "") \
PARAM_DEF(ADC_ALPHA_BATTERY, f32, 0.5, "-") \
PARAM_DEF(ADC_ALPHA_ISENS, f32, 0.6, "-") \
PARAM_DEF(ADC_ALPHA_IAZ, f32, 0.005, "-") \
PARAM_DEF(ADC_DB_IAZ, f32, 5.0, "A") \
PARAM_DEF(EFUSE_INOM_1, f32, 40.0, "A") \
PARAM_DEF(EFUSE_INOM_2, f32, 6.0, "A") \
PARAM_DEF(EFUSE_INOM_3, f32, 4.0, "A") \
PARAM_DEF(EFUSE_HEAT_THRESH, f32, 60.0, "i/i^2-s") \
PARAM_DEF(EFUSE_KINST, f32, 5.0, "i/i") \
PARAM_DEF(EFUSE_TAUCOOL, f32, 0.2, "i") \
PARAM_DEF(EFUSE_TCOOL, u32, 5000000, "us") \
PARAM_DEF(LOW_PROTECTION_V, f32, 10.0, "V") \
PARAM_DEF(LOW_PROTECTION_S, u32, 10, "s") \
PARAM_DEF(CHG_LOW_V, f32, 5.0, "V") \
PARAM_DEF(CHG_LOW_S, u32, 5, "s") \
PARAM_DEF(CHG_BULK_S, u32, 20, "s") \
PARAM_DEF(RF_PULSE_LENGTH, u32, 350000, "us") \
PARAM_DEF(V_SENS_OFFSET, f32, 0.4, "V") \
PARAM_DEF(WIFI_CHANNEL, u16, 6, "") \
PARAM_DEF(WIFI_SSID, str, "sc.local", "") \
PARAM_DEF(WIFI_PASS, str, "password", "") \
PARAM_DEF(EFUSE_INRUSH_US, u32, 300000, "us") \
PARAM_DEF(JACK_I_UP, f32, 5.0, "A") \
PARAM_DEF(JACK_I_DOWN, f32, 8.0, "A") \
PARAM_DEF(V_SENS_K, f32, 0.00766666666, "V/mV") \
PARAM_DEF(BUILD_VERSION, str, "undefined", "") \
// Generate enum for parameter indices
#define PARAM_DEF(name, type, default_val) PARAM_##name,
#define PARAM_DEF(name, type, default_val, unit) PARAM_##name,
typedef enum {
PARAM_LIST
NUM_PARAMS
} param_idx_t;
#undef PARAM_DEF
#define PARAMS_SIZE sizeof(param_stored_t)
#define FLASH_SECTOR_SIZE 4096
#define PARAMS_OFFSET 0
#define PARAMS_TOTAL_SIZE (NUM_PARAMS * PARAMS_SIZE)
#define PARAMETER_NUM_SECTORS 4
#define LOG_START_OFFSET (FLASH_SECTOR_SIZE * PARAMETER_NUM_SECTORS)
// External declarations
extern param_value_t parameter_table[NUM_PARAMS];
extern const param_value_t parameter_defaults[NUM_PARAMS];
extern const param_type_e parameter_types[NUM_PARAMS];
extern const char* parameter_names[NUM_PARAMS];
extern const char parameter_units[NUM_PARAMS][8];
esp_err_t storage_init();
esp_err_t log_init();
// Core functions
esp_err_t storage_init(void);
esp_err_t log_init(void);
void storage_deinit(void);
// Parameter access functions
param_value_t get_param_value_t(param_idx_t id);
esp_err_t set_param_value_t(param_idx_t id, param_value_t val);
param_type_e get_param_type(param_idx_t id);
const char* get_param_name(param_idx_t id);
param_value_t get_param_default(param_idx_t id);
const char* get_param_unit(param_idx_t id);
const char* get_param_json_string(param_idx_t id, char* buffer, size_t buf_size);
esp_err_t commit_params();
// Helper functions for string parameters
esp_err_t set_param_string(param_idx_t id, const char* str);
char* get_param_string(param_idx_t id);
// Storage operations
esp_err_t commit_params(void);
esp_err_t factory_reset(void);
// Log functions
#define LOG_ENTRY_SIZE 32
#define LOG_NUM_ENTRIES 512
#define FLASH_SECTOR_SIZE 4096
uint32_t get_log_head(void);
uint32_t get_log_tail(void);
uint32_t get_log_offset(void);
esp_err_t write_log(char* entry);
void storage_deinit();
// Test functions
esp_err_t write_dummy_log_1(void);
esp_err_t write_dummy_log_2(void);
esp_err_t write_dummy_log_3(void);
#endif /* MAIN_STORAGE_H_ */

View File

@@ -51,16 +51,9 @@ static bool parse_uint64(const char *str, uint64_t *result) {
static void print_param_value(param_idx_t id, param_value_t val) {
param_type_e type = get_param_type(id);
char sbuf[9] = {0};
switch (type) {
case PARAM_TYPE_u8:
printf("%u (0x%02X)\n", val.u8, val.u8);
break;
case PARAM_TYPE_i8:
printf("%d (0x%02X)\n",
val.i8, (uint8_t)val.i8);
break;
case PARAM_TYPE_u16:
printf("%u (0x%04X)\n",
val.u16, val.u16);
@@ -81,25 +74,21 @@ static void print_param_value(param_idx_t id, param_value_t val) {
(long)val.i32, (unsigned long)val.i32);
break;
case PARAM_TYPE_u64:
printf("%llu (0x%016llX)\n",
(unsigned long long)val.u64,
(unsigned long long)val.u64);
break;
case PARAM_TYPE_i64:
printf("%lld (0x%016llX)\n",
(long long)val.i64, (unsigned long long)val.i64);
break;
case PARAM_TYPE_f32:
printf("%.6f (0x%08lX as bits)\n",
val.f32, (unsigned long)val.u32);
break;
case PARAM_TYPE_f64:
printf("%.6f (0x%016llX as bits)\n",
val.f64, (unsigned long long)val.u64);
val.f64, (unsigned long long)val.f64);
break;
case PARAM_TYPE_str:
memcpy(val.str, sbuf, 8);
sbuf[8] = '\0';
printf("\"%s\"", sbuf);
break;
default:
@@ -114,7 +103,7 @@ static esp_err_t parse_param_value(const char *orig_str, param_type_e type, para
while (isspace((unsigned char)*str)) str++;
// Check for negative sign on unsigned integer types
bool is_unsigned_int = (type == PARAM_TYPE_u8 || type == PARAM_TYPE_u16 || type == PARAM_TYPE_u32 || type == PARAM_TYPE_u64);
bool is_unsigned_int = (type == PARAM_TYPE_u16 || type == PARAM_TYPE_u32);
if (is_unsigned_int && *str == '-') {
return ESP_FAIL;
}
@@ -123,18 +112,18 @@ static esp_err_t parse_param_value(const char *orig_str, param_type_e type, para
errno = 0;
switch (type) {
case PARAM_TYPE_u8:
case PARAM_TYPE_u16:
val->u16 = strtoull(str, &endptr, 0);
break;
case PARAM_TYPE_u32:
case PARAM_TYPE_u64:
val->u64 = strtoull(str, &endptr, 0);
val->u32 = strtoull(str, &endptr, 0);
break;
case PARAM_TYPE_i8:
case PARAM_TYPE_i16:
val->i16 = strtoll(str, &endptr, 0);
break;
case PARAM_TYPE_i32:
case PARAM_TYPE_i64:
val->i64 = strtoll(str, &endptr, 0);
val->i32 = strtoll(str, &endptr, 0);
break;
case PARAM_TYPE_f32:

37
main/version.cmake Normal file
View File

@@ -0,0 +1,37 @@
# version.cmake
execute_process(
COMMAND git describe --tags --always --dirty
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
OUTPUT_VARIABLE GIT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S" UTC)
# Fallback if not in a git repo
if(NOT GIT_VERSION)
set(GIT_VERSION "unknown")
endif()
if(NOT GIT_BRANCH)
set(GIT_BRANCH "unknown")
endif()
message(STATUS "Firmware Version: ${GIT_VERSION}")
message(STATUS "Git Branch: ${GIT_BRANCH}")
message(STATUS "Build Date: ${BUILD_DATE}")
configure_file(
${CMAKE_CURRENT_LIST_DIR}/version.h.in
${CMAKE_BINARY_DIR}/version.h
@ONLY
)

10
main/version.h.in Normal file
View File

@@ -0,0 +1,10 @@
// version.h.in
#ifndef VERSION_H
#define VERSION_H
#define FIRMWARE_VERSION "@GIT_VERSION@"
#define FIRMWARE_BRANCH "@GIT_BRANCH@"
#define BUILD_DATE "@BUILD_DATE@"
#define FIRMWARE_STRING "V_" FIRMWARE_VERSION " (" BUILD_DATE ")"
#endif

File diff suppressed because one or more lines are too long

383
main/webpage_brotli.h Normal file
View File

@@ -0,0 +1,383 @@
#ifndef HTML_CONTENT_BR_H
#define HTML_CONTENT_BR_H
#include <Arduino.h>
const unsigned char PROGMEM html_content_br[] = {
0x1b, 0x75, 0x60, 0x11, 0x95, 0xac, 0x8c, 0x00, 0x3a, 0x12, 0xca, 0xb6, 0x9b, 0x34, 0xff, 0x95,
0xc9, 0x8e, 0xfd, 0x65, 0x22, 0x13, 0x79, 0x9e, 0x98, 0xd2, 0x08, 0x49, 0x66, 0xff, 0xf7, 0x75,
0xfa, 0xdf, 0xcd, 0xcf, 0x97, 0x18, 0x75, 0x74, 0xc3, 0x45, 0x88, 0xd5, 0x61, 0x45, 0xa6, 0x34,
0x21, 0xdd, 0xa4, 0x99, 0x6f, 0xf0, 0x38, 0xbe, 0x06, 0x1b, 0xea, 0x13, 0x23, 0x38, 0x42, 0x59,
0x4f, 0x08, 0xfa, 0x5f, 0x8b, 0xb5, 0x72, 0xe1, 0x2c, 0x82, 0x76, 0x43, 0xb2, 0x5e, 0x34, 0xf8,
0xc1, 0x11, 0x17, 0x76, 0x19, 0xdc, 0x7d, 0x20, 0xc2, 0x53, 0x2e, 0xf7, 0xca, 0xde, 0xa1, 0x76,
0xa9, 0x29, 0x80, 0xdb, 0x18, 0x87, 0xaa, 0x2f, 0x19, 0x42, 0x75, 0xa5, 0xfd, 0xcc, 0xff, 0x3f,
0x5b, 0x96, 0x97, 0x81, 0x01, 0xa3, 0xcd, 0x2e, 0x48, 0x9c, 0x44, 0xcc, 0xd1, 0xc5, 0x4e, 0x42,
0xf5, 0x87, 0xaa, 0x55, 0x75, 0x57, 0xeb, 0xdd, 0xe0, 0xbb, 0x99, 0x91, 0xb4, 0x34, 0xd2, 0x01,
0xc0, 0xff, 0x55, 0xd5, 0x83, 0xda, 0x15, 0x2c, 0x8d, 0xb4, 0x7c, 0x00, 0x1c, 0x19, 0x5f, 0xee,
0x30, 0x04, 0x0a, 0x42, 0x67, 0x91, 0x64, 0x4e, 0x2f, 0x74, 0x66, 0xab, 0xbb, 0xce, 0xde, 0x45,
0x0a, 0xdf, 0xed, 0x31, 0x4a, 0x6b, 0x9f, 0xa4, 0x4f, 0xd2, 0xb4, 0x77, 0x9b, 0x19, 0x45, 0x44,
0x54, 0x14, 0x74, 0x9e, 0x36, 0xf4, 0x84, 0x57, 0x73, 0x64, 0x71, 0x43, 0xca, 0x02, 0xc4, 0x0c,
0x2e, 0x0b, 0x83, 0x18, 0x48, 0xc1, 0xf3, 0x18, 0x09, 0x4a, 0x34, 0xa7, 0xcb, 0x4d, 0xcc, 0x0f,
0x86, 0x7f, 0x7c, 0x92, 0x91, 0x7c, 0x83, 0x1e, 0x53, 0x3c, 0x16, 0x9d, 0x85, 0x2f, 0x3c, 0xfb,
0x18, 0xa5, 0x59, 0xc7, 0x5b, 0xab, 0x5f, 0x7c, 0x09, 0x8d, 0x90, 0xfb, 0x7e, 0x53, 0x2f, 0x19,
0x6a, 0xc3, 0x50, 0x52, 0x34, 0x8f, 0xd3, 0xd2, 0xd6, 0xd0, 0xc0, 0x4e, 0x2e, 0xc4, 0x38, 0x16,
0x48, 0x98, 0x04, 0x7d, 0x19, 0x4e, 0x7c, 0xb6, 0xad, 0xf5, 0x56, 0xff, 0x9d, 0xa2, 0x5a, 0x47,
0x98, 0xb3, 0x15, 0xbd, 0xfe, 0xf4, 0xe9, 0xd6, 0x6d, 0xd9, 0x4d, 0x66, 0xf6, 0x78, 0x5f, 0xd8,
0x3c, 0x33, 0x19, 0x2f, 0xab, 0x19, 0x1c, 0x6f, 0xec, 0xd9, 0x62, 0x13, 0x92, 0xa2, 0xc0, 0x24,
0x55, 0x58, 0x51, 0x86, 0x88, 0xc7, 0x3a, 0x86, 0xc7, 0xdd, 0x8b, 0x55, 0x91, 0x17, 0x1f, 0x55,
0x3e, 0x96, 0xc9, 0x77, 0xf7, 0x45, 0x7d, 0x7c, 0x37, 0x5c, 0x28, 0x00, 0x77, 0xb4, 0x95, 0x97,
0x8f, 0xfb, 0x38, 0x77, 0xc8, 0x01, 0x41, 0x74, 0xcb, 0x67, 0xc5, 0x1a, 0xbc, 0x6c, 0x9c, 0xe3,
0x42, 0x3a, 0x2c, 0x84, 0x31, 0x1f, 0xc9, 0xc3, 0x1b, 0xcd, 0x96, 0x9b, 0xd8, 0x37, 0xb0, 0x1d,
0x6c, 0x37, 0x62, 0x9a, 0x47, 0xce, 0x0d, 0xfe, 0x3b, 0x39, 0xc4, 0x91, 0xa9, 0xde, 0x0c, 0x82,
0xd3, 0xdb, 0xd8, 0x98, 0xa9, 0xff, 0xaa, 0x74, 0x11, 0x63, 0x89, 0x2b, 0x73, 0x54, 0x04, 0xfe,
0x2b, 0xf4, 0xb2, 0xc1, 0xc7, 0x7e, 0xee, 0x6c, 0x53, 0x68, 0x1c, 0xd1, 0xe3, 0xc0, 0xe0, 0x6c,
0x7f, 0xdf, 0xeb, 0xbf, 0x59, 0x44, 0xc6, 0x10, 0x60, 0x28, 0x37, 0xa8, 0x72, 0x8b, 0x68, 0x79,
0xc9, 0x60, 0xf8, 0x56, 0x15, 0xd4, 0x23, 0x32, 0x83, 0xd2, 0x02, 0x55, 0xde, 0x6e, 0x7b, 0x2f,
0x65, 0x68, 0xe5, 0x6c, 0x93, 0x1c, 0x36, 0x5c, 0xbd, 0xc6, 0x44, 0x67, 0xff, 0x87, 0xc3, 0x2b,
0x64, 0xfa, 0x46, 0xd7, 0x2e, 0x75, 0x63, 0xf1, 0xfa, 0xc9, 0xc1, 0x60, 0x80, 0x79, 0x7b, 0xa2,
0xcf, 0xe0, 0xe6, 0xc9, 0x44, 0x55, 0x14, 0x48, 0xde, 0xd8, 0xc1, 0xbd, 0x7c, 0xa1, 0x20, 0x79,
0x8b, 0x98, 0xce, 0xc6, 0x73, 0xff, 0x07, 0xf8, 0x50, 0xe2, 0x4c, 0xf4, 0x50, 0x65, 0x56, 0x89,
0xa7, 0x41, 0x8c, 0x41, 0x0d, 0xe8, 0xe7, 0x16, 0x7d, 0x5b, 0x6c, 0xe8, 0xf7, 0x2c, 0xb1, 0x1c,
0xb9, 0x4f, 0x07, 0x95, 0xb1, 0x3e, 0x32, 0x7c, 0xbc, 0xc6, 0x66, 0xad, 0x19, 0x6e, 0xe3, 0x9f,
0x4b, 0xa9, 0xed, 0x7b, 0xce, 0xb1, 0xfc, 0xf5, 0xed, 0x02, 0xb8, 0xf4, 0x85, 0x21, 0x48, 0x1c,
0x11, 0x94, 0x59, 0x7e, 0x5a, 0x5c, 0xf4, 0x95, 0x20, 0x21, 0x37, 0xd8, 0x60, 0x0a, 0x11, 0x0c,
0x32, 0x6f, 0x17, 0xd3, 0xbc, 0x58, 0x12, 0xd5, 0xd4, 0x32, 0xf8, 0x5c, 0x97, 0xea, 0x56, 0x92,
0xc0, 0x92, 0xdd, 0x29, 0x36, 0xaa, 0x4b, 0x55, 0xf3, 0x78, 0x89, 0xa4, 0x09, 0x4c, 0xf2, 0x76,
0xa4, 0xd4, 0x4e, 0xcb, 0x27, 0x01, 0x32, 0x13, 0x57, 0x8a, 0x12, 0xf3, 0x23, 0x03, 0x4b, 0x77,
0xac, 0xfa, 0xa1, 0xec, 0xa1, 0x6a, 0xe5, 0x7e, 0xb1, 0x64, 0x83, 0xdd, 0x0b, 0x7b, 0x77, 0x33,
0x05, 0xb6, 0x1a, 0xe4, 0x78, 0xcd, 0xb2, 0x3f, 0xbd, 0xf7, 0x34, 0x44, 0x54, 0x26, 0x99, 0x28,
0xc1, 0x1c, 0xf5, 0xee, 0xa2, 0x13, 0xfb, 0x23, 0xdd, 0xfc, 0x93, 0xac, 0x2a, 0x8f, 0x2c, 0x16,
0xe3, 0xd5, 0x39, 0xd5, 0x7d, 0x59, 0x6c, 0x6b, 0xf0, 0x15, 0x04, 0x25, 0x87, 0xac, 0x13, 0xe6,
0x87, 0x40, 0xf9, 0xb7, 0xeb, 0x2c, 0x63, 0xfb, 0x68, 0xb1, 0x2b, 0xbd, 0x6b, 0xb1, 0xe5, 0xf2,
0x09, 0xbe, 0xe2, 0x44, 0x32, 0x07, 0x5c, 0x9b, 0x68, 0xb3, 0x38, 0xb1, 0x46, 0x1f, 0xd3, 0xe5,
0x5e, 0xbc, 0xec, 0x9f, 0xb0, 0x8b, 0xff, 0x91, 0xc4, 0x6e, 0x8d, 0x75, 0x95, 0x35, 0x8a, 0xb4,
0x63, 0xbd, 0x90, 0x01, 0x54, 0xe8, 0xa3, 0x60, 0xff, 0x0e, 0x55, 0x6b, 0x81, 0x12, 0x6e, 0xf8,
0x35, 0xcc, 0xf2, 0xa6, 0x19, 0x8a, 0x07, 0xcb, 0xc3, 0xe3, 0xb2, 0x5e, 0xa1, 0x18, 0x47, 0x96,
0x9b, 0xd2, 0xf1, 0xf5, 0x7c, 0x12, 0x24, 0xd2, 0x99, 0x87, 0xb1, 0x5c, 0x7b, 0x80, 0x42, 0x21,
0xc5, 0xd2, 0xe4, 0x1e, 0x1f, 0xb2, 0xf7, 0x72, 0x30, 0x04, 0x80, 0x15, 0x9e, 0xda, 0x7e, 0xea,
0xd1, 0x78, 0x5e, 0xc7, 0x04, 0xd9, 0xcc, 0x24, 0x04, 0x4d, 0xfc, 0xf4, 0x4c, 0x3c, 0xfc, 0xda,
0x5d, 0x93, 0x3e, 0x92, 0x81, 0x3a, 0x62, 0xff, 0xad, 0xe0, 0xc8, 0xcf, 0x86, 0xfe, 0xf0, 0x32,
0xb5, 0x9f, 0x48, 0x56, 0x72, 0x96, 0x52, 0xa4, 0x18, 0x2d, 0xcd, 0x72, 0x9b, 0x5d, 0xc6, 0xfc,
0xea, 0x92, 0x96, 0x8a, 0x3b, 0x6b, 0x2a, 0x70, 0xf3, 0x1c, 0x5b, 0x25, 0xbc, 0x21, 0x41, 0x23,
0x22, 0x97, 0x49, 0xeb, 0xc5, 0x0a, 0xb5, 0x20, 0x4f, 0xb3, 0x9f, 0x3a, 0x3f, 0xde, 0x47, 0xb5,
0x05, 0x18, 0xa2, 0x7a, 0x78, 0x39, 0xf2, 0xa4, 0x79, 0x64, 0xdb, 0x1b, 0xa6, 0x39, 0x52, 0x0d,
0xd6, 0xe6, 0xed, 0xee, 0x12, 0xb4, 0x75, 0xad, 0x54, 0x43, 0xb9, 0x64, 0x1f, 0xdf, 0x07, 0x1a,
0xe0, 0x63, 0x3c, 0x9a, 0x7c, 0x8b, 0xf7, 0xea, 0xdd, 0xf4, 0xfc, 0xf2, 0x12, 0x13, 0xe8, 0x35,
0x59, 0xc4, 0x60, 0xe1, 0x8d, 0xfd, 0x64, 0xcc, 0xef, 0xca, 0x66, 0xad, 0x99, 0xb4, 0xa5, 0xa0,
0xa4, 0x7a, 0xeb, 0xdc, 0xfd, 0x9d, 0xee, 0xc7, 0xbb, 0x1c, 0x7a, 0x0c, 0x2d, 0x00, 0x9e, 0x5a,
0x94, 0x37, 0xbb, 0x07, 0x26, 0x51, 0x75, 0xdd, 0x35, 0x69, 0x83, 0x36, 0x02, 0xc8, 0xfd, 0x79,
0xbb, 0x91, 0xd0, 0x4c, 0x58, 0x3c, 0x01, 0x5a, 0xc7, 0xf8, 0x2c, 0x98, 0xd8, 0x28, 0xcb, 0xfd,
0x87, 0xfe, 0xd9, 0xb7, 0x4e, 0x7d, 0xe6, 0x90, 0xdc, 0xbc, 0x9a, 0x25, 0x5b, 0x18, 0x8d, 0x26,
0x27, 0xb5, 0x51, 0xb2, 0x62, 0x03, 0xcc, 0x12, 0x53, 0x63, 0x0c, 0x7b, 0x3a, 0x9f, 0xa7, 0x49,
0x2c, 0xf0, 0xa6, 0x7f, 0x1a, 0x08, 0x84, 0x3a, 0xe1, 0x0c, 0xf8, 0x26, 0x07, 0x76, 0xb6, 0x13,
0x93, 0xbf, 0x40, 0xdb, 0x7b, 0x48, 0x2f, 0x79, 0x63, 0xef, 0x36, 0x9e, 0x40, 0xe7, 0x3c, 0x7b,
0xbd, 0x74, 0xa7, 0x77, 0xbf, 0xbe, 0xde, 0xec, 0x1b, 0xb6, 0x29, 0x8d, 0x96, 0xef, 0x24, 0x6f,
0xc7, 0x1e, 0xa0, 0xc0, 0x85, 0xf7, 0xb3, 0x60, 0x63, 0xf7, 0x02, 0xbb, 0x78, 0x7f, 0xaf, 0x74,
0xfc, 0x73, 0x7c, 0xfd, 0x8b, 0x4f, 0xa2, 0xe9, 0x1a, 0x83, 0x4f, 0x3f, 0x3f, 0xcf, 0x46, 0x65,
0xfc, 0x04, 0xf8, 0x2a, 0x10, 0xd0, 0x2b, 0x5f, 0x4f, 0x7e, 0xa5, 0xe7, 0xdf, 0x6e, 0xd2, 0x87,
0x1a, 0xd9, 0x76, 0x34, 0x0c, 0xc0, 0x21, 0x16, 0xab, 0x1e, 0x35, 0x8c, 0x55, 0xd5, 0xb0, 0x71,
0x37, 0xf8, 0xe9, 0x75, 0xc8, 0xf4, 0x04, 0xab, 0x01, 0x52, 0x3d, 0x9c, 0x0d, 0x2f, 0x4d, 0xb0,
0x70, 0x6c, 0x01, 0xbb, 0xe2, 0xc2, 0x23, 0xc0, 0x8e, 0xcb, 0xef, 0x03, 0x64, 0xf5, 0x76, 0x62,
0xf6, 0xf6, 0x89, 0xba, 0xa9, 0x71, 0x7d, 0x71, 0x8a, 0x04, 0xd2, 0x4a, 0x22, 0x6e, 0xf9, 0xcf,
0x7d, 0x36, 0x27, 0x53, 0xb2, 0x04, 0x08, 0x7c, 0x20, 0xa8, 0x3c, 0x93, 0x71, 0x72, 0x61, 0xc4,
0x50, 0x4d, 0x0b, 0x04, 0xd0, 0xb4, 0x0d, 0x13, 0x11, 0x98, 0xed, 0x9e, 0xa2, 0xde, 0x93, 0x43,
0x1c, 0x54, 0x32, 0x08, 0x6e, 0x39, 0xb3, 0x39, 0xfc, 0x38, 0x34, 0xdb, 0xf0, 0xb1, 0xbf, 0x69,
0x31, 0xcd, 0x0b, 0xdd, 0xe1, 0x90, 0x58, 0x47, 0xf0, 0x8c, 0xc9, 0xf2, 0x5e, 0xcc, 0xff, 0x73,
0xcf, 0xc4, 0x5c, 0xc2, 0xed, 0x8d, 0x22, 0x39, 0x67, 0xe4, 0xfc, 0x33, 0x8e, 0x3b, 0x27, 0x32,
0xeb, 0x40, 0x25, 0xc5, 0x68, 0xc8, 0x73, 0xe4, 0x8a, 0x95, 0x0a, 0xd1, 0xf2, 0xce, 0xfc, 0x78,
0xf5, 0xcb, 0x8f, 0xd1, 0x24, 0xf2, 0x1a, 0xa8, 0x03, 0xfb, 0x73, 0xd0, 0x55, 0xba, 0x63, 0x2a,
0xdb, 0xb1, 0x0a, 0x35, 0x01, 0x60, 0x77, 0x94, 0xd3, 0x35, 0x0d, 0xdf, 0x4a, 0x58, 0xbb, 0xf9,
0x71, 0xf7, 0x66, 0x42, 0xda, 0x5e, 0x98, 0xfc, 0xbe, 0x92, 0x5c, 0xfc, 0x75, 0x1e, 0xeb, 0xca,
0xe8, 0x5f, 0x0d, 0x96, 0x27, 0x72, 0xbc, 0x7b, 0x7f, 0x5f, 0x6d, 0xd7, 0x93, 0xfb, 0x93, 0x7d,
0xdc, 0x5d, 0xc8, 0xed, 0x6e, 0xbe, 0x9d, 0xb2, 0xbb, 0xdf, 0x17, 0x58, 0x5c, 0xfd, 0x41, 0xae,
0x77, 0xf3, 0x03, 0x8e, 0xd1, 0x61, 0x60, 0xaf, 0x67, 0x9f, 0x3d, 0xbe, 0x5c, 0x94, 0xe3, 0x5d,
0xfd, 0x77, 0x76, 0x7c, 0xf7, 0x77, 0x41, 0x64, 0xb3, 0x76, 0x3c, 0x9f, 0xdf, 0xe1, 0x7c, 0x3c,
0xfd, 0x32, 0xb9, 0x3e, 0x3d, 0xd2, 0xea, 0x2d, 0x20, 0x48, 0xca, 0xc3, 0x07, 0xab, 0x2f, 0x6c,
0xb9, 0x94, 0x28, 0xa7, 0x15, 0x4b, 0x87, 0x10, 0x4f, 0xd8, 0x79, 0xcc, 0x06, 0xd4, 0xab, 0x04,
0x0d, 0xc0, 0x10, 0x35, 0xf9, 0x8e, 0x1d, 0xb3, 0x70, 0x45, 0x0b, 0xad, 0xd8, 0x35, 0x56, 0x94,
0xb3, 0xda, 0x98, 0x7b, 0x79, 0xa3, 0xf5, 0x01, 0xad, 0x4d, 0xfe, 0x0d, 0x03, 0x55, 0x1f, 0xf3,
0x39, 0xb3, 0x05, 0x04, 0xea, 0x0c, 0x3f, 0xac, 0xc5, 0xa2, 0xc9, 0x6c, 0x7e, 0xe5, 0xd4, 0xa5,
0xaa, 0x27, 0x92, 0x76, 0x95, 0x36, 0x0d, 0xab, 0x2d, 0x70, 0xb0, 0x29, 0x61, 0xb2, 0x1e, 0x75,
0x22, 0xeb, 0x77, 0x5a, 0xcc, 0x61, 0x63, 0xbe, 0xc1, 0x85, 0x25, 0xbb, 0xa6, 0xc6, 0x1a, 0x54,
0x28, 0x33, 0x17, 0x65, 0x85, 0xb0, 0x0c, 0x6d, 0x7b, 0xe5, 0xfa, 0xcd, 0xf4, 0x56, 0x67, 0x3a,
0xb8, 0x00, 0x8f, 0xf3, 0xd2, 0x79, 0xe6, 0xa9, 0xb5, 0x16, 0x73, 0x03, 0xc0, 0xc5, 0x27, 0x72,
0x65, 0xba, 0x9c, 0x5e, 0xb3, 0xe8, 0x68, 0x06, 0xdf, 0x2c, 0xa8, 0x5d, 0x4f, 0x0e, 0xef, 0xc4,
0xbc, 0x56, 0x0a, 0x0c, 0x35, 0x40, 0x83, 0xbd, 0xcc, 0xa1, 0xfe, 0x3e, 0x39, 0x76, 0x94, 0x68,
0x5e, 0x89, 0x75, 0xd4, 0xeb, 0xae, 0xac, 0xd0, 0x1a, 0xd1, 0x22, 0x1b, 0xb9, 0xde, 0xa8, 0xef,
0x1e, 0x35, 0x2e, 0x1b, 0x4e, 0x77, 0x0f, 0x56, 0x00, 0x64, 0x31, 0xb6, 0x83, 0x14, 0x69, 0x03,
0xc7, 0x38, 0xcb, 0xb2, 0xb3, 0xf6, 0x16, 0x47, 0x96, 0xe2, 0xc5, 0xd1, 0xd1, 0x9f, 0x46, 0xd1,
0xd3, 0xa9, 0x70, 0x43, 0x11, 0x0e, 0x6b, 0x1f, 0x50, 0x1f, 0x86, 0x83, 0x94, 0x79, 0x2b, 0x05,
0x87, 0x74, 0xad, 0x8b, 0x2a, 0x2d, 0x33, 0x4a, 0x3c, 0xd7, 0x2a, 0xa7, 0x9b, 0x37, 0xe6, 0x59,
0xb5, 0x11, 0x0d, 0x5c, 0x5a, 0xaa, 0x6d, 0xab, 0x7e, 0x40, 0x6c, 0x5d, 0xe8, 0x32, 0xd7, 0x89,
0x56, 0x63, 0xb5, 0xcc, 0x41, 0xe9, 0x42, 0x40, 0xff, 0xe1, 0x7e, 0xc1, 0xca, 0xcb, 0xc1, 0x89,
0xe4, 0x91, 0x26, 0x1e, 0x91, 0xee, 0xeb, 0xf5, 0x1c, 0x55, 0x28, 0x37, 0xe8, 0x16, 0xc0, 0x63,
0xca, 0x84, 0x5f, 0x93, 0x0e, 0x63, 0x9c, 0xf4, 0xe9, 0x12, 0x20, 0xf4, 0x93, 0x9a, 0x54, 0x9e,
0xd0, 0x69, 0x8b, 0x00, 0x8d, 0x8a, 0x7f, 0xc8, 0x31, 0x3c, 0x18, 0xe3, 0x8a, 0xbf, 0xf9, 0x6f,
0x66, 0x98, 0x5e, 0x76, 0x04, 0x78, 0xa2, 0xdf, 0x38, 0x43, 0x18, 0x18, 0x14, 0x68, 0x35, 0xcf,
0x8b, 0x04, 0x8a, 0x0e, 0xb0, 0xdf, 0x69, 0xac, 0xc9, 0x1a, 0xd3, 0x54, 0x87, 0xf5, 0x56, 0x35,
0xeb, 0xf5, 0x72, 0x43, 0x7e, 0x27, 0x52, 0x0a, 0x07, 0xb1, 0x6f, 0x31, 0xe2, 0xb8, 0x07, 0x34,
0x5c, 0x75, 0x4d, 0xf7, 0x5b, 0x49, 0x08, 0xe2, 0xae, 0xdb, 0x19, 0x40, 0xb8, 0x02, 0x94, 0x71,
0x9d, 0xc3, 0x81, 0x50, 0x11, 0x4e, 0x51, 0x1b, 0x16, 0xb8, 0xa2, 0xc2, 0x32, 0x73, 0xf1, 0xa9,
0x07, 0xde, 0xe8, 0x87, 0x1c, 0x50, 0x8f, 0x9e, 0xe4, 0xc1, 0xf6, 0x2f, 0x82, 0x4e, 0x30, 0xec,
0x3c, 0x5d, 0x6f, 0x06, 0x7a, 0x29, 0x01, 0xe2, 0xa8, 0x40, 0x0e, 0x2b, 0x29, 0x65, 0x35, 0x75,
0x3a, 0x44, 0x8a, 0x0f, 0x69, 0x35, 0xd3, 0x1c, 0x38, 0xd5, 0x2b, 0x0f, 0x87, 0xc4, 0x4a, 0x7b,
0xd2, 0x4f, 0x5f, 0xa1, 0xc4, 0xe8, 0xe4, 0x2f, 0x49, 0x4a, 0xbe, 0x02, 0x27, 0x4d, 0x02, 0x28,
0x3a, 0x95, 0x1a, 0x25, 0xcb, 0x96, 0xb1, 0xa4, 0x0b, 0xad, 0x19, 0x45, 0x8e, 0xb1, 0x8c, 0x83,
0x73, 0x12, 0x7b, 0x73, 0x96, 0xe7, 0x1c, 0x27, 0x96, 0x23, 0x4b, 0x51, 0x44, 0x36, 0x05, 0x90,
0x21, 0xad, 0xe3, 0x14, 0x9d, 0xb0, 0xde, 0x77, 0x01, 0x42, 0x48, 0x98, 0x1e, 0x44, 0xf8, 0xf1,
0x38, 0x8d, 0x46, 0x82, 0x93, 0x31, 0x95, 0xfb, 0x2f, 0x89, 0x22, 0x2d, 0xcb, 0x0a, 0x1e, 0x54,
0x0d, 0xb0, 0x54, 0xa8, 0x01, 0xaa, 0x56, 0x29, 0x93, 0xe7, 0x1a, 0x13, 0xa3, 0x4e, 0xcd, 0x5c,
0x2e, 0x31, 0x1e, 0x47, 0x02, 0xe1, 0xb5, 0x76, 0x94, 0xd6, 0x6c, 0x01, 0xf8, 0xc5, 0x76, 0x32,
0xf4, 0x0e, 0x21, 0x1c, 0x0e, 0x10, 0x20, 0x8d, 0x16, 0x1f, 0x78, 0xdd, 0xc8, 0xcd, 0x5f, 0xa1,
0xae, 0x3a, 0xf9, 0x72, 0xc5, 0xab, 0x78, 0xe0, 0x55, 0x9b, 0x31, 0x31, 0xae, 0xb0, 0x04, 0xbe,
0x50, 0xd3, 0x92, 0x6f, 0x26, 0xe5, 0x63, 0x7c, 0x8e, 0xd1, 0x01, 0xc2, 0x49, 0x9c, 0x7a, 0x66,
0xb9, 0xa5, 0xc5, 0x39, 0x45, 0x36, 0x80, 0xf2, 0x34, 0x32, 0x7a, 0xea, 0x45, 0x3a, 0xe0, 0xcb,
0x4e, 0x5c, 0x63, 0x70, 0xa4, 0x17, 0x7e, 0x8c, 0x44, 0x46, 0xd1, 0xca, 0xe9, 0x33, 0xb7, 0x3b,
0xb5, 0xbb, 0x73, 0x98, 0x2e, 0x98, 0xc2, 0xa8, 0x98, 0xd1, 0xe6, 0x6f, 0x12, 0xaf, 0xd4, 0xb0,
0x4c, 0xfd, 0x33, 0xa9, 0xf6, 0xc7, 0x7a, 0xf2, 0xa3, 0x67, 0x43, 0x8a, 0xd8, 0x64, 0x17, 0x85,
0x66, 0x50, 0xca, 0x71, 0x64, 0x9e, 0x85, 0x1f, 0x09, 0xd6, 0x8e, 0xa2, 0x9c, 0x42, 0xaa, 0x71,
0x34, 0x36, 0xa0, 0x2d, 0x11, 0xdd, 0xb1, 0xb0, 0x9c, 0x07, 0x5e, 0x85, 0x97, 0x13, 0x20, 0x04,
0xb1, 0x16, 0x58, 0x37, 0xa2, 0x59, 0xc2, 0x63, 0x20, 0x98, 0x71, 0x92, 0x3e, 0x47, 0x6f, 0x50,
0xb9, 0x2a, 0x09, 0x96, 0x12, 0xd0, 0xe3, 0xdf, 0xa6, 0x50, 0x95, 0x49, 0x4e, 0x1f, 0x7c, 0x3d,
0xe5, 0xd5, 0xb3, 0x84, 0xc2, 0x28, 0x3d, 0xcb, 0xb5, 0xfa, 0x6e, 0x76, 0x4b, 0xa2, 0xeb, 0x7f,
0x20, 0xbf, 0x62, 0xc0, 0x82, 0x0c, 0xf8, 0xe0, 0x1f, 0xd5, 0x48, 0xae, 0x44, 0xa8, 0xfa, 0x6d,
0x6a, 0x56, 0x95, 0x64, 0x18, 0xb6, 0x68, 0x78, 0x3e, 0x37, 0x25, 0x4d, 0x7e, 0x97, 0x22, 0x36,
0x27, 0x2f, 0x54, 0xf0, 0x9f, 0x35, 0xa1, 0x94, 0x90, 0xaf, 0xb5, 0x8e, 0xdd, 0x98, 0x33, 0x16,
0xad, 0x13, 0x1e, 0x52, 0x6d, 0xbf, 0xef, 0x06, 0x5c, 0xd9, 0xbe, 0xfc, 0xb1, 0xa2, 0x29, 0x91,
0xb7, 0xb3, 0xf4, 0x31, 0xc3, 0x1c, 0x02, 0xea, 0x38, 0x15, 0x4e, 0xf0, 0x85, 0x36, 0xf8, 0xb4,
0x8b, 0x2e, 0x03, 0x46, 0x0f, 0x42, 0x9d, 0xbc, 0xbb, 0x63, 0xd9, 0xdd, 0x24, 0x34, 0x1c, 0xc1,
0x0a, 0xf3, 0x94, 0x6a, 0xd0, 0xcd, 0xd4, 0x59, 0x85, 0x9e, 0x34, 0xa2, 0xe8, 0xc2, 0xb9, 0x75,
0xf3, 0xc8, 0x16, 0xe8, 0x54, 0xd9, 0xb6, 0x20, 0x5e, 0x43, 0xc9, 0xbf, 0xdb, 0x92, 0xa7, 0x8d,
0xad, 0x66, 0x82, 0xd5, 0x56, 0x6c, 0x18, 0x8b, 0x2f, 0x18, 0xe5, 0xb1, 0x38, 0xfe, 0x13, 0x51,
0x18, 0xb8, 0x34, 0x86, 0x02, 0x3d, 0x01, 0x74, 0x7e, 0xdd, 0x0c, 0x31, 0x59, 0x55, 0x44, 0xa0,
0x60, 0xce, 0x0f, 0x72, 0x73, 0x87, 0xd2, 0xac, 0x09, 0x31, 0x00, 0x02, 0xfb, 0xdf, 0xba, 0x65,
0xa3, 0x24, 0xc2, 0x86, 0x08, 0xb3, 0x30, 0x19, 0x81, 0xc1, 0xb6, 0x7b, 0xcb, 0xa1, 0x0d, 0x19,
0x1f, 0x7e, 0xc5, 0xdd, 0x84, 0x20, 0x5c, 0xcf, 0x0b, 0x60, 0xe3, 0x76, 0x59, 0x2f, 0xf0, 0xb9,
0x95, 0xa7, 0xe5, 0xd9, 0x94, 0xc0, 0xfa, 0xda, 0x3a, 0xdb, 0xa2, 0xdd, 0x06, 0x51, 0x51, 0xe4,
0xb3, 0xce, 0x67, 0x09, 0x94, 0xd7, 0xd9, 0x28, 0xe2, 0x4b, 0xb4, 0x98, 0x61, 0xd7, 0x8d, 0x00,
0x7a, 0xd7, 0x9e, 0x9a, 0x53, 0xe9, 0xbd, 0x0a, 0x0b, 0xe3, 0xbe, 0x35, 0x60, 0xcf, 0xe4, 0x6b,
0x84, 0x80, 0xbe, 0xe8, 0x53, 0xbe, 0x76, 0x9c, 0xe9, 0x2c, 0x62, 0x77, 0x30, 0xc3, 0xfc, 0x85,
0x53, 0x1d, 0xbb, 0xc1, 0x11, 0x72, 0xfb, 0x67, 0xdf, 0xab, 0xd1, 0x86, 0x4e, 0xda, 0xe5, 0x2f,
0xee, 0xff, 0x66, 0xb7, 0x36, 0x6e, 0xfd, 0xbf, 0xc8, 0x05, 0x71, 0x99, 0xd5, 0x86, 0xc4, 0x79,
0xaa, 0xc4, 0x63, 0x37, 0x76, 0x19, 0x06, 0x12, 0x38, 0xa0, 0x4e, 0xa9, 0x61, 0x36, 0x3f, 0x0d,
0xf4, 0x8c, 0xba, 0x90, 0x17, 0xa9, 0x39, 0xf3, 0x17, 0x78, 0x2e, 0x6b, 0x8a, 0xfc, 0x72, 0x35,
0xb9, 0xbc, 0xd5, 0xb5, 0x6e, 0xd3, 0x25, 0xe3, 0x4d, 0xc4, 0xa4, 0xd3, 0xed, 0x87, 0x03, 0xf7,
0xb8, 0x51, 0x62, 0xac, 0x79, 0x13, 0x7e, 0xb4, 0x0d, 0xb8, 0x16, 0x21, 0xf3, 0xd9, 0x5d, 0x55,
0x82, 0xf2, 0x19, 0xad, 0xe4, 0xc1, 0x58, 0x94, 0x4c, 0xc5, 0xcd, 0xd1, 0x25, 0xf2, 0xb1, 0x7f,
0x1c, 0x2d, 0x15, 0x27, 0xb7, 0x40, 0xa8, 0x70, 0xe3, 0xfa, 0xeb, 0xdf, 0xf2, 0x42, 0x5b, 0x44,
0x83, 0xbe, 0xff, 0xdf, 0x6c, 0x37, 0xb8, 0x09, 0x7c, 0x43, 0xda, 0xac, 0x78, 0x86, 0x7a, 0xce,
0x57, 0x6f, 0xc2, 0xe6, 0x9f, 0x3a, 0x65, 0xfa, 0x41, 0x4a, 0xd8, 0xa1, 0x96, 0x8d, 0xfe, 0x3f,
0xc1, 0x6d, 0xc7, 0x96, 0x85, 0x5c, 0xe0, 0x3d, 0xf7, 0xff, 0xfc, 0x6c, 0x14, 0x85, 0x65, 0x94,
0x11, 0x44, 0x73, 0x8b, 0x28, 0x62, 0x3c, 0xb1, 0x76, 0x0b, 0x40, 0x9c, 0x95, 0xcf, 0x48, 0x2e,
0x0a, 0xb8, 0x07, 0xd6, 0xfc, 0xce, 0xa1, 0x54, 0xfd, 0x63, 0x63, 0x1e, 0x83, 0x13, 0xd4, 0xf2,
0x4a, 0xe5, 0x1e, 0x5a, 0x2f, 0x5b, 0x68, 0x9a, 0x70, 0xb5, 0x91, 0x9b, 0x4b, 0x3a, 0x90, 0x38,
0xcb, 0x7e, 0x09, 0xc8, 0x67, 0x48, 0x9c, 0x23, 0x14, 0x45, 0x35, 0x86, 0xd3, 0x95, 0xa5, 0x63,
0x90, 0x21, 0x1a, 0x11, 0xd2, 0x50, 0xc3, 0xa2, 0x3d, 0xe6, 0x91, 0x8f, 0x7b, 0xe4, 0xd9, 0x04,
0x10, 0x45, 0x50, 0xa9, 0x43, 0x25, 0xe9, 0x4e, 0x9c, 0x45, 0x25, 0x18, 0xb9, 0xee, 0xb1, 0x36,
0xfb, 0x76, 0x94, 0xe1, 0xfc, 0x94, 0x66, 0xb7, 0x25, 0xbb, 0x63, 0x5b, 0x58, 0xdb, 0x82, 0x25,
0xe6, 0x94, 0x51, 0x90, 0xd8, 0xad, 0x52, 0xf9, 0x86, 0x8e, 0x23, 0x20, 0x1d, 0xb0, 0x12, 0x30,
0xa4, 0xb1, 0x9d, 0x2c, 0x34, 0xb0, 0x07, 0xae, 0x70, 0xcb, 0x3f, 0x1c, 0xd1, 0xb1, 0x9a, 0xec,
0x11, 0xb6, 0xef, 0xaa, 0xa0, 0xc3, 0x40, 0xb6, 0x41, 0x68, 0xc6, 0xda, 0xdf, 0xc7, 0xad, 0x5e,
0x5e, 0xe5, 0xed, 0xe3, 0xff, 0x1e, 0xc3, 0x7e, 0x12, 0xac, 0x71, 0x3b, 0x14, 0xf0, 0x90, 0xaa,
0x61, 0x53, 0xd5, 0x64, 0x5e, 0xfa, 0xf0, 0x72, 0xf5, 0xca, 0xdb, 0x72, 0xe4, 0x71, 0x4c, 0xb0,
0x71, 0x00, 0x69, 0x63, 0xb0, 0xb8, 0x51, 0x6c, 0x1f, 0xc0, 0x63, 0xa2, 0xa4, 0x00, 0xcf, 0xb0,
0xb6, 0xa5, 0xe3, 0xa6, 0xf7, 0x5d, 0x1c, 0xcf, 0x90, 0xbd, 0x1a, 0x77, 0x4d, 0x0f, 0x81, 0x4c,
0x36, 0xa7, 0x84, 0xc5, 0x30, 0xb1, 0x5b, 0x3b, 0xd0, 0x6c, 0xd4, 0x53, 0x42, 0xba, 0x4d, 0x1e,
0xd5, 0x46, 0xa3, 0x71, 0xa3, 0x15, 0xa9, 0x42, 0xdb, 0x83, 0x1c, 0xc1, 0x9c, 0xd6, 0xe7, 0x15,
0xbc, 0xd5, 0xee, 0xfb, 0x10, 0x95, 0x6b, 0x5e, 0x01, 0xd1, 0xe7, 0xf1, 0x2f, 0x68, 0x6b, 0x0a,
0x08, 0x37, 0x0b, 0x51, 0x27, 0x3f, 0x66, 0x20, 0x08, 0xcc, 0x2b, 0x7a, 0x58, 0xa1, 0x9d, 0x4e,
0x82, 0xab, 0x41, 0x18, 0x96, 0x02, 0xd6, 0xd0, 0x71, 0x7b, 0x2d, 0x8d, 0xe8, 0x7b, 0x9e, 0x3e,
0xfd, 0x68, 0x8c, 0x1d, 0xb2, 0x8e, 0x08, 0x1a, 0xe5, 0x08, 0xb4, 0x0c, 0xc6, 0x17, 0x0c, 0x05,
0x06, 0xe1, 0xd7, 0x9a, 0x1e, 0x72, 0x2b, 0x88, 0xfd, 0x76, 0x04, 0xde, 0xf8, 0xdb, 0x78, 0x91,
0xbd, 0xce, 0x43, 0xe3, 0x88, 0x08, 0x70, 0x22, 0x0d, 0x58, 0x66, 0x3e, 0xd8, 0x8e, 0x1d, 0x77,
0x20, 0x9c, 0xb3, 0x14, 0xc2, 0x49, 0x44, 0xae, 0x6b, 0xcb, 0x01, 0x9e, 0xc1, 0x92, 0x65, 0x0f,
0x81, 0xac, 0xb6, 0xc4, 0x85, 0xda, 0xe0, 0xe4, 0x01, 0x79, 0x68, 0xb0, 0x4c, 0x63, 0xd9, 0x45,
0xd7, 0x9a, 0x74, 0xfb, 0x19, 0xc6, 0x67, 0x3d, 0x3b, 0x61, 0x9f, 0xd2, 0xf4, 0x40, 0xf9, 0x6e,
0x49, 0x53, 0x76, 0xd2, 0x99, 0x49, 0x86, 0xe4, 0xb1, 0x9b, 0xf5, 0x09, 0x8e, 0x51, 0x44, 0x97,
0x40, 0x2d, 0xe7, 0xbe, 0x9e, 0x3b, 0xd0, 0xf4, 0xc6, 0x67, 0x21, 0x06, 0xa6, 0x46, 0x84, 0x53,
0xa1, 0x7c, 0xb3, 0x86, 0x76, 0xd3, 0x8f, 0x39, 0xe1, 0xf5, 0x78, 0x4a, 0x96, 0x1f, 0xa4, 0xad,
0x2e, 0x88, 0x5d, 0xe5, 0x67, 0x91, 0xe4, 0xf9, 0x02, 0x92, 0x24, 0xf7, 0x10, 0xf4, 0x71, 0xb8,
0x7e, 0x7e, 0xcd, 0x5b, 0x9c, 0xb4, 0x31, 0x7a, 0xfd, 0x9a, 0x31, 0x6f, 0xd1, 0xb5, 0xfa, 0x5f,
0xda, 0x84, 0xec, 0xe7, 0xdf, 0x44, 0x68, 0x07, 0x82, 0x94, 0x90, 0x75, 0x91, 0x5a, 0xff, 0x1a,
0x6b, 0x05, 0xbe, 0xa8, 0x3a, 0x76, 0x8a, 0xe5, 0x23, 0x33, 0xa0, 0xa9, 0xd0, 0xe9, 0xb6, 0x08,
0x76, 0x2c, 0x17, 0xfa, 0x67, 0xd1, 0x01, 0x0a, 0xdc, 0x91, 0xdf, 0x22, 0xc8, 0x44, 0x13, 0x02,
0x42, 0x6e, 0x9a, 0xc7, 0x97, 0x44, 0x21, 0x63, 0xb1, 0x8f, 0xc2, 0xf9, 0x45, 0x7b, 0xbd, 0x26,
0xeb, 0x91, 0x25, 0x42, 0x6c, 0x3c, 0xb2, 0xd4, 0x9a, 0x62, 0x64, 0x4b, 0xd8, 0x12, 0x64, 0xcb,
0x6a, 0xf9, 0x37, 0xd0, 0x5c, 0x0c, 0x09, 0x69, 0xf8, 0xbb, 0xaf, 0x97, 0x34, 0x64, 0xac, 0x45,
0xb1, 0x1f, 0x7a, 0x24, 0xe5, 0x27, 0x3c, 0x46, 0xc3, 0xa5, 0xb4, 0xe9, 0x1d, 0xce, 0x8a, 0x32,
0x37, 0xd4, 0x7f, 0x76, 0x84, 0x40, 0x40, 0x6f, 0xf3, 0xa5, 0xd7, 0x30, 0x64, 0xfe, 0xb2, 0xe0,
0x4c, 0xa1, 0xe7, 0x9c, 0x30, 0x89, 0x5c, 0x9e, 0xcf, 0x4e, 0xef, 0x2f, 0x1f, 0xd7, 0xaf, 0xd7,
0xce, 0x99, 0x8d, 0x79, 0xbc, 0xd1, 0x43, 0x89, 0x01, 0xb1, 0xa9, 0x1d, 0x1f, 0x86, 0xd0, 0x06,
0x15, 0x82, 0xe5, 0xf7, 0x5c, 0x60, 0xf5, 0x4f, 0xde, 0xc9, 0xd6, 0xfb, 0x3c, 0xe4, 0xd4, 0x83,
0x13, 0xf1, 0x80, 0xe3, 0x40, 0x5e, 0xc3, 0x45, 0xdf, 0x66, 0x8e, 0x87, 0x79, 0x11, 0x1c, 0x0e,
0x32, 0x29, 0x69, 0xd8, 0xc0, 0xf8, 0x4d, 0xb2, 0xdc, 0x99, 0xab, 0x5f, 0x69, 0xb9, 0xd0, 0xf7,
0x6c, 0xed, 0x54, 0x79, 0x9a, 0x6a, 0x76, 0xe0, 0x03, 0x0c, 0xa2, 0x69, 0x97, 0xab, 0x10, 0x4c,
0x46, 0xdd, 0xa2, 0xe9, 0xbd, 0x74, 0x66, 0x97, 0x39, 0x49, 0xe3, 0xc9, 0x90, 0xe4, 0x61, 0xa9,
0xe7, 0x35, 0xd0, 0xb3, 0x87, 0x43, 0xcd, 0x3b, 0x0a, 0x14, 0x66, 0xb5, 0xe4, 0x71, 0x33, 0x84,
0x44, 0x32, 0xfa, 0x9c, 0xd8, 0x36, 0x71, 0x68, 0xcc, 0x04, 0xbf, 0x2f, 0x6b, 0xb3, 0x24, 0x55,
0x71, 0x88, 0x25, 0xde, 0x47, 0xd9, 0xa8, 0x1e, 0xa9, 0x11, 0xa6, 0xf5, 0x3d, 0x3a, 0xfd, 0x77,
0x52, 0x42, 0xfa, 0xa6, 0x3b, 0xff, 0xc8, 0xdb, 0xe7, 0xe8, 0xbc, 0x93, 0xa0, 0x12, 0xa5, 0xad,
0x29, 0x07, 0x30, 0xcc, 0xe4, 0x82, 0x82, 0xc3, 0x8a, 0x95, 0x0f, 0xc4, 0x0b, 0x21, 0xe8, 0x61,
0x3c, 0xc2, 0xb5, 0xb7, 0x6e, 0xaf, 0xd0, 0x8d, 0x88, 0xc8, 0x39, 0xd4, 0x7e, 0xe1, 0xa8, 0x1b,
0x1a, 0x92, 0x73, 0xd7, 0xad, 0x1d, 0x57, 0x15, 0x07, 0xb4, 0xa1, 0x39, 0x56, 0x7f, 0xbc, 0x40,
0xf3, 0x4a, 0xd6, 0x1b, 0x0e, 0xea, 0x2d, 0xea, 0x17, 0xad, 0xd5, 0x6b, 0x23, 0x6f, 0x45, 0xb7,
0xa0, 0x38, 0x0d, 0xa7, 0xb4, 0xd4, 0x30, 0x08, 0xa5, 0xa5, 0x32, 0xd7, 0xd9, 0x14, 0x42, 0xc9,
0xa8, 0xb6, 0x59, 0x7d, 0x40, 0xce, 0x96, 0xf2, 0xe8, 0x3a, 0x63, 0x8c, 0x33, 0xfb, 0x7b, 0xc0,
0x1f, 0x99, 0xd5, 0x87, 0xd4, 0x10, 0x39, 0x8e, 0xcf, 0xe1, 0x55, 0x5c, 0x39, 0xf4, 0xf1, 0x50,
0xaf, 0x4b, 0xa9, 0x0a, 0xc8, 0x31, 0x35, 0x68, 0x31, 0x17, 0xf6, 0x36, 0xa1, 0x79, 0x8d, 0x2d,
0xd8, 0x3d, 0xb6, 0xd4, 0x79, 0x9d, 0x2d, 0xc4, 0xbd, 0x8a, 0xad, 0x13, 0x4c, 0xa5, 0xb8, 0x03,
0x82, 0xb9, 0x41, 0xdc, 0xdd, 0x9e, 0x71, 0x1c, 0x09, 0x25, 0x40, 0x62, 0x1c, 0x8b, 0x0a, 0x03,
0xb9, 0x0e, 0x9d, 0x96, 0x30, 0xa6, 0xb0, 0xa2, 0x81, 0x31, 0xea, 0x6a, 0x11, 0x04, 0x65, 0x07,
0x36, 0xb9, 0x54, 0x1e, 0x57, 0x85, 0x40, 0xe8, 0x6d, 0x44, 0x01, 0xa4, 0xf3, 0xaa, 0x06, 0x53,
0x0f, 0x9b, 0x6c, 0xd9, 0x20, 0xa0, 0x2c, 0xf2, 0x0f, 0x8a, 0x71, 0xba, 0x80, 0xec, 0x2d, 0x01,
0xf9, 0x4b, 0x02, 0x9e, 0xa0, 0x32, 0x11, 0x59, 0x54, 0x1d, 0xa1, 0x28, 0xe2, 0xae, 0x13, 0xb1,
0x69, 0x01, 0x46, 0x40, 0x8c, 0xbe, 0x9a, 0x2b, 0x9d, 0x49, 0xb7, 0x70, 0x5d, 0x98, 0x21, 0xd5,
0x98, 0x6b, 0x7e, 0xbe, 0x34, 0xb8, 0xf9, 0x2a, 0xce, 0x7a, 0xe6, 0x6b, 0x86, 0xf3, 0x20, 0x50,
0x02, 0xe8, 0xc3, 0x05, 0x4a, 0x36, 0xe4, 0xd0, 0x07, 0x21, 0x15, 0xda, 0x89, 0xb1, 0x61, 0x7c,
0x39, 0x0f, 0x3a, 0x1b, 0xbc, 0x64, 0xa8, 0x73, 0x91, 0xd5, 0x21, 0x51, 0xd1, 0x3b, 0x31, 0x95,
0x9c, 0xd4, 0x8b, 0x2b, 0x3f, 0xd2, 0xa5, 0x83, 0x6f, 0x58, 0xfe, 0x07, 0x4a, 0xe1, 0x31, 0x58,
0x66, 0x09, 0xe8, 0x70, 0xab, 0x24, 0xf6, 0x3f, 0xde, 0x1a, 0x47, 0x89, 0x60, 0xd8, 0x06, 0x83,
0x95, 0x29, 0x40, 0x9e, 0x69, 0x03, 0x3a, 0x3b, 0xc5, 0xe0, 0xcd, 0xd2, 0x19, 0x38, 0x8c, 0x83,
0xb3, 0xce, 0x80, 0x1f, 0xb6, 0x73, 0x45, 0x8e, 0x5c, 0xfa, 0xb2, 0x75, 0xdc, 0x08, 0x5f, 0x0d,
0xc0, 0x0a, 0x77, 0xf9, 0xee, 0x1b, 0xa7, 0x4a, 0x18, 0x21, 0x9c, 0xdc, 0x9b, 0x90, 0x01, 0x66,
0xb6, 0x5f, 0x63, 0x55, 0x90, 0x16, 0xb0, 0x9c, 0xff, 0xf3, 0x5b, 0x10, 0x36, 0xcb, 0x74, 0x8f,
0x3b, 0x1c, 0xba, 0xc9, 0x30, 0xf4, 0x3b, 0x8a, 0xcd, 0x5a, 0xd7, 0xb4, 0x0d, 0xb6, 0x86, 0x0e,
0x39, 0x48, 0xcb, 0x8c, 0x6e, 0x12, 0x54, 0x15, 0xc6, 0x1d, 0xfb, 0x60, 0xd3, 0xc6, 0xa0, 0x2f,
0x5e, 0x15, 0xf0, 0xc9, 0x1f, 0x5d, 0xe1, 0x3c, 0xb1, 0xe1, 0x37, 0x06, 0x00, 0x94, 0xb5, 0x38,
0x10, 0xd2, 0xa7, 0xa6, 0x31, 0x1b, 0x0f, 0x87, 0xe5, 0x10, 0x01, 0x0f, 0x80, 0xd9, 0x8d, 0xfd,
0x1d, 0xf1, 0xfe, 0x9a, 0x5a, 0xc4, 0x38, 0xb5, 0x40, 0x1c, 0x69, 0x11, 0x50, 0xd6, 0x86, 0x02,
0x76, 0xea, 0xb7, 0x86, 0x42, 0xb1, 0x42, 0x05, 0xdc, 0xc8, 0xd8, 0xf8, 0x9a, 0x9f, 0x4a, 0x12,
0xc4, 0x0f, 0x10, 0x45, 0x57, 0xd4, 0x2c, 0x14, 0x53, 0x03, 0xa5, 0xb5, 0x60, 0x04, 0x16, 0xee,
0xa4, 0x20, 0xf7, 0xca, 0xb7, 0xaa, 0xfc, 0xa9, 0xde, 0x38, 0xf3, 0xdd, 0xed, 0x59, 0x13, 0xc6,
0xba, 0xa8, 0xfe, 0xb0, 0x3e, 0x2a, 0x3e, 0xec, 0x67, 0xd5, 0x87, 0xbd, 0x57, 0x7f, 0xd8, 0x47,
0xf2, 0xa3, 0x0c, 0xa9, 0xb7, 0x7c, 0xf9, 0xe8, 0xae, 0xca, 0x1a, 0x5a, 0x26, 0xe2, 0xaf, 0x31,
0xbc, 0xb8, 0x34, 0xab, 0x32, 0xbb, 0x49, 0xca, 0xca, 0xc4, 0xd7, 0xbe, 0x29, 0x09, 0x38, 0xd0,
0xbf, 0xfb, 0xee, 0x48, 0x23, 0x51, 0xbf, 0xf6, 0xa2, 0x88, 0x97, 0x2b, 0x0b, 0xbf, 0x0d, 0x76,
0xce, 0x82, 0x2b, 0xb6, 0x0f, 0xb8, 0x46, 0x55, 0x8d, 0x3f, 0x91, 0x07, 0xb5, 0x70, 0xe2, 0xd4,
0x3d, 0xf4, 0xa1, 0xb2, 0xa3, 0x86, 0x33, 0xc4, 0x7c, 0x19, 0x01, 0x77, 0x86, 0x28, 0xe2, 0x2b,
0x38, 0xed, 0x63, 0xb8, 0xd9, 0x42, 0xec, 0x0a, 0x2f, 0x0a, 0xdb, 0x08, 0x61, 0x90, 0xfb, 0x9c,
0x7b, 0x8e, 0x71, 0xc6, 0x0f, 0xe8, 0x39, 0xb9, 0x51, 0xeb, 0x8a, 0xdc, 0x71, 0x63, 0xaf, 0x87,
0x75, 0xf3, 0xcf, 0x0b, 0x56, 0x88, 0xde, 0x0e, 0x4e, 0xd2, 0xe5, 0x03, 0x24, 0xc3, 0x72, 0xd2,
0x29, 0x53, 0x6a, 0xa3, 0x8d, 0xbf, 0x2f, 0x7e, 0x68, 0x5e, 0xe7, 0x0b, 0x9d, 0x43, 0x1e, 0x08,
0x9b, 0x4f, 0x79, 0x90, 0x9a, 0x8f, 0xcc, 0x6e, 0xbc, 0x8a, 0x62, 0x97, 0xcc, 0xb7, 0x2b, 0x86,
0x00, 0x21, 0x19, 0x69, 0x6a, 0x65, 0xa2, 0x22, 0x64, 0x2c, 0x5c, 0x7f, 0xe1, 0xa0, 0x56, 0xd2,
0xdc, 0x5f, 0xc8, 0xac, 0x1a, 0x1c, 0x24, 0xd9, 0xa1, 0x80, 0x39, 0xdd, 0x1b, 0x7e, 0x45, 0x28,
0x6a, 0x6a, 0xd5, 0x3c, 0x78, 0x44, 0x82, 0x06, 0x4c, 0x64, 0x06, 0x11, 0xe7, 0x15, 0xeb, 0xb3,
0x86, 0x70, 0xc5, 0xbe, 0xee, 0xae, 0x55, 0x32, 0x30, 0x3b, 0xc1, 0x35, 0x5d, 0x5d, 0xe7, 0x35,
0x70, 0xb6, 0xe2, 0x0b, 0xf7, 0xfa, 0x35, 0x7b, 0x5d, 0x0f, 0xca, 0x8f, 0xd6, 0x69, 0xad, 0xde,
0x78, 0x55, 0x34, 0x4d, 0x33, 0xbc, 0x61, 0xd3, 0x28, 0x25, 0x09, 0xf3, 0x44, 0x2c, 0x44, 0x9c,
0x86, 0x21, 0x1c, 0x36, 0x68, 0xa4, 0x7c, 0x2c, 0x63, 0x29, 0xb9, 0xc4, 0xd6, 0xcd, 0x04, 0x9d,
0x56, 0x93, 0x91, 0xb4, 0xbc, 0xd4, 0x0c, 0x67, 0x50, 0xd3, 0x04, 0x84, 0xc7, 0x06, 0x15, 0x21,
0x92, 0xda, 0xec, 0x85, 0x3b, 0x61, 0x2a, 0xb5, 0xe8, 0xb6, 0x24, 0x10, 0x1b, 0x9d, 0xbf, 0x46,
0x6d, 0x1c, 0xd0, 0x02, 0x63, 0x23, 0x21, 0x32, 0x80, 0x00, 0xfd, 0xbc, 0x64, 0x81, 0xca, 0x74,
0x2a, 0x98, 0xff, 0x4d, 0x96, 0x62, 0x5d, 0xd0, 0x4e, 0x15, 0x6f, 0xb3, 0xab, 0x56, 0xd5, 0xec,
0xda, 0x2a, 0x36, 0x6b, 0xd6, 0x05, 0xd9, 0x81, 0x18, 0xed, 0x5b, 0x8a, 0xb6, 0x9a, 0x7c, 0x68,
0x12, 0x82, 0x6e, 0x87, 0x1d, 0x01, 0xba, 0xa5, 0x0a, 0x76, 0x44, 0xbb, 0x87, 0x67, 0x60, 0x71,
0x02, 0xd9, 0x06, 0x9e, 0x93, 0x36, 0x06, 0x3e, 0xe0, 0xd8, 0x0c, 0xfa, 0xa2, 0x30, 0x79, 0xd3,
0x09, 0x6c, 0x77, 0xe1, 0x23, 0x22, 0xdf, 0xf2, 0x3c, 0x0f, 0x74, 0x12, 0x89, 0xc0, 0x35, 0x86,
0x25, 0x64, 0xf6, 0x94, 0xde, 0x6f, 0xb6, 0xd5, 0x35, 0x91, 0x47, 0x8f, 0xab, 0xac, 0xde, 0xf4,
0x38, 0xbc, 0x9e, 0x50, 0x1f, 0xfd, 0x67, 0x1e, 0x46, 0x43, 0x83, 0x73, 0x48, 0x11, 0x65, 0x47,
0x44, 0xdf, 0x38, 0xce, 0x7f, 0xcd, 0x27, 0x48, 0xa0, 0x59, 0xd8, 0x77, 0x04, 0x28, 0x57, 0x2d,
0x73, 0x55, 0x2a, 0x46, 0x46, 0x03, 0x5b, 0x54, 0x26, 0x7d, 0xb6, 0x11, 0x32, 0x57, 0x45, 0x6c,
0xbe, 0x09, 0x1e, 0x48, 0xf9, 0x9a, 0x06, 0xc9, 0xd1, 0x9b, 0x37, 0xec, 0x3f, 0x03, 0xf0, 0x1e,
0x98, 0xc2, 0x86, 0x27, 0x4b, 0x11, 0x08, 0x29, 0x3a, 0x74, 0x85, 0x10, 0xb6, 0xa7, 0xc3, 0x09,
0x52, 0x4f, 0xc0, 0x01, 0xd3, 0x3a, 0x4c, 0x51, 0xf3, 0x29, 0xd1, 0x4c, 0x1f, 0x44, 0xd4, 0xb2,
0xbd, 0x2f, 0x5e, 0xc7, 0x97, 0x6f, 0xc3, 0xa7, 0x15, 0x42, 0x65, 0xdf, 0x7d, 0xf9, 0xd8, 0x1e,
0x8a, 0xc0, 0x7c, 0xb5, 0xf3, 0xaa, 0x54, 0x08, 0xb0, 0xb1, 0xfc, 0x65, 0x02, 0x6a, 0x13, 0xda,
0x9c, 0x51, 0x7f, 0x8a, 0xd9, 0x51, 0x62, 0xc8, 0x54, 0x94, 0x55, 0x78, 0xe6, 0x1e, 0x33, 0x89,
0x0c, 0x75, 0x34, 0x60, 0x64, 0x1b, 0xbc, 0x9e, 0x17, 0xc5, 0x7a, 0xa7, 0x38, 0xb6, 0x08, 0x77,
0x62, 0xae, 0xe8, 0x11, 0x7a, 0xba, 0x81, 0x69, 0x28, 0xb6, 0x4b, 0x04, 0xea, 0x29, 0xea, 0xff,
0xc5, 0x43, 0xdf, 0x42, 0xe8, 0xdc, 0xad, 0x45, 0x6f, 0x09, 0x42, 0xf6, 0x22, 0xa8, 0x9c, 0x3b,
0xc7, 0x74, 0x4e, 0x35, 0x57, 0x8f, 0x3b, 0xa5, 0x0a, 0x2a, 0x57, 0xa0, 0x4e, 0x84, 0x2c, 0x00,
0x64, 0x91, 0x50, 0x1c, 0x71, 0xab, 0xec, 0x95, 0x12, 0x1d, 0xa4, 0x8c, 0xa2, 0x85, 0xb7, 0x9e,
0x46, 0x29, 0x47, 0x99, 0xbb, 0x88, 0x01, 0x4f, 0xe4, 0x92, 0x22, 0xb6, 0xb3, 0x6c, 0x3e, 0xb3,
0x6a, 0x18, 0x09, 0xdc, 0x87, 0x25, 0xba, 0x99, 0x0d, 0xa5, 0x5a, 0x02, 0x82, 0xd8, 0x7e, 0xe3,
0x7b, 0x85, 0x70, 0x75, 0x8b, 0x97, 0x66, 0x9c, 0x25, 0x12, 0x25, 0x07, 0xf2, 0xb9, 0x6c, 0xb1,
0xb5, 0xae, 0x7a, 0xc4, 0xa8, 0xa5, 0x5e, 0xaf, 0x0a, 0xc8, 0x59, 0x7c, 0x44, 0xc1, 0x29, 0xa2,
0xe2, 0x4e, 0x8e, 0xbb, 0x22, 0x3a, 0x5b, 0x97, 0x51, 0xa0, 0x8b, 0x7b, 0x1c, 0x6f, 0xd2, 0x8f,
0xce, 0xc3, 0x18, 0x3d, 0x1f, 0xdf, 0x4d, 0x78, 0x97, 0xed, 0xd2, 0x15, 0xae, 0x3d, 0xb9, 0x8d,
0x85, 0x31, 0x6b, 0x72, 0x5e, 0x98, 0x98, 0x32, 0x59, 0xa7, 0xbd, 0x32, 0x5f, 0x30, 0xa5, 0x0a,
0x9d, 0xb4, 0xb6, 0x6c, 0xdb, 0xa5, 0xf4, 0xad, 0xf0, 0xd6, 0x93, 0xf9, 0x37, 0x6b, 0xac, 0xb0,
0x6b, 0x96, 0xfb, 0x97, 0x1b, 0x4a, 0xb3, 0x16, 0xe2, 0xe0, 0x45, 0xfe, 0x7e, 0x37, 0xb0, 0xb1,
0x5d, 0x11, 0xca, 0xba, 0x36, 0x52, 0x99, 0x4d, 0x9f, 0x74, 0xd0, 0xc8, 0x5e, 0x09, 0x3b, 0x80,
0xf1, 0x7f, 0x9d, 0x97, 0xe0, 0xd5, 0xae, 0x87, 0xb0, 0xef, 0x34, 0xe2, 0x8b, 0x86, 0xf0, 0x1b,
0x23, 0xec, 0x2e, 0xbc, 0x1e, 0x14, 0x30, 0x62, 0x03, 0x2b, 0xa5, 0x29, 0x35, 0x74, 0x28, 0x82,
0x12, 0x8a, 0xf7, 0xb1, 0x28, 0x1b, 0xbc, 0x77, 0x4a, 0x19, 0xee, 0x70, 0xb7, 0x23, 0xe4, 0x3c,
0x8d, 0x9a, 0x73, 0x9c, 0x9d, 0xaa, 0x73, 0x66, 0xee, 0x05, 0x87, 0x2c, 0x04, 0xfe, 0x71, 0x94,
0x73, 0x2d, 0x36, 0x63, 0x03, 0x86, 0x37, 0x35, 0x36, 0x18, 0x12, 0xfa, 0xd1, 0xe9, 0xd9, 0x01,
0xaa, 0x98, 0xbd, 0x68, 0xab, 0x1f, 0x1d, 0xcd, 0x09, 0x7d, 0xd3, 0xe5, 0x0f, 0x16, 0x6d, 0x3a,
0x60, 0x33, 0xf5, 0x62, 0xf2, 0xc2, 0x7c, 0x67, 0xcf, 0xf7, 0x61, 0xdd, 0x0b, 0xf5, 0x37, 0xa5,
0x05, 0x72, 0xe0, 0x5b, 0x37, 0x75, 0x6c, 0x76, 0x14, 0x3a, 0x3d, 0x11, 0x00, 0x90, 0x04, 0xbe,
0x78, 0x8b, 0xda, 0x5a, 0x44, 0xda, 0x83, 0x11, 0xe3, 0xde, 0x4d, 0xfc, 0x3c, 0x46, 0xf8, 0xc6,
0x5e, 0xe1, 0x61, 0x26, 0x1b, 0x13, 0x11, 0x0a, 0x28, 0xd6, 0xf2, 0xb9, 0x12, 0x8d, 0x21, 0xee,
0xd5, 0x6c, 0x93, 0x2c, 0x3f, 0xcb, 0x7e, 0x5d, 0x5e, 0x4e, 0xa3, 0x1e, 0x62, 0xc5, 0x04, 0xcd,
0xbf, 0x7a, 0xd9, 0x1d, 0xca, 0x56, 0x37, 0x74, 0x06, 0x05, 0x86, 0xc8, 0x68, 0xa8, 0xdb, 0x03,
0x99, 0x15, 0x79, 0x1b, 0x59, 0x38, 0xec, 0x64, 0x81, 0x0d, 0xc6, 0xa2, 0x89, 0xee, 0x38, 0x8f,
0x80, 0x6a, 0x9c, 0x7c, 0x53, 0xae, 0x80, 0x89, 0x57, 0x61, 0x1e, 0xc6, 0x0e, 0xdf, 0xda, 0x69,
0x4f, 0x47, 0x76, 0x39, 0x06, 0x67, 0x96, 0x6d, 0x3f, 0x9a, 0x99, 0xe7, 0xee, 0x91, 0x83, 0x20,
0x64, 0x34, 0x35, 0xa1, 0xfa, 0x5f, 0x4b, 0x80, 0xb4, 0xaf, 0x4f, 0xba, 0x99, 0x65, 0x0f, 0x67,
0x5d, 0x92, 0x6f, 0x12, 0xb2, 0x6d, 0xbc, 0x63, 0xda, 0xcc, 0x53, 0x08, 0x0c, 0x9e, 0xd6, 0x4f,
0x2e, 0xcb, 0x4b, 0x4c, 0xda, 0x42, 0x6b, 0x9c, 0x30, 0xae, 0x89, 0x7d, 0xe7, 0x13, 0x95, 0x4f,
0xb8, 0xe1, 0x81, 0x0b, 0xf9, 0xd1, 0x53, 0x5a, 0x39, 0x13, 0xbb, 0x50, 0x6f, 0x0d, 0x08, 0xd3,
0x69, 0x15, 0xb7, 0x89, 0x61, 0xa9, 0xb3, 0xfa, 0x74, 0x0f, 0x25, 0x43, 0x03, 0x57, 0x76, 0x16,
0x38, 0x79, 0x55, 0xed, 0x82, 0x71, 0xad, 0xc5, 0x2e, 0x5d, 0xe2, 0x25, 0x46, 0xbc, 0x83, 0x87,
0xbf, 0x2b, 0x68, 0x87, 0xd2, 0xa6, 0x2f, 0xbb, 0x79, 0xa2, 0xdd, 0x33, 0xa1, 0xe8, 0xc6, 0x6c,
0x49, 0xc8, 0xc6, 0x53, 0xf4, 0x54, 0xb5, 0x74, 0x03, 0xbd, 0x59, 0xc6, 0x10, 0xae, 0x70, 0x66,
0xed, 0xd7, 0xe8, 0x7e, 0x85, 0xff, 0x1a, 0xbd, 0xaf, 0x08, 0x5f, 0xa3, 0xff, 0xff, 0x15, 0xe7,
0x41, 0x95, 0xf3, 0x22, 0xa6, 0x58, 0x06, 0xea, 0x02, 0x7e, 0x98, 0x94, 0xaf, 0xa6, 0x65, 0x3b,
0x46, 0x41, 0xa9, 0x6e, 0x75, 0xe8, 0x28, 0xe7, 0x0d, 0x47, 0x39, 0xc7, 0xfa, 0xe3, 0x4a, 0x9a,
0x47, 0xaa, 0xb6, 0x8e, 0x69, 0x7e, 0xdf, 0x3b, 0x79, 0xa6, 0x5d, 0x98, 0x7a, 0x19, 0x45, 0x6e,
0x1e, 0x54, 0xde, 0xd6, 0xcc, 0xb0, 0x21, 0x27, 0x6b, 0x37, 0xed, 0xd1, 0xe7, 0xad, 0x1d, 0x1d,
0x91, 0x4a, 0xdc, 0x58, 0x29, 0xe0, 0x47, 0x70, 0x77, 0x6e, 0x69, 0x6a, 0xa9, 0x6e, 0xdc, 0xb5,
0xb7, 0x91, 0x76, 0xe2, 0x14, 0xbb, 0xdc, 0xaa, 0xe9, 0x81, 0x17, 0xc1, 0xe7, 0x64, 0x95, 0x0e,
0x0c, 0x4a, 0xd7, 0x48, 0xac, 0xe1, 0x4c, 0x56, 0x9c, 0x42, 0x9c, 0xe5, 0xa4, 0xa9, 0x83, 0xb5,
0xd4, 0xbc, 0x2a, 0xbf, 0x94, 0xfb, 0xbd, 0x59, 0x05, 0x91, 0xf9, 0x67, 0xad, 0xe9, 0x51, 0x69,
0x84, 0xd4, 0xa8, 0x79, 0xc0, 0xd2, 0x23, 0xfa, 0xf5, 0x0c, 0x92, 0xb2, 0x68, 0x8c, 0x32, 0xb1,
0x10, 0xc4, 0xb0, 0x3b, 0xac, 0xb1, 0x71, 0x11, 0xd6, 0x23, 0x63, 0x72, 0xc4, 0x0c, 0x29, 0x8a,
0xb8, 0xcd, 0xa1, 0x15, 0xe9, 0x55, 0xb8, 0x4b, 0x66, 0x30, 0x7d, 0x74, 0xb3, 0x74, 0xfb, 0x9b,
0x5d, 0x37, 0x38, 0x30, 0x9f, 0xbf, 0x21, 0x41, 0x64, 0x73, 0x0f, 0xf8, 0x8a, 0x2f, 0x67, 0x36,
0x02, 0xe8, 0x8a, 0x5a, 0x49, 0x59, 0x82, 0x52, 0xdf, 0x3c, 0x20, 0x96, 0x9b, 0x69, 0x7d, 0xe3,
0x70, 0x78, 0x7e, 0x52, 0x90, 0x09, 0xa9, 0x6d, 0xea, 0xeb, 0x38, 0x9a, 0x50, 0x95, 0xf5, 0x05,
0x04, 0x41, 0x2a, 0x80, 0x9b, 0xf7, 0xd3, 0x9a, 0xee, 0xae, 0xc0, 0x10, 0xc3, 0x3c, 0x3d, 0xe3,
0xa9, 0xb9, 0x20, 0xeb, 0xdf, 0xcc, 0xfc, 0xd9, 0xc6, 0x29, 0x7f, 0xdf, 0xe2, 0xb9, 0x87, 0x13,
0x8f, 0xa1, 0xc3, 0x4c, 0x90, 0x4d, 0x47, 0xa3, 0xbd, 0x87, 0x05, 0x79, 0xe2, 0xb3, 0xf3, 0xba,
0xc6, 0x7d, 0x66, 0x32, 0xc5, 0x50, 0x28, 0xb8, 0x54, 0xe1, 0x2a, 0xf2, 0xb5, 0xc6, 0x73, 0x09,
0xa7, 0x7e, 0x59, 0x65, 0x3b, 0x48, 0x56, 0x32, 0x7a, 0xa6, 0x6a, 0x65, 0xf1, 0x01, 0xed, 0x77,
0x59, 0x14, 0x9d, 0x64, 0xb9, 0x77, 0xa7, 0xdb, 0x03, 0xbc, 0x65, 0xf5, 0xd7, 0x70, 0xef, 0xe9,
0xa4, 0x2b, 0x7a, 0xf8, 0xc0, 0xb7, 0x2a, 0x89, 0x6c, 0x24, 0x3b, 0x3f, 0x26, 0xe4, 0x3f, 0xc5,
0x01, 0xa8, 0xe5, 0xd8, 0xf7, 0xf6, 0x11, 0x7f, 0xd0, 0x46, 0x8f, 0xdd, 0xb4, 0xf8, 0x4a, 0x44,
0x40, 0xb7, 0xf8, 0x98, 0x14, 0x8b, 0x5c, 0x76, 0xdb, 0x58, 0x30, 0x23, 0x79, 0x1c, 0x15, 0x0a,
0xed, 0x2b, 0xa2, 0xbb, 0x29, 0xb7, 0xba, 0xe5, 0xfe, 0xad, 0xce, 0x47, 0x6e, 0x3f, 0x4a, 0xeb,
0x70, 0x08, 0x37, 0x92, 0xc8, 0x34, 0xe1, 0x87, 0x4a, 0xdc, 0x72, 0x59, 0x43, 0x40, 0x0b, 0xa3,
0xb5, 0x79, 0xe2, 0x1c, 0xa6, 0x1f, 0x13, 0x3e, 0xe9, 0xf5, 0x0d, 0x9d, 0x55, 0x20, 0x5e, 0x27,
0xef, 0xf7, 0x0b, 0xff, 0x99, 0x67, 0x0c, 0x85, 0x59, 0xc6, 0x4b, 0xef, 0x96, 0xbb, 0x66, 0x52,
0x8b, 0x6d, 0xfc, 0xe9, 0x78, 0x9a, 0x7b, 0xf6, 0xa4, 0x5c, 0xa3, 0x0d, 0x7d, 0x9e, 0xb4, 0x20,
0x88, 0x31, 0x36, 0x3d, 0x95, 0x21, 0x72, 0x18, 0x9a, 0xaf, 0x30, 0x2d, 0x23, 0x8a, 0x38, 0x91,
0xed, 0x9e, 0x24, 0x00, 0xcb, 0x85, 0x64, 0x8c, 0xcf, 0x37, 0xcd, 0x69, 0xe6, 0x12, 0xe3, 0x21,
0xb4, 0xb9, 0xa5, 0xdb, 0x50, 0x12, 0x36, 0xda, 0x6c, 0xc4, 0x39, 0x5c, 0xd2, 0x92, 0x65, 0xdb,
0xfd, 0x60, 0xd4, 0xfd, 0x54, 0x69, 0xa6, 0x75, 0xd7, 0x24, 0xca, 0x9e, 0x33, 0xc4, 0xc2, 0x49,
0x26, 0x7a, 0x55, 0x70, 0x4c, 0x4f, 0x08, 0x82, 0x23, 0x69, 0x0f, 0x28, 0xd8, 0xb6, 0x4a, 0x95,
0x72, 0x7c, 0x10, 0x15, 0x78, 0x70, 0x9a, 0x95, 0x8a, 0x8f, 0xa7, 0xb2, 0x72, 0x4d, 0xd5, 0xaa,
0x09, 0x68, 0x9e, 0xe9, 0x51, 0x43, 0x34, 0x78, 0x33, 0x34, 0xec, 0x63, 0xdb, 0xb1, 0x6f, 0x17,
0x05, 0x22, 0x2c, 0x3f, 0x7b, 0xae, 0x68, 0xf2, 0x4f, 0x13, 0x9e, 0x77, 0x5c, 0x91, 0x2f, 0x39,
0x25, 0x1a, 0xfb, 0xfc, 0xdf, 0xbc, 0xe9, 0x67, 0xf4, 0x7d, 0x3c, 0x25, 0xe4, 0x62, 0xf2, 0x99,
0x90, 0x5f, 0xe3, 0x6b, 0x42, 0xc6, 0xbf, 0xaf, 0x3f, 0xae, 0xfc, 0xa7, 0xfb, 0xfd, 0x6e, 0xfa,
0xaf, 0x2b, 0x7f, 0xee, 0x1b, 0xdf, 0x7d, 0xd9, 0x77, 0x33, 0xf9, 0xbd, 0xef, 0xf2, 0xec, 0x76,
0xdf, 0xf4, 0xf2, 0xfe, 0x35, 0x9c, 0x4f, 0xce, 0x68, 0x3e, 0x1b, 0x36, 0x70, 0xf2, 0xf3, 0x30,
0xc8, 0x20, 0xcd, 0x94, 0x63, 0x50, 0xc3, 0x01, 0x1e, 0x4e, 0xf8, 0xb2, 0x95, 0x5e, 0x5d, 0xff,
0xd4, 0x31, 0x93, 0x29, 0x41, 0xc6, 0x39, 0x4e, 0xbd, 0xb3, 0x03, 0xcc, 0x4a, 0x68, 0x97, 0x09,
0xe3, 0x70, 0x28, 0x7e, 0xf7, 0xd4, 0x2f, 0x5b, 0x61, 0x16, 0x89, 0xd9, 0xd4, 0xf4, 0xaa, 0xed,
0x6f, 0x4f, 0x4d, 0xde, 0x3c, 0x28, 0x2e, 0x27, 0xe0, 0xbc, 0xf2, 0x4a, 0x69, 0xbc, 0x9a, 0x01,
0xb3, 0x86, 0x55, 0x3b, 0xe5, 0x76, 0x8d, 0xfd, 0x0d, 0x48, 0xff, 0xb4, 0xe5, 0x69, 0xfb, 0xb0,
0xf4, 0x68, 0x2b, 0x19, 0x16, 0x59, 0x3c, 0xca, 0x7a, 0xd4, 0x64, 0xb7, 0xa7, 0xde, 0x48, 0xdd,
0xd0, 0x6b, 0xca, 0x30, 0x12, 0x20, 0x1c, 0xec, 0x13, 0x05, 0x90, 0x76, 0xb3, 0x7a, 0xc1, 0x59,
0x4f, 0x2e, 0xa3, 0xfb, 0xec, 0xa5, 0xd3, 0xc7, 0xc2, 0x58, 0xf4, 0x07, 0xf1, 0x09, 0x90, 0x0a,
0x48, 0x64, 0x1c, 0x9f, 0x72, 0x61, 0x1e, 0xa9, 0x13, 0x01, 0x88, 0x6d, 0x12, 0x50, 0xe1, 0xa4,
0x75, 0xe6, 0xd6, 0x2f, 0x6d, 0x2d, 0x60, 0x7c, 0x84, 0xcb, 0xe9, 0x88, 0x25, 0x6e, 0x6d, 0x08,
0xe3, 0xec, 0x79, 0x3b, 0x34, 0x32, 0xd8, 0x61, 0x59, 0x78, 0x6a, 0xc2, 0x8f, 0x86, 0x6d, 0x37,
0xe8, 0xcd, 0x41, 0x00,
};
const unsigned int html_content_br_len = 5940;
#endif // HTML_CONTENT_BR_H

View File

@@ -1,31 +1,225 @@
#!/usr/bin/env python3
"""
Enhanced HTML/CSS/JS Minifier and Compressor
This script provides multiple levels of optimization for embedded web pages
"""
import minify_html
import gzip
import brotli
import re
import sys
from pathlib import Path
with open("landingpage.html", "r", encoding="utf-8") as fin:
original_html = fin.read()
# Try to import optional JavaScript minifier
try:
import jsmin
HAS_JSMIN = True
except ImportError:
HAS_JSMIN = False
print("Warning: jsmin not installed. Install with: pip install jsmin --break-system-packages")
minified_html = minify_html.minify(
original_html,
minify_js=True,
minify_css=True,
keep_comments=False, # Remove comments
keep_html_and_head_opening_tags=False, # Remove <html> and <head> if possible
keep_closing_tags=True, # Keep closing tags (safer; set False for more aggression if you test thoroughly)
remove_processing_instructions=True, # Remove <?xml ...> etc.
#keep_comments=False,
remove_bangs=False # Keep <!DOCTYPE> bang (removing saves bytes but can break some edge cases)
)
# Try to import rjsmin (faster alternative)
try:
import rjsmin
HAS_RJSMIN = True
except ImportError:
HAS_RJSMIN = False
with open("webpage_minified.html", "w") as fout:
fout.write(minified_html)
minified_bytes = minified_html.encode('utf-8')
gzipped_bytes = gzip.compress(minified_bytes, compresslevel=9)
with open("webpage.h", "w") as fout:
fout.write("const char html_content[] = {")
fout.write(','.join(f'0x{byte:02x}' for byte in gzipped_bytes))
fout.write("};\n\n")
fout.write(f"const unsigned int html_content_len = {len(gzipped_bytes)};\n")
def extract_and_minify_js(html_content):
"""Extract JavaScript, minify it, and reinsert into HTML"""
if not (HAS_JSMIN or HAS_RJSMIN):
return html_content
# Find all script tags
script_pattern = re.compile(r'<script>(.*?)</script>', re.DOTALL)
def minify_script_content(match):
script_content = match.group(1)
# Skip if empty or too small
if len(script_content.strip()) < 50:
return match.group(0)
try:
# Use rjsmin if available (faster), otherwise jsmin
if HAS_RJSMIN:
minified = rjsmin.jsmin(script_content)
elif HAS_JSMIN:
minified = jsmin.jsmin(script_content)
else:
minified = script_content
return f'<script>{minified}</script>'
except Exception as e:
print(f"Warning: JS minification failed: {e}")
return match.group(0)
return script_pattern.sub(minify_script_content, html_content)
def aggressive_html_minify(html_content):
"""Apply aggressive minification strategies"""
# First pass: minify JavaScript
html_content = extract_and_minify_js(html_content)
# Use minify_html library
minified = minify_html.minify(
html_content,
minify_js=True,
minify_css=True,
keep_comments=False,
keep_html_and_head_opening_tags=False,
keep_closing_tags=True, # Safer - can set to False for more aggressive
remove_processing_instructions=True,
remove_bangs=False, # Keep <!DOCTYPE>
#do_not_minify_doctype=True,
#ensure_spec_compliant_unquoted_attribute_values=True,
#keep_spaces_between_attributes=False,
)
return minified
def generate_c_header(data, variable_name="html_content", use_progmem=True):
"""Generate C/C++ header file with compressed data"""
lines = []
# Add header guard
guard = variable_name.upper() + "_H"
lines.append(f"#ifndef {guard}")
lines.append(f"#define {guard}")
lines.append("")
# Add includes if using PROGMEM
if use_progmem:
lines.append("#include <Arduino.h>")
lines.append("")
# Add array declaration
progmem_keyword = "PROGMEM " if use_progmem else ""
lines.append(f"const unsigned char {progmem_keyword}{variable_name}[] = {{")
# Format bytes in rows of 16
hex_bytes = [f'0x{byte:02x}' for byte in data]
for i in range(0, len(hex_bytes), 16):
row = hex_bytes[i:i+16]
lines.append(" " + ", ".join(row) + ",")
lines.append("};")
lines.append("")
lines.append(f"const unsigned int {variable_name}_len = {len(data)};")
lines.append("")
lines.append(f"#endif // {guard}")
return "\n".join(lines)
def print_compression_stats(original_size, minified_size, gzip_size, brotli_size=None):
"""Print compression statistics"""
print("\n" + "="*60)
print("COMPRESSION STATISTICS")
print("="*60)
print(f"Original HTML: {original_size:,} bytes")
print(f"Minified HTML: {minified_size:,} bytes ({minified_size/original_size*100:.1f}%)")
print(f"Gzip (level 9): {gzip_size:,} bytes ({gzip_size/original_size*100:.1f}%)")
if brotli_size:
print(f"Brotli (level 11): {brotli_size:,} bytes ({brotli_size/original_size*100:.1f}%)")
print(f"\nSavings (gzip): {original_size - gzip_size:,} bytes ({(1-gzip_size/original_size)*100:.1f}% reduction)")
if brotli_size:
print(f"Savings (brotli): {original_size - brotli_size:,} bytes ({(1-brotli_size/original_size)*100:.1f}% reduction)")
print("="*60 + "\n")
def main():
input_file = "landingpage.html"
# Check if input file exists
if not Path(input_file).exists():
print(f"Error: {input_file} not found")
sys.exit(1)
# Read original HTML
print(f"Reading {input_file}...")
with open(input_file, "r", encoding="utf-8") as fin:
original_html = fin.read()
original_size = len(original_html.encode('utf-8'))
# Minify HTML
print("Minifying HTML/CSS/JS...")
minified_html = aggressive_html_minify(original_html)
minified_size = len(minified_html.encode('utf-8'))
# Save minified HTML
with open("webpage_minified.html", "w", encoding="utf-8") as fout:
fout.write(minified_html)
print("Saved: webpage_minified.html")
# Compress with gzip
print("Compressing with gzip (level 9)...")
minified_bytes = minified_html.encode('utf-8')
gzipped_bytes = gzip.compress(minified_bytes, compresslevel=9)
gzip_size = len(gzipped_bytes)
# Compress with brotli (if available)
brotli_size = None
brotli_bytes = None
try:
print("Compressing with brotli (level 11)...")
brotli_bytes = brotli.compress(minified_bytes, quality=11)
brotli_size = len(brotli_bytes)
except Exception as e:
print(f"Brotli compression not available: {e}")
# Generate C headers
print("\nGenerating C header files...")
# Gzip version
with open("webpage_gzip.h", "w") as fout:
fout.write(generate_c_header(gzipped_bytes, "html_content_gz", use_progmem=True))
print("Saved: webpage_gzip.h")
# Brotli version (if available)
if brotli_bytes:
with open("webpage_brotli.h", "w") as fout:
fout.write(generate_c_header(brotli_bytes, "html_content_br", use_progmem=True))
print("Saved: webpage_brotli.h")
# Also generate the old format for compatibility
with open("webpage.h", "w") as fout:
fout.write("const char html_content[] = {")
fout.write(','.join(f'0x{byte:02x}' for byte in gzipped_bytes))
fout.write("};\n\n")
fout.write(f"const unsigned int html_content_len = {len(gzipped_bytes)};\n")
print("Saved: webpage.h (legacy format)")
# Print statistics
print_compression_stats(original_size, minified_size, gzip_size, brotli_size)
# Recommendations
print("RECOMMENDATIONS:")
print("-" * 60)
if brotli_size and brotli_size < gzip_size:
savings = gzip_size - brotli_size
print(f"Use Brotli compression (saves {savings} bytes vs gzip)")
print(f" Include: webpage_brotli.h")
else:
print(f"Use Gzip compression")
print(f" Include: webpage_gzip.h")
if not HAS_RJSMIN and not HAS_JSMIN:
print("\nInstall rjsmin for better JS compression:")
print(" pip install rjsmin --break-system-packages")
print("-" * 60)
if __name__ == "__main__":
main()

439
main/webpage_gzip.h Normal file
View File

@@ -0,0 +1,439 @@
#ifndef HTML_CONTENT_GZ_H
#define HTML_CONTENT_GZ_H
#include <Arduino.h>
const unsigned char PROGMEM html_content_gz[] = {
0x1f, 0x8b, 0x08, 0x00, 0x38, 0xff, 0x5b, 0x69, 0x02, 0xff, 0xed, 0x3d, 0x6b, 0x57, 0xdb, 0x48,
0xb2, 0x9f, 0x77, 0x7e, 0x45, 0xc3, 0x24, 0x8c, 0x94, 0x08, 0xd9, 0x06, 0x32, 0x0f, 0x1b, 0x99,
0x25, 0xe0, 0x4c, 0x32, 0x09, 0x8f, 0xc3, 0x23, 0x33, 0x7b, 0x59, 0x0e, 0x92, 0xad, 0x36, 0xd6,
0x20, 0x4b, 0x1a, 0x49, 0x86, 0x30, 0xc2, 0xff, 0x7d, 0xab, 0xfa, 0x21, 0xb5, 0x64, 0xd9, 0x98,
0x64, 0x33, 0x73, 0xee, 0xb9, 0x77, 0xf7, 0x2c, 0xb1, 0x5b, 0xdd, 0xd5, 0xd5, 0xf5, 0xae, 0xea,
0x92, 0x77, 0x7b, 0xc5, 0x0d, 0x07, 0xe9, 0x7d, 0x44, 0xc9, 0x28, 0x1d, 0xfb, 0xdd, 0xed, 0xd4,
0x4b, 0x7d, 0xda, 0xdd, 0x0b, 0x83, 0x34, 0x0e, 0x7d, 0x72, 0xec, 0x04, 0xd4, 0xdf, 0x6e, 0xf0,
0xc1, 0xed, 0x31, 0x4d, 0x1d, 0x32, 0x80, 0x47, 0x34, 0x48, 0xad, 0xd5, 0x3b, 0xcf, 0x4d, 0x47,
0x96, 0x4b, 0x6f, 0xbd, 0x01, 0x5d, 0x67, 0x5f, 0x0c, 0x2f, 0xf0, 0x52, 0xcf, 0xf1, 0xd7, 0x93,
0x81, 0xe3, 0x53, 0xab, 0x65, 0x36, 0x57, 0x49, 0xe0, 0x8c, 0xa9, 0x75, 0xeb, 0xd1, 0xbb, 0x28,
0x8c, 0xd3, 0xee, 0x76, 0x92, 0xde, 0x03, 0xa4, 0x6f, 0xef, 0x62, 0x27, 0x8a, 0x68, 0x9c, 0xa5,
0xf4, 0x53, 0xba, 0xee, 0xf8, 0xde, 0x75, 0xd0, 0x1e, 0x00, 0x50, 0x1a, 0x77, 0xfa, 0xe1, 0xa7,
0xf5, 0xc4, 0xfb, 0xd3, 0x0b, 0xae, 0xdb, 0xfd, 0x30, 0x76, 0x69, 0xbc, 0x0e, 0x23, 0xd3, 0x6f,
0xc5, 0xae, 0xd9, 0xd8, 0xf9, 0xc4, 0xf7, 0x6a, 0xbf, 0x6a, 0x36, 0xa3, 0x4f, 0x9d, 0xb1, 0x13,
0x5f, 0x7b, 0x41, 0xdb, 0x99, 0xa4, 0x61, 0x27, 0x72, 0x5c, 0x17, 0xd7, 0x35, 0x49, 0x0b, 0x1e,
0x4d, 0xfb, 0xa1, 0x7b, 0x5f, 0xb3, 0x81, 0x58, 0xd1, 0x2c, 0xa6, 0x4f, 0x5f, 0x64, 0x83, 0xd0,
0x0f, 0xe3, 0xf6, 0xb7, 0x1b, 0x43, 0xfc, 0x6f, 0xa7, 0xef, 0x0c, 0x6e, 0xae, 0xe3, 0x70, 0x12,
0xb8, 0xeb, 0xe2, 0xc1, 0x70, 0x38, 0xec, 0x0c, 0x01, 0x85, 0xf5, 0xa1, 0x33, 0xf6, 0xfc, 0xfb,
0xf6, 0x61, 0x98, 0x86, 0xe4, 0xd4, 0x09, 0x12, 0xe3, 0x23, 0x8d, 0x5d, 0x27, 0x70, 0x8c, 0x04,
0xbe, 0xac, 0x27, 0x34, 0xf6, 0xc4, 0x44, 0x38, 0x03, 0x6d, 0xb7, 0xcc, 0x8d, 0x98, 0x8e, 0xa7,
0x5e, 0x10, 0x4d, 0x52, 0xa3, 0x3f, 0x49, 0xd3, 0x30, 0x50, 0x31, 0x8a, 0xbd, 0xeb, 0x51, 0x5a,
0x7f, 0xe2, 0x1a, 0x1c, 0xe8, 0x90, 0xba, 0xf4, 0xa7, 0x0e, 0x9f, 0xd3, 0x6e, 0x45, 0x9f, 0x48,
0x12, 0xfa, 0x9e, 0x4b, 0xbe, 0xed, 0x3b, 0x3f, 0x7d, 0xff, 0xaa, 0x2f, 0x1e, 0xac, 0xc7, 0x8e,
0xeb, 0x4d, 0x92, 0xf6, 0x2b, 0x20, 0x0e, 0x27, 0x54, 0xab, 0xd9, 0x7c, 0xce, 0x51, 0xb8, 0x40,
0x3e, 0x5b, 0x88, 0xc0, 0xa5, 0xa1, 0x0c, 0x04, 0x93, 0x71, 0x9f, 0xc6, 0x97, 0x99, 0x7a, 0xc0,
0x71, 0x18, 0x84, 0x49, 0xe4, 0x0c, 0xa8, 0x58, 0x19, 0x53, 0xc7, 0x0d, 0x03, 0xff, 0xfe, 0x32,
0xab, 0x41, 0x6c, 0x0b, 0xff, 0x3b, 0x9d, 0x3d, 0x1f, 0xa7, 0xf8, 0xd4, 0x1c, 0x8c, 0x9c, 0xe0,
0x9a, 0xba, 0x06, 0x70, 0x71, 0x3c, 0xf6, 0xd2, 0xab, 0x7e, 0x1a, 0x64, 0x05, 0x61, 0x57, 0xbc,
0x31, 0xca, 0x86, 0x13, 0xa4, 0x35, 0x87, 0xde, 0x70, 0xb6, 0x7e, 0xda, 0x74, 0x8b, 0x29, 0x20,
0x09, 0x4e, 0x30, 0xa0, 0xfe, 0x53, 0x60, 0xfc, 0xb0, 0xb1, 0x59, 0x02, 0x90, 0x23, 0x61, 0x94,
0x80, 0x4d, 0xe2, 0x04, 0x66, 0x47, 0xa1, 0x27, 0xe4, 0x90, 0x91, 0x39, 0x08, 0x03, 0x2a, 0xe8,
0xb8, 0xf5, 0xea, 0xb9, 0x10, 0x9e, 0xf5, 0x34, 0x8c, 0xda, 0x28, 0x63, 0xb9, 0x0c, 0xb1, 0x2f,
0x8c, 0x7e, 0x77, 0x14, 0x99, 0xda, 0xfe, 0xa1, 0xd9, 0x54, 0x77, 0xba, 0x70, 0xbd, 0xc4, 0xe9,
0xfb, 0xd4, 0xbd, 0x54, 0xf7, 0x2c, 0x46, 0xe5, 0x51, 0x7e, 0xfc, 0xf1, 0xc7, 0x8e, 0x40, 0x24,
0x08, 0x91, 0x8c, 0x7e, 0x78, 0x47, 0xdd, 0x9a, 0x33, 0x6d, 0x6d, 0x6d, 0x29, 0x67, 0x4a, 0x11,
0x4a, 0x26, 0x04, 0x00, 0x66, 0xf8, 0x4e, 0x94, 0xd0, 0xb6, 0xfc, 0xd0, 0x51, 0x58, 0xe2, 0xd3,
0x61, 0xaa, 0xca, 0x45, 0xea, 0x66, 0xb9, 0xd0, 0x01, 0xfb, 0xc6, 0xaa, 0x5c, 0x09, 0x81, 0x93,
0x67, 0xfc, 0x11, 0x74, 0x2a, 0x99, 0x8c, 0x81, 0x04, 0x25, 0xb5, 0x62, 0x10, 0x15, 0x35, 0xa9,
0xa5, 0x7f, 0x8d, 0x70, 0x4a, 0xb0, 0xe6, 0x26, 0xa8, 0xc8, 0x0c, 0xed, 0xcc, 0xc1, 0x58, 0x62,
0xc6, 0x79, 0xa0, 0x2a, 0xd5, 0x2b, 0x54, 0xaa, 0x6f, 0xc7, 0xc9, 0x75, 0x8d, 0xb0, 0x8d, 0x5a,
0x59, 0x31, 0x75, 0x43, 0x4c, 0x8d, 0xc2, 0x68, 0x12, 0xad, 0x87, 0xb7, 0x34, 0xf6, 0x9d, 0xfb,
0xec, 0xcf, 0x75, 0x2f, 0x70, 0xe9, 0x27, 0x24, 0x40, 0xb3, 0x06, 0xdd, 0x26, 0xfb, 0x4f, 0x7f,
0xb3, 0xf3, 0xfb, 0x24, 0x49, 0xbd, 0xe1, 0xfd, 0xba, 0xb0, 0x3d, 0xd2, 0x80, 0xb0, 0xed, 0xd6,
0xbd, 0x94, 0x8e, 0x13, 0x39, 0x54, 0x10, 0xb4, 0x33, 0xe2, 0x67, 0x60, 0x9f, 0x81, 0xbd, 0x11,
0x6c, 0xc8, 0x0f, 0x10, 0x85, 0x09, 0x18, 0xc7, 0x30, 0x68, 0x0f, 0xbd, 0x4f, 0xc0, 0x52, 0x14,
0xa1, 0x66, 0x07, 0x89, 0x07, 0x06, 0x48, 0x20, 0x28, 0x8d, 0x9c, 0x42, 0xcd, 0x1a, 0x03, 0x39,
0x47, 0x47, 0x2a, 0x24, 0x6e, 0x71, 0xeb, 0x28, 0xad, 0xe5, 0x56, 0x53, 0x15, 0xd7, 0x4d, 0xfc,
0xc2, 0xec, 0xce, 0x08, 0xd4, 0xfa, 0x0e, 0x2c, 0xe6, 0x16, 0xb0, 0xfd, 0x7b, 0xf8, 0x9f, 0x38,
0xfd, 0x96, 0x5b, 0xc1, 0x89, 0x8c, 0x36, 0xb2, 0x85, 0x4c, 0x16, 0x48, 0x28, 0x0a, 0x52, 0x3d,
0x16, 0x89, 0x96, 0x82, 0xa0, 0x32, 0xba, 0x85, 0xdc, 0xfb, 0xe7, 0x98, 0xba, 0x9e, 0x43, 0x92,
0x41, 0x4c, 0x69, 0x40, 0x9c, 0xc0, 0x25, 0x1a, 0x3b, 0xd3, 0xb6, 0xb5, 0xf9, 0x0a, 0xce, 0xa1,
0x67, 0x35, 0xce, 0x81, 0x91, 0xbf, 0xf0, 0x07, 0x20, 0x6f, 0x5c, 0x47, 0x48, 0x1a, 0x13, 0x26,
0xf2, 0x75, 0x26, 0x57, 0x59, 0x2a, 0x39, 0xd7, 0xf7, 0xc3, 0xc1, 0x4d, 0xbe, 0x34, 0x13, 0xa7,
0x93, 0xaa, 0x82, 0x54, 0x2c, 0xcf, 0x94, 0x47, 0xe6, 0xb6, 0x30, 0xc9, 0xe6, 0x9f, 0xd2, 0xa7,
0x80, 0x2b, 0x78, 0xc1, 0x36, 0xfe, 0x99, 0x27, 0x6a, 0xd7, 0x4e, 0x24, 0x19, 0x99, 0x93, 0x75,
0x43, 0xdd, 0x15, 0xc1, 0x54, 0xf6, 0x24, 0xc2, 0x0e, 0x97, 0x5d, 0x5a, 0xd5, 0xba, 0xfd, 0x17,
0xbc, 0xcb, 0x18, 0x10, 0xca, 0x49, 0x56, 0xb1, 0x85, 0x64, 0xa3, 0xd6, 0x20, 0xd6, 0x21, 0xda,
0x1e, 0xa1, 0x5e, 0x1a, 0xb5, 0xcf, 0xcc, 0x28, 0xf6, 0x98, 0xbd, 0x59, 0x28, 0x38, 0x1c, 0xc3,
0xe9, 0x42, 0x08, 0x7c, 0x97, 0x1a, 0x7e, 0xfc, 0xe8, 0xfc, 0xd0, 0xdc, 0x7a, 0x25, 0x17, 0x33,
0x67, 0xc7, 0xb8, 0xe0, 0x78, 0x41, 0xed, 0xfc, 0x92, 0x9c, 0x33, 0x66, 0x90, 0x66, 0x69, 0x75,
0x85, 0xf0, 0xcb, 0x28, 0xf0, 0x97, 0x78, 0xf6, 0x1a, 0x17, 0xa4, 0x2a, 0x8f, 0x99, 0xfc, 0x81,
0xce, 0x8d, 0xcf, 0xdf, 0x7c, 0xf5, 0xbc, 0x6c, 0x02, 0xaa, 0x0c, 0xda, 0x6e, 0xf0, 0xf8, 0x6c,
0xbb, 0x31, 0x02, 0x7f, 0xdf, 0xdd, 0xc6, 0x10, 0xaa, 0xbb, 0xed, 0x7a, 0xb7, 0xc4, 0x73, 0x2d,
0x11, 0xb3, 0xe5, 0xdf, 0x85, 0xac, 0x76, 0xb7, 0x47, 0xad, 0xee, 0x9e, 0x3f, 0x19, 0xdc, 0xec,
0x81, 0xbb, 0x03, 0xf5, 0x84, 0xc5, 0x2d, 0x88, 0x23, 0x51, 0x67, 0xe0, 0x1f, 0x98, 0x9f, 0x22,
0x24, 0xc6, 0x0d, 0x12, 0x06, 0x03, 0xdf, 0x1b, 0xdc, 0x58, 0xab, 0x09, 0x0d, 0x5c, 0x31, 0x5f,
0xfb, 0x2e, 0x49, 0x9d, 0x38, 0xfd, 0x4e, 0x5f, 0x25, 0x03, 0xdf, 0x49, 0x12, 0x0b, 0x4c, 0x7f,
0xf7, 0xf4, 0x6c, 0xf7, 0xe4, 0x6c, 0xbb, 0xc1, 0x97, 0x01, 0x3e, 0x08, 0x63, 0x09, 0x38, 0x61,
0x54, 0x02, 0x43, 0xd8, 0x71, 0xac, 0xc7, 0xbc, 0x13, 0xec, 0x76, 0x74, 0xfc, 0xd4, 0xcd, 0x00,
0x44, 0x58, 0xc6, 0xf9, 0xfc, 0x70, 0xff, 0xa8, 0x02, 0xa5, 0x81, 0xe7, 0x6f, 0x48, 0x5a, 0xa8,
0x24, 0x81, 0x38, 0xda, 0x87, 0x00, 0x2b, 0xb0, 0x36, 0xbb, 0xdb, 0x4c, 0x6e, 0x90, 0xa4, 0xe0,
0xd2, 0x88, 0x8c, 0xb4, 0x54, 0x08, 0x0b, 0x96, 0x9c, 0xff, 0x76, 0x75, 0xf6, 0xee, 0xa0, 0x87,
0x48, 0xb2, 0x28, 0xcb, 0x02, 0xc9, 0xbc, 0xd9, 0xe3, 0x01, 0x97, 0x96, 0x8e, 0xbc, 0x44, 0x07,
0x22, 0xd0, 0xc8, 0x6a, 0x11, 0x16, 0xeb, 0xb9, 0x4e, 0x4a, 0x53, 0x6f, 0x4c, 0xd7, 0xc1, 0x5c,
0x39, 0x7e, 0x97, 0xc8, 0x33, 0x02, 0xa4, 0x20, 0xbc, 0xc3, 0xa0, 0x24, 0x3f, 0x6e, 0x42, 0xd3,
0x33, 0x98, 0x79, 0x16, 0x1e, 0x86, 0x77, 0x9a, 0xde, 0x3d, 0xbd, 0x0f, 0x06, 0x04, 0x07, 0xea,
0x8e, 0x28, 0xd8, 0x7c, 0x3a, 0x18, 0x51, 0x77, 0x02, 0xb6, 0xf2, 0x14, 0x39, 0x5a, 0x10, 0x52,
0xc5, 0xf6, 0xe0, 0xe8, 0x63, 0xef, 0x8a, 0xb1, 0xb7, 0xc0, 0x99, 0xff, 0x23, 0x57, 0x0b, 0xb4,
0x79, 0xb0, 0x0a, 0x1b, 0x2e, 0xda, 0xa8, 0x87, 0xf2, 0x36, 0x77, 0x9b, 0xde, 0xe1, 0xfe, 0x17,
0x6c, 0xf2, 0x2d, 0x39, 0x00, 0xa3, 0x91, 0x34, 0xf6, 0x9d, 0xfb, 0xfa, 0x3d, 0x0e, 0xcf, 0x0f,
0xd8, 0x3e, 0xa7, 0x04, 0x6c, 0xa1, 0xd5, 0x5c, 0x66, 0x2b, 0x1e, 0x6b, 0xd7, 0x6c, 0x76, 0x08,
0x76, 0x82, 0xed, 0x47, 0x76, 0xe7, 0x10, 0xee, 0xb0, 0xf7, 0xdb, 0xd9, 0xd5, 0xee, 0x87, 0xdd,
0x93, 0x83, 0x5c, 0x46, 0x6a, 0x79, 0x3a, 0x0b, 0xfb, 0x84, 0x8e, 0xc1, 0x9c, 0x99, 0x64, 0xdf,
0x03, 0x4d, 0x83, 0xe0, 0x93, 0x68, 0xc3, 0x54, 0xaf, 0xdf, 0xe4, 0xa4, 0x77, 0x70, 0xb5, 0xff,
0xee, 0x54, 0xe1, 0x0d, 0xfc, 0x75, 0x7d, 0xca, 0x41, 0x80, 0xe1, 0x40, 0x18, 0x5c, 0xbc, 0x96,
0x3a, 0x16, 0x3b, 0xd1, 0x12, 0xfb, 0xee, 0x9f, 0xbc, 0x03, 0x7e, 0xb1, 0x9d, 0xbf, 0x9c, 0x96,
0xbf, 0x80, 0x92, 0x93, 0xb7, 0xcc, 0xac, 0x11, 0xcd, 0x0b, 0xe6, 0x6c, 0xf9, 0xcb, 0xee, 0xde,
0xfb, 0xff, 0xd6, 0x8e, 0xaf, 0x9d, 0x14, 0x4c, 0xfb, 0x3d, 0xd1, 0x3e, 0xd6, 0x6d, 0x76, 0x1b,
0xfa, 0xa9, 0x73, 0x4d, 0x17, 0xa8, 0x76, 0xf7, 0x38, 0x0e, 0xaf, 0x63, 0x67, 0x4c, 0x4e, 0xde,
0x10, 0x20, 0x75, 0x98, 0xd2, 0xb9, 0x56, 0x28, 0xe2, 0x33, 0x4f, 0xde, 0x9c, 0xd2, 0x3f, 0x26,
0x14, 0xa8, 0x0a, 0xba, 0x29, 0x57, 0xef, 0xfa, 0x3e, 0x79, 0xcd, 0xfd, 0xdf, 0x22, 0x43, 0x24,
0x20, 0xca, 0xec, 0x83, 0x19, 0xf3, 0x3c, 0x29, 0xc9, 0x37, 0x42, 0x69, 0xc2, 0x80, 0xd5, 0x8c,
0xa9, 0x1f, 0x3a, 0xae, 0xa6, 0x77, 0xba, 0xc0, 0xc8, 0x81, 0x13, 0xbb, 0x05, 0xec, 0x3a, 0x48,
0x79, 0xd2, 0x93, 0x43, 0xe2, 0x43, 0xc7, 0x0e, 0xa0, 0x98, 0xa0, 0x25, 0x71, 0x40, 0x26, 0xb8,
0x0c, 0x29, 0x68, 0xf6, 0x63, 0xfe, 0x3f, 0x97, 0x82, 0xef, 0xf5, 0x93, 0xee, 0xb6, 0xc8, 0x35,
0xba, 0x20, 0x92, 0x47, 0x67, 0x3d, 0xb2, 0x77, 0x74, 0x78, 0x76, 0x72, 0xf4, 0x01, 0x5c, 0x94,
0x18, 0x27, 0x38, 0x9d, 0x14, 0xe4, 0x19, 0x87, 0x93, 0x84, 0x42, 0x34, 0x1b, 0x80, 0xa1, 0x46,
0xf3, 0xc3, 0xe9, 0xa8, 0x7d, 0x37, 0xbc, 0x73, 0xbf, 0x33, 0x08, 0xbd, 0x05, 0x5f, 0x05, 0xe6,
0x1a, 0x9c, 0x56, 0x38, 0x19, 0x8c, 0xd8, 0x8c, 0x85, 0x13, 0xb9, 0x5d, 0x67, 0xee, 0x53, 0x02,
0xf7, 0x29, 0x60, 0x6e, 0xa1, 0x97, 0x11, 0x4b, 0x74, 0xf9, 0x64, 0x12, 0x55, 0x87, 0xd9, 0x2e,
0xe0, 0x2f, 0x4a, 0xe3, 0xdd, 0x37, 0xbf, 0xee, 0xe7, 0x27, 0x5e, 0x02, 0xf5, 0x98, 0xde, 0x2e,
0x87, 0x7a, 0x79, 0xe2, 0x57, 0x41, 0xfd, 0xa4, 0xf7, 0xf1, 0x29, 0xa8, 0x4f, 0xa2, 0xe5, 0x30,
0x2f, 0xcd, 0xfb, 0x2a, 0x88, 0x9f, 0x1f, 0x3f, 0x05, 0x6f, 0x1c, 0x5a, 0x0e, 0xf3, 0xca, 0xcc,
0xaf, 0x82, 0xfb, 0xfe, 0xd1, 0xaf, 0x87, 0x4f, 0xc1, 0xde, 0x99, 0x7c, 0x5a, 0x0e, 0xf9, 0xf2,
0xc4, 0xaf, 0x82, 0xfb, 0xee, 0xf9, 0x6f, 0x8a, 0x11, 0xca, 0xd5, 0xba, 0x56, 0xc7, 0xf7, 0x77,
0x0f, 0x7f, 0xee, 0x9d, 0x90, 0xff, 0x39, 0x3a, 0xec, 0xa9, 0x0a, 0x5e, 0x0e, 0x22, 0xf7, 0x20,
0x8c, 0xee, 0xc7, 0xcc, 0x26, 0xcd, 0x8f, 0xd1, 0x06, 0x62, 0x12, 0x9c, 0xf1, 0x77, 0x70, 0x03,
0x10, 0xa1, 0x71, 0x77, 0x50, 0x5a, 0x3c, 0x4b, 0xd0, 0xd9, 0xd5, 0x6e, 0xec, 0xdd, 0x52, 0x5c,
0xbe, 0x8f, 0x1f, 0x6a, 0xd7, 0xcf, 0x9a, 0xf1, 0xbd, 0x49, 0x1c, 0x63, 0xaa, 0xfb, 0x91, 0xc6,
0x49, 0x09, 0xcd, 0xc2, 0x17, 0xf0, 0x27, 0x8b, 0x7c, 0xc1, 0x1b, 0x2f, 0x1e, 0xdf, 0x39, 0x31,
0xad, 0xae, 0x76, 0x06, 0x03, 0x1a, 0xa5, 0x96, 0xd9, 0xf7, 0x58, 0xbc, 0x36, 0x14, 0xd3, 0xae,
0x86, 0x1e, 0x04, 0x42, 0x3c, 0xd2, 0x55, 0x2b, 0x0e, 0xdc, 0x65, 0xe1, 0xc3, 0x2e, 0x51, 0xe3,
0xbc, 0x49, 0x84, 0xe6, 0xbc, 0x64, 0xa0, 0xf9, 0x90, 0xdc, 0x17, 0x75, 0x86, 0x0d, 0x90, 0x02,
0x93, 0xb9, 0x27, 0xfe, 0x10, 0x5e, 0xc3, 0x34, 0x7f, 0xd6, 0x5f, 0xc1, 0x4e, 0x7e, 0x78, 0x5d,
0xda, 0x06, 0x05, 0x16, 0xe1, 0xc2, 0x1a, 0x5c, 0x82, 0xf2, 0x2d, 0x46, 0x08, 0x0c, 0x2d, 0xf0,
0x58, 0x42, 0x12, 0x10, 0xa6, 0x10, 0x89, 0xfc, 0xc1, 0xc2, 0x18, 0x3d, 0xa6, 0xfd, 0x30, 0x4c,
0x3f, 0x2b, 0x25, 0x38, 0xe9, 0xbd, 0x3e, 0x3a, 0x3a, 0x5b, 0x20, 0x2a, 0xe5, 0xcc, 0xc3, 0xa7,
0xf4, 0xe9, 0xa9, 0xc7, 0xc6, 0xe6, 0x0f, 0xdd, 0xd3, 0x0f, 0xbd, 0xde, 0x71, 0x9d, 0x9e, 0x34,
0x20, 0xcd, 0x92, 0x7f, 0x45, 0xc6, 0x55, 0x2a, 0x63, 0x55, 0x46, 0x8b, 0x6c, 0x6c, 0xa3, 0x18,
0x14, 0x75, 0xfb, 0xc6, 0x68, 0xa3, 0xbb, 0x1d, 0x15, 0xc3, 0x63, 0x9a, 0x24, 0x10, 0x8f, 0xc0,
0x83, 0xa8, 0x02, 0xa5, 0x92, 0xff, 0xd6, 0x48, 0x95, 0x22, 0xca, 0xca, 0x92, 0x5a, 0x44, 0x45,
0x26, 0x5e, 0x3e, 0x0a, 0xff, 0x9b, 0x0c, 0x62, 0x2f, 0x4a, 0xbb, 0x3e, 0x4d, 0x09, 0x44, 0xaf,
0x8e, 0x95, 0x4d, 0x8d, 0x08, 0xc3, 0x83, 0x33, 0xe4, 0xea, 0x1e, 0xe8, 0x46, 0x4a, 0x5d, 0x6b,
0xa5, 0x65, 0x44, 0xa1, 0xef, 0xbf, 0xc3, 0xac, 0xf9, 0xd6, 0xf1, 0x21, 0xfa, 0xf2, 0x7d, 0x63,
0x1c, 0xba, 0x8e, 0x7f, 0x42, 0x21, 0x41, 0xbe, 0xa5, 0x6c, 0xa4, 0x03, 0xd8, 0x26, 0x29, 0x81,
0xb8, 0x0d, 0x36, 0xee, 0xba, 0xe1, 0x60, 0x32, 0x06, 0x3a, 0x98, 0xd7, 0x34, 0xed, 0xf9, 0x14,
0x3f, 0xbe, 0xbe, 0x7f, 0xe7, 0x6a, 0x9e, 0xab, 0x77, 0x86, 0x93, 0x60, 0x80, 0xfa, 0x4b, 0x92,
0x51, 0x78, 0x77, 0x80, 0x70, 0x34, 0x46, 0x20, 0x43, 0xd0, 0x43, 0x54, 0xe8, 0x13, 0xeb, 0xe2,
0xd2, 0x08, 0x23, 0x9c, 0x99, 0x00, 0x62, 0x7a, 0x86, 0x58, 0x0a, 0xa2, 0x5b, 0xf3, 0xe0, 0xdb,
0x25, 0xde, 0xd8, 0xba, 0xc1, 0x20, 0xf7, 0xfc, 0xc7, 0x16, 0xb0, 0x69, 0x30, 0x5d, 0xa0, 0xf0,
0xf8, 0x02, 0x31, 0x11, 0x96, 0x08, 0x6c, 0x1f, 0x5f, 0x22, 0x26, 0xc2, 0x12, 0xc6, 0xab, 0x3d,
0xc9, 0xdd, 0xc7, 0xd6, 0x55, 0x84, 0x41, 0xae, 0x7f, 0x7c, 0x43, 0x36, 0xcd, 0xd6, 0x3b, 0x82,
0x06, 0x26, 0xd6, 0x40, 0xf6, 0xc4, 0xb5, 0x51, 0x89, 0xe2, 0xf0, 0xcc, 0x0b, 0x00, 0xf4, 0xdb,
0xb3, 0x83, 0x0f, 0x96, 0xe4, 0x82, 0x20, 0xbd, 0x89, 0x5c, 0x7a, 0x87, 0x90, 0x76, 0xb4, 0x32,
0xde, 0x26, 0x93, 0x4a, 0x53, 0x48, 0xa5, 0x65, 0xb3, 0xd2, 0x9b, 0x2d, 0x91, 0x33, 0x99, 0xd9,
0x93, 0x40, 0xd8, 0xe0, 0x19, 0x8c, 0x3c, 0x3c, 0xd8, 0x88, 0x46, 0x31, 0x0d, 0x24, 0x6a, 0x52,
0x99, 0xf7, 0x11, 0x87, 0x60, 0x62, 0x31, 0x09, 0x36, 0x18, 0xd0, 0x51, 0xe8, 0xbb, 0x40, 0xad,
0xd2, 0xd4, 0xe3, 0xe2, 0x01, 0x5b, 0x20, 0x92, 0xe5, 0x70, 0x92, 0x6a, 0x9a, 0x6e, 0x75, 0xe5,
0xfa, 0x21, 0x10, 0x0a, 0x42, 0x5e, 0xa3, 0xd5, 0x6c, 0xea, 0x7a, 0x7b, 0xf1, 0x31, 0x50, 0xbb,
0xec, 0x82, 0xab, 0x0a, 0x65, 0x6c, 0xbb, 0x83, 0x32, 0xc8, 0x1f, 0x09, 0x7a, 0xa3, 0x94, 0x76,
0xc4, 0x64, 0xd8, 0x26, 0xee, 0x39, 0x83, 0x91, 0x06, 0xc6, 0xd6, 0xea, 0x66, 0xc5, 0xdc, 0x82,
0x51, 0x03, 0xa6, 0x53, 0x62, 0xad, 0x66, 0xf3, 0xc7, 0xc0, 0x22, 0x51, 0x25, 0x53, 0x39, 0x04,
0x40, 0xd8, 0x77, 0x03, 0x3f, 0x88, 0xf2, 0xd9, 0xda, 0x9a, 0x98, 0xc8, 0xec, 0xdb, 0x07, 0xc8,
0xe7, 0x4c, 0xc7, 0x45, 0x7e, 0xf3, 0xc7, 0xb9, 0x34, 0x9a, 0xd2, 0x3c, 0x22, 0x11, 0x18, 0x22,
0x31, 0x4d, 0x26, 0x7e, 0x6a, 0xcd, 0x32, 0x35, 0x13, 0xe5, 0x3f, 0xdc, 0x85, 0xf1, 0x82, 0x13,
0xbd, 0x5d, 0xe2, 0xcf, 0xb4, 0x78, 0xdc, 0x51, 0x35, 0x7f, 0x6d, 0xcd, 0xd2, 0xd4, 0xef, 0x1a,
0xdf, 0x46, 0x37, 0xd0, 0x22, 0xe8, 0xc6, 0xc8, 0x73, 0x29, 0x57, 0x70, 0x9d, 0x9d, 0x02, 0xdc,
0xba, 0x8f, 0xe6, 0x17, 0x8e, 0xa1, 0x7c, 0x93, 0x8b, 0xa6, 0x0a, 0xd1, 0xb1, 0xc4, 0x05, 0x76,
0x7d, 0xe4, 0xf9, 0xae, 0xc6, 0x47, 0xe5, 0xd1, 0x24, 0xdd, 0xcd, 0x68, 0x92, 0x8c, 0x34, 0x81,
0x3d, 0x42, 0x9f, 0xea, 0x53, 0x9d, 0xf1, 0xe7, 0x86, 0xde, 0xbf, 0x65, 0xc9, 0x72, 0x6c, 0x51,
0x38, 0xbd, 0x37, 0xd4, 0xa8, 0x09, 0x63, 0x96, 0x65, 0xd9, 0x3d, 0xb4, 0x61, 0xb6, 0x9e, 0x51,
0x20, 0x28, 0x0b, 0xba, 0xf6, 0xe9, 0xd0, 0x81, 0xbd, 0x35, 0xbe, 0x52, 0x90, 0xf1, 0x35, 0xb0,
0xaf, 0xb2, 0xd9, 0xd0, 0x03, 0xf7, 0xd2, 0xb7, 0xba, 0x7d, 0x53, 0xe1, 0x86, 0xde, 0x29, 0x16,
0xec, 0x14, 0x1f, 0xcd, 0x9c, 0x47, 0xc0, 0x01, 0x4d, 0x6f, 0x57, 0x40, 0xf9, 0x34, 0xb8, 0x4e,
0x47, 0xdd, 0xa6, 0xe4, 0xa5, 0x7c, 0x70, 0x51, 0x3b, 0x6f, 0xbd, 0x75, 0x59, 0x81, 0x37, 0xa5,
0x7e, 0x02, 0x2e, 0x58, 0x3d, 0x15, 0x64, 0x83, 0x11, 0x9d, 0x7f, 0x2c, 0x9e, 0x54, 0x2e, 0x38,
0xd5, 0x4a, 0xe5, 0x58, 0xf9, 0x82, 0x9d, 0xfc, 0xd3, 0xe7, 0x1e, 0xaa, 0x39, 0x83, 0xfe, 0xb4,
0x73, 0x07, 0xdb, 0x86, 0x77, 0x26, 0x13, 0x9c, 0xf7, 0x39, 0xb3, 0xd6, 0xd6, 0x72, 0x2d, 0x89,
0x21, 0x76, 0xbd, 0xa5, 0x3d, 0x3c, 0x09, 0xca, 0x38, 0x05, 0x05, 0xd4, 0x6c, 0x38, 0x2c, 0x06,
0x2e, 0xb6, 0x51, 0xbf, 0x5c, 0x9f, 0x33, 0x6e, 0x15, 0xe2, 0x60, 0xe4, 0x1b, 0x80, 0xd2, 0xcc,
0x83, 0x7e, 0xa3, 0x40, 0x14, 0x6e, 0xa4, 0x6a, 0x21, 0xb0, 0xda, 0x6f, 0x4f, 0x73, 0x37, 0xa6,
0x48, 0xf9, 0x67, 0xf9, 0xa8, 0xce, 0x9c, 0x6d, 0xb8, 0x21, 0x5a, 0xa8, 0x6e, 0x2b, 0x2d, 0xa9,
0x6a, 0xf3, 0x68, 0xaa, 0x7d, 0x1d, 0xa2, 0xb2, 0x4d, 0x0b, 0x12, 0xb0, 0xc7, 0x60, 0xb8, 0x30,
0x16, 0xd6, 0xa4, 0x03, 0x61, 0x2e, 0xc6, 0xb2, 0xc5, 0x30, 0x88, 0x67, 0x4c, 0xd3, 0x49, 0x1c,
0x90, 0x80, 0xde, 0x91, 0xe3, 0x38, 0x1c, 0x7b, 0x09, 0xb3, 0x17, 0x2c, 0x82, 0xe8, 0x66, 0xa5,
0x80, 0x42, 0x0c, 0x1b, 0xf3, 0x22, 0x84, 0x0b, 0x76, 0xe3, 0xd8, 0xb6, 0xf7, 0x98, 0x78, 0xda,
0x06, 0xb3, 0x4c, 0xed, 0x95, 0xd6, 0xd4, 0x10, 0x0f, 0x8e, 0xde, 0xe7, 0x83, 0x4d, 0x43, 0xde,
0x3d, 0xac, 0x34, 0xa7, 0x97, 0x60, 0x24, 0x2a, 0x68, 0xef, 0xc2, 0x79, 0xd2, 0x2a, 0xd2, 0x87,
0x61, 0xea, 0x0d, 0xe8, 0x57, 0xc1, 0xf9, 0x09, 0xa8, 0xe1, 0x8e, 0xd1, 0x0c, 0x6e, 0xcc, 0x6e,
0xdb, 0x86, 0xcb, 0x75, 0x9c, 0x39, 0x4b, 0x70, 0x4d, 0x4f, 0x44, 0x15, 0x9d, 0x41, 0x37, 0xe3,
0x1f, 0xd6, 0xd6, 0xf8, 0xbf, 0x42, 0x53, 0x77, 0xe2, 0x92, 0x31, 0xe7, 0x8e, 0x56, 0x6f, 0xcb,
0x51, 0xce, 0xfa, 0xaf, 0xc0, 0x1a, 0x23, 0xcb, 0xbd, 0x12, 0x3e, 0xca, 0x43, 0x86, 0xb6, 0x1a,
0x31, 0xb0, 0xe3, 0xb6, 0xd5, 0xb3, 0x1b, 0xd5, 0x40, 0xa0, 0x6d, 0xdb, 0xd3, 0x12, 0x2d, 0x11,
0xec, 0x09, 0x4b, 0x41, 0xa4, 0x9a, 0x16, 0xc8, 0xdb, 0xfb, 0xac, 0x71, 0x86, 0xf0, 0xe7, 0x5e,
0x70, 0x6d, 0x1b, 0xf6, 0x31, 0x16, 0x09, 0xef, 0x3c, 0xdf, 0x07, 0xaf, 0x39, 0x84, 0x63, 0x8f,
0x08, 0x64, 0x7a, 0xdb, 0x58, 0xf2, 0xc7, 0xa0, 0x7a, 0x55, 0x06, 0xfa, 0x93, 0x20, 0x45, 0xdd,
0x59, 0xed, 0xbe, 0x82, 0x5c, 0x19, 0x1e, 0x76, 0x49, 0x42, 0x21, 0x4a, 0x73, 0x13, 0xd3, 0x34,
0x6d, 0xe3, 0xe2, 0x52, 0x18, 0x5f, 0x39, 0xcf, 0x7a, 0x65, 0xe4, 0x9f, 0xf3, 0x58, 0x1a, 0xe2,
0x15, 0xf9, 0x99, 0x05, 0x2c, 0x59, 0x3e, 0x65, 0x7d, 0xbd, 0xbc, 0xfc, 0xf1, 0x70, 0x2f, 0x9f,
0x0a, 0x26, 0x45, 0x59, 0x06, 0x36, 0x40, 0xf9, 0x56, 0x0a, 0x31, 0xf2, 0x71, 0xbd, 0x40, 0x6d,
0xdb, 0x6a, 0xe2, 0x0a, 0x9f, 0x3a, 0x71, 0x8e, 0xd9, 0x0c, 0xde, 0xba, 0x31, 0x53, 0x95, 0x04,
0x91, 0x68, 0xd1, 0xcd, 0x2a, 0xd5, 0xcf, 0xf6, 0xf0, 0xbe, 0xa2, 0x86, 0xec, 0x18, 0xa5, 0x11,
0xd0, 0x32, 0x72, 0x4a, 0x81, 0xb5, 0xf6, 0xd9, 0x88, 0x12, 0xde, 0xc2, 0x04, 0xa9, 0x1b, 0x84,
0x91, 0x20, 0xc5, 0xd4, 0x4d, 0x48, 0x1a, 0x92, 0x3e, 0xe4, 0xd4, 0x00, 0x62, 0x14, 0x87, 0x81,
0xf7, 0x27, 0x75, 0x91, 0xb2, 0x42, 0x90, 0xf2, 0x9b, 0x90, 0x5a, 0x79, 0x32, 0x64, 0x78, 0xd1,
0x76, 0x70, 0x3d, 0x23, 0x6e, 0xf9, 0x2a, 0xc5, 0x70, 0xee, 0x1c, 0x0f, 0x29, 0xac, 0x56, 0x45,
0xa7, 0xa8, 0x87, 0x3c, 0x95, 0x81, 0x8c, 0x3b, 0xa0, 0xee, 0xd1, 0xb0, 0x77, 0x74, 0x06, 0x39,
0x50, 0x39, 0x6d, 0x81, 0x31, 0x79, 0x28, 0x65, 0xda, 0xc3, 0x83, 0x56, 0x5a, 0xd4, 0x54, 0xb4,
0xc4, 0x3e, 0x82, 0x6c, 0x2d, 0x1c, 0x92, 0xb3, 0xd8, 0xb9, 0x45, 0xbd, 0x90, 0x82, 0x07, 0x1e,
0x36, 0x00, 0x32, 0xa0, 0x6d, 0x26, 0x40, 0x65, 0xcf, 0x87, 0x8f, 0x31, 0xde, 0x72, 0xe3, 0x34,
0xac, 0xe8, 0xf2, 0x92, 0xbd, 0x97, 0x10, 0xd1, 0x85, 0xa2, 0x50, 0x60, 0xbe, 0x29, 0xd1, 0xa7,
0x28, 0x3c, 0x9e, 0x60, 0xd7, 0x3b, 0x97, 0xa7, 0x68, 0xf9, 0x11, 0x62, 0x5e, 0x1c, 0x1a, 0xf0,
0xbc, 0x19, 0x18, 0xa3, 0x24, 0xd1, 0x72, 0xb0, 0x93, 0xc6, 0xf7, 0x59, 0xe0, 0xdc, 0x7a, 0xd7,
0x4e, 0x1a, 0xc6, 0xe6, 0xad, 0x28, 0xc8, 0x6c, 0x40, 0x08, 0x3d, 0x05, 0xde, 0x0f, 0x46, 0xd9,
0x54, 0x61, 0xb6, 0x52, 0xd4, 0x12, 0x00, 0x0c, 0x5e, 0xd5, 0xca, 0xd8, 0x3f, 0x6b, 0x6b, 0xec,
0x9f, 0x99, 0xf8, 0xc4, 0x28, 0x50, 0x5c, 0x5b, 0x2b, 0x0b, 0x5d, 0xf1, 0x44, 0x37, 0x2a, 0xf8,
0x2a, 0xab, 0x66, 0x35, 0xa8, 0x32, 0x17, 0xc4, 0xf2, 0x55, 0x53, 0x15, 0x4b, 0xa5, 0x36, 0x96,
0xa9, 0xbb, 0x5b, 0xda, 0xfc, 0xfd, 0xb9, 0xcd, 0x63, 0x82, 0x44, 0x0a, 0x48, 0x2a, 0xd1, 0xc6,
0xee, 0xa1, 0x33, 0xa6, 0x3a, 0x46, 0x9b, 0x2b, 0xf2, 0x1b, 0xc6, 0x66, 0x8c, 0x30, 0xf6, 0xda,
0xda, 0x0a, 0x17, 0xb6, 0x92, 0xa3, 0xb4, 0x7f, 0x45, 0x1b, 0xd3, 0xa7, 0xd7, 0x1e, 0xda, 0xfb,
0x5b, 0x30, 0x3e, 0x64, 0x9d, 0x44, 0x80, 0x04, 0xc4, 0x77, 0x03, 0x3e, 0xc7, 0xb4, 0x75, 0x1d,
0x16, 0xab, 0x10, 0x79, 0x31, 0x65, 0x1e, 0x48, 0x21, 0x55, 0xc2, 0x7a, 0xe1, 0x4c, 0x80, 0xc9,
0x0e, 0x86, 0xe0, 0xb9, 0x6e, 0x61, 0xef, 0x86, 0x14, 0x2c, 0x93, 0xec, 0x82, 0xb4, 0xdd, 0x87,
0x13, 0x92, 0x4c, 0x62, 0xba, 0x33, 0xbb, 0x1d, 0x2b, 0xa9, 0x2c, 0xb3, 0x1b, 0x9b, 0x38, 0x0b,
0x4e, 0x47, 0x31, 0x12, 0x19, 0x48, 0x04, 0x7a, 0x45, 0x2d, 0x0e, 0x68, 0x48, 0x41, 0x84, 0x34,
0xdb, 0x6c, 0x44, 0x61, 0x02, 0x16, 0x20, 0x1b, 0xd3, 0x74, 0x14, 0xba, 0x6d, 0xfb, 0xf8, 0xe8,
0xf4, 0xcc, 0x36, 0xf0, 0xde, 0x9b, 0xc6, 0x49, 0x3b, 0x5b, 0x15, 0xb6, 0x6a, 0x1d, 0x3d, 0xc1,
0x6a, 0xdb, 0x86, 0xc4, 0x00, 0xe2, 0x48, 0x66, 0x7a, 0x1a, 0xbf, 0x27, 0x90, 0x3b, 0x41, 0xd6,
0x10, 0xba, 0xf7, 0xed, 0x5f, 0x4e, 0x8f, 0x0e, 0x21, 0x80, 0xc2, 0x53, 0x7a, 0xc3, 0x7b, 0x2d,
0x83, 0x13, 0xb4, 0xc5, 0x29, 0x58, 0x62, 0x80, 0x7c, 0x91, 0x18, 0x98, 0xe1, 0x8d, 0x9e, 0x29,
0xc7, 0xe1, 0x11, 0x80, 0x2d, 0x78, 0x49, 0xbe, 0x7b, 0x96, 0xc9, 0x95, 0xdf, 0x91, 0xa1, 0xe3,
0xf9, 0xd4, 0x6d, 0x93, 0x67, 0x59, 0xbe, 0x1a, 0x08, 0x97, 0x4e, 0x92, 0xe9, 0xec, 0xd0, 0x19,
0x28, 0xe5, 0x14, 0x8c, 0x2f, 0x77, 0xc4, 0xd3, 0x59, 0x9e, 0xed, 0xcc, 0x78, 0xa2, 0xb6, 0x92,
0xb0, 0x32, 0x8a, 0x9c, 0x32, 0x48, 0x46, 0xa1, 0x65, 0x1a, 0x08, 0x15, 0xda, 0xa3, 0x10, 0x42,
0x43, 0x3f, 0xbc, 0x86, 0xaf, 0xc6, 0x2c, 0xea, 0x87, 0x34, 0xbd, 0x0b, 0xe3, 0x1b, 0x42, 0xe3,
0x38, 0x8c, 0x11, 0x59, 0x6a, 0x0a, 0x7f, 0x0c, 0xf8, 0x4c, 0xab, 0x82, 0x5b, 0x54, 0x57, 0x31,
0x47, 0xe7, 0x41, 0xab, 0x44, 0x96, 0x65, 0xed, 0x80, 0x31, 0x16, 0x6d, 0xed, 0x1d, 0x1b, 0xa6,
0x5e, 0xb1, 0x8f, 0x6d, 0xf6, 0x91, 0x15, 0x63, 0x6d, 0x24, 0x66, 0x9d, 0x34, 0x9c, 0x8d, 0xc0,
0x4a, 0x31, 0x59, 0xc8, 0x77, 0x20, 0x29, 0x18, 0xf6, 0x67, 0x19, 0x42, 0x9d, 0x9a, 0x04, 0x79,
0xe9, 0x05, 0x13, 0x26, 0x17, 0xff, 0x7b, 0xc4, 0xa2, 0xa8, 0x38, 0x7f, 0x89, 0x34, 0xcc, 0x02,
0xe6, 0x64, 0xc1, 0xae, 0xcc, 0x78, 0x37, 0xd5, 0x9a, 0xba, 0x99, 0x86, 0xe7, 0xd8, 0xd9, 0xb1,
0x07, 0xfa, 0xaf, 0xe9, 0x2f, 0xd9, 0xc3, 0x04, 0x4e, 0x45, 0xb5, 0x96, 0x3e, 0xcd, 0x49, 0x9a,
0x5b, 0x5b, 0xf4, 0x04, 0xba, 0xb1, 0x94, 0xfc, 0x3c, 0x59, 0x60, 0x14, 0x1b, 0xa7, 0x7a, 0x4c,
0xc6, 0xb0, 0x20, 0xbc, 0xb3, 0x1a, 0x2f, 0xc8, 0x3f, 0xaf, 0xae, 0x8e, 0xcf, 0x4f, 0x7a, 0x57,
0x57, 0xe4, 0x45, 0x83, 0xc5, 0x9c, 0xfb, 0xc0, 0x6e, 0x1e, 0x8b, 0x59, 0xd7, 0x54, 0xb3, 0x45,
0x43, 0x04, 0x10, 0x80, 0x8d, 0x89, 0x3a, 0x0f, 0x1c, 0x1a, 0xd6, 0x63, 0x00, 0xf3, 0x06, 0x4c,
0xea, 0xbf, 0xc0, 0x2c, 0x81, 0xcb, 0x5d, 0x7f, 0x96, 0x9d, 0x32, 0x16, 0x69, 0xe2, 0xd9, 0x01,
0x30, 0x77, 0x04, 0x24, 0x68, 0xe9, 0x66, 0xe4, 0xb8, 0xac, 0x89, 0x41, 0xdb, 0x30, 0xec, 0xa6,
0x5d, 0x33, 0x17, 0xb7, 0x85, 0xe8, 0x63, 0x66, 0xe2, 0x59, 0x75, 0xe2, 0xdb, 0x70, 0x12, 0x27,
0x75, 0x33, 0xdb, 0x33, 0xdb, 0x83, 0x8c, 0xa6, 0x74, 0xb9, 0xb9, 0xa7, 0x3c, 0xe4, 0xab, 0x9b,
0x0b, 0xb9, 0x9b, 0xd2, 0x03, 0xc2, 0xa3, 0x68, 0x25, 0xc0, 0x57, 0x9e, 0x51, 0x1f, 0xdc, 0xa4,
0x5f, 0x2d, 0xea, 0x88, 0x76, 0x5d, 0x60, 0x32, 0xd2, 0xb3, 0xb8, 0xcf, 0xb5, 0x75, 0x53, 0x5e,
0xf4, 0x62, 0x6d, 0x96, 0x3d, 0xcc, 0xaf, 0x8d, 0xcb, 0x0f, 0x95, 0x24, 0x75, 0x6e, 0xfb, 0x00,
0x47, 0x2c, 0xab, 0xc1, 0x95, 0x87, 0x41, 0x89, 0xb8, 0x80, 0x67, 0x81, 0x79, 0x62, 0x5d, 0xd8,
0x45, 0xe7, 0x08, 0x44, 0x31, 0xb2, 0xbf, 0x03, 0x3e, 0xe6, 0x6d, 0x18, 0x18, 0xdc, 0xe4, 0x7d,
0x04, 0xf0, 0x25, 0xbf, 0xe1, 0xb7, 0x2f, 0x8b, 0x10, 0xa4, 0x72, 0xbb, 0x3f, 0xf9, 0x74, 0x25,
0x10, 0x01, 0x95, 0x64, 0x35, 0xe8, 0xab, 0x42, 0x96, 0x8e, 0x77, 0x4f, 0x76, 0x0f, 0xae, 0x9e,
0x65, 0x72, 0x92, 0xe9, 0xb9, 0x66, 0x32, 0xe9, 0x73, 0xbd, 0xd6, 0x20, 0xe8, 0xc4, 0xca, 0xad,
0x82, 0x7f, 0x0e, 0xac, 0x34, 0xaa, 0x40, 0xd5, 0x8d, 0x1c, 0x94, 0x34, 0x78, 0xd8, 0xb9, 0x61,
0x33, 0x09, 0xbf, 0x18, 0xa1, 0xa4, 0x18, 0x63, 0x2e, 0x05, 0x97, 0x56, 0x3e, 0x95, 0x89, 0xb0,
0x09, 0xd9, 0xb9, 0x07, 0x1a, 0xd4, 0x06, 0x4a, 0x8f, 0x9d, 0x48, 0x3b, 0x64, 0xbd, 0x08, 0x7a,
0x47, 0x81, 0x2e, 0x64, 0x5d, 0x63, 0x70, 0x5e, 0x7c, 0xdf, 0x7c, 0x29, 0x40, 0xe9, 0xf0, 0x99,
0x57, 0x6f, 0x66, 0x27, 0xc3, 0x48, 0x42, 0xdf, 0x40, 0x28, 0x9d, 0x6a, 0xe5, 0xfd, 0xf4, 0x87,
0x87, 0x66, 0xc1, 0xc6, 0x49, 0x84, 0x5d, 0x26, 0xa7, 0x25, 0x96, 0x80, 0x5e, 0x0e, 0xc3, 0x58,
0x63, 0x01, 0x9f, 0xd5, 0xec, 0x78, 0xdb, 0x65, 0x8e, 0x89, 0xda, 0x4c, 0xc7, 0x7b, 0xf9, 0x52,
0x07, 0xda, 0x4a, 0xe8, 0x52, 0x49, 0x9f, 0x65, 0xe5, 0xe9, 0x17, 0xde, 0x25, 0x92, 0x73, 0x1e,
0x0b, 0x6a, 0x27, 0xaf, 0x68, 0x2b, 0x12, 0xec, 0xc3, 0xc3, 0x8a, 0x4a, 0x67, 0x0c, 0x26, 0xf2,
0xf3, 0x14, 0xf2, 0x2d, 0x8a, 0xd9, 0x89, 0x22, 0xe4, 0x0f, 0x0f, 0x45, 0x75, 0x06, 0xce, 0x7a,
0x2b, 0x8b, 0xa4, 0xc0, 0x9b, 0x9c, 0x9d, 0x2a, 0xfe, 0x35, 0x8c, 0x23, 0x8c, 0xe0, 0xd6, 0x81,
0x93, 0x8e, 0xcc, 0xa1, 0x1f, 0x02, 0x4d, 0x66, 0xe8, 0xdc, 0xd8, 0xfc, 0x1e, 0x2c, 0xa3, 0xe4,
0xed, 0xc2, 0xa9, 0xcf, 0x71, 0x6a, 0xe3, 0xfb, 0xa6, 0xde, 0x29, 0x33, 0x04, 0x6d, 0x98, 0xb0,
0x01, 0x6c, 0xbf, 0x45, 0x46, 0x42, 0x32, 0x7e, 0xd6, 0x36, 0x70, 0x39, 0xa8, 0x40, 0x9e, 0xc1,
0x61, 0xc6, 0x73, 0x97, 0x72, 0x15, 0xee, 0xb8, 0x39, 0x01, 0x85, 0x76, 0xe6, 0x34, 0xfc, 0x63,
0x42, 0xe3, 0xfb, 0x53, 0xea, 0xd3, 0x01, 0x04, 0xef, 0xbb, 0x3e, 0xe4, 0x1f, 0x82, 0x07, 0x92,
0xde, 0xe8, 0xfa, 0x4a, 0x6b, 0x85, 0x9c, 0x00, 0x49, 0x9b, 0x3a, 0xf7, 0x59, 0xbc, 0x26, 0xea,
0xdc, 0x63, 0x82, 0x67, 0x65, 0xd3, 0x4e, 0x2e, 0x65, 0xec, 0xe6, 0x09, 0x72, 0x99, 0xd2, 0x7a,
0x8e, 0x0e, 0x24, 0xc8, 0x52, 0x45, 0x71, 0x07, 0xf8, 0x0a, 0x2c, 0xca, 0x7d, 0x01, 0x9b, 0xe2,
0xa6, 0x96, 0xf4, 0x17, 0x9a, 0x2a, 0xe8, 0xc6, 0x3d, 0xb8, 0x03, 0xcb, 0x4d, 0xcb, 0xde, 0xc1,
0x18, 0xa3, 0x33, 0x10, 0xc3, 0xc2, 0x31, 0x18, 0x2e, 0x96, 0xd7, 0xd2, 0xc2, 0xfc, 0x1b, 0x9c,
0xf3, 0x7c, 0x48, 0x18, 0xfa, 0x9c, 0xc9, 0x62, 0xa9, 0x34, 0xea, 0x86, 0xc8, 0xd2, 0xc5, 0x78,
0x6e, 0xc0, 0x3b, 0xe2, 0xa4, 0x26, 0x0a, 0x94, 0x2a, 0x19, 0xb8, 0x85, 0x79, 0x7e, 0xb6, 0xa7,
0x21, 0x7e, 0x1c, 0x1d, 0x44, 0xc0, 0x28, 0x99, 0x09, 0x09, 0x55, 0x6f, 0xb0, 0x2c, 0x58, 0xd6,
0x68, 0xf3, 0xf3, 0xcb, 0x86, 0x2e, 0x5b, 0x97, 0xdb, 0xc4, 0xd2, 0x16, 0x5f, 0x61, 0x2c, 0xae,
0x1a, 0x80, 0x8a, 0xf6, 0x77, 0x0a, 0x60, 0x26, 0xf3, 0xfc, 0xc9, 0xaf, 0x1e, 0x50, 0x41, 0xa8,
0x24, 0x84, 0x52, 0x19, 0xe7, 0x13, 0x08, 0x05, 0x0b, 0xe1, 0x4a, 0xc6, 0xf1, 0x7b, 0x9d, 0x67,
0x88, 0x96, 0x02, 0xb4, 0x53, 0x56, 0x20, 0xde, 0x4d, 0x05, 0x21, 0xbe, 0x36, 0x63, 0x8a, 0x2a,
0x98, 0xa0, 0x59, 0xe0, 0xc8, 0xb3, 0xdd, 0x28, 0x24, 0x48, 0xc9, 0xc3, 0x03, 0xbf, 0x35, 0xac,
0x8e, 0x5f, 0xe4, 0x08, 0x5d, 0x5a, 0x5c, 0x96, 0xa7, 0x29, 0xeb, 0x20, 0x2e, 0x42, 0x59, 0xfb,
0x14, 0x32, 0x28, 0x4c, 0x4b, 0xf0, 0xee, 0xb1, 0x6d, 0x4b, 0x20, 0xbc, 0x6e, 0xf2, 0x97, 0xc6,
0x85, 0x72, 0xe7, 0xe5, 0x22, 0xc2, 0x37, 0x2c, 0x0a, 0xc4, 0xe2, 0x44, 0x82, 0x2d, 0x54, 0x5c,
0x0b, 0x92, 0xcf, 0xcb, 0x11, 0x4a, 0x1a, 0x28, 0xef, 0x8f, 0xb8, 0xd9, 0xed, 0x56, 0xad, 0x26,
0xaf, 0xe0, 0x2a, 0x36, 0x73, 0x61, 0x64, 0xd0, 0x5c, 0x14, 0x19, 0x34, 0xbf, 0x52, 0xe0, 0x58,
0xb1, 0x57, 0x0a, 0x64, 0x30, 0x57, 0x8f, 0x85, 0xfc, 0xa0, 0x8d, 0x76, 0x0d, 0x03, 0xa4, 0xc8,
0xb0, 0x2d, 0x55, 0xea, 0xb3, 0x85, 0x84, 0xd3, 0x15, 0xc4, 0xa7, 0x42, 0xe9, 0x9c, 0xc6, 0xec,
0x66, 0x9b, 0x6f, 0x95, 0x4f, 0x41, 0x59, 0xd0, 0xb0, 0xea, 0xa5, 0x48, 0xe3, 0xcf, 0x61, 0x2a,
0x25, 0x11, 0xff, 0x81, 0x18, 0x81, 0x39, 0xdb, 0xf3, 0x77, 0x5a, 0x4d, 0x2a, 0x26, 0x90, 0xe9,
0xe1, 0x3f, 0x1c, 0x11, 0x14, 0xe4, 0x1c, 0x17, 0xaa, 0x06, 0xd1, 0x05, 0x9c, 0x0c, 0x01, 0x9b,
0x71, 0x3a, 0xb8, 0x02, 0xf2, 0x3f, 0x3c, 0xcc, 0x96, 0xc8, 0xd8, 0xce, 0xe6, 0x38, 0xb9, 0x5e,
0xb1, 0xac, 0xdb, 0xd0, 0x73, 0x09, 0x16, 0xe3, 0x90, 0x8f, 0x30, 0x04, 0x0c, 0xe4, 0x0a, 0x2a,
0xe7, 0x88, 0xd9, 0xa2, 0xad, 0xb1, 0xba, 0x42, 0x0c, 0x97, 0x57, 0x89, 0x41, 0x48, 0x35, 0xde,
0xe0, 0xeb, 0x2f, 0xda, 0x06, 0xc8, 0x90, 0xc6, 0x9e, 0xf4, 0x27, 0x9e, 0xef, 0x5e, 0x89, 0xb6,
0x18, 0x70, 0xc6, 0xc5, 0x18, 0x22, 0xaf, 0x4b, 0xa0, 0xfc, 0x79, 0x19, 0x68, 0x69, 0xe9, 0x4b,
0x9b, 0x68, 0xf6, 0xcb, 0xca, 0xf2, 0x97, 0xb6, 0x6e, 0x73, 0xb5, 0x46, 0xe3, 0xfa, 0xae, 0x2e,
0x49, 0x18, 0x72, 0x34, 0xf0, 0xb9, 0x72, 0x92, 0x95, 0x7c, 0xfe, 0xe2, 0xe8, 0x41, 0xb9, 0x3c,
0x2a, 0x45, 0x0f, 0x00, 0x2a, 0x87, 0x90, 0x7b, 0x9f, 0x79, 0xd9, 0x4b, 0x81, 0xc1, 0x0b, 0x34,
0xe2, 0x9d, 0x62, 0xef, 0xdc, 0xf1, 0x73, 0x9f, 0x01, 0xce, 0xa0, 0x36, 0x7d, 0xc9, 0x9f, 0x2e,
0x95, 0xc0, 0xe4, 0xb3, 0x1f, 0x4f, 0x61, 0xf2, 0xa9, 0x4b, 0x24, 0x31, 0x05, 0x12, 0xcb, 0xa4,
0x31, 0xf9, 0xec, 0x45, 0x89, 0xcc, 0x54, 0x72, 0xee, 0x68, 0x92, 0x2a, 0xac, 0x2b, 0x3a, 0xa1,
0x15, 0x06, 0x06, 0x60, 0xe0, 0xae, 0x1c, 0xdf, 0x89, 0xc7, 0x39, 0x1b, 0x97, 0x24, 0x7c, 0xb1,
0xb2, 0x20, 0x3f, 0xdf, 0xf0, 0xff, 0x3c, 0xfd, 0x19, 0x7d, 0x0a, 0xdf, 0x0a, 0xca, 0xc8, 0x6d,
0xca, 0x71, 0xde, 0xac, 0xa3, 0x49, 0x73, 0x55, 0xcd, 0x0d, 0xa4, 0x37, 0x55, 0x12, 0xbf, 0x92,
0xfe, 0x15, 0x81, 0x49, 0x87, 0x1b, 0xa7, 0x52, 0x58, 0xa2, 0xaa, 0xe2, 0x2c, 0x8c, 0xcf, 0xd6,
0xc9, 0x59, 0x50, 0x70, 0xa4, 0x1a, 0xf8, 0x8a, 0x91, 0x29, 0xe3, 0x95, 0x1b, 0xb0, 0x96, 0xae,
0x57, 0x0d, 0xad, 0x4a, 0x14, 0x26, 0x79, 0xac, 0x47, 0x8d, 0x9d, 0x97, 0x7d, 0xc2, 0xaa, 0x49,
0x88, 0xf5, 0x13, 0x1e, 0x47, 0x5b, 0x47, 0xfd, 0xdf, 0x21, 0x4a, 0x36, 0x01, 0xb3, 0xd8, 0x03,
0x76, 0x55, 0x68, 0xad, 0x9b, 0x38, 0x59, 0xd3, 0x1c, 0xa3, 0xaf, 0x5b, 0x5d, 0x07, 0x2f, 0xb4,
0x59, 0x7f, 0x3f, 0xdd, 0x0b, 0xc7, 0x11, 0x76, 0xe9, 0xf5, 0x61, 0x48, 0x67, 0xf2, 0x3f, 0xd3,
0x3a, 0xa5, 0x8b, 0x88, 0xf9, 0xe2, 0x86, 0xde, 0xf3, 0x30, 0xec, 0x12, 0xa2, 0x66, 0x75, 0x73,
0x11, 0x34, 0xcf, 0xa4, 0x59, 0xb0, 0x60, 0xca, 0x95, 0x8a, 0x67, 0x3e, 0x2b, 0xde, 0x17, 0xd1,
0xdb, 0x13, 0x24, 0x56, 0x93, 0x0d, 0x1e, 0xd5, 0xb1, 0xe0, 0x32, 0xab, 0x6d, 0xfa, 0x9a, 0xa5,
0xa6, 0xf4, 0xa8, 0x2c, 0xbc, 0xcd, 0x18, 0x31, 0xcb, 0xed, 0x31, 0x4b, 0x9e, 0x37, 0x0e, 0xef,
0x2c, 0xb9, 0x3a, 0x81, 0xb0, 0xe2, 0x84, 0x5d, 0xc7, 0x40, 0x9c, 0xe2, 0xb7, 0x2c, 0x78, 0x26,
0x46, 0xf7, 0xe0, 0xbb, 0xd6, 0xe4, 0xe3, 0x1b, 0xd5, 0xf1, 0x96, 0xde, 0x61, 0xf3, 0x4b, 0xf7,
0x59, 0xb0, 0x6d, 0xa7, 0xa0, 0xe7, 0xbc, 0xa6, 0x1b, 0xd9, 0x16, 0xa5, 0x44, 0xc1, 0xf8, 0x07,
0x30, 0xe5, 0x84, 0xc1, 0xa2, 0x3d, 0xea, 0xa6, 0xbd, 0xc3, 0x2f, 0x1e, 0xdb, 0x32, 0x42, 0x36,
0x64, 0x66, 0x63, 0x95, 0xf9, 0x64, 0xcc, 0x90, 0x55, 0x8c, 0xe4, 0xaf, 0x32, 0x48, 0x09, 0xd5,
0xca, 0x75, 0x16, 0xf6, 0x4a, 0xc3, 0x94, 0x1f, 0xb0, 0xd4, 0xf2, 0x22, 0x0a, 0x30, 0x8f, 0x65,
0x73, 0xe0, 0x86, 0x2b, 0x21, 0x63, 0xc6, 0x37, 0x9e, 0xed, 0x6f, 0x60, 0xdd, 0x17, 0xb6, 0xa1,
0x20, 0x82, 0x9b, 0x9b, 0x09, 0x83, 0xa9, 0xe1, 0x75, 0xa9, 0x5e, 0xd7, 0xfa, 0xd7, 0x9c, 0x09,
0xe9, 0xaa, 0x3d, 0xaa, 0x8c, 0xa1, 0xd8, 0xdd, 0x5a, 0x58, 0x95, 0x52, 0x47, 0x2c, 0x10, 0x3a,
0x7f, 0x2a, 0x7b, 0x40, 0x44, 0x05, 0xbb, 0xfe, 0xa6, 0xba, 0x98, 0x9d, 0x93, 0x0f, 0xef, 0x75,
0xe4, 0xf5, 0xb3, 0xec, 0xf2, 0xc1, 0x69, 0x56, 0x31, 0x17, 0x3f, 0x61, 0xb7, 0x09, 0xaa, 0x0c,
0x7e, 0xe6, 0x37, 0x31, 0xf8, 0xc9, 0xc4, 0x9f, 0x3e, 0x00, 0xed, 0x76, 0x45, 0xda, 0x84, 0x7d,
0xbb, 0x98, 0x34, 0xcd, 0xc6, 0xb6, 0xc7, 0xfc, 0xea, 0x85, 0xd3, 0x84, 0x38, 0x84, 0x75, 0xf8,
0x8a, 0x33, 0x08, 0xc9, 0x9f, 0x53, 0xf9, 0x16, 0x7d, 0xba, 0xf2, 0xe0, 0x6c, 0x11, 0x59, 0x7d,
0x96, 0xe5, 0xfb, 0x4f, 0x57, 0x77, 0xfe, 0x1d, 0xfc, 0x3b, 0xa8, 0xb9, 0x9d, 0x71, 0x86, 0x60,
0x65, 0x04, 0x55, 0x4d, 0xa5, 0x2e, 0xee, 0xc4, 0xb1, 0x73, 0xff, 0x7a, 0x32, 0x1c, 0xd2, 0x58,
0xc6, 0xc9, 0x08, 0x4c, 0x19, 0x06, 0x7d, 0x54, 0xae, 0x17, 0x39, 0x0a, 0x18, 0x84, 0x4a, 0xde,
0xd8, 0x86, 0xcd, 0x1a, 0x41, 0x79, 0xf3, 0xe8, 0xea, 0x4c, 0xdf, 0x2b, 0x91, 0x2f, 0x51, 0xae,
0x76, 0xbf, 0xf9, 0xc7, 0x3f, 0x48, 0xe5, 0x3f, 0xcb, 0x2d, 0x25, 0xe5, 0x57, 0x71, 0xd9, 0x2f,
0x3e, 0x00, 0xbc, 0x02, 0x19, 0x95, 0x06, 0xa6, 0x69, 0xf2, 0xbe, 0xd3, 0xcf, 0xdf, 0xae, 0x78,
0xb1, 0x92, 0xe0, 0x4f, 0x3a, 0x88, 0x01, 0xf1, 0xea, 0x24, 0xe9, 0x87, 0x3e, 0x9c, 0x86, 0x5d,
0xd2, 0x73, 0x82, 0xae, 0xb3, 0xb7, 0x74, 0x20, 0x3f, 0x59, 0xed, 0x36, 0x9f, 0xd7, 0xee, 0xcd,
0x07, 0x8b, 0xeb, 0xfa, 0x4f, 0xa3, 0x98, 0x55, 0x27, 0x7e, 0x3b, 0xf8, 0xf0, 0x36, 0x4d, 0xa3,
0x13, 0x7c, 0xb9, 0x27, 0x49, 0x3b, 0x30, 0x6c, 0x0a, 0x1e, 0xcd, 0xea, 0x96, 0xdc, 0x04, 0xc2,
0x7e, 0xd9, 0x6f, 0xc6, 0x6b, 0x29, 0xe8, 0x22, 0x26, 0xcc, 0xd0, 0x89, 0x1c, 0x9d, 0xc6, 0xf8,
0xf2, 0x29, 0x0e, 0xc3, 0x57, 0x51, 0x64, 0x60, 0x07, 0xc5, 0x25, 0x00, 0x9c, 0xba, 0x0d, 0x8c,
0xcd, 0x53, 0xc7, 0x7f, 0x81, 0xad, 0x8a, 0x86, 0x84, 0xbc, 0xe8, 0xf2, 0xbf, 0x72, 0x52, 0x1b,
0xbb, 0xd2, 0xe4, 0x2a, 0x30, 0xf9, 0xc5, 0x97, 0x92, 0xa9, 0xac, 0xa0, 0xf2, 0xd2, 0x7e, 0x8e,
0xe9, 0x1b, 0xa7, 0x01, 0x87, 0x28, 0x34, 0xd3, 0x52, 0xb5, 0x54, 0xaa, 0x29, 0xe4, 0x5a, 0xe8,
0x33, 0xf1, 0xba, 0x15, 0x29, 0x33, 0x4b, 0x12, 0x04, 0x60, 0x1b, 0x9a, 0x9c, 0xc0, 0xf3, 0xa2,
0xae, 0x05, 0xd9, 0xe5, 0xda, 0x5a, 0x31, 0xb0, 0xbd, 0xd9, 0x6c, 0xe6, 0xdd, 0x26, 0xd8, 0x61,
0x82, 0x40, 0xb5, 0x6a, 0x94, 0xd8, 0xe3, 0xa9, 0x96, 0xd4, 0xb1, 0xfc, 0x0a, 0xa6, 0x80, 0x33,
0x2d, 0x7d, 0x13, 0x29, 0x36, 0x5a, 0xb4, 0x7a, 0xe4, 0x58, 0xee, 0x26, 0xb0, 0x5b, 0xbc, 0x67,
0x29, 0xdb, 0x25, 0xee, 0x84, 0x5d, 0xa2, 0x72, 0xf2, 0x2c, 0xda, 0xc0, 0xe9, 0x83, 0xcb, 0x5b,
0x6a, 0x03, 0x71, 0x28, 0x9e, 0xa5, 0xfb, 0x94, 0x43, 0x05, 0x36, 0x20, 0xdc, 0x10, 0xfc, 0x81,
0x26, 0x0a, 0x1c, 0x90, 0x1d, 0x83, 0x54, 0xd8, 0x7c, 0x43, 0xc8, 0x1d, 0x85, 0x5c, 0xbe, 0x65,
0x75, 0x0f, 0xbc, 0xba, 0x2c, 0xaa, 0x1e, 0x30, 0x59, 0xad, 0x7a, 0x84, 0x83, 0x94, 0x82, 0xca,
0xa4, 0x60, 0xd1, 0xc7, 0xf9, 0x7a, 0x10, 0x38, 0xc5, 0x90, 0x48, 0x7b, 0x5c, 0xe2, 0xbb, 0xda,
0xba, 0x20, 0x2d, 0x0a, 0x39, 0x67, 0x21, 0x01, 0xec, 0x90, 0x8f, 0xf0, 0x35, 0x90, 0x8f, 0x27,
0x93, 0xc1, 0x00, 0xc4, 0x6c, 0x08, 0x71, 0xf9, 0xfd, 0x0a, 0x11, 0x86, 0xce, 0x4b, 0x84, 0x99,
0x03, 0xba, 0xa1, 0xf6, 0xf7, 0xe3, 0xee, 0xff, 0x37, 0xd7, 0x7c, 0x41, 0x73, 0x4d, 0x5e, 0x86,
0x50, 0x3b, 0x6b, 0x67, 0x1d, 0x59, 0xaf, 0xbe, 0x38, 0x53, 0x04, 0xca, 0xf9, 0xcb, 0x8b, 0x9a,
0xa7, 0x67, 0x7f, 0xc1, 0x95, 0xab, 0x1d, 0x0f, 0xaf, 0xf0, 0xd0, 0x81, 0x6d, 0xa0, 0x4b, 0x0f,
0xa8, 0xdf, 0xf6, 0x58, 0x8f, 0x56, 0x25, 0xb8, 0xa8, 0x79, 0xa9, 0x52, 0x69, 0xa3, 0xc6, 0x62,
0x22, 0x5e, 0x40, 0xbd, 0x09, 0x63, 0x90, 0x3d, 0xb0, 0x2f, 0xf6, 0x09, 0xc5, 0xaa, 0x03, 0x0a,
0xe4, 0x79, 0x84, 0xb7, 0x4e, 0xc8, 0x9f, 0x4b, 0x83, 0xed, 0x44, 0xdd, 0xbd, 0xd0, 0xc5, 0xe9,
0xec, 0x75, 0x85, 0xf2, 0x9f, 0xcb, 0xc7, 0x6f, 0xb0, 0x05, 0x26, 0xd8, 0x74, 0x43, 0xb6, 0xf0,
0x75, 0x50, 0xde, 0x56, 0x42, 0xe4, 0xcf, 0x1a, 0x78, 0x78, 0x37, 0xca, 0x71, 0x34, 0xbf, 0xf9,
0xe6, 0x18, 0x2d, 0x2c, 0x39, 0x7a, 0xcf, 0x1b, 0x96, 0xc0, 0x21, 0x1a, 0x78, 0xf1, 0x0d, 0xc1,
0x43, 0x88, 0x3d, 0x3b, 0xec, 0x12, 0x3c, 0x62, 0xbd, 0x7c, 0x89, 0xf9, 0xb7, 0x5e, 0x7d, 0x23,
0x1f, 0x98, 0xf4, 0x5d, 0xa5, 0x74, 0x1c, 0xd9, 0xd3, 0x27, 0x17, 0x3c, 0xd9, 0x62, 0x24, 0x07,
0xae, 0xc7, 0x5e, 0x9a, 0x98, 0x95, 0x98, 0x94, 0xf0, 0xe8, 0xef, 0x39, 0x94, 0xa8, 0x6c, 0xb2,
0x13, 0x19, 0xcb, 0x1f, 0x47, 0x2c, 0xc3, 0x03, 0x0d, 0xf8, 0x4f, 0x6d, 0x25, 0xc5, 0x61, 0xca,
0x97, 0x6a, 0x5b, 0xec, 0x02, 0x2d, 0xfb, 0x4b, 0x4f, 0x55, 0xa7, 0x32, 0x8b, 0x62, 0x67, 0xa5,
0xa6, 0x2b, 0x1d, 0x35, 0x7b, 0xe3, 0xa1, 0xc6, 0x44, 0xf0, 0xd7, 0x93, 0xc1, 0x44, 0x78, 0x2f,
0x5b, 0xd3, 0xc6, 0x16, 0x1a, 0x0b, 0x45, 0xc9, 0xf0, 0x5e, 0x0f, 0xe3, 0x55, 0x2e, 0xd7, 0xbc,
0x87, 0xa3, 0xf2, 0x98, 0xe4, 0x2f, 0x62, 0x61, 0xbb, 0x4f, 0x2c, 0xf5, 0x03, 0xaf, 0xc5, 0x8b,
0x95, 0xa0, 0x11, 0x77, 0xa8, 0x07, 0x2e, 0xbe, 0x07, 0xa7, 0x81, 0x0b, 0xc5, 0x1f, 0x58, 0x01,
0x55, 0x28, 0xb4, 0x85, 0xbd, 0xdf, 0x48, 0x26, 0x81, 0xd0, 0xb7, 0x31, 0x24, 0xcd, 0x26, 0xb6,
0x60, 0x0a, 0xf5, 0x7b, 0x2d, 0x5e, 0x90, 0x30, 0xfe, 0x4e, 0xba, 0xaf, 0xb7, 0x98, 0xa2, 0xfc,
0xbd, 0x6a, 0xcb, 0x63, 0x9b, 0x7a, 0x95, 0xad, 0x2d, 0x4a, 0x0f, 0x46, 0x74, 0x70, 0x83, 0xf1,
0x0a, 0x08, 0xf7, 0xdc, 0x1a, 0x79, 0xc9, 0x56, 0x02, 0x5b, 0x2d, 0x7e, 0xdb, 0x94, 0xe5, 0x6f,
0x84, 0xd5, 0xd6, 0xcd, 0x3b, 0xd5, 0x55, 0xac, 0x66, 0x32, 0xc0, 0x6f, 0x3b, 0xc5, 0x47, 0x78,
0xd0, 0x86, 0xfc, 0x71, 0x71, 0xe9, 0xbc, 0x16, 0x4b, 0x5a, 0x83, 0xd7, 0xb4, 0x3a, 0x62, 0xa1,
0x2f, 0xfd, 0x6b, 0x59, 0x40, 0x53, 0x6e, 0x37, 0x0d, 0xfe, 0xc3, 0x55, 0x9e, 0x81, 0xe7, 0x84,
0x33, 0xb2, 0x6e, 0xe3, 0x3a, 0x6b, 0x51, 0x57, 0xdc, 0x79, 0xdf, 0xfb, 0xd7, 0xde, 0xd1, 0x7e,
0xef, 0x0a, 0x54, 0x6f, 0x2a, 0xab, 0x10, 0x95, 0xd2, 0x4c, 0xe5, 0xa8, 0x35, 0x7d, 0x24, 0xbc,
0xbf, 0xb3, 0xa8, 0x52, 0x59, 0x99, 0x04, 0xdb, 0x6c, 0x97, 0x56, 0x37, 0x2f, 0x0d, 0xf9, 0xa4,
0x55, 0x7e, 0xd2, 0x2a, 0x9e, 0x6c, 0x94, 0x9f, 0x6c, 0x14, 0x4f, 0x36, 0xcb, 0x4f, 0x36, 0x2f,
0xa7, 0x9d, 0xbf, 0xdc, 0xc6, 0x17, 0xa7, 0x64, 0xf6, 0xaf, 0x64, 0xdc, 0xbf, 0x90, 0xea, 0x8f,
0xde, 0xb0, 0x4d, 0x97, 0xbf, 0x50, 0x57, 0xee, 0xcf, 0xc5, 0x65, 0xc9, 0x67, 0x5d, 0xcc, 0x89,
0xbb, 0xe3, 0x47, 0x6f, 0x1d, 0x99, 0xcb, 0x72, 0x9f, 0x7a, 0xed, 0xf8, 0x97, 0xea, 0x0b, 0x0d,
0x72, 0x9f, 0xcc, 0xc2, 0x6d, 0xf1, 0x42, 0xb9, 0x65, 0xe7, 0xbf, 0xae, 0x41, 0x84, 0x9d, 0x1f,
0xa3, 0x05, 0x90, 0xb9, 0xe8, 0xca, 0x37, 0xdf, 0xd8, 0x9d, 0x3a, 0xc6, 0xd6, 0x58, 0x80, 0x1d,
0x01, 0xf3, 0x25, 0x5e, 0x0e, 0x54, 0x3c, 0x54, 0x9b, 0xac, 0xe3, 0x6f, 0x53, 0x34, 0xba, 0x76,
0x7b, 0xe1, 0xac, 0x67, 0x59, 0x05, 0xf0, 0x94, 0xaf, 0xea, 0xcc, 0xf0, 0x40, 0x80, 0x29, 0x79,
0x23, 0x79, 0xbb, 0xb9, 0xe8, 0xd2, 0x14, 0xce, 0x1b, 0x29, 0x07, 0x7d, 0x6a, 0x97, 0xa6, 0xf3,
0xc9, 0x4b, 0x1e, 0xbf, 0x3d, 0xfd, 0x8a, 0xbc, 0xc4, 0xd6, 0xcf, 0x67, 0x19, 0xa2, 0x31, 0xbd,
0xe2, 0x0d, 0xcd, 0x4f, 0x8f, 0x1c, 0xd9, 0x3a, 0x22, 0xa0, 0xa8, 0xdd, 0x8c, 0x75, 0x02, 0x5c,
0x04, 0x61, 0xac, 0x14, 0x36, 0x4e, 0x2d, 0x05, 0xb4, 0x78, 0x2b, 0xc6, 0xe6, 0x21, 0x46, 0x11,
0x86, 0xa0, 0xea, 0xc6, 0x26, 0x29, 0x0d, 0x3b, 0xd7, 0x0e, 0x44, 0xea, 0x6c, 0xfb, 0x30, 0x22,
0x5e, 0x0a, 0xb1, 0xfa, 0x19, 0x04, 0x24, 0x06, 0x7b, 0x29, 0x9f, 0x05, 0x36, 0xce, 0x20, 0x9d,
0x38, 0xbe, 0xe8, 0xb7, 0x47, 0x5c, 0x8b, 0x96, 0x7b, 0xc8, 0x48, 0x03, 0xf0, 0x4f, 0xe8, 0x91,
0xd4, 0x2e, 0x50, 0x5e, 0x97, 0x47, 0xa4, 0x2c, 0xd6, 0x56, 0xff, 0xf0, 0xc0, 0x3f, 0xe3, 0x3b,
0x38, 0x8b, 0xdb, 0x47, 0x95, 0x34, 0x5f, 0x3d, 0x9f, 0xdc, 0x51, 0xed, 0xbc, 0x00, 0x90, 0xbc,
0xfc, 0x9f, 0x1c, 0x3a, 0x87, 0x9a, 0x9c, 0x81, 0x5d, 0x52, 0xe2, 0x23, 0xa4, 0x92, 0x75, 0xdb,
0xbd, 0x0b, 0xc0, 0x85, 0x78, 0xca, 0x29, 0xd8, 0x8f, 0x9d, 0x51, 0x17, 0xe8, 0xc2, 0xab, 0x9b,
0xec, 0x3b, 0x71, 0x08, 0xff, 0x1d, 0x44, 0x30, 0x24, 0xbc, 0xb8, 0x6d, 0xfe, 0xdd, 0x41, 0x3c,
0xca, 0x18, 0x36, 0x00, 0x3c, 0x31, 0x82, 0xbf, 0x66, 0xef, 0x37, 0x16, 0x24, 0x66, 0x17, 0xf8,
0x8f, 0x4a, 0x14, 0xac, 0xd8, 0x9f, 0x1f, 0xdd, 0x20, 0x77, 0x41, 0x4a, 0x91, 0xa5, 0xbc, 0xe1,
0x99, 0xbb, 0x94, 0x1b, 0x6a, 0x89, 0x75, 0x26, 0x6d, 0xe4, 0x2c, 0x69, 0xb4, 0x36, 0x74, 0xe3,
0x26, 0xcd, 0x1f, 0xa5, 0xe5, 0x47, 0x2f, 0x5a, 0xe6, 0x86, 0x71, 0xa3, 0x14, 0xc7, 0xb9, 0x3b,
0xe2, 0xfd, 0x91, 0xef, 0x7b, 0x36, 0xae, 0x9d, 0xf3, 0x10, 0xaf, 0xe4, 0xc4, 0xca, 0x1d, 0xed,
0xa6, 0x74, 0x13, 0x7d, 0xa3, 0xde, 0xe0, 0x97, 0xc2, 0x03, 0x31, 0x4f, 0xd7, 0xdb, 0x95, 0x70,
0x6b, 0x2f, 0x9c, 0xf8, 0x2e, 0xc1, 0x57, 0x4d, 0xf0, 0xad, 0x4f, 0x32, 0x0f, 0x0d, 0xd8, 0xa9,
0x74, 0xf7, 0xa6, 0x94, 0x28, 0x6f, 0x2a, 0xfd, 0x94, 0x62, 0xe2, 0xd3, 0xb6, 0x3a, 0xc3, 0xad,
0x38, 0x8e, 0x0f, 0x0f, 0x72, 0xcf, 0x59, 0x06, 0xf3, 0x1f, 0xfb, 0x50, 0x19, 0x3b, 0x90, 0xde,
0x01, 0xb3, 0x0b, 0xbc, 0x78, 0xe1, 0x4b, 0xed, 0xf7, 0x3d, 0x0d, 0x7f, 0x58, 0x2d, 0x69, 0x0c,
0x53, 0xbd, 0x8d, 0xe3, 0x0a, 0x65, 0x20, 0x87, 0xb1, 0xf1, 0x6d, 0x30, 0x18, 0x4e, 0xe5, 0xf4,
0x33, 0x2d, 0xe5, 0x29, 0x12, 0x19, 0xcb, 0x35, 0xe5, 0x33, 0xca, 0x45, 0x90, 0xc3, 0x88, 0xfb,
0x00, 0xa5, 0xc9, 0x07, 0xd4, 0xa4, 0xbd, 0x0c, 0xb6, 0xb2, 0x4a, 0x59, 0x21, 0x85, 0xa4, 0x37,
0x81, 0xc8, 0x57, 0x12, 0x44, 0x89, 0xe2, 0x00, 0x7a, 0xde, 0x39, 0x26, 0x25, 0x90, 0x75, 0xdf,
0x0b, 0x01, 0x54, 0xa5, 0x4c, 0x0a, 0x59, 0x8d, 0xfc, 0xb0, 0x7e, 0x5b, 0x2e, 0x3e, 0x9f, 0xcd,
0xd4, 0x9a, 0x94, 0x91, 0xfd, 0x7e, 0xcb, 0x5c, 0x96, 0xc0, 0x41, 0x54, 0xca, 0x7a, 0x81, 0x8e,
0x6a, 0x38, 0x43, 0xda, 0x79, 0x54, 0xd5, 0xdb, 0xda, 0x32, 0x52, 0x94, 0x1f, 0x6d, 0x19, 0xfc,
0xe6, 0x30, 0x41, 0xc0, 0x28, 0xe8, 0x8e, 0xdb, 0x4f, 0x17, 0xf9, 0x6f, 0xd5, 0x86, 0x2f, 0xe7,
0xbc, 0x67, 0x7e, 0x62, 0xe5, 0x51, 0xf7, 0xed, 0x87, 0xd7, 0xf6, 0x13, 0x5d, 0xaa, 0xdc, 0x84,
0xc0, 0x5a, 0x76, 0x3b, 0xf4, 0x59, 0x2d, 0x68, 0xac, 0xbe, 0xe6, 0x87, 0xfd, 0xaa, 0x31, 0xc4,
0x31, 0x4d, 0x37, 0x16, 0xb6, 0xfb, 0x43, 0x94, 0x36, 0xc6, 0x9f, 0x5c, 0x73, 0xf1, 0x9b, 0xd2,
0x18, 0xfb, 0x48, 0x6f, 0xfe, 0xb3, 0xec, 0xc2, 0xfe, 0x65, 0xf7, 0x10, 0xeb, 0xc8, 0xbd, 0xd7,
0xd8, 0x44, 0xbe, 0x7b, 0x02, 0x7f, 0x77, 0x8f, 0x4f, 0xd8, 0xe7, 0x7f, 0x61, 0xcb, 0xf8, 0xf9,
0x21, 0xfb, 0xfb, 0x01, 0xc7, 0xcf, 0x7f, 0x86, 0xbf, 0xa7, 0xbd, 0x63, 0xf8, 0x7b, 0xb4, 0x87,
0xc5, 0xf0, 0xc3, 0xa3, 0x8f, 0x58, 0xed, 0xeb, 0xed, 0xd9, 0x97, 0x17, 0xe5, 0x57, 0x06, 0x2e,
0xa7, 0x4b, 0xbd, 0x60, 0x30, 0xb7, 0x8d, 0xe3, 0x09, 0xaf, 0x02, 0xd8, 0xc6, 0x24, 0xf6, 0xad,
0xf3, 0x93, 0x0f, 0xe2, 0x7e, 0x99, 0xb7, 0x11, 0xc0, 0x77, 0x0d, 0x29, 0x07, 0x02, 0x3a, 0xf7,
0x02, 0xda, 0xc1, 0x40, 0xdc, 0x1c, 0xc5, 0x74, 0x68, 0x01, 0x08, 0xc3, 0x31, 0x25, 0x23, 0xf1,
0xe6, 0x99, 0x55, 0xd4, 0x00, 0xe1, 0x12, 0x69, 0xa7, 0xec, 0xd2, 0xb2, 0x78, 0x7f, 0x1d, 0xdd,
0x68, 0xe9, 0xc2, 0xd8, 0x81, 0xfd, 0xf2, 0xdb, 0xd5, 0xf2, 0x34, 0x9e, 0xd3, 0xe4, 0xd3, 0x10,
0xe1, 0x98, 0xde, 0x86, 0x37, 0x0a, 0xc2, 0x80, 0x45, 0x1e, 0xbf, 0xa2, 0x78, 0xd7, 0x89, 0x1d,
0x4f, 0xd7, 0x25, 0xa6, 0x18, 0xc6, 0xaa, 0x52, 0xc7, 0x96, 0xcd, 0x79, 0x85, 0x04, 0x89, 0x76,
0x1c, 0xfa, 0x3e, 0x92, 0x55, 0x54, 0x9b, 0x65, 0xd0, 0x5c, 0xfe, 0x61, 0x19, 0xb5, 0x5e, 0xaf,
0xf6, 0x24, 0x6e, 0x96, 0xdf, 0x2b, 0x85, 0x48, 0xae, 0x00, 0xa7, 0x02, 0x98, 0x7d, 0x87, 0x4f,
0x7d, 0x2a, 0xdf, 0xe2, 0x5b, 0xf0, 0x23, 0x00, 0xb7, 0x5e, 0xe2, 0xf5, 0x3d, 0xdf, 0x4b, 0xef,
0xb9, 0x61, 0x2a, 0xdd, 0x97, 0xe7, 0xeb, 0x46, 0x9e, 0xeb, 0xd2, 0x60, 0xa7, 0x84, 0x47, 0x5b,
0xfb, 0xcc, 0x73, 0x61, 0xb4, 0x23, 0xde, 0xb1, 0x0f, 0xb9, 0x0c, 0x28, 0x5b, 0x7e, 0x41, 0x77,
0x67, 0x89, 0xe8, 0xd3, 0xce, 0x76, 0x43, 0xfc, 0xd4, 0xcf, 0x76, 0x83, 0xff, 0x98, 0x6c, 0x83,
0xfd, 0xdf, 0x0c, 0xfc, 0x07, 0x7d, 0x53, 0xca, 0x86, 0x76, 0x60, 0x00, 0x00,
};
const unsigned int html_content_gz_len = 6845;
#endif // HTML_CONTENT_GZ_H

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
a8049b1e609679fb54b2d57b0399dd29c4d1fda09a797edac9926f7810aa5703
689853bb8993434f9556af0f2816e808bf77b5d22100144b21f3519993daf237

View File

@@ -1,7 +1,7 @@
---
name: esp_idf_lib_helpers
description: Common support library for esp-idf-lib
version: 1.3.10
version: 1.4.0
groups:
- common
code_owners:

View File

@@ -2,6 +2,11 @@ examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build/
examples/**/dependencies.lock
examples/**/managed_components/
docs/_*/
docs/doxygen.log
docs/**/*.log
*.swp
*.bak
*.orig
build/
.vscode/

View File

@@ -74,7 +74,7 @@
* The default SPI_HOST for spi_host_device_t
*/
#if CONFIG_IDF_TARGET_ESP32
#define HELPER_SPI_HOST_DEFAULT HSPI_HOST
#define HELPER_SPI_HOST_DEFAULT SPI1_HOST
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define HELPER_SPI_HOST_DEFAULT SPI2_HOST
#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61

View File

@@ -12,7 +12,7 @@ maintainers:
- Ruslan V. Uss (@UncleRus) <unclerus@gmail.com>
repository: git://github.com/esp-idf-lib/esp_idf_lib_helpers.git
repository_info:
commit_sha: 57bbd8f3cda9c5ad390fc3ea5585e0ad80672584
commit_sha: 918d82cafb1f00fd86f1ad8571271cb3e910588b
path: .
targets:
- esp32
@@ -26,4 +26,4 @@ targets:
- esp32s2
- esp32s3
url: https://github.com/esp-idf-lib/core
version: 1.3.10
version: 1.4.0

View File

@@ -1 +1 @@
11c08f9e1a7d346b5dd763196dc2567cf2209ae49042402c2c2d296624601c14
4f3838b2e68ab2b77fd43737139fa97dd0243b46af7b4a04588c67ff6b275ba1

View File

@@ -1,10 +1,12 @@
name: i2cdev
description: ESP-IDF I2C master thread-safe utilities
version: 2.0.8
version: 2.1.0
groups:
- common
code_owners:
- UncleRus
- quinkq
- trombik
depends:
- driver
- freertos
@@ -25,4 +27,6 @@ targets:
license: MIT
copyrights:
- name: UncleRus
year: 2018
year: 2018
- name: quinkq
year: 2025

View File

@@ -2,6 +2,11 @@ examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build/
examples/**/dependencies.lock
examples/**/managed_components/
docs/_*/
docs/doxygen.log
docs/**/*.log
*.swp
*.bak
*.orig
build/
.vscode/

View File

@@ -1,5 +1,10 @@
# ESP-IDF CMake component for i2cdev library
set(req driver freertos esp_idf_lib_helpers)
if(${IDF_VERSION_MAJOR} STREQUAL 5 AND ${IDF_VERSION_MINOR} LESS 3)
# Use driver component as esp_driver_gpio is not available before 5.3
set(req driver freertos log esp_timer)
else()
set(req esp_driver_gpio esp_driver_i2c freertos esp_idf_lib_helpers)
endif()
# ESP-IDF version detection for automatic driver selection
# Check for manual override via Kconfig
@@ -14,7 +19,7 @@ elseif(IDF_VERSION_MAJOR LESS 5)
set(USE_LEGACY_DRIVER TRUE)
message(STATUS "i2cdev: ESP-IDF v${IDF_VERSION_MAJOR}.x detected, using legacy driver")
elseif(IDF_VERSION_MAJOR EQUAL 5 AND IDF_VERSION_MINOR LESS 3)
set(USE_LEGACY_DRIVER TRUE)
set(USE_LEGACY_DRIVER TRUE)
message(STATUS "i2cdev: ESP-IDF v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR} detected, using legacy driver")
else()
set(USE_LEGACY_DRIVER FALSE)

View File

@@ -107,7 +107,7 @@ static void deregister_device(i2c_dev_t *dev)
}
}
esp_err_t i2c_init(void)
esp_err_t i2cdev_init(void)
{
ESP_LOGV(TAG, "Initializing I2C subsystem...");
memset(active_devices, 0, sizeof(active_devices));

View File

@@ -46,12 +46,18 @@
#ifndef __I2CDEV_H__
#define __I2CDEV_H__
#include <driver/gpio.h>
#include <driver/i2c.h>
#include <esp_err.h>
#include <esp_idf_lib_helpers.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <esp_idf_version.h>
#include <driver/gpio.h>
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#include <driver/i2c_master.h>
#else
#include <driver/i2c.h>
#endif
// Define missing types for older ESP-IDF versions
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
@@ -193,7 +199,7 @@ typedef struct
*
* @return ESP_OK on success
*/
esp_err_t i2c_init(void);
esp_err_t i2cdev_init(void);
/**
* @brief Release I2C subsystem (deletes all devices, buses, and mutexes)

View File

@@ -499,7 +499,7 @@ static esp_err_t i2c_setup_port(i2c_dev_t *dev)
vTaskDelay(1);
// Target-specific driver installation/configuration sequence
#if HELPER_TARGET_IS_ESP32 || HELPER_TARGET_IS_ESP32S2 || HELPER_TARGET_IS_ESP32S3 || HELPER_TARGET_IS_ESP32C3 || HELPER_TARGET_IS_ESP32C6
#if HELPER_TARGET_IS_ESP32
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
ESP_LOGD(TAG, "Using IDF >= 5.1.0 driver install order for ESP32 family");
err = i2c_driver_install(dev->port, legacy_cfg.mode, 0, 0, 0);
@@ -559,7 +559,7 @@ static esp_err_t i2c_setup_port(i2c_dev_t *dev)
}
// Part 2: Timeout Configuration (ESP32 family specific hardware timeout)
#if HELPER_TARGET_IS_ESP32 || HELPER_TARGET_IS_ESP32S2 || HELPER_TARGET_IS_ESP32S3 || HELPER_TARGET_IS_ESP32C3 || HELPER_TARGET_IS_ESP32C6
#if HELPER_TARGET_IS_ESP32
int current_timeout_hw;
err = i2c_get_timeout(dev->port, &current_timeout_hw);
if (err != ESP_OK)

View File

@@ -13,7 +13,7 @@ maintainers:
- Ruslan V. Uss (@UncleRus) <unclerus@gmail.com>
repository: git://github.com/esp-idf-lib/i2cdev.git
repository_info:
commit_sha: abf0ebc8f0f826373e9a9ee07cf96727a49ed87b
commit_sha: b4f09df5a02e576af61846954557bf7a61d35274
path: .
targets:
- esp32
@@ -27,4 +27,4 @@ targets:
- esp32s2
- esp32s3
url: https://github.com/esp-idf-lib/core
version: 2.0.8
version: 2.1.0

View File

@@ -0,0 +1 @@
29e47564b1a7ee778135e17fbbf2a2773f71c97ebabfe626c8eda7c958a7ad16

View File

@@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(mdns): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py mdns
tag_format: mdns-v$version
version: 1.9.1
version_files:
- idf_component.yml

View File

@@ -0,0 +1,607 @@
# Changelog
## [1.9.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.9.1)
### Bug Fixes
- Fix to use tagged AFL image + minor format fix ([2b2f009a](https://github.com/espressif/esp-protocols/commit/2b2f009a))
- Fix unused variable `dcst` warning for wifi-remote chips ([081eef88](https://github.com/espressif/esp-protocols/commit/081eef88))
## [1.9.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.9.0)
### Features
- support null value for boolean txt records ([fa96de3b](https://github.com/espressif/esp-protocols/commit/fa96de3b))
### Bug Fixes
- Add test case for bool/NULL txt handling ([5068f221](https://github.com/espressif/esp-protocols/commit/5068f221))
- Temporary fix for build issues on IDF master ([0197c994](https://github.com/espressif/esp-protocols/commit/0197c994))
- Add tests for delegated answers ([487a746d](https://github.com/espressif/esp-protocols/commit/487a746d))
- Add fuzzing into mdns CI ([af6bb1b5](https://github.com/espressif/esp-protocols/commit/af6bb1b5))
- Host test to use hw_support include dir ([8bba3a97](https://github.com/espressif/esp-protocols/commit/8bba3a97))
- Fixes case where we create our own malloc/free allocators, therefore we need to call mdns_mem_free and not free ([63bf7091](https://github.com/espressif/esp-protocols/commit/63bf7091))
- put srv/txt records in additional section for ptr queries ([b7b8c5db](https://github.com/espressif/esp-protocols/commit/b7b8c5db))
### Updated
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
## [1.8.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.8.2)
### Bug Fixes
- Fix parsing incorrect txt records ([8fd2c99f](https://github.com/espressif/esp-protocols/commit/8fd2c99f))
## [1.8.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.8.1)
### Bug Fixes
- Fix potential task delete race ([8ca45f34](https://github.com/espressif/esp-protocols/commit/8ca45f34))
## [1.8.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.8.0)
### Features
- Add version keys ([e01e67e7](https://github.com/espressif/esp-protocols/commit/e01e67e7))
### Bug Fixes
- Reformat mdns sources per indent-cont=120 ([c7663cde](https://github.com/espressif/esp-protocols/commit/c7663cde))
## [1.7.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.7.0)
### Features
- Support user defined allocators ([88162d1f](https://github.com/espressif/esp-protocols/commit/88162d1f))
- Allow allocate memory with configured caps ([7d29b476](https://github.com/espressif/esp-protocols/commit/7d29b476))
### Bug Fixes
- Adjust some formatting per indent-cont=120 ([5b2077e3](https://github.com/espressif/esp-protocols/commit/5b2077e3))
## [1.6.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.6.0)
### Features
- support allocating mDNS task from SPIRAM ([8fcad10c](https://github.com/espressif/esp-protocols/commit/8fcad10c))
### Bug Fixes
- Use correct task delete function ([eb4ab524](https://github.com/espressif/esp-protocols/commit/eb4ab524))
### Updated
- ci(mdns): Fix mdns host test layers with static task creation ([0690eba3](https://github.com/espressif/esp-protocols/commit/0690eba3))
## [1.5.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.3)
### Bug Fixes
- Fix responder to ignore only invalid queries ([cd07228f](https://github.com/espressif/esp-protocols/commit/cd07228f), [#754](https://github.com/espressif/esp-protocols/issues/754))
## [1.5.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.2)
### Bug Fixes
- Fix potential NULL deref when sending sub-buy ([e7273c46](https://github.com/espressif/esp-protocols/commit/e7273c46))
- Fix _mdns_append_fqdn excessive stack usage ([bd23c233](https://github.com/espressif/esp-protocols/commit/bd23c233))
## [1.5.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.1)
### Bug Fixes
- Fix incorrect memory free for mdns browse ([4451a8c5](https://github.com/espressif/esp-protocols/commit/4451a8c5))
## [1.5.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.0)
### Features
- supported removal of subtype when updating service ([4ad88e29](https://github.com/espressif/esp-protocols/commit/4ad88e29))
### Bug Fixes
- Fix zero-sized VLA clang-tidy warnings ([196198ec](https://github.com/espressif/esp-protocols/commit/196198ec))
- Remove dead store to arg variable shared ([e838bf03](https://github.com/espressif/esp-protocols/commit/e838bf03))
- Fix name mangling not to use strcpy() ([99b54ac3](https://github.com/espressif/esp-protocols/commit/99b54ac3))
- Fix potential null derefernce in _mdns_execute_action() ([f5be2f41](https://github.com/espressif/esp-protocols/commit/f5be2f41))
- Fix AFL test mock per espressif/esp-idf@a5bc08fb55c ([3d8835cf](https://github.com/espressif/esp-protocols/commit/3d8835cf))
- Fixed potential out-of-bound interface error ([24f55ce9](https://github.com/espressif/esp-protocols/commit/24f55ce9))
- Fixed incorrect error conversion ([8f8516cc](https://github.com/espressif/esp-protocols/commit/8f8516cc))
- Fixed potential overflow when allocating txt data ([75a8e864](https://github.com/espressif/esp-protocols/commit/75a8e864))
- Move MDNS_NAME_BUF_LEN to public headers ([907087c0](https://github.com/espressif/esp-protocols/commit/907087c0), [#724](https://github.com/espressif/esp-protocols/issues/724))
- Cleanup includes in mdns.c ([68a9e148](https://github.com/espressif/esp-protocols/commit/68a9e148), [#725](https://github.com/espressif/esp-protocols/issues/725))
- Allow advertizing service with port==0 ([827ea65f](https://github.com/espressif/esp-protocols/commit/827ea65f))
- Fixed complier warning if MDNS_MAX_SERVICES==0 ([95377216](https://github.com/espressif/esp-protocols/commit/95377216), [#611](https://github.com/espressif/esp-protocols/issues/611))
## [1.4.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.3)
### Features
- support zero item when update subtype ([5bd82c01](https://github.com/espressif/esp-protocols/commit/5bd82c01))
## [1.4.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.2)
### Features
- support update subtype ([062b8dca](https://github.com/espressif/esp-protocols/commit/062b8dca))
### Updated
- chore(mdns): Add more info to idf_component.yml ([4a1cb65c](https://github.com/espressif/esp-protocols/commit/4a1cb65c))
## [1.4.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.1)
### Features
- Send PTR query for mdns browse when interface is ready ([010a404a](https://github.com/espressif/esp-protocols/commit/010a404a))
### Bug Fixes
- Prevent deadlock when deleting a browse request ([3f48f9ea](https://github.com/espressif/esp-protocols/commit/3f48f9ea))
- Fix use after free reported by coverity ([25b3d5fd](https://github.com/espressif/esp-protocols/commit/25b3d5fd))
- Fixed dead-code reported by coverity ([11846c7d](https://github.com/espressif/esp-protocols/commit/11846c7d))
## [1.4.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.0)
### Major changes
- Fixed mdns API issues when add/remove/update records from multiple threads ([Fix services API races to directly add/remove services](https://github.com/espressif/esp-protocols/commit/8a690503))
### Features
- Unit tests for add/remove/update deleg/selfhosted services ([0660ece1](https://github.com/espressif/esp-protocols/commit/0660ece1))
- Add console command for mdns browsing ([1e8ede33](https://github.com/espressif/esp-protocols/commit/1e8ede33))
- Console test: set instance for service ([f107dcd1](https://github.com/espressif/esp-protocols/commit/f107dcd1))
- Console test: add subtype for service ([ee00e97b](https://github.com/espressif/esp-protocols/commit/ee00e97b))
- Console test: set port for (delegated) srvs ([07b79abf](https://github.com/espressif/esp-protocols/commit/07b79abf))
- Console test: add/remove TXT recs for delegated srvs ([c9a58d73](https://github.com/espressif/esp-protocols/commit/c9a58d73))
- Console test for changing TXT records ([6b9a6ce6](https://github.com/espressif/esp-protocols/commit/6b9a6ce6))
- Console test for add/remove delegated service APIs ([43de7e5c](https://github.com/espressif/esp-protocols/commit/43de7e5c))
- Console test for add/remove delegated host APIs ([ce7f326a](https://github.com/espressif/esp-protocols/commit/ce7f326a))
- Console test for lookup service APIs ([a91ead8e](https://github.com/espressif/esp-protocols/commit/a91ead8e))
- Add linux console functional tests ([50d059af](https://github.com/espressif/esp-protocols/commit/50d059af))
- check if the txt items is changed when browsing ([e2f0477a](https://github.com/espressif/esp-protocols/commit/e2f0477a))
### Bug Fixes
- Fix mdns_delegate_hostname_add() to block until done ([2c1b1661](https://github.com/espressif/esp-protocols/commit/2c1b1661))
- Fix API races when removing all services ([169405b5](https://github.com/espressif/esp-protocols/commit/169405b5))
- Fix API races setting instance name for services ([643dc6d4](https://github.com/espressif/esp-protocols/commit/643dc6d4))
- Fix API races while adding subtypes for services ([f9f234c4](https://github.com/espressif/esp-protocols/commit/f9f234c4))
- Fix API races removing txt item for services ([3f97a822](https://github.com/espressif/esp-protocols/commit/3f97a822))
- Fix API races adding txt item for services ([c62b920b](https://github.com/espressif/esp-protocols/commit/c62b920b))
- Fix API races while setting txt for services ([a927bf3a](https://github.com/espressif/esp-protocols/commit/a927bf3a))
- Fix API races while setting port for services ([99d5fb27](https://github.com/espressif/esp-protocols/commit/99d5fb27))
- Fix services API races to directly add/remove services ([8a690503](https://github.com/espressif/esp-protocols/commit/8a690503))
- Fix mdns mdns_lookup_service() to handle empty TXT ([d4da9cb0](https://github.com/espressif/esp-protocols/commit/d4da9cb0))
## [1.3.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.3.2)
### Features
- add check of instance when handling PTR query ([6af6ca5](https://github.com/espressif/esp-protocols/commit/6af6ca5))
### Bug Fixes
- Fix of mdns afl tests ([139166c](https://github.com/espressif/esp-protocols/commit/139166c))
- remove same protocol services with different instances ([042533a](https://github.com/espressif/esp-protocols/commit/042533a))
## [1.3.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.3.1)
### Bug Fixes
- free txt value len ([afd98bb](https://github.com/espressif/esp-protocols/commit/afd98bb))
## [1.3.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.3.0)
### Features
- add a new mdns query mode `browse` ([af330b6](https://github.com/espressif/esp-protocols/commit/af330b6))
- Make including mdns_console KConfigurable ([27adbfe](https://github.com/espressif/esp-protocols/commit/27adbfe))
### Bug Fixes
- Schedule all queued Tx packets from timer task ([d4e693e](https://github.com/espressif/esp-protocols/commit/d4e693e))
- add lock for some common apis ([21c84bf](https://github.com/espressif/esp-protocols/commit/21c84bf))
- fix mdns answer append while host is invalid ([7be16bc](https://github.com/espressif/esp-protocols/commit/7be16bc))
## [1.2.5](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.5)
### Bug Fixes
- Fixed build issues for targets without WiFi caps ([302b46f](https://github.com/espressif/esp-protocols/commit/302b46f))
## [1.2.4](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.4)
### Bug Fixes
- Correction on 6d2c475 MDNS_PREDEF_NETIF_ETH fix ([fc59f87c4e](https://github.com/espressif/esp-protocols/commit/fc59f87c4e))
- fix the logic of creating pcb for networking socket ([5000a9a20a](https://github.com/espressif/esp-protocols/commit/5000a9a20a))
- fix compiling issue when disabling IPv4 ([2646dcd23a](https://github.com/espressif/esp-protocols/commit/2646dcd23a))
- Fix compile error when MDNS_PREDEF_NETIF_ETH is defined, but ETH_ENABLED is not (#459) ([6d2c475c20](https://github.com/espressif/esp-protocols/commit/6d2c475c20))
## [1.2.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.3)
### Bug Fixes
- fixed CI issues for host and afl tests ([4be5efc84e](https://github.com/espressif/esp-protocols/commit/4be5efc84e))
- fix copy delegated host addr ([19fb36000c](https://github.com/espressif/esp-protocols/commit/19fb36000c))
- enable CONFIG_ESP_WIFI_ENABLED if CONFIG_SOC_WIFI_SUPPORTED is also enabled (for ESP-IDF <= 5.1) ([d20a718320](https://github.com/espressif/esp-protocols/commit/d20a718320))
- remove protocol_examples_common ([1ee9dae6bf](https://github.com/espressif/esp-protocols/commit/1ee9dae6bf))
- move the example into a subdirectory ([d28232b9f8](https://github.com/espressif/esp-protocols/commit/d28232b9f8))
- reference protocol_examples_common from IDF ([c83b76ea8f](https://github.com/espressif/esp-protocols/commit/c83b76ea8f))
## [1.2.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.2)
### Bug Fixes
- add terminator for the getting host name ([b6a4d94](https://github.com/espressif/esp-protocols/commit/b6a4d94))
- Enable ESP_WIFI_CONFIG when ESP-IDF <= 5.1 ([0b783c0](https://github.com/espressif/esp-protocols/commit/0b783c0))
- set host list NULL on destroy ([ea54eef](https://github.com/espressif/esp-protocols/commit/ea54eef))
- removed Wno-format flag and fixed formatting warnings ([c48e442](https://github.com/espressif/esp-protocols/commit/c48e442))
- remove the the range of MDNS_MAX_SERVICES and fix issues of string functions ([3dadce2](https://github.com/espressif/esp-protocols/commit/3dadce2))
## [1.2.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.1)
### Features
- Allow setting length of mDNS action queue in menuconfig ([28cd898](https://github.com/espressif/esp-protocols/commit/28cd898))
### Bug Fixes
- fix build issue if CONFIG_ESP_WIFI_ENABLED disabled ([24f7031](https://github.com/espressif/esp-protocols/commit/24f7031))
- added idf_component.yml for examples ([d273e10](https://github.com/espressif/esp-protocols/commit/d273e10))
- added guard check for null pointer ([71bb461](https://github.com/espressif/esp-protocols/commit/71bb461))
## [1.2.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.0)
### Features
- add an API for setting address to a delegated host ([ddc3eb6](https://github.com/espressif/esp-protocols/commit/ddc3eb6))
- Add support for lwip build under linux ([588465d](https://github.com/espressif/esp-protocols/commit/588465d))
- Allow for adding a delegated host with no address ([c562461](https://github.com/espressif/esp-protocols/commit/c562461))
- Add APIs for looking up self hosted services and getting the self hostname ([f0df12d](https://github.com/espressif/esp-protocols/commit/f0df12d))
### Bug Fixes
- Refactor freertos linux compat layers ([79a0e57](https://github.com/espressif/esp-protocols/commit/79a0e57))
- Fix delegated service PTR response ([cab0e1d](https://github.com/espressif/esp-protocols/commit/cab0e1d))
- Added unit tests to CI + minor fix to pass it ([c974c14](https://github.com/espressif/esp-protocols/commit/c974c14))
### Updated
- docs: update documentation links ([4de5298](https://github.com/espressif/esp-protocols/commit/4de5298))
## [1.1.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.1.0)
### Features
- Decouple main module from mdns-networking ([d238e93](https://github.com/espressif/esp-protocols/commit/d238e93))
### Bug Fixes
- Use idf-build-apps package for building mdns ([1a0a41f](https://github.com/espressif/esp-protocols/commit/1a0a41f))
- socket networking to init interfaces properly ([ee9b04f](https://github.com/espressif/esp-protocols/commit/ee9b04f))
- Removed unused internal lock from mdns_server struct ([a06fb77](https://github.com/espressif/esp-protocols/commit/a06fb77))
- Resolve conflicts only on self hosted items ([e69a9eb](https://github.com/espressif/esp-protocols/commit/e69a9eb), [#185](https://github.com/espressif/esp-protocols/issues/185))
- Fix memory issues reported by valgrind ([0a682e7](https://github.com/espressif/esp-protocols/commit/0a682e7))
### Updated
- docs(common): updated component and example links ([f48d9b2](https://github.com/espressif/esp-protocols/commit/f48d9b2))
- Add APIs to look up delegated services ([87dcd7d](https://github.com/espressif/esp-protocols/commit/87dcd7d))
- Fix deadly mdns crash ([4fa3023](https://github.com/espressif/esp-protocols/commit/4fa3023))
- docs(common): improving documentation ([ca3fce0](https://github.com/espressif/esp-protocols/commit/ca3fce0))
- append all ipv6 address in mdns answer ([5ed3e9a](https://github.com/espressif/esp-protocols/commit/5ed3e9a))
- test(mdns): Host tests to use IDF's esp_event_stub ([537d170](https://github.com/espressif/esp-protocols/commit/537d170))
## [1.0.9](https://github.com/espressif/esp-protocols/commits/mdns-v1.0.9)
### Features
- Add reverse lookup to the example and test ([d464ed7](https://github.com/espressif/esp-protocols/commit/d464ed7))
- Add support for IPv6 reverse query ([d4825f5](https://github.com/espressif/esp-protocols/commit/d4825f5))
### Bug Fixes
- Reintroduce missing CHANGELOGs ([200cbb3](https://github.com/espressif/esp-protocols/commit/200cbb3))
- use semaphore instead of task notification bits (IDFGH-9380) ([73f2800](https://github.com/espressif/esp-protocols/commit/73f2800), [IDF#10754](https://github.com/espressif/esp-idf/issues/10754))
### Updated
- ci(common): force scoping commit messages with components ([c55fcc0](https://github.com/espressif/esp-protocols/commit/c55fcc0))
- Add homepage URL and License to all components ([ef3f0ee](https://github.com/espressif/esp-protocols/commit/ef3f0ee))
- docs: fix of mdns link translation ([1c850dd](https://github.com/espressif/esp-protocols/commit/1c850dd))
- unite all tags under common structure py test: update tags under common structure ([c6db3ea](https://github.com/espressif/esp-protocols/commit/c6db3ea))
## [1.0.8](https://github.com/espressif/esp-protocols/commits/b9b4a75)
### Features
- Add support for IPv4 reverse query ([b87bef5](https://github.com/espressif/esp-protocols/commit/b87bef5))
### Bug Fixes
- Host test with IDFv5.1 ([fb8a2f0](https://github.com/espressif/esp-protocols/commit/fb8a2f0))
- Remove strict mode as it's invalid ([d0c9070](https://github.com/espressif/esp-protocols/commit/d0c9070))
- Allow setting instance name only after hostname set ([a8339e4](https://github.com/espressif/esp-protocols/commit/a8339e4), [#190](https://github.com/espressif/esp-protocols/issues/190))
- Make unit test executable with pytest ([12cfcb5](https://github.com/espressif/esp-protocols/commit/12cfcb5))
- AFL port layer per IDF-latest changes ([0247926](https://github.com/espressif/esp-protocols/commit/0247926))
### Updated
- bump the component version to 1.0.8 ([b9b4a75](https://github.com/espressif/esp-protocols/commit/b9b4a75))
- Make reverse query conditional per Kconfig ([91134f1](https://github.com/espressif/esp-protocols/commit/91134f1))
- Added badges with version of components to the respective README files ([e4c8a59](https://github.com/espressif/esp-protocols/commit/e4c8a59))
- fix some coverity reported issues ([c73c797](https://github.com/espressif/esp-protocols/commit/c73c797))
- Examples: using pytest.ini from top level directory ([aee016d](https://github.com/espressif/esp-protocols/commit/aee016d))
- mDNS: test_app pytest migration ([f71f61f](https://github.com/espressif/esp-protocols/commit/f71f61f))
- CI: fixing the files to be complient with pre-commit hooks ([945bd17](https://github.com/espressif/esp-protocols/commit/945bd17))
- prevent crash when hostname is null ([3498e86](https://github.com/espressif/esp-protocols/commit/3498e86))
- Example tests integration ([a045c1c](https://github.com/espressif/esp-protocols/commit/a045c1c))
- Replace hardcoded TTL values with named defines ([bb4c002](https://github.com/espressif/esp-protocols/commit/bb4c002))
- Fix add_service() to report error if no-hostname ([656ab21](https://github.com/espressif/esp-protocols/commit/656ab21))
## [1.0.7](https://github.com/espressif/esp-protocols/commits/088f7ac)
### Updated
- bump up the component version ([088f7ac](https://github.com/espressif/esp-protocols/commit/088f7ac))
- fix IPV4 only build and also update CI configuration ([e079f8b](https://github.com/espressif/esp-protocols/commit/e079f8b))
- add test configuration for IPV6 disabled build ([330332a](https://github.com/espressif/esp-protocols/commit/330332a))
## [1.0.6](https://github.com/espressif/esp-protocols/commits/48c157b)
### Bug Fixes
- Example makefile to add only mdns as extra comps ([d74c296](https://github.com/espressif/esp-protocols/commit/d74c296))
- ignore authoritative flag on reception ([415e04a](https://github.com/espressif/esp-protocols/commit/415e04a))
### Updated
- fix build issue with CONFIG_LWIP_IPV6 disabled ([48c157b](https://github.com/espressif/esp-protocols/commit/48c157b))
- fix bit order issue in DNS header flags ([c4e85bd](https://github.com/espressif/esp-protocols/commit/c4e85bd))
- updated package version to 0.1.19 ([469f953](https://github.com/espressif/esp-protocols/commit/469f953))
## [1.0.5](https://github.com/espressif/esp-protocols/commits/36de9af)
### Features
- Define explicit dependencies on esp-wifi ([36de9af](https://github.com/espressif/esp-protocols/commit/36de9af))
### Updated
- bugfix: mdns IPv6 address convert error ([238ee96](https://github.com/espressif/esp-protocols/commit/238ee96))
## [1.0.4](https://github.com/espressif/esp-protocols/commits/57afa38)
### Updated
- Bump asio/mdns/esp_websocket_client versions ([57afa38](https://github.com/espressif/esp-protocols/commit/57afa38))
- ignore format warnings ([d66f9dc](https://github.com/espressif/esp-protocols/commit/d66f9dc))
- Fix test_app build ([0b102f6](https://github.com/espressif/esp-protocols/commit/0b102f6))
## [1.0.3](https://github.com/espressif/esp-protocols/commits/4868689)
### Updated
- Updated mDNS to explicitely use esp-eth dependency if needed ([4868689](https://github.com/espressif/esp-protocols/commit/4868689), [IDF@5e19b9c](https://github.com/espressif/esp-idf/commit/5e19b9c9518ae253d82400ab24b86af8af832425))
## [1.0.2](https://github.com/espressif/esp-protocols/commits/8fe2a3a)
### Features
- fix bug when clean action memory ([81c219d](https://github.com/espressif/esp-protocols/commit/81c219d), [IDF@3d4deb9](https://github.com/espressif/esp-idf/commit/3d4deb972620cae8e8ce4d0050153cc6f39db372))
### Bug Fixes
- add the maximum number of services ([0191d6f](https://github.com/espressif/esp-protocols/commit/0191d6f), [IDF@ba458c6](https://github.com/espressif/esp-idf/commit/ba458c69cfb2f18478d73690c289b09641c62004))
- fix the exception when remove one of multiple service ([b26c866](https://github.com/espressif/esp-protocols/commit/b26c866), [IDF@696d733](https://github.com/espressif/esp-idf/commit/696d733eb04ee98f764dffdc82bcef51a724c9c6))
### Updated
- Minor fixes here and there ([8fe2a3a](https://github.com/espressif/esp-protocols/commit/8fe2a3a))
- mDNS: Initial version based on IDF 5.0 ([b6b20ad](https://github.com/espressif/esp-protocols/commit/b6b20ad))
- soc: moved kconfig options out of the target component. ([4a52cf2](https://github.com/espressif/esp-protocols/commit/4a52cf2), [IDF@d287209](https://github.com/espressif/esp-idf/commit/d2872095f93ed82fb91c776081bc1d032493d93e))
- cmake: fix issue with passing cxx_std option for GCC 11, a common workaround ([87c2699](https://github.com/espressif/esp-protocols/commit/87c2699), [IDF@ea0d212](https://github.com/espressif/esp-idf/commit/ea0d2123c806bd0ad77bc49843ee905cf9be65ff))
- kconfig: Changed default values of bool configs - Some bool configs were using default values true and false, instead of y and n. ([eb536a7](https://github.com/espressif/esp-protocols/commit/eb536a7), [IDF@25c5c21](https://github.com/espressif/esp-idf/commit/25c5c214f38ca690b03533e12fb5a4d774c7eae0))
- esp_netif: Remove tcpip_adapter compatibility layer ([3e93ea9](https://github.com/espressif/esp-protocols/commit/3e93ea9), [IDF@795b7ed](https://github.com/espressif/esp-idf/commit/795b7ed993784e3134195e12b0978504d83dfd56))
- Fix copyright messages, update API descrition ([2c764b1](https://github.com/espressif/esp-protocols/commit/2c764b1), [IDF@42ba8a8](https://github.com/espressif/esp-idf/commit/42ba8a8338fd5efd82498a5989fc5c105938d447))
- Add API to control custom network interfaces ([f836ae7](https://github.com/espressif/esp-protocols/commit/f836ae7), [IDF@b02468d](https://github.com/espressif/esp-idf/commit/b02468dc98d614f931d14cd8b5e2373ca51fb18d))
- CI/mdns: Fix fuzzer build ([4b5f24f](https://github.com/espressif/esp-protocols/commit/4b5f24f), [IDF@98e9426](https://github.com/espressif/esp-idf/commit/98e9426b660a6e825f811cccd45a0722cc801ccd))
- Add support for registering custom netif ([30f37c0](https://github.com/espressif/esp-protocols/commit/30f37c0), [IDF@bec42ff](https://github.com/espressif/esp-idf/commit/bec42ff85d5091d71e1cb1063bea20d7c6ac8c76))
- Indicate interface using esp_netif in search results ([ddc58e8](https://github.com/espressif/esp-protocols/commit/ddc58e8), [IDF@f8495f1](https://github.com/espressif/esp-idf/commit/f8495f1e86de9a8e7d046bf13d0ca04775041b4c))
- Use predefined interfaces to prepare for custom netifs ([fa951bf](https://github.com/espressif/esp-protocols/commit/fa951bf), [IDF@f90b3b7](https://github.com/espressif/esp-idf/commit/f90b3b798b446382d848f8c55c5e1653c81871cd))
- Prepare for dynamic esp-netif support ([605d1fa](https://github.com/espressif/esp-protocols/commit/605d1fa), [IDF@f9892f7](https://github.com/espressif/esp-idf/commit/f9892f77b88ba77dc6608ba746175f6dc64a7607))
- esp_hw_support/esp_system: Re-evaluate header inclusions and include directories ([58bf218](https://github.com/espressif/esp-protocols/commit/58bf218), [IDF@a9fda54](https://github.com/espressif/esp-idf/commit/a9fda54d39d1321005c3bc9b3cc268d0b7e9f052))
- system: move kconfig options out of target component ([ec491ec](https://github.com/espressif/esp-protocols/commit/ec491ec), [IDF@bb88338](https://github.com/espressif/esp-idf/commit/bb88338118957c2214a4c0a33cd4a152e2e1f8ba))
- Update to drop our own packet if bounced back ([94ae672](https://github.com/espressif/esp-protocols/commit/94ae672), [IDF@b5149e3](https://github.com/espressif/esp-idf/commit/b5149e3ee73728f790798e6757d732fe426e21c7))
- Fix potential read behind parsed packet ([e5a3a3d](https://github.com/espressif/esp-protocols/commit/e5a3a3d), [IDF@51a5de2](https://github.com/espressif/esp-idf/commit/51a5de2525d0e82adea2e298a0edcc9b2dee5edd))
- Fix memleak when adding delegated host ([7710ea9](https://github.com/espressif/esp-protocols/commit/7710ea9), [IDF@9cbdb87](https://github.com/espressif/esp-idf/commit/9cbdb8767bdf6e9745e895b2c5af74d0376965e7))
- Fix null-service issue when parsing packets ([034c55e](https://github.com/espressif/esp-protocols/commit/034c55e), [IDF#8307](https://github.com/espressif/esp-idf/issues/8307), [IDF@a57be7b](https://github.com/espressif/esp-idf/commit/a57be7b7d1135ddb29f9da636e9ad315f7fa1fa7))
- Update fuzzer test (add delegation, check memory) ([ec03fec](https://github.com/espressif/esp-protocols/commit/ec03fec), [IDF@2c10071](https://github.com/espressif/esp-idf/commit/2c1007156e01b4707b5c89d73cad05c0eef0264f))
- Remove legacy esp_event API ([5909e9e](https://github.com/espressif/esp-protocols/commit/5909e9e), [IDF@e46aa51](https://github.com/espressif/esp-idf/commit/e46aa515bdf5606a3d868f1034774d5fc96904b8))
- added missing includes ([82e2a5d](https://github.com/espressif/esp-protocols/commit/82e2a5d), [IDF@28d09c7](https://github.com/espressif/esp-idf/commit/28d09c7dbe145ffa6a7dd90531062d4f7669a9c8))
- Clear notification value in mdns_hostname_set ([48e4d40](https://github.com/espressif/esp-protocols/commit/48e4d40), [IDF@83a4ddb](https://github.com/espressif/esp-idf/commit/83a4ddbd250e2b386bccabb4705d4c58c1a22bcb))
- esp_timer: remove legacy ESP32 FRC timer implementation. ([ac6dcb6](https://github.com/espressif/esp-protocols/commit/ac6dcb6), [IDF@edb76f1](https://github.com/espressif/esp-idf/commit/edb76f14d6b3e925568ff04a87befe733ecc4517))
- freertos: Remove legacy data types ([085dbd8](https://github.com/espressif/esp-protocols/commit/085dbd8), [IDF@57fd78f](https://github.com/espressif/esp-idf/commit/57fd78f5baf93a368a82cf4b2e00ca17ffc09115))
- Tools: Custom baud-rate setup is not possible for IDF Monitor from menuconfig anymore ([f78e8cf](https://github.com/espressif/esp-protocols/commit/f78e8cf), [IDF@36a4011](https://github.com/espressif/esp-idf/commit/36a4011ff8985bfbae08ba0272194e6c3ef93bbf))
- Use memcpy() for copy to support non-text TXTs ([6cdf5ee](https://github.com/espressif/esp-protocols/commit/6cdf5ee), [IDF@6aefe9c](https://github.com/espressif/esp-idf/commit/6aefe9c18563ed567d384a956cf02b6f57d6894c))
- Support for null-value TXT records ([fcb5515](https://github.com/espressif/esp-protocols/commit/fcb5515), [IDF#8267](https://github.com/espressif/esp-idf/issues/8267), [IDF@23c2db4](https://github.com/espressif/esp-idf/commit/23c2db406dee8df09dbdba21cb7eef9fbca8bf27))
- Fix alloc issue if TXT has empty value ([9fdbe5f](https://github.com/espressif/esp-protocols/commit/9fdbe5f), [IDF@205f6ba](https://github.com/espressif/esp-idf/commit/205f6ba8541e12d958c7c56af5a7136090f12a0e))
- Fix random crash when defalt service instance queried ([20e6e9e](https://github.com/espressif/esp-protocols/commit/20e6e9e), [IDF@f46dffc](https://github.com/espressif/esp-idf/commit/f46dffca627e9578e49a510580f9754ec1e27e2e))
- Fix minor memory leaks when creating services ([c588263](https://github.com/espressif/esp-protocols/commit/c588263), [IDF@fad62cc](https://github.com/espressif/esp-idf/commit/fad62cc1ed3dce63b58297172a72a489d7af2d9d))
- Fix mDNS memory leak ([6258edf](https://github.com/espressif/esp-protocols/commit/6258edf), [IDF@119b4a9](https://github.com/espressif/esp-idf/commit/119b4a9dd12cf89cc5eaf63f8aa19730607ef30b))
- Fix mDNS memory leak ([c8b0d5e](https://github.com/espressif/esp-protocols/commit/c8b0d5e), [IDF@f5ffd53](https://github.com/espressif/esp-idf/commit/f5ffd53aeb402afc1333a98168bb2fa35d7cdc77))
- Use multi/uni-cast types in API ([5252b1d](https://github.com/espressif/esp-protocols/commit/5252b1d), [IDF@125c312](https://github.com/espressif/esp-idf/commit/125c3125524c71f4f48f635eda12e22fa3bca500))
- Allow for unicast PTR queries ([4e11cc8](https://github.com/espressif/esp-protocols/commit/4e11cc8), [IDF@7eeeb01](https://github.com/espressif/esp-idf/commit/7eeeb01ea705745b027bd8bc11d2b142418e9927))
- Fix potential null deref for ANY query type ([7af91ec](https://github.com/espressif/esp-protocols/commit/7af91ec), [IDF@99dd8ee](https://github.com/espressif/esp-idf/commit/99dd8eedb1a0e957f5f74344e3e4172e61c29ef8))
- Make fuzzer layers compatible with llvm>=6 ([01256d3](https://github.com/espressif/esp-protocols/commit/01256d3), [IDF@1882cbe](https://github.com/espressif/esp-idf/commit/1882cbe44e6140bebb2d27dc18af06dfcb0157f5))
- Fix copyright ([5a2d4ea](https://github.com/espressif/esp-protocols/commit/5a2d4ea), [IDF@c83678f](https://github.com/espressif/esp-idf/commit/c83678f64fe27844fc28050bde6433ccb04a0704))
- Add mDNS miss comment ([9de3f53](https://github.com/espressif/esp-protocols/commit/9de3f53), [IDF@08e0813](https://github.com/espressif/esp-idf/commit/08e081340d9d76d1244e9f2dc527e5ae370b1fbe))
- freertos: remove FREERTOS_ASSERT option ([bcabc8e](https://github.com/espressif/esp-protocols/commit/bcabc8e), [IDF@7255497](https://github.com/espressif/esp-idf/commit/72554971467a5edc9bd6e390cf8fe7b05e6ade81))
- Minor err print fix in socket-networking layer ([dfb27b3](https://github.com/espressif/esp-protocols/commit/dfb27b3), [IDF@f1b8f5c](https://github.com/espressif/esp-idf/commit/f1b8f5c1023df7d649161bc76f2bcc9a8f8f4d8b))
- unified errno format ([076c095](https://github.com/espressif/esp-protocols/commit/076c095), [IDF@87506f4](https://github.com/espressif/esp-idf/commit/87506f46e2922710f48a6b96ca75e53543ff45c4))
- always send A/AAAA records in announcements ([7dd0bc1](https://github.com/espressif/esp-protocols/commit/7dd0bc1), [IDF@456f80b](https://github.com/espressif/esp-idf/commit/456f80b754ebd0bd74e02c7febdf461c6b573b7a))
- filter instance name for ANY queries ([7e82a7c](https://github.com/espressif/esp-protocols/commit/7e82a7c), [IDF@5d0c473](https://github.com/espressif/esp-idf/commit/5d0c47303dd9ead0f2ad291dca1d4b7ce4e23b2b))
- Fix potential null deref reported by fuzzer test ([ae381b7](https://github.com/espressif/esp-protocols/commit/ae381b7), [IDF@cb5653f](https://github.com/espressif/esp-idf/commit/cb5653fd940a9cd41e8554a6d753fab46e0459d7))
- Minor fix of API description and API usage ([941dc5c](https://github.com/espressif/esp-protocols/commit/941dc5c), [IDF@c297301](https://github.com/espressif/esp-idf/commit/c297301ecc350f8315d7eaf78c72b4aba68d422a))
- Added results count to MDNS ([525c649](https://github.com/espressif/esp-protocols/commit/525c649), [IDF@f391d61](https://github.com/espressif/esp-idf/commit/f391d610e8185631b5361dc6c844c4c04aac30b1))
- fix mdns server instance mismatch ([f0839d9](https://github.com/espressif/esp-protocols/commit/f0839d9), [IDF@6173dd7](https://github.com/espressif/esp-idf/commit/6173dd78097216261277c20ebd92a53c68c47f89))
- support multiple instance for mdns service txt set ([69902ea](https://github.com/espressif/esp-protocols/commit/69902ea), [IDF@50f6302](https://github.com/espressif/esp-idf/commit/50f6302c5d7c0498fa1baa6fd6129d8233971a81))
- fix wrong PTR record count ([d0bbe88](https://github.com/espressif/esp-protocols/commit/d0bbe88), [IDF@5d3f815](https://github.com/espressif/esp-idf/commit/5d3f8157e0e481363ef93d54a29d957fc91cca86))
- Build & config: Remove leftover files from the unsupported "make" build system ([4a9d55e](https://github.com/espressif/esp-protocols/commit/4a9d55e), [IDF@766aa57](https://github.com/espressif/esp-idf/commit/766aa5708443099f3f033b739cda0e1de101cca6))
- Build & config: Remove the "make" build system ([be2a924](https://github.com/espressif/esp-protocols/commit/be2a924), [IDF@9c1d4f5](https://github.com/espressif/esp-idf/commit/9c1d4f5b549d6a7125e5c7c323c80d37361991cb))
- freertos: update freertos folder structure to match upstream ([76fcd41](https://github.com/espressif/esp-protocols/commit/76fcd41), [IDF@4846222](https://github.com/espressif/esp-idf/commit/48462221029c7da4b1ea233e9e781cd57ff91c7e))
- support service subtype ([fd8499c](https://github.com/espressif/esp-protocols/commit/fd8499c), [IDF#5508](https://github.com/espressif/esp-idf/issues/5508), [IDF@e7e8610](https://github.com/espressif/esp-idf/commit/e7e8610f563e0b8532a093ea8b803f0eb132fd0e))
- Fix parsing non-standard queries ([38b4fe2](https://github.com/espressif/esp-protocols/commit/38b4fe2), [IDF#7694](https://github.com/espressif/esp-idf/issues/7694), [IDF@d16f9ba](https://github.com/espressif/esp-idf/commit/d16f9bade5beab3785677dd5b39ebc4e9c895008))
- allow mutiple instances with same service type ([b266062](https://github.com/espressif/esp-protocols/commit/b266062), [IDF@b7a99f4](https://github.com/espressif/esp-idf/commit/b7a99f46587a69a2cd07e7616c3bb30b7b1a6edf))
- Update copyright header ([5e087d8](https://github.com/espressif/esp-protocols/commit/5e087d8), [IDF@2a2b95b](https://github.com/espressif/esp-idf/commit/2a2b95b9c22bc5090d87a4f4317288b64b14fcd9))
- Fix potential null dereference identified by fuzzer tests ([91a3d95](https://github.com/espressif/esp-protocols/commit/91a3d95), [IDF@e7dabb1](https://github.com/espressif/esp-idf/commit/e7dabb14f7c8fd9bd2bea55d8f1accc65323a1c0))
- components/bt: move config BT_RESERVE_DRAM from bluedroid to ESP32 controller ([6d6dd2b](https://github.com/espressif/esp-protocols/commit/6d6dd2b), [IDF@b310c06](https://github.com/espressif/esp-idf/commit/b310c062cd25f249e00dd03dd27baed783921630))
- add notification callback for async APIs ([52306e9](https://github.com/espressif/esp-protocols/commit/52306e9), [IDF@986603c](https://github.com/espressif/esp-idf/commit/986603cf07413b46c88c76c324bf500edcfb6171))
- add more mdns result attributes ([d37ab6d](https://github.com/espressif/esp-protocols/commit/d37ab6d), [IDF@76ec76c](https://github.com/espressif/esp-idf/commit/76ec76c12c871554147343bb7141da1e5de58011))
- Add host test using linux target ([5c55ea6](https://github.com/espressif/esp-protocols/commit/5c55ea6), [IDF@fc7e2d9](https://github.com/espressif/esp-idf/commit/fc7e2d9e908f61fb4b852cfae72aa5ff7c662ebc))
- Implement mdns_networking using BSD sockets ([0c71c7b](https://github.com/espressif/esp-protocols/commit/0c71c7b), [IDF@73dfe84](https://github.com/espressif/esp-idf/commit/73dfe84bf295a850edfad39b6b097a71f15964dc))
- fix crash when adding services without hostname set ([4c368c0](https://github.com/espressif/esp-protocols/commit/4c368c0), [IDF@5e98772](https://github.com/espressif/esp-idf/commit/5e98772eaf7e50d96cf2e6ecdfedcd928b61c864))
- Fix fuzzer IDF-mock layer ([af22753](https://github.com/espressif/esp-protocols/commit/af22753), [IDF@619235c](https://github.com/espressif/esp-idf/commit/619235c2ee5a1fe8411bd2be2de8798209f95902))
- Clean the main mdns module from lwip dependencies ([b0957e7](https://github.com/espressif/esp-protocols/commit/b0957e7), [IDF@54e3294](https://github.com/espressif/esp-idf/commit/54e329444a5dd19c51e84b5f1e16455a0f1c6225))
- Add asynchronous query API ([47c7266](https://github.com/espressif/esp-protocols/commit/47c7266), [IDF#7090](https://github.com/espressif/esp-idf/issues/7090), [IDF@d81482d](https://github.com/espressif/esp-idf/commit/d81482d699232b22f4a5cbee2a76199a5285dadb))
- Fix crashes reported by the fuzzer tests ([40da0d2](https://github.com/espressif/esp-protocols/commit/40da0d2), [IDF@4a2e726](https://github.com/espressif/esp-idf/commit/4a2e72677c6fb7681a7e2acd1a878d3deb114079))
- mdns/fuzzer: Fix non-instrumentation test to reproduce fuzzer issues ([5f6b6f9](https://github.com/espressif/esp-protocols/commit/5f6b6f9), [IDF@dae8033](https://github.com/espressif/esp-idf/commit/dae803335e6bc6d9751a360cd3f675ce4027853b))
- return ESP_OK rather than ERR_OK in API functions ([8a12082](https://github.com/espressif/esp-protocols/commit/8a12082), [IDF@2386113](https://github.com/espressif/esp-idf/commit/2386113972ee51ea93e9740d8c34bfe9289ce909))
- fix memory leak in mdns_free when adding delegated hostnames ([46f28a8](https://github.com/espressif/esp-protocols/commit/46f28a8), [IDF@0baee93](https://github.com/espressif/esp-idf/commit/0baee932111268c4a2103e1c1adeb7d99914a937))
- Support for One-Shot mDNS queries ([5a81eae](https://github.com/espressif/esp-protocols/commit/5a81eae), [IDF@f167238](https://github.com/espressif/esp-idf/commit/f167238fac37818aed75dc689eed54ad47528ab9))
- allow explicit txt value length ([2ddaee2](https://github.com/espressif/esp-protocols/commit/2ddaee2), [IDF@b4e0088](https://github.com/espressif/esp-idf/commit/b4e0088b68321acc4698b01faec7e2ffbe1e37c1))
- Fix crashes reported by the fuzzer ([27fc285](https://github.com/espressif/esp-protocols/commit/27fc285), [IDF@79ba738](https://github.com/espressif/esp-idf/commit/79ba738626d643d8c6f32bdcd455e0d2476f94c7))
- Minor correction of the test code ([93e6efe](https://github.com/espressif/esp-protocols/commit/93e6efe), [IDF@7d76245](https://github.com/espressif/esp-idf/commit/7d762451731cb305c3b090509827740f0195a496))
- Fix fuzzer from miss-interpreting adding services as timeouts ([bc4cda8](https://github.com/espressif/esp-protocols/commit/bc4cda8), [IDF@14099fe](https://github.com/espressif/esp-idf/commit/14099fe15efb1b0cde0a8370096c55bba62ff937))
- fix test script delayed response ([8a8d58d](https://github.com/espressif/esp-protocols/commit/8a8d58d), [IDF@a4f2639](https://github.com/espressif/esp-idf/commit/a4f263948c35c13340b6f4b59a649c5073787d5e))
- fix wrong SRV/PTR record handling ([402baeb](https://github.com/espressif/esp-protocols/commit/402baeb), [IDF@e613555](https://github.com/espressif/esp-idf/commit/e6135552d26480e39e11632437020535b1667b7a))
- fix wrong service hostname after mangling ([9fa25ef](https://github.com/espressif/esp-protocols/commit/9fa25ef), [IDF@439b31d](https://github.com/espressif/esp-idf/commit/439b31d065eddfdfb6eb4cf9c00454edfebc3d9b))
- fix empty address change announce packets ([121b525](https://github.com/espressif/esp-protocols/commit/121b525), [IDF@7bbb72d](https://github.com/espressif/esp-idf/commit/7bbb72d86540f04d37b0e2c4efb6dc66ee9c9ea0))
- fix mdns probe/reply behavior ([418fb60](https://github.com/espressif/esp-protocols/commit/418fb60), [IDF@d2a5d25](https://github.com/espressif/esp-idf/commit/d2a5d25984432d149ca31aea4a0d177f3509dd7b))
- make delegate host address a list ([4049b3b](https://github.com/espressif/esp-protocols/commit/4049b3b), [IDF@2d34352](https://github.com/espressif/esp-idf/commit/2d34352f3db0fa71366a838933a29138a90eb2af))
- add remove delegate host api ([c882119](https://github.com/espressif/esp-protocols/commit/c882119), [IDF@2174693](https://github.com/espressif/esp-idf/commit/2174693096b73ce93261611c44ecba647cd01859))
- add mdns delegation ([1eb5df9](https://github.com/espressif/esp-protocols/commit/1eb5df9), [IDF@401ff56](https://github.com/espressif/esp-idf/commit/401ff56cc1ad1d11284143a348cc0c0e4a363e98))
- fix memory free issue when repeating the query in reply ([b62b4b3](https://github.com/espressif/esp-protocols/commit/b62b4b3), [IDF@5f244c8](https://github.com/espressif/esp-idf/commit/5f244c86f29da46c17610563a245d1663a46b439))
- Fix of crash when wifi interface get deleted and mdns receives the packets ([4d8aec1](https://github.com/espressif/esp-protocols/commit/4d8aec1), [IDF#6973](https://github.com/espressif/esp-idf/issues/6973), [IDF@03de74a](https://github.com/espressif/esp-idf/commit/03de74a728d4b278f55e1fc30e0425483b806e80))
- Docs: Added README.md for lwip fuzzer tests ([6d64910](https://github.com/espressif/esp-protocols/commit/6d64910), [IDF@53c18a8](https://github.com/espressif/esp-idf/commit/53c18a85db104bb37ebeadec2faf5d42d764d0f9))
- Fixed the ip header TTL to be correctly set to 255 ([ab3fa69](https://github.com/espressif/esp-protocols/commit/ab3fa69), [IDF@5cce919](https://github.com/espressif/esp-idf/commit/5cce919cbef87f543bb9f5275b77b97b3b1ea67e))
- Fix parsing answers with questions when instance name not set ([c3a5826](https://github.com/espressif/esp-protocols/commit/c3a5826), [IDF#6598](https://github.com/espressif/esp-idf/issues/6598), [IDF@3404945](https://github.com/espressif/esp-idf/commit/34049454dfaf5132d9b258ef4d04921befc8997b))
- Fix the resolver to correctly parse it's own non-strict answers ([cbcbe4f](https://github.com/espressif/esp-protocols/commit/cbcbe4f), [IDF@b649603](https://github.com/espressif/esp-idf/commit/b649603a0d70ec804567f57752c3eddaed56198f))
- Add MDNS_STRICT_MODE config option ([adc3430](https://github.com/espressif/esp-protocols/commit/adc3430), [IDF@0eee315](https://github.com/espressif/esp-idf/commit/0eee31546dd4e6df0d1c1cc2740da0675dffb4bf))
- freertos: common config header ([c30617d](https://github.com/espressif/esp-protocols/commit/c30617d), [IDF@39cf818](https://github.com/espressif/esp-idf/commit/39cf818838b0259b3e00b3c198ad47b4add41939))
- Removed freeRTOS dependancies from fuzzer tests ([1e5eeb1](https://github.com/espressif/esp-protocols/commit/1e5eeb1), [IDF@5571694](https://github.com/espressif/esp-idf/commit/55716945a9908e057743d69e1d59399df03e49bd))
- mDNS: Updated APIs description and shows the warning when hostname contains domain name during the query ([22c7c0a](https://github.com/espressif/esp-protocols/commit/22c7c0a), [IDF#6590](https://github.com/espressif/esp-idf/issues/6590), [IDF@9f8d2b9](https://github.com/espressif/esp-idf/commit/9f8d2b944d2b3736a012e0dff1a8459b6941d295))
- components: Use CONFIG_LWIP_IPV6 to strip IPv6 function in components ([1623c0e](https://github.com/espressif/esp-protocols/commit/1623c0e), [IDF@da58235](https://github.com/espressif/esp-idf/commit/da58235a0ee262ff552c5f1155d531b5c31e8de6))
- add bound check when setting interface as duplicate ([b114ed6](https://github.com/espressif/esp-protocols/commit/b114ed6), [IDF@2b9d2c0](https://github.com/espressif/esp-idf/commit/2b9d2c06f54924b680c41ae641978c8d81612f65))
- mDNS: Fix of text length calculation when detecting a collision ([2ffd223](https://github.com/espressif/esp-protocols/commit/2ffd223), [IDF@be0ae1e](https://github.com/espressif/esp-idf/commit/be0ae1ebbbe9fae6ecf7de09e8d50cba063b61f4))
- global: fix sign-compare warnings ([1fe901f](https://github.com/espressif/esp-protocols/commit/1fe901f), [IDF@753a929](https://github.com/espressif/esp-idf/commit/753a9295259126217a9fe6ef1c5e9da21e9b4e28))
- lwip: Moved default SNTP API to esp_sntp.h ([2cf9fd8](https://github.com/espressif/esp-protocols/commit/2cf9fd8), [IDF@76f6dd6](https://github.com/espressif/esp-idf/commit/76f6dd6214ca583b1a94c7c553ccac739a27f6d5))
- Allow resolve its own non-strict answers ([89439e0](https://github.com/espressif/esp-protocols/commit/89439e0), [IDF#6190](https://github.com/espressif/esp-idf/issues/6190), [IDF@0693e17](https://github.com/espressif/esp-idf/commit/0693e172de392086b9bfd8cf1474d8d133af3298))
- mDNS: Fix of collision detection during txt length calculation ([becd5d0](https://github.com/espressif/esp-protocols/commit/becd5d0), [IDF#6114](https://github.com/espressif/esp-idf/issues/6114), [IDF@f33772c](https://github.com/espressif/esp-idf/commit/f33772c96037c795366e60082bdbbefe2a69165f))
- esp32c3: Apply one-liner/small changes for ESP32-C3 ([0d7a309](https://github.com/espressif/esp-protocols/commit/0d7a309), [IDF@5228d9f](https://github.com/espressif/esp-idf/commit/5228d9f9ced16118d87326f94d9f9dfd411e0be9))
- test: fix several test build error ([1fdffbb](https://github.com/espressif/esp-protocols/commit/1fdffbb), [IDF@b7ecccd](https://github.com/espressif/esp-idf/commit/b7ecccd9010f1deaba83de54374231c3c7f5b472))
- freertos: Add RISC-V port ([988d120](https://github.com/espressif/esp-protocols/commit/988d120), [IDF@87e13ba](https://github.com/espressif/esp-idf/commit/87e13baaf12fe6deae715d95e912a310fea4ba88))
- Fix wrong mdns source address if lwIP IPv6 zones disabled ([fd47df3](https://github.com/espressif/esp-protocols/commit/fd47df3), [IDF@7ac9761](https://github.com/espressif/esp-idf/commit/7ac97616c119e4d2f4cdd377dfc5abbf75ec5e30))
- Whitespace: Automated whitespace fixes (large commit) ([2cb3a6e](https://github.com/espressif/esp-protocols/commit/2cb3a6e), [IDF@66fb5a2](https://github.com/espressif/esp-idf/commit/66fb5a29bbdc2482d67c52e6f66b303378c9b789))
- test_compile_fuzzers: Fix include paths for host build ([825652f](https://github.com/espressif/esp-protocols/commit/825652f), [IDF@98a0cc7](https://github.com/espressif/esp-idf/commit/98a0cc783f701b238bea232b53250a538d34920a))
- CI: Add a test to pre-check fuzzer tests compilation before weekly run ([fc53888](https://github.com/espressif/esp-protocols/commit/fc53888), [IDF@637f5c0](https://github.com/espressif/esp-idf/commit/637f5c0a6842c42ee6cf7f41d3c5ae0cb28a68af))
- soc: descriptive part occupy whole component ([7635c04](https://github.com/espressif/esp-protocols/commit/7635c04), [IDF@79887fd](https://github.com/espressif/esp-idf/commit/79887fdc6c3d9a2e509cc189bb43c998d3f0f4ee))
- Coredump config option rename throughout IDF ([d5fe42b](https://github.com/espressif/esp-protocols/commit/d5fe42b), [IDF@20af94f](https://github.com/espressif/esp-idf/commit/20af94ff53c5147a76342800d007a6c49be50a7b))
- mdns, dns, dhcp, dhcps: update fuzzer test to work in CI ([e0bc60a](https://github.com/espressif/esp-protocols/commit/e0bc60a), [IDF@a43c06a](https://github.com/espressif/esp-idf/commit/a43c06a592bcf9404297b22268c33bb7a246632c))
- cmock: added cmock as component ([9772e49](https://github.com/espressif/esp-protocols/commit/9772e49), [IDF@20c068e](https://github.com/espressif/esp-idf/commit/20c068ef3b49999387896b90f8011b02f718485f))
- Support queries in responses in mDNS non-strict mode ([6021a88](https://github.com/espressif/esp-protocols/commit/6021a88), [IDF#5521](https://github.com/espressif/esp-idf/issues/5521), [IDF@bcfa36d](https://github.com/espressif/esp-idf/commit/bcfa36db8ffff997f1f95eaf6b011ffc4d46a10f))
- Fix include query ID in reponses ([78f71ec](https://github.com/espressif/esp-protocols/commit/78f71ec), [IDF#5574](https://github.com/espressif/esp-idf/issues/5574), [IDF@f62e321](https://github.com/espressif/esp-idf/commit/f62e321d87c1d520cccca951715c27730e06607a))
- Allow config mDNS task stack size ([3319844](https://github.com/espressif/esp-protocols/commit/3319844), [IDF@cf7e48c](https://github.com/espressif/esp-idf/commit/cf7e48c779edd84c3f99d5e8ed81027932302382))
- Remove mbedtls dependency ([ac70c9a](https://github.com/espressif/esp-protocols/commit/ac70c9a), [IDF@f4a4549](https://github.com/espressif/esp-idf/commit/f4a4549a344e7ff2444a188adbebbc136b47a7bb))
- limit the GOT_IP6_EVENT to only known network interfaces ([2b7d43e](https://github.com/espressif/esp-protocols/commit/2b7d43e), [IDF@ab8cab1](https://github.com/espressif/esp-idf/commit/ab8cab1c553ee5312ef47a7dea002f2585605006))
- esp32: add implementation of esp_timer based on TG0 LAC timer ([4eb3e89](https://github.com/espressif/esp-protocols/commit/4eb3e89), [IDF@739eb05](https://github.com/espressif/esp-idf/commit/739eb05bb97736b70507e7ebcfee58e670672d23))
- fixed typos in the variable names and the comments ([b5e5a64](https://github.com/espressif/esp-protocols/commit/b5e5a64), [IDF@ecca39e](https://github.com/espressif/esp-idf/commit/ecca39e19f663e32e16aef2a09df15443de347e9))
- fix preset of esp_netif ptr for local interfaces ([6713ffe](https://github.com/espressif/esp-protocols/commit/6713ffe), [IDF@09e36f9](https://github.com/espressif/esp-idf/commit/09e36f9f3354092b2a528baaaaccab28ff4774d6))
- fixed crash on event during deinit ([817c4fd](https://github.com/espressif/esp-protocols/commit/817c4fd), [IDF@eaa2f12](https://github.com/espressif/esp-idf/commit/eaa2f12d6761710d2633b4934fe09f6f45e20f4f))
- respond to discovery with the proper pseudo name _services._dns-sd._udp ([8f0dc6d](https://github.com/espressif/esp-protocols/commit/8f0dc6d), [IDF#4369](https://github.com/espressif/esp-idf/issues/4369), [IDF@de17a14](https://github.com/espressif/esp-idf/commit/de17a1487f8ba6f432b06199f2261132ec6e735f))
- fixed forgotten merge conflicts in debug code ([d20666f](https://github.com/espressif/esp-protocols/commit/d20666f), [IDF@d9433ef](https://github.com/espressif/esp-idf/commit/d9433ef69223a32d05abdca543fb530f2e6679e4))
- add missing include of esp_task.h ([662a4ce](https://github.com/espressif/esp-protocols/commit/662a4ce), [IDF@5884b80](https://github.com/espressif/esp-idf/commit/5884b80908d680874e27fa0c8b2df85b69d03dd3))
- add configuration values for task priority, affinity and internal service timeouts ([fb1de80](https://github.com/espressif/esp-protocols/commit/fb1de80), [IDF@c6f38f0](https://github.com/espressif/esp-idf/commit/c6f38f04f8eec1aae937cc87c111609772681cb3))
- tcpip_adapter: updated tcpip_adapter compatablity layer to include all public API and keep 100% backward compatibility update build of tcpip adapter when ethernet disabled ([1f35e9a](https://github.com/espressif/esp-protocols/commit/1f35e9a), [IDF@7f5cda1](https://github.com/espressif/esp-idf/commit/7f5cda1b825586903f85dc4ad7736b35712e46d7))
- update mdns to use esp-netif for mdns supported services such as STA, AP, ETH ([48b819b](https://github.com/espressif/esp-protocols/commit/48b819b), [IDF@19e24fe](https://github.com/espressif/esp-idf/commit/19e24fe61ed5ea6698dfd5e1f427e783360aa846))
- esp_netif: Introduction of esp-netif component as a replacement of tcpip_adpter ([53e2aa3](https://github.com/espressif/esp-protocols/commit/53e2aa3), [IDF@ffe043b](https://github.com/espressif/esp-idf/commit/ffe043b1a81a0f9e1cc2cfa8873e21318ec89143))
- examples: removed ip4addr_ntoa and used prefered IP2STR for displaying IP addresses ([3cc6446](https://github.com/espressif/esp-protocols/commit/3cc6446), [IDF@ec9f245](https://github.com/espressif/esp-idf/commit/ec9f245dd35d3e8e7b19a8dec5e05e003dc21f39))
- esp_event, mdns: fixes for CONFIG_ETH_ENABLED=n ([248b11b](https://github.com/espressif/esp-protocols/commit/248b11b), [IDF@569ad75](https://github.com/espressif/esp-idf/commit/569ad7545c32a2f1d0eff3f1e81df70fb76ad125))
- build and link hello-world for esp32s2beta ([901124b](https://github.com/espressif/esp-protocols/commit/901124b), [IDF@84b2f9f](https://github.com/espressif/esp-idf/commit/84b2f9f14d16533c84db2210f13a24cd817e0b0a))
- fix crash for hostname queries ([f6ff165](https://github.com/espressif/esp-protocols/commit/f6ff165), [IDF#4224](https://github.com/espressif/esp-idf/issues/4224), [IDF@3d11700](https://github.com/espressif/esp-idf/commit/3d1170031b340a231949fdc0d9c46d87af0d1b5d))
- fix possible race condition when checking DHCP status on WIFI_EVENT_STA_CONNECTED event. ([f44c569](https://github.com/espressif/esp-protocols/commit/f44c569), [IDF@7f410a0](https://github.com/espressif/esp-idf/commit/7f410a0bcbafa85dba05807c53c3c38999506509))
- use constant size of AAAA answer in mdns packets instead of deriving from lwip struct size, since the struct could contain also zones ([286c646](https://github.com/espressif/esp-protocols/commit/286c646), [IDF@e5e31c5](https://github.com/espressif/esp-idf/commit/e5e31c5d0172d68fd207fa31cc5d3bba82dad020))
- esp_wifi: wifi support new event mechanism ([c70d527](https://github.com/espressif/esp-protocols/commit/c70d527), [IDF@003a987](https://github.com/espressif/esp-idf/commit/003a9872b7de69d799e9d37521cfbcaff9b37e85))
- fix missing bye packet if services removed with mdns_service_remove_all() or mdns_free() ([7cdf96c](https://github.com/espressif/esp-protocols/commit/7cdf96c), [IDF#3660](https://github.com/espressif/esp-idf/issues/3660), [IDF@a001998](https://github.com/espressif/esp-idf/commit/a001998d5283b29ca9a374adf7cef3357b39a03a))
- mdns_service_remove_all doesn't take an argument ([407875d](https://github.com/espressif/esp-protocols/commit/407875d), [IDF@c2764f6](https://github.com/espressif/esp-idf/commit/c2764f6fe85681cfaf5dbbe168295284f09c09cd))
- tools: Mass fixing of empty prototypes (for -Wstrict-prototypes) ([3e753f5](https://github.com/espressif/esp-protocols/commit/3e753f5), [IDF@afbaf74](https://github.com/espressif/esp-idf/commit/afbaf74007e89d016dbade4072bf2e7a3874139a))
- fix ignoring mdns packet with some invalid name entries in question field ([144d4ad](https://github.com/espressif/esp-protocols/commit/144d4ad), [IDF@4bd4c7c](https://github.com/espressif/esp-idf/commit/4bd4c7caf3f9ef8402c5a27ab44561537407eb60))
- add esp_eth component ([680bad6](https://github.com/espressif/esp-protocols/commit/680bad6), [IDF@90c4827](https://github.com/espressif/esp-idf/commit/90c4827bd22aa61894a5b22b3b39247a7e44d6cf))
- components: use new component registration api ([7fb6686](https://github.com/espressif/esp-protocols/commit/7fb6686), [IDF@9eccd7c](https://github.com/espressif/esp-idf/commit/9eccd7c0826d6cc2e9de59304d1e5f76c0063ccf))
- fix static analysis warnings ([4912bef](https://github.com/espressif/esp-protocols/commit/4912bef), [IDF@c34de4c](https://github.com/espressif/esp-idf/commit/c34de4cba658e8331f8a3ab2f466190c7640595b))
- added initial suite of api unit tests ([181a22e](https://github.com/espressif/esp-protocols/commit/181a22e), [IDF@e680191](https://github.com/espressif/esp-idf/commit/e6801912c5c4861f828ab1f447280628bba9a5d7))
- mdns tests: adapt mdns fuzzer test to compile with event loop library ([4172219](https://github.com/espressif/esp-protocols/commit/4172219), [IDF@38d15cb](https://github.com/espressif/esp-idf/commit/38d15cbd637e8b8baacda9fc43e8e99d224530f5))
- fixed mdns crashing on reception of txt packet without a corresponding service closes #2866 ([98d2c1a](https://github.com/espressif/esp-protocols/commit/98d2c1a), [IDF@af48977](https://github.com/espressif/esp-idf/commit/af48977f21cea6b18dae10b2c8b64a78acfc647f))
- use const char* for mdns txt items types to remove warning when assigning ([84cbb1f](https://github.com/espressif/esp-protocols/commit/84cbb1f), [IDF@c050a75](https://github.com/espressif/esp-idf/commit/c050a75616803c7871ef11c060e440fae09000d9))
- updated doxygen comments documenting mdns api ([4c6818e](https://github.com/espressif/esp-protocols/commit/4c6818e), [IDF#1718](https://github.com/espressif/esp-idf/issues/1718), [IDF@a851aac](https://github.com/espressif/esp-idf/commit/a851aac255311124529f504486ca55bad15c1951))
- update mdns_out_question_s to be in line with mdns_parsed_question_s struct ([c440114](https://github.com/espressif/esp-protocols/commit/c440114), [IDF#1568]( https://github.com/espressif/esp-idf/issues/1568), [IDF@eddd5c4](https://github.com/espressif/esp-idf/commit/eddd5c4f2c686d9a1d6d3258569cc33752e78880))
- use esp_event library to handle events ([6ea0ea9](https://github.com/espressif/esp-protocols/commit/6ea0ea9), [IDF@a2d5952](https://github.com/espressif/esp-idf/commit/a2d59525e53099ee1ad63c3d60ff853f573ab535))
- fuzzer tests: update of mdns and lwip host compilation for fuzzer testing ([d9aec9f](https://github.com/espressif/esp-protocols/commit/d9aec9f), [IDF@bc60bbb](https://github.com/espressif/esp-idf/commit/bc60bbbeaf89f2bbfc5db4bd4f1e7ace81a2ab37))
- fix possible crash when probing on particular interface with duplicated service instances due to naming conflicts on network ([985e691](https://github.com/espressif/esp-protocols/commit/985e691), [IDF@265e983](https://github.com/espressif/esp-idf/commit/265e983a452a7eaefc1662cdc0e6ed839a37fe1a))
- enable pcbs before starting service thread to avoid updating pcb's internal variables from concurent tasks ([75deebb](https://github.com/espressif/esp-protocols/commit/75deebb), [IDF@c87f0cb](https://github.com/espressif/esp-idf/commit/c87f0cb6cad3c36b077f4aaeb1ca52fe6ed0cdaf))
- fix possible deadlock on mdns deinit calling mdns_free() ([fdd27dc](https://github.com/espressif/esp-protocols/commit/fdd27dc), [IDF#1696](https://github.com/espressif/esp-idf/issues/1696), [IDF@48b5501](https://github.com/espressif/esp-idf/commit/48b5501c250ed90da51a55ad4fc18e09f466a517))
- mdsn: fix race condition in updating packet data from user task when failed to allocate or queue a new service ([2ec3b55](https://github.com/espressif/esp-protocols/commit/2ec3b55), [IDF@021dc5d](https://github.com/espressif/esp-idf/commit/021dc5d453e21e2d1707f194668e69cf63ef4e84))
- fix possible crash when packet scheduled to transmit contained service which might have been already removed ([450cbf0](https://github.com/espressif/esp-protocols/commit/450cbf0), [IDF@67051a2](https://github.com/espressif/esp-idf/commit/67051a286ba60a01d4755c3682129153c2f95953))
- use binary semaphore instead of mutex when searching ([34f6d8d](https://github.com/espressif/esp-protocols/commit/34f6d8d), [IDF@eef0b50](https://github.com/espressif/esp-idf/commit/eef0b5090aee87efef1a6a37772b3b88c9ce8df8))
- fix memory leak in pbuf if tcpipadapter failed to get netif ([b6efc68](https://github.com/espressif/esp-protocols/commit/b6efc68), [IDF@8462751](https://github.com/espressif/esp-idf/commit/8462751f95a3ff18bdc1b01d02fabd1829fd9135))
- fix malfuctional query_txt ([90e4bab](https://github.com/espressif/esp-protocols/commit/90e4bab), [IDF@1a02773](https://github.com/espressif/esp-idf/commit/1a027734af06abf08fcb1c34ee65bdf50d12be4d))
- fix possible crash when mdns_free called while action queue not empty ([c546ab8](https://github.com/espressif/esp-protocols/commit/c546ab8), [IDF@206b47c](https://github.com/espressif/esp-idf/commit/206b47c03aca0acdf40d1d9c250e18aeddfe1bd7))
- fix memory leak when query for service receives multiple ptr entries for one instance ([6582b41](https://github.com/espressif/esp-protocols/commit/6582b41), [IDF@9a4da97](https://github.com/espressif/esp-idf/commit/9a4da97fb4b3c241998cb969a08c3a917ffb4cd1))
- fix crash after init if no memory for task ([358d26c](https://github.com/espressif/esp-protocols/commit/358d26c), [IDF@a47768d](https://github.com/espressif/esp-idf/commit/a47768dc4e4750fd7e1c29b15d6e2dd3c76e6591))
- fixed crash on free undefined ptr after skipped strdup ([2ac83d0](https://github.com/espressif/esp-protocols/commit/2ac83d0), [IDF@e0a8044](https://github.com/espressif/esp-idf/commit/e0a8044a16907e642001b883469618a999dbe6db))
- Correct Kconfigs according to the coding style ([98e3171](https://github.com/espressif/esp-protocols/commit/98e3171), [IDF@37126d3](https://github.com/espressif/esp-idf/commit/37126d3451eabb44eeeb48b8e2ee554dc233e2a8))
- fix networking running udp_sendif from lwip thread ([2f85c07](https://github.com/espressif/esp-protocols/commit/2f85c07), [IDF@f7d4a4b](https://github.com/espressif/esp-idf/commit/f7d4a4be6a9e0b0ac5edb9400d3b123dbbed2ffc))
- fixed static memory leak ([b30a7fe](https://github.com/espressif/esp-protocols/commit/b30a7fe), [IDF@6bb68a5](https://github.com/espressif/esp-idf/commit/6bb68a5a7567a94c3605136d44960ff060c74663))
- check all mallocs for failure and add default hook to log error with free heap ([7a4fdad](https://github.com/espressif/esp-protocols/commit/7a4fdad), [IDF@c8cb4cd](https://github.com/espressif/esp-idf/commit/c8cb4cd3c8eb56d5901ade03302ad1231d7f3de5))
- resolve memory leak when txt record received multiple times ([b4e5742](https://github.com/espressif/esp-protocols/commit/b4e5742), [IDF@a6b2b73](https://github.com/espressif/esp-idf/commit/a6b2b73f03bbb75a39685ddba6cf877fd1e5e6d7))
- skip sending search when finished, not properly locked timer task ([2763bcd](https://github.com/espressif/esp-protocols/commit/2763bcd), [IDF@31163f0](https://github.com/espressif/esp-idf/commit/31163f02d5c414d8b492dce6f729b43a0061581b))
- sending search packets also in probing and announcing state ([8cd0e8a](https://github.com/espressif/esp-protocols/commit/8cd0e8a), [IDF@d16762a](https://github.com/espressif/esp-idf/commit/d16762a036e35ce86ece86bb44e6e99f9cc7c431))
- fixed crashes on network changes ([9b3b41c](https://github.com/espressif/esp-protocols/commit/9b3b41c), [IDF@097282a](https://github.com/espressif/esp-idf/commit/097282a8e3f85958747430d9931ce0a545d37700))
- Update network code for mDNS to work with newest LwIP ([ea23007](https://github.com/espressif/esp-protocols/commit/ea23007), [IDF@3ec0e7e](https://github.com/espressif/esp-idf/commit/3ec0e7e2d2ddea70e9f8fb5025664d0fe24c301a))
- bugfix: mdns_service_txt_set() wasn't allocating memory for TXT records ([0c17121](https://github.com/espressif/esp-protocols/commit/0c17121), [IDF@e5e2702](https://github.com/espressif/esp-idf/commit/e5e2702ca3f63a29da57eb138f75a20c74fb2a94))
- cmake: make main a component again ([67173f6](https://github.com/espressif/esp-protocols/commit/67173f6), [IDF@d9939ce](https://github.com/espressif/esp-idf/commit/d9939cedd9b44d63dc148354c3a0a139b9c7113d))
- Feature/sync lwip as submodule ([fed787f](https://github.com/espressif/esp-protocols/commit/fed787f), [IDF@3578fe3](https://github.com/espressif/esp-idf/commit/3578fe39e01ba0c2d54824ac70c3276502661c6b))
- Fix a portion of the queries are issued with the wildcard query type ([b4ab30b](https://github.com/espressif/esp-protocols/commit/b4ab30b), [IDF@f3f0445](https://github.com/espressif/esp-idf/commit/f3f0445f4db7c9ad97ae10a9728767337aa7bb62))
- added CI job for AFL fuzzer tests ([dd71494](https://github.com/espressif/esp-protocols/commit/dd71494), [IDF@0c14764](https://github.com/espressif/esp-idf/commit/0c147648f7642d058b63fbe2ddd5de31c2326304))
- Minor fix for mdns_service_remove() ([39de491](https://github.com/espressif/esp-protocols/commit/39de491), [IDF@5c7eb7e](https://github.com/espressif/esp-idf/commit/5c7eb7e27be7508130459d896cf7d13ffefda87f))
- Replace all DOS line endings with Unix ([19acac7](https://github.com/espressif/esp-protocols/commit/19acac7), [IDF@a67d5d8](https://github.com/espressif/esp-idf/commit/a67d5d89e0e90390fa7ff02816a6a79008d75d40))
- remove executable permission from source files ([98069f9](https://github.com/espressif/esp-protocols/commit/98069f9), [IDF@cb649e4](https://github.com/espressif/esp-idf/commit/cb649e452f3c64a7db1f4a61e162a16b70248204))
- Fixed nullptr dereference in MDNS.c ([ad29d34](https://github.com/espressif/esp-protocols/commit/ad29d34), [IDF@fffbf7b](https://github.com/espressif/esp-idf/commit/fffbf7b75065b5852e064e04b0c5102dd0fc2244))
- MDNS-Fuzzer: AFL fuzzer tests for mdsn packet parser ([9f1be36](https://github.com/espressif/esp-protocols/commit/9f1be36), [IDF@e983230](https://github.com/espressif/esp-idf/commit/e983230be933fb83cebdd1945ba6539a7dc99b28))
- cmake: Add component dependency support ([c7701d4](https://github.com/espressif/esp-protocols/commit/c7701d4), [IDF@1cb5712](https://github.com/espressif/esp-idf/commit/1cb5712463a8963cd3e8331da90fb5e03f13575f))
- cmake: Remove defaults for COMPONENT_SRCDIRS, COMPONENT_SRCS, COMPONENT_ADD_INCLUDEDIRS ([f1ccc40](https://github.com/espressif/esp-protocols/commit/f1ccc40), [IDF@4f1a856](https://github.com/espressif/esp-idf/commit/4f1a856dbfd752336cd71730105e02ad8c045541))
- build system: Initial cmake support, work in progress ([84bd1d7](https://github.com/espressif/esp-protocols/commit/84bd1d7), [IDF@c671a0c](https://github.com/espressif/esp-idf/commit/c671a0c3ebf90f18576d6db55b51b62730c58301))
- fix the bug that in mdns test code redefine esp_err_t to uint32_t, which should be int32_t ([259d3fc](https://github.com/espressif/esp-protocols/commit/259d3fc), [IDF@81e4cad](https://github.com/espressif/esp-idf/commit/81e4cad61593cde879a5c44a08060d9d336e5a3f))
- Fix exception when service is removed while there are pending packets that depend on it ([7784d00](https://github.com/espressif/esp-protocols/commit/7784d00), [IDF@421c6f1](https://github.com/espressif/esp-idf/commit/421c6f154b10d9253b78875ab28ee6bdcaaaf3c0))
- Fix case where service is NULL and that will cause exception ([bce7d52](https://github.com/espressif/esp-protocols/commit/bce7d52), [IDF@4fa130a](https://github.com/espressif/esp-idf/commit/4fa130ae4fb5de99ddddc5a7bed7e26ae645591c))
- Fix issue with some mDNS parsers ([ef924f1](https://github.com/espressif/esp-protocols/commit/ef924f1), [IDF@51dde19](https://github.com/espressif/esp-idf/commit/51dde19a765533af67fc7be21f116641773a9be4))
- Import mDNS changes ([ad8c92d](https://github.com/espressif/esp-protocols/commit/ad8c92d), [IDF@4bddbc0](https://github.com/espressif/esp-idf/commit/4bddbc031cee83935c0e4df1dc72cc7000c97ba5))
- Fix compilation errors when using gcc-7.2.0 for the crosstool-ng toolchain ([3aa605f](https://github.com/espressif/esp-protocols/commit/3aa605f), [IDF@519edc3](https://github.com/espressif/esp-idf/commit/519edc332dae0160069fd790467cde8de78f1a0e))
- components/mdns: wrong Message compression detect ([00a72b8](https://github.com/espressif/esp-protocols/commit/00a72b8), [IDF@6e24566](https://github.com/espressif/esp-idf/commit/6e24566186c52dc5432b6b25c81abda577c21e85))
- fix leak after _mdns_create_service if we have a malloc error. ([907e7ee](https://github.com/espressif/esp-protocols/commit/907e7ee), [IDF@b6b36bd](https://github.com/espressif/esp-idf/commit/b6b36bd9ddf169039a5528f8b766048d97b975f7))
- Use LwIP IPC for low-level API calls ([b367484](https://github.com/espressif/esp-protocols/commit/b367484), [IDF@713964f](https://github.com/espressif/esp-idf/commit/713964fe9e98b4fa34145c497b7ab638dc57614c))
- Add AFL fuzz test ([4a8582f](https://github.com/espressif/esp-protocols/commit/4a8582f), [IDF@4c26227](https://github.com/espressif/esp-idf/commit/4c2622755d92efa1818d062d433725553437993c))
- implement fixes for issues found while fuzz testing ([75de31c](https://github.com/espressif/esp-protocols/commit/75de31c), [IDF@99d3990](https://github.com/espressif/esp-idf/commit/99d39909c4f19c63909d663e927ac0a8933a3ed5))
- add simple dns-sd meta query support ([4acf639](https://github.com/espressif/esp-protocols/commit/4acf639), [IDF@96e8a3c](https://github.com/espressif/esp-idf/commit/96e8a3c725095562d2725aaefa15adcfc5d78dd5))
- address security issues with mDNS ([91bb509](https://github.com/espressif/esp-protocols/commit/91bb509), [IDF@c89e11c](https://github.com/espressif/esp-idf/commit/c89e11c8fa64641edddf9a055745d825ae3fab9d))
- Initial mDNS component and example ([7fbf8e5](https://github.com/espressif/esp-protocols/commit/7fbf8e5), [IDF@dd3f18d](https://github.com/espressif/esp-idf/commit/dd3f18d2d88ee78909d4af2840dfdf0b9f715f28))

View File

@@ -0,0 +1,42 @@
if(CONFIG_MDNS_NETWORKING_SOCKET)
set(MDNS_NETWORKING "mdns_networking_socket.c")
else()
set(MDNS_NETWORKING "mdns_networking_lwip.c")
endif()
if(CONFIG_MDNS_ENABLE_CONSOLE_CLI)
set(MDNS_CONSOLE "mdns_console.c")
else()
set(MDNS_CONSOLE "")
endif()
set(MDNS_MEMORY "mdns_mem_caps.c")
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
set(dependencies esp_netif_linux esp_event)
set(private_dependencies esp_timer console esp_system)
set(srcs "mdns.c" ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
else()
set(dependencies lwip console esp_netif)
set(private_dependencies esp_timer esp_wifi)
set(srcs "mdns.c" ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
endif()
idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include"
REQUIRES ${dependencies}
PRIV_REQUIRES ${private_dependencies})
if(${target} STREQUAL "linux")
target_link_libraries(${COMPONENT_LIB} PRIVATE "-lbsd")
endif()
if(CONFIG_ETH_ENABLED)
idf_component_optional_requires(PRIVATE esp_eth)
endif()
idf_component_get_property(MDNS_VERSION ${COMPONENT_NAME} COMPONENT_VERSION)
target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DESP_MDNS_VERSION_NUMBER=\"${MDNS_VERSION}\"")

View File

@@ -0,0 +1,190 @@
menu "mDNS"
config MDNS_MAX_INTERFACES
int "Max number of interfaces"
range 1 9
default 3
help
Number of network interfaces to be served by the mDNS library.
Lowering this number helps to reduce some static RAM usage.
config MDNS_MAX_SERVICES
int "Max number of services"
default 10
help
Services take up a certain amount of memory, and allowing fewer
services to be open at the same time conserves memory. Specify
the maximum amount of services here.
config MDNS_TASK_PRIORITY
int "mDNS task priority"
range 1 255
default 1
help
Allows setting mDNS task priority. Please do not set the task priority
higher than priorities of system tasks. Compile time warning/error
would be emitted if the chosen task priority were too high.
config MDNS_ACTION_QUEUE_LEN
int "Maximum actions pending to the server"
range 8 64
default 16
help
Allows setting the length of mDNS action queue.
config MDNS_TASK_STACK_SIZE
int "mDNS task stack size"
default 4096
help
Allows setting mDNS task stacksize.
choice MDNS_TASK_AFFINITY
prompt "mDNS task affinity"
default MDNS_TASK_AFFINITY_CPU0
help
Allows setting mDNS tasks affinity, i.e. whether the task is pinned to
CPU0, pinned to CPU1, or allowed to run on any CPU.
config MDNS_TASK_AFFINITY_NO_AFFINITY
bool "No affinity"
config MDNS_TASK_AFFINITY_CPU0
bool "CPU0"
config MDNS_TASK_AFFINITY_CPU1
bool "CPU1"
depends on !FREERTOS_UNICORE
endchoice
config MDNS_TASK_AFFINITY
hex
default FREERTOS_NO_AFFINITY if MDNS_TASK_AFFINITY_NO_AFFINITY
default 0x0 if MDNS_TASK_AFFINITY_CPU0
default 0x1 if MDNS_TASK_AFFINITY_CPU1
menu "MDNS Memory Configuration"
choice MDNS_TASK_MEMORY_ALLOC_FROM
prompt "Select mDNS task create on which type of memory"
default MDNS_TASK_CREATE_FROM_INTERNAL
config MDNS_TASK_CREATE_FROM_SPIRAM
bool "mDNS task creates on the SPIRAM (READ HELP)"
depends on (SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC)
help
mDNS task creates on the SPIRAM.
This option requires FreeRTOS component to allow creating
tasks on the external memory.
Please read the documentation about FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
config MDNS_TASK_CREATE_FROM_INTERNAL
bool "mDNS task creates on the internal RAM"
endchoice
choice MDNS_MEMORY_ALLOC_FROM
prompt "Select mDNS memory allocation type"
default MDNS_MEMORY_ALLOC_INTERNAL
config MDNS_MEMORY_ALLOC_SPIRAM
bool "Allocate mDNS memory from SPIRAM"
depends on (SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC)
config MDNS_MEMORY_ALLOC_INTERNAL
bool "Allocate mDNS memory from internal RAM"
endchoice
config MDNS_MEMORY_CUSTOM_IMPL
bool "Implement custom memory functions"
default n
help
Enable to implement custom memory functions for mDNS library.
This option is useful when the application wants to use custom
memory allocation functions for mDNS library.
endmenu # MDNS Memory Configuration
config MDNS_SERVICE_ADD_TIMEOUT_MS
int "mDNS adding service timeout (ms)"
range 10 30000
default 2000
help
Configures timeout for adding a new mDNS service. Adding a service
fails if could not be completed within this time.
config MDNS_TIMER_PERIOD_MS
int "mDNS timer period (ms)"
range 10 10000
default 100
help
Configures period of mDNS timer, which periodically transmits packets
and schedules mDNS searches.
config MDNS_NETWORKING_SOCKET
bool "Use BSD sockets for mDNS networking"
default n
help
Enables optional mDNS networking implementation using BSD sockets
in UDP multicast mode.
This option creates a new thread to serve receiving packets (TODO).
This option uses additional N sockets, where N is number of interfaces.
config MDNS_SKIP_SUPPRESSING_OWN_QUERIES
bool "Skip suppressing our own packets"
default n
help
Enable only if the querier and the responder share the same IP address.
This usually happens in test mode, where we may run multiple instances of
responders/queriers on the same interface.
config MDNS_ENABLE_DEBUG_PRINTS
bool "Enable debug prints of mDNS packets"
default n
help
Enable for the library to log received and sent mDNS packets to stdout.
config MDNS_ENABLE_CONSOLE_CLI
bool "Enable Command Line Interface on device console"
default y
help
Enable for the console cli to be available on the device.
config MDNS_RESPOND_REVERSE_QUERIES
bool "Enable responding to IPv4 reverse queries"
default n
help
Enables support for IPv4 reverse lookup. If enabled, the mDNS library
response to PTR queries of "A.B.C.D.in-addr.arpa" type.
config MDNS_MULTIPLE_INSTANCE
bool "Multiple instances under the same service type"
default y
help
Enables adding multiple service instances under the same service type.
menu "MDNS Predefined interfaces"
config MDNS_PREDEF_NETIF_STA
bool "Use predefined interface for WiFi Station"
default y
help
Set up mDNS for the default WiFi station.
Disable this option if you do not need mDNS on default WiFi STA.
config MDNS_PREDEF_NETIF_AP
bool "Use predefined interface for WiFi Access Point"
default y
help
Set up mDNS for the default WiFi Access Point.
Disable this option if you do not need mDNS on default WiFi AP.
config MDNS_PREDEF_NETIF_ETH
bool "Use predefined interface for Ethernet"
depends on ETH_ENABLED
default y
help
Set up mDNS for the default Ethernet interface.
Disable this option if you do not need mDNS on default Ethernet.
endmenu # MDNS Predefined interfaces
endmenu

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,14 @@
# mDNS Service
[![Component Registry](https://components.espressif.com/components/espressif/mdns/badge.svg)](https://components.espressif.com/components/espressif/mdns)
mDNS is a multicast UDP service that is used to provide local network service and host discovery.
## Examples
Get started with example test [Example](https://github.com/espressif/esp-protocols/tree/master/components/mdns/examples):
## Documentation
* View the full [documentation(English)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html)
* View the full [documentation(Chinese)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/zh_CN/index.html)

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(query_advertise)

View File

@@ -0,0 +1,91 @@
# mDNS example
Shows how to use mDNS to advertise and query services and hosts
## Example workflow
- mDNS is initialized with host name and instance name defined through the project configuration and `_http._tcp` service is added to be advertised
- A delegated host `esp32-delegated._local` is added and another `_http._tcp` service is added for this host.
- WiFi STA is started and trying to connect to the access point defined through the project configuration
- The system event handler is used to pass the network events to mDNS so the service is aware when the interface comes up or down
- GPIO0 (BOOT Button) is initialized as pulled-up input that can be monitored for button press
- Example task is started to check if the button is pressed so it can execute the mDNS queries defined
### Configure the project
* Open the project configuration menu (`idf.py menuconfig`)
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu
* Set `mDNS Hostname` as host name prefix for the device and its instance name in `mDNS Instance Name`
* Disable `Resolve test services` to prevent the example from querying defined names/services on startup (cause warnings in example logs, as illustrated below)
### 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
```
- Wait for WiFi to connect to your access point
- You can now ping the device at `[board-hostname].local`, where `[board-hostname]` is preconfigured hostname, `esp32-mdns` by default.
- You can also browse for `_http._tcp` on the same network to find the advertised service
- Pressing the BOOT button will start querying the local network for the predefined in `check_button` hosts and services
- Note that for purpose of CI tests, configuration options of `MDNS_RESOLVE_TEST_SERVICES` and `MDNS_ADD_MAC_TO_HOSTNAME` are available, but disabled by default. If enabled, then the hostname suffix of last 3 bytes from device MAC address is added, e.g. `esp32-mdns-80FFFF`, and a query for test service is issued.
(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
```
I (0) cpu_start: Starting scheduler on APP CPU.
I (276) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (276) mdns-test: mdns hostname set to: [esp32-mdns]
I (286) wifi: wifi driver task: 3ffc2fa4, prio:23, stack:3584, core=0
I (286) wifi: wifi firmware version: a3be639
I (286) wifi: config NVS flash: enabled
I (296) wifi: config nano formating: disabled
I (296) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (306) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (336) wifi: Init dynamic tx buffer num: 32
I (336) wifi: Init data frame dynamic rx buffer num: 32
I (336) wifi: Init management frame dynamic rx buffer num: 32
I (346) wifi: Init static rx buffer size: 1600
I (346) wifi: Init static rx buffer num: 10
I (346) wifi: Init dynamic rx buffer num: 32
I (356) mdns-test: Setting WiFi configuration SSID myssid...
I (426) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0
I (426) wifi: mode : sta (30:ae:a4:80:FF:FF)
I (426) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (1756) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1
I (2736) wifi: state: init -> auth (b0)
I (2756) wifi: state: auth -> assoc (0)
I (2766) wifi: state: assoc -> run (10)
I (2786) wifi: connected with myssid, channel 11
I (2786) wifi: pm start, type: 1
I (4786) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2
I (21126) mdns-test: Query A: esp32.local
W (23176) mdns-test: ESP_ERR_NOT_FOUND: Host was not found!
I (23176) mdns-test: Query PTR: _arduino._tcp.local
W (26276) mdns-test: No results found!
I (26276) mdns-test: Query PTR: _http._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:80
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : switch4e4919
SRV : switch4e4919.local:80
TXT : [1] path=/config/authentication_page.htm;
A : 192.168.0.118
I (29396) mdns-test: Query PTR: _printer._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:515
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : HP Color LaserJet MFP M277dw (7C2E10)
```

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mdns_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,55 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
config MDNS_HOSTNAME
string "mDNS Hostname"
default "esp32-mdns"
help
mDNS Hostname for example to use
config MDNS_INSTANCE
string "mDNS Instance Name"
default "ESP32 with mDNS"
help
mDNS Instance Name for example to use
config MDNS_PUBLISH_DELEGATE_HOST
bool "Publish a delegated host"
help
Enable publishing a delegated host other than ESP32.
The example will also add a mock service for this host.
config MDNS_RESOLVE_TEST_SERVICES
bool "Resolve test services"
default n
help
Enable resolving test services on startup.
These services are advertized and evaluated in automated tests.
When executed locally, these will not be resolved and warnings appear in the log.
Please set to false to disable initial querying to avoid warnings.
config MDNS_ADD_MAC_TO_HOSTNAME
bool "Add mac suffix to hostname"
default n
help
If enabled, a portion of MAC address is added to the hostname, this is used
for evaluation of tests in CI
config MDNS_BUTTON_GPIO
int "Button GPIO to trigger querries"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 0
help
Set the GPIO number used as mDNS test button
config MDNS_ADD_CUSTOM_NETIF
bool "Add user netif to mdns service"
default n
help
If enabled, we try to add a custom netif to mdns service.
Note that for using with common connection example code, we have to disable
all predefined interfaces in mdns component setup (since we're adding one
of the default interfaces)
endmenu

View File

@@ -0,0 +1,6 @@
dependencies:
espressif/mdns:
version: ^1.0.0
idf: '>=5.0'
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@@ -0,0 +1,423 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/*
* MDNS-SD Query and advertise Example
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif_ip_addr.h"
#include "esp_mac.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "mdns.h"
#include "driver/gpio.h"
#include "netdb.h"
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)
/* CONFIG_LWIP_IPV4 was introduced in IDF v5.1, set CONFIG_LWIP_IPV4 to 1 by default for IDF v5.0 */
#ifndef CONFIG_LWIP_IPV4
#define CONFIG_LWIP_IPV4 1
#endif // CONFIG_LWIP_IPV4
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)
#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE
#define EXAMPLE_BUTTON_GPIO CONFIG_MDNS_BUTTON_GPIO
static const char *TAG = "mdns-test";
static char *generate_hostname(void);
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
static void query_mdns_host_with_gethostbyname(char *host);
static void query_mdns_host_with_getaddrinfo(char *host);
#endif
static void initialise_mdns(void)
{
char *hostname = generate_hostname();
//initialize mDNS
ESP_ERROR_CHECK(mdns_init());
//set mDNS hostname (required if you want to advertise services)
ESP_ERROR_CHECK(mdns_hostname_set(hostname));
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
//set default mDNS instance name
ESP_ERROR_CHECK(mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE));
//structure with TXT records
mdns_txt_item_t serviceTxtData[3] = {
{"board", "esp32"},
{"u", "user"},
{"p", "password"}
};
//initialize service
ESP_ERROR_CHECK(mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3));
ESP_ERROR_CHECK(mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server"));
#if CONFIG_MDNS_MULTIPLE_INSTANCE
ESP_ERROR_CHECK(mdns_service_add("ESP32-WebServer1", "_http", "_tcp", 80, NULL, 0));
#endif
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
char *delegated_hostname;
if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) {
abort();
}
mdns_ip_addr_t addr4, addr6;
esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4);
addr4.addr.type = ESP_IPADDR_TYPE_V4;
esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6);
addr6.addr.type = ESP_IPADDR_TYPE_V6;
addr4.next = &addr6;
addr6.next = NULL;
ESP_ERROR_CHECK(mdns_delegate_hostname_add(delegated_hostname, &addr4));
ESP_ERROR_CHECK(mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3));
free(delegated_hostname);
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
//add another TXT item
ESP_ERROR_CHECK(mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar"));
//change TXT item value
ESP_ERROR_CHECK(mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")));
free(hostname);
}
/* these strings match mdns_ip_protocol_t enumeration */
static const char *ip_protocol_str[] = {"V4", "V6", "MAX"};
static void mdns_print_results(mdns_result_t *results)
{
mdns_result_t *r = results;
mdns_ip_addr_t *a = NULL;
int i = 1, t;
while (r) {
if (r->esp_netif) {
printf("%d: Interface: %s, Type: %s, TTL: %" PRIu32 "\n", i++, esp_netif_get_ifkey(r->esp_netif),
ip_protocol_str[r->ip_protocol], r->ttl);
}
if (r->instance_name) {
printf(" PTR : %s.%s.%s\n", r->instance_name, r->service_type, r->proto);
}
if (r->hostname) {
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
}
if (r->txt_count) {
printf(" TXT : [%zu] ", r->txt_count);
for (t = 0; t < r->txt_count; t++) {
printf("%s=%s(%d); ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL", r->txt_value_len[t]);
}
printf("\n");
}
a = r->addr;
while (a) {
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
} else {
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
}
a = a->next;
}
r = r->next;
}
}
static void query_mdns_service(const char *service_name, const char *proto)
{
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
mdns_result_t *results = NULL;
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
if (err) {
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
return;
}
if (!results) {
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
static void lookup_mdns_delegated_service(const char *service_name, const char *proto)
{
ESP_LOGI(TAG, "Lookup delegated service: %s.%s.local", service_name, proto);
mdns_result_t *results = NULL;
esp_err_t err = mdns_lookup_delegated_service(NULL, service_name, proto, 20, &results);
if (err) {
ESP_LOGE(TAG, "Lookup Failed: %s", esp_err_to_name(err));
return;
}
if (!results) {
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
static void lookup_mdns_selfhosted_service(const char *service_name, const char *proto)
{
ESP_LOGI(TAG, "Lookup selfhosted service: %s.%s.local", service_name, proto);
mdns_result_t *results = NULL;
esp_err_t err = mdns_lookup_selfhosted_service(NULL, service_name, proto, 20, &results);
if (err) {
ESP_LOGE(TAG, "Lookup Failed: %s", esp_err_to_name(err));
return;
}
if (!results) {
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
static bool check_and_print_result(mdns_search_once_t *search)
{
// Check if any result is available
mdns_result_t *result = NULL;
if (!mdns_query_async_get_results(search, 0, &result, NULL)) {
return false;
}
if (!result) { // search timeout, but no result
return true;
}
// If yes, print the result
mdns_ip_addr_t *a = result->addr;
while (a) {
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
} else {
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
}
a = a->next;
}
// and free the result
mdns_query_results_free(result);
return true;
}
static void query_mdns_hosts_async(const char *host_name)
{
ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name);
mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1, NULL);
mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1, NULL);
while (s_a || s_aaaa) {
if (s_a && check_and_print_result(s_a)) {
ESP_LOGI(TAG, "Query A %s.local finished", host_name);
mdns_query_async_delete(s_a);
s_a = NULL;
}
if (s_aaaa && check_and_print_result(s_aaaa)) {
ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name);
mdns_query_async_delete(s_aaaa);
s_aaaa = NULL;
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
#ifdef CONFIG_LWIP_IPV4
static void query_mdns_host(const char *host_name)
{
ESP_LOGI(TAG, "Query A: %s.local", host_name);
struct esp_ip4_addr addr;
addr.addr = 0;
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
if (err) {
if (err == ESP_ERR_NOT_FOUND) {
ESP_LOGW(TAG, "%s: Host was not found!", esp_err_to_name(err));
return;
}
ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err));
return;
}
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
}
#endif // CONFIG_LWIP_IPV4
static void initialise_button(void)
{
gpio_config_t io_conf = {0};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = BIT64(EXAMPLE_BUTTON_GPIO);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
io_conf.pull_down_en = 0;
gpio_config(&io_conf);
}
static void check_button(void)
{
static bool old_level = true;
bool new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO);
if (!new_level && old_level) {
query_mdns_hosts_async("esp32-mdns");
#ifdef CONFIG_LWIP_IPV4
query_mdns_host("esp32");
#endif
query_mdns_service("_arduino", "_tcp");
query_mdns_service("_http", "_tcp");
query_mdns_service("_printer", "_tcp");
query_mdns_service("_ipp", "_tcp");
query_mdns_service("_afpovertcp", "_tcp");
query_mdns_service("_smb", "_tcp");
query_mdns_service("_ftp", "_tcp");
query_mdns_service("_nfs", "_tcp");
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
lookup_mdns_delegated_service("_http", "_tcp");
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
lookup_mdns_selfhosted_service("_http", "_tcp");
}
old_level = new_level;
}
static void mdns_example_task(void *pvParameters)
{
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
/* Send initial queries that are started by CI tester */
#ifdef CONFIG_LWIP_IPV4
query_mdns_host("tinytester");
#endif
query_mdns_host_with_gethostbyname("tinytester-lwip.local");
query_mdns_host_with_getaddrinfo("tinytester-lwip.local");
#endif
while (1) {
check_button();
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_LOGI(TAG, "mDNS Ver: %s", ESP_MDNS_VERSION_NUMBER);
initialise_mdns();
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
#if defined(CONFIG_MDNS_ADD_CUSTOM_NETIF) && !defined(CONFIG_MDNS_PREDEF_NETIF_STA) && !defined(CONFIG_MDNS_PREDEF_NETIF_ETH)
/* Demonstration of adding a custom netif to mdns service, but we're adding the default example one,
* so we must disable all predefined interfaces (PREDEF_NETIF_STA, AP and ETH) first
*/
ESP_ERROR_CHECK(mdns_register_netif(EXAMPLE_INTERFACE));
/* It is not enough to just register the interface, we have to enable is manually.
* This is typically performed in "GOT_IP" event handler, but we call it here directly
* since the `EXAMPLE_INTERFACE` netif is connected already, to keep the example simple.
*/
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ENABLE_IP4 | MDNS_EVENT_ENABLE_IP6));
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ANNOUNCE_IP4 | MDNS_EVENT_ANNOUNCE_IP6));
#if defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES)
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_IP4_REVERSE_LOOKUP | MDNS_EVENT_IP6_REVERSE_LOOKUP));
#endif
#endif // CONFIG_MDNS_ADD_CUSTOM_NETIF
initialise_button();
xTaskCreate(&mdns_example_task, "mdns_example_task", 2048, NULL, 5, NULL);
}
/** Generate host name based on sdkconfig, optionally adding a portion of MAC address to it.
* @return host name string allocated from the heap
*/
static char *generate_hostname(void)
{
#ifndef CONFIG_MDNS_ADD_MAC_TO_HOSTNAME
return strdup(CONFIG_MDNS_HOSTNAME);
#else
uint8_t mac[6];
char *hostname;
esp_read_mac(mac, ESP_MAC_WIFI_STA);
if (-1 == asprintf(&hostname, "%s-%02X%02X%02X", CONFIG_MDNS_HOSTNAME, mac[3], mac[4], mac[5])) {
abort();
}
return hostname;
#endif
}
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
/**
* @brief Executes gethostbyname and displays list of resolved addresses.
* Note: This function is used only to test advertised mdns hostnames resolution
*/
static void query_mdns_host_with_gethostbyname(char *host)
{
struct hostent *res = gethostbyname(host);
if (res) {
unsigned int i = 0;
while (res->h_addr_list[i] != NULL) {
ESP_LOGI(TAG, "gethostbyname: %s resolved to: %s", host,
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_LWIP_IPV4)
res->h_addrtype == AF_INET ? inet_ntoa(*(struct in_addr *)(res->h_addr_list[i])) :
inet6_ntoa(*(struct in6_addr *)(res->h_addr_list[i]))
#elif defined(CONFIG_LWIP_IPV6)
inet6_ntoa(*(struct in6_addr *)(res->h_addr_list[i]))
#else
inet_ntoa(*(struct in_addr *)(res->h_addr_list[i]))
#endif
);
i++;
}
}
}
/**
* @brief Executes getaddrinfo and displays list of resolved addresses.
* Note: This function is used only to test advertised mdns hostnames resolution
*/
static void query_mdns_host_with_getaddrinfo(char *host)
{
struct addrinfo hints;
struct addrinfo *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (!getaddrinfo(host, NULL, &hints, &res)) {
while (res) {
char *resolved_addr;
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_LWIP_IPV4)
resolved_addr = res->ai_family == AF_INET ?
inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr) :
inet6_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr);
#elif defined(CONFIG_LWIP_IPV6)
resolved_addr = inet6_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr);
#else
resolved_addr = inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr);
#endif // CONFIG_LWIP_IPV6
ESP_LOGI(TAG, "getaddrinfo: %s resolved to: %s", host, resolved_addr);
res = res->ai_next;
}
}
}
#endif

View File

@@ -0,0 +1,203 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import re
import select
import socket
import struct
import subprocess
import time
from threading import Event, Thread
try:
import dpkt
import dpkt.dns
except ImportError:
pass
def get_dns_query_for_esp(esp_host):
dns = dpkt.dns.DNS(
b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01'
)
dns.qd[0].name = esp_host + u'.local'
print('Created query for esp host: {} '.format(dns.__repr__()))
return dns.pack()
def get_dns_answer_to_mdns(tester_host):
dns = dpkt.dns.DNS(
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
)
dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA
dns.rcode = dpkt.dns.DNS_RCODE_NOERR
arr = dpkt.dns.DNS.RR()
arr.cls = dpkt.dns.DNS_IN
arr.type = dpkt.dns.DNS_A
arr.name = tester_host
arr.ip = socket.inet_aton('127.0.0.1')
dns.an.append(arr)
print('Created answer to mdns query: {} '.format(dns.__repr__()))
return dns.pack()
def get_dns_answer_to_mdns_lwip(tester_host, id):
dns = dpkt.dns.DNS(
b'\x5e\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x0a\x64\x61\x76\x69\x64'
b'\x2d\x63\x6f\x6d\x70\x05\x6c\x6f\x63\x61\x6c\x00\x00\x01\x00\x01\xc0\x0c'
b'\x00\x01\x00\x01\x00\x00\x00\x0a\x00\x04\xc0\xa8\x0a\x6c')
dns.qd[0].name = tester_host
dns.an[0].name = tester_host
dns.an[0].ip = socket.inet_aton('127.0.0.1')
dns.an[0].rdata = socket.inet_aton('127.0.0.1')
dns.id = id
print('Created answer to mdns (lwip) query: {} '.format(dns.__repr__()))
return dns.pack()
def mdns_server(esp_host, events):
UDP_IP = '0.0.0.0'
UDP_PORT = 5353
MCAST_GRP = '224.0.0.251'
TESTER_NAME = u'tinytester.local'
TESTER_NAME_LWIP = u'tinytester-lwip.local'
QUERY_TIMEOUT = 0.2
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock.setblocking(False)
sock.bind((UDP_IP, UDP_PORT))
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
last_query_timepoint = time.time()
while not events['stop'].is_set():
try:
current_time = time.time()
if current_time - last_query_timepoint > QUERY_TIMEOUT:
last_query_timepoint = current_time
if not events['esp_answered'].is_set():
sock.sendto(get_dns_query_for_esp(esp_host),
(MCAST_GRP, UDP_PORT))
if not events['esp_delegated_answered'].is_set():
sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'),
(MCAST_GRP, UDP_PORT))
timeout = max(
0, QUERY_TIMEOUT - (current_time - last_query_timepoint))
read_socks, _, _ = select.select([sock], [], [], timeout)
if not read_socks:
continue
data, addr = sock.recvfrom(1024)
dns = dpkt.dns.DNS(data)
if len(dns.qd) > 0:
for dns_query in dns.qd:
if dns_query.type == dpkt.dns.DNS_A:
if dns_query.name == TESTER_NAME:
print('Received query: {} '.format(dns.__repr__()))
sock.sendto(get_dns_answer_to_mdns(TESTER_NAME),
(MCAST_GRP, UDP_PORT))
elif dns_query.name == TESTER_NAME_LWIP:
print('Received query: {} '.format(dns.__repr__()))
sock.sendto(
get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id),
addr)
if len(dns.an) > 0:
for dns_answer in dns.an:
if dns_answer.type == dpkt.dns.DNS_A:
print('Received answer from {}'.format(dns_answer.name))
if dns_answer.name == esp_host + u'.local':
print('Received answer to esp32-mdns query: {}'.format(
dns.__repr__()))
events['esp_answered'].set()
if dns_answer.name == esp_host + u'-delegated.local':
print('Received answer to esp32-mdns-delegate query: {}'.format(
dns.__repr__()))
events['esp_delegated_answered'].set()
except socket.timeout:
break
except dpkt.UnpackError:
continue
def test_examples_protocol_mdns(dut):
"""
steps: |
1. obtain IP address + init mdns example
2. get the dut host name (and IP address)
3. check the mdns name is accessible
4. check DUT output if mdns advertized host is resolved
5. check if DUT responds to dig
6. check the DUT is searchable via reverse IP lookup
"""
specific_host = dut.expect(r'mdns hostname set to: \[(.*?)\]')[1].decode()
mdns_server_events = {
'stop': Event(),
'esp_answered': Event(),
'esp_delegated_answered': Event()
}
mdns_responder = Thread(target=mdns_server,
args=(str(specific_host), mdns_server_events))
ip_addresses = []
if dut.app.sdkconfig.get('LWIP_IPV4') is True:
ipv4 = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]',
timeout=30)[1].decode()
ip_addresses.append(ipv4)
if dut.app.sdkconfig.get('LWIP_IPV6') is True:
ipv6_r = r':'.join((r'[0-9a-fA-F]{4}', ) * 8)
ipv6 = dut.expect(ipv6_r, timeout=30)[0].decode()
ip_addresses.append(ipv6)
print('Connected with IP addresses: {}'.format(','.join(ip_addresses)))
try:
# TODO: Add test for example disabling IPV4
mdns_responder.start()
if dut.app.sdkconfig.get('LWIP_IPV4') is True:
# 3. check the mdns name is accessible.
if not mdns_server_events['esp_answered'].wait(timeout=30):
raise ValueError(
'Test has failed: did not receive mdns answer within timeout')
if not mdns_server_events['esp_delegated_answered'].wait(timeout=30):
raise ValueError(
'Test has failed: did not receive mdns answer for delegated host within timeout'
)
# 4. check DUT output if mdns advertized host is resolved
dut.expect(
re.compile(
b'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1')
)
dut.expect(
re.compile(
b'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1'
))
dut.expect(
re.compile(
b'mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1'
))
# 5. check the DUT answers to `dig` command
dig_output = subprocess.check_output([
'dig', '+short', '-p', '5353', '@224.0.0.251',
'{}.local'.format(specific_host)
])
print('Resolving {} using "dig" succeeded with:\n{}'.format(
specific_host, dig_output))
if not ipv4.encode('utf-8') in dig_output:
raise ValueError(
'Test has failed: Incorrectly resolved DUT hostname using dig'
"Output should've contained DUT's IP address:{}".format(ipv4))
# 6. check the DUT reverse lookup
if dut.app.sdkconfig.get('MDNS_RESPOND_REVERSE_QUERIES') is True:
for ip_address in ip_addresses:
dig_output = subprocess.check_output([
'dig', '+short', '-p', '5353', '@224.0.0.251', '-x',
'{}'.format(ip_address)
])
print('Reverse lookup for {} using "dig" succeeded with:\n{}'.
format(ip_address, dig_output))
if specific_host not in dig_output.decode():
raise ValueError(
'Test has failed: Incorrectly resolved DUT IP address using dig'
"Output should've contained DUT's name:{}".format(
specific_host))
finally:
mdns_server_events['stop'].set()
mdns_responder.join()

View File

@@ -0,0 +1,21 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
CONFIG_MDNS_PREDEF_NETIF_STA=n
CONFIG_MDNS_PREDEF_NETIF_AP=n
CONFIG_MDNS_PREDEF_NETIF_ETH=n
CONFIG_MDNS_ADD_CUSTOM_NETIF=y
CONFIG_MDNS_RESPOND_REVERSE_QUERIES=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_PHY_GENERIC=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_MDNS_BUTTON_GPIO=32

View File

@@ -0,0 +1,15 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_MDNS_BUTTON_GPIO=32

View File

@@ -0,0 +1,15 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_LWIP_IPV4=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_MDNS_BUTTON_GPIO=32

View File

@@ -0,0 +1,16 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_LWIP_IPV6=n
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_MDNS_BUTTON_GPIO=32

View File

@@ -0,0 +1,16 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
CONFIG_MDNS_NETWORKING_SOCKET=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_MDNS_BUTTON_GPIO=32

View File

@@ -0,0 +1,13 @@
dependencies:
idf:
version: '>=5.0'
description: Multicast UDP service used to provide local network service and host
discovery.
documentation: https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html
issues: https://github.com/espressif/esp-protocols/issues
repository: git://github.com/espressif/esp-protocols.git
repository_info:
commit_sha: 3bfa00389de6f0d6d40efda8bea808380899a43d
path: components/mdns
url: https://github.com/espressif/esp-protocols/tree/master/components/mdns
version: 1.9.1

View File

@@ -0,0 +1,937 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_MDNS_H_
#define ESP_MDNS_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
#include <esp_netif.h>
#define MDNS_TYPE_A 0x0001
#define MDNS_TYPE_PTR 0x000C
#define MDNS_TYPE_TXT 0x0010
#define MDNS_TYPE_AAAA 0x001C
#define MDNS_TYPE_SRV 0x0021
#define MDNS_TYPE_OPT 0x0029
#define MDNS_TYPE_NSEC 0x002F
#define MDNS_TYPE_ANY 0x00FF
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES)
#define MDNS_NAME_MAX_LEN (64+4) // Need to account for IPv6 reverse queries (64 char address + ".ip6" )
#else
#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto
#endif
#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto
/**
* @brief Asynchronous query handle
*/
typedef struct mdns_search_once_s mdns_search_once_t;
/**
* @brief Daemon query handle
*/
typedef struct mdns_browse_s mdns_browse_t;
typedef enum {
MDNS_EVENT_ENABLE_IP4 = 1 << 1,
MDNS_EVENT_ENABLE_IP6 = 1 << 2,
MDNS_EVENT_ANNOUNCE_IP4 = 1 << 3,
MDNS_EVENT_ANNOUNCE_IP6 = 1 << 4,
MDNS_EVENT_DISABLE_IP4 = 1 << 5,
MDNS_EVENT_DISABLE_IP6 = 1 << 6,
MDNS_EVENT_IP4_REVERSE_LOOKUP = 1 << 7,
MDNS_EVENT_IP6_REVERSE_LOOKUP = 1 << 8,
} mdns_event_actions_t;
/**
* @brief mDNS enum to specify the ip_protocol type
*/
typedef enum {
MDNS_IP_PROTOCOL_V4,
MDNS_IP_PROTOCOL_V6,
MDNS_IP_PROTOCOL_MAX
} mdns_ip_protocol_t;
/**
* @brief mDNS basic text item structure
* Used in mdns_service_add()
*/
typedef struct {
const char *key; /*!< item key name */
const char *value; /*!< item value string */
} mdns_txt_item_t;
/**
* @brief mDNS basic subtype item structure
* Used in mdns_service_subtype_xxx() APIs
*/
typedef struct {
const char *subtype; /*!< subtype name */
} mdns_subtype_item_t;
/**
* @brief mDNS query linked list IP item
*/
typedef struct mdns_ip_addr_s {
esp_ip_addr_t addr; /*!< IP address */
struct mdns_ip_addr_s *next; /*!< next IP, or NULL for the last IP in the list */
} mdns_ip_addr_t;
/**
* @brief mDNS query type to be explicitly set to either Unicast or Multicast
*/
typedef enum {
MDNS_QUERY_UNICAST,
MDNS_QUERY_MULTICAST,
} mdns_query_transmission_type_t;
/**
* @brief mDNS query result structure
*/
typedef struct mdns_result_s {
struct mdns_result_s *next; /*!< next result, or NULL for the last result in the list */
esp_netif_t *esp_netif; /*!< ptr to corresponding esp-netif */
uint32_t ttl; /*!< time to live */
mdns_ip_protocol_t ip_protocol; /*!< ip_protocol type of the interface (v4/v6) */
// PTR
char *instance_name; /*!< instance name */
char *service_type; /*!< service type */
char *proto; /*!< srevice protocol */
// SRV
char *hostname; /*!< hostname */
uint16_t port; /*!< service port */
// TXT
mdns_txt_item_t *txt; /*!< txt record */
uint8_t *txt_value_len; /*!< array of txt value len of each record */
size_t txt_count; /*!< number of txt items */
// A and AAAA
mdns_ip_addr_t *addr; /*!< linked list of IP addresses found */
} mdns_result_t;
typedef void (*mdns_query_notify_t)(mdns_search_once_t *search);
typedef void (*mdns_browse_notify_t)(mdns_result_t *result);
/**
* @brief Initialize mDNS on given interface
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE when failed to register event handler
* - ESP_ERR_NO_MEM on memory error
* - ESP_FAIL when failed to start mdns task
*/
esp_err_t mdns_init(void);
/**
* @brief Stop and free mDNS server
*
*/
void mdns_free(void);
/**
* @brief Set the hostname for mDNS server
* required if you want to advertise services
*
* @param hostname Hostname to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_hostname_set(const char *hostname);
/**
* @brief Get the hostname for mDNS server
*
* @param hostname pointer to the hostname, it should be allocated
* and hold at least MDNS_NAME_BUF_LEN chars
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_INVALID_STATE when mdns is not initialized
*/
esp_err_t mdns_hostname_get(char *hostname);
/**
* @brief Adds a hostname and address to be delegated
* A/AAAA queries will be replied for the hostname and
* services can be added to this host.
*
* @param hostname Hostname to add
* @param address_list The IP address list of the host
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*
*/
esp_err_t mdns_delegate_hostname_add(const char *hostname, const mdns_ip_addr_t *address_list);
/**
* @brief Set the address to a delegated hostname
*
* @param hostname Hostname to set
* @param address_list The IP address list of the host
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*
*/
esp_err_t mdns_delegate_hostname_set_address(const char *hostname, const mdns_ip_addr_t *address_list);
/**
* @brief Remove a delegated hostname
* All the services added to this host will also be removed.
*
* @param hostname Hostname to remove
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*
*/
esp_err_t mdns_delegate_hostname_remove(const char *hostname);
/**
* @brief Query whether a hostname has been added
*
* @param hostname Hostname to query
*
* @return
* - true The hostname has been added.
* - false The hostname has not been added.
*
*/
bool mdns_hostname_exists(const char *hostname);
/**
* @brief Set the default instance name for mDNS server
*
* @param instance_name Instance name to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_instance_name_set(const char *instance_name);
/**
* @brief Add service to mDNS server
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param instance_name instance name to set. If NULL,
* global instance name or hostname will be used.
* Note that MDNS_MULTIPLE_INSTANCE config option
* needs to be enabled for adding multiple instances
* with the same instance type.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param port service port
* @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
* - ESP_FAIL failed to add service
*/
esp_err_t mdns_service_add(const char *instance_name, const char *service_type, const char *proto, uint16_t port, mdns_txt_item_t txt[], size_t num_items);
/**
* @brief Add service to mDNS server with a delegated hostname
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param instance_name instance name to set. If NULL,
* global instance name or hostname will be used
* Note that MDNS_MULTIPLE_INSTANCE config option
* needs to be enabled for adding multiple instances
* with the same instance type.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param port service port
* @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM memory error
* - ESP_FAIL failed to add service
*/
esp_err_t mdns_service_add_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, uint16_t port, mdns_txt_item_t txt[], size_t num_items);
/**
* @brief Check whether a service has been added.
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, checks for the local hostname.
*
* @return
* - true Correspondding service has been added.
* - false Service not found.
*/
bool mdns_service_exists(const char *service_type, const char *proto, const char *hostname);
/**
* @brief Check whether a service has been added.
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, checks for the local hostname.
*
* @return
* - true Correspondding service has been added.
* - false Service not found.
*/
bool mdns_service_exists_with_instance(const char *instance, const char *service_type, const char *proto,
const char *hostname);
/**
* @brief Remove service from mDNS server
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_remove(const char *service_type, const char *proto);
/**
* @brief Remove service from mDNS server with hostname
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_remove_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname);
/**
* @brief Set instance name for service
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param instance_name instance name to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_instance_name_set(const char *service_type, const char *proto, const char *instance_name);
/**
* @brief Set instance name for service with hostname
*
* @param instance_old original instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param instance_name instance name to set
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_instance_name_set_for_host(const char *instance_old, const char *service_type, const char *proto, const char *hostname,
const char *instance_name);
/**
* @brief Set service port
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param port service port
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_port_set(const char *service_type, const char *proto, uint16_t port);
/**
* @brief Set service port with hostname
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param port service port
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_port_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname,
uint16_t port);
/**
* @brief Replace all TXT items for service
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param txt array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_set(const char *service_type, const char *proto, mdns_txt_item_t txt[], uint8_t num_items);
/**
* @brief Replace all TXT items for service with hostname
*
* @note The value length of txt items will be automatically decided by strlen
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param txt array of TXT data (eg. {{"var","val"},{"other","2"}})
* @param num_items number of items in TXT data
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname,
mdns_txt_item_t txt[], uint8_t num_items);
/**
* @brief Set/Add TXT item for service TXT record
*
* @note The value length will be automatically decided by strlen
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to add/update
* @param value the new value of the key
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set(const char *service_type, const char *proto, const char *key, const char *value);
/**
* @brief Set/Add TXT item for service TXT record
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to add/update
* @param value the new value of the key
* @param value_len the length of the value
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_with_explicit_value_len(const char *service_type, const char *proto,
const char *key, const char *value, uint8_t value_len);
/**
* @brief Set/Add TXT item for service TXT record with hostname
*
* @note The value length will be automatically decided by strlen
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param key the key that you want to add/update
* @param value the new value of the key
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname,
const char *key, const char *value);
/**
* @brief Set/Add TXT item for service TXT record with hostname and txt value length
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param key the key that you want to add/update
* @param value the new value of the key
* @param value_len the length of the value
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_for_host_with_explicit_value_len(const char *instance, const char *service_type, const char *proto,
const char *hostname, const char *key,
const char *value, uint8_t value_len);
/**
* @brief Remove TXT item for service TXT record
*
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param key the key that you want to remove
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_remove(const char *service_type, const char *proto, const char *key);
/**
* @brief Remove TXT item for service TXT record with hostname
*
* @param instance instance name
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param key the key that you want to remove
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_remove_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname,
const char *key);
/**
* @brief Add a subtype for service.
*
* @param instance_name instance name. If NULL, will find the first service with the same service type and protocol.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param subtype The subtype to add.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_subtype_add_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, const char *subtype);
/**
* @brief Remove a subtype for service.
*
* @param instance_name instance name. If NULL, will find the first service with the same service type and protocol.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param subtype The subtype to remove.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
*/
esp_err_t mdns_service_subtype_remove_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, const char *subtype);
/**
* @brief Add multiple subtypes for service at once.
*
* @param instance_name instance name. If NULL, will find the first service with the same service type and protocol.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param subtype the pointer of subtype array to add.
* @param num_items number of items in subtype array
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_subtype_add_multiple_items_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items);
/**
* @brief Update subtype for service.
*
* @param instance_name instance name. If NULL, will find the first service with the same service type and protocol.
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param hostname service hostname. If NULL, local hostname will be used.
* @param subtype the pointer of subtype array to add.
* @param num_items number of items in subtype array
*
* @note If `num_items` is 0, then remove all subtypes.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_FOUND Service not found
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_subtype_update_multiple_items_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items);
/**
* @brief Remove and free all services from mDNS server
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t mdns_service_remove_all(void);
/**
* @brief Deletes the finished query. Call this only after the search has ended!
*
* @param search pointer to search object
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE search has not finished
* - ESP_ERR_INVALID_ARG pointer to search object is NULL
*/
esp_err_t mdns_query_async_delete(mdns_search_once_t *search);
/**
* @brief Get results from search pointer. Results available as a pointer to the output parameter.
* Pointer to search object has to be deleted via `mdns_query_async_delete` once the query has finished.
* The results although have to be freed manually.
*
* @param search pointer to search object
* @param timeout time in milliseconds to wait for answers
* @param results pointer to the results of the query
* @param num_results pointer to the number of the actual result items (set to NULL to ignore this return value)
*
* @return
* True if search has finished before or at timeout
* False if search timeout is over
*/
bool mdns_query_async_get_results(mdns_search_once_t *search, uint32_t timeout, mdns_result_t **results, uint8_t *num_results);
/**
* @brief Query mDNS for host or service asynchronousely.
* Search has to be tested for progress and deleted manually!
*
* @param name service instance or host name (NULL for PTR queries)
* @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries)
* @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries)
* @param type type of query (MDNS_TYPE_*)
* @param timeout time in milliseconds during which mDNS query is active
* @param max_results maximum results to be collected
* @param notifier Notification function to be called when the result is ready, can be NULL
*
* @return mdns_search_once_s pointer to new search object if query initiated successfully.
* NULL otherwise.
*/
mdns_search_once_t *mdns_query_async_new(const char *name, const char *service_type, const char *proto, uint16_t type,
uint32_t timeout, size_t max_results, mdns_query_notify_t notifier);
/**
* @brief Generic mDNS query
* All following query methods are derived from this one
*
* @param name service instance or host name (NULL for PTR queries)
* @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries)
* @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries)
* @param type type of query (MDNS_TYPE_*)
* @param transmission_type either Unicast query, or Multicast query
* @param timeout time in milliseconds to wait for answers.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
* results must be freed using mdns_query_results_free below
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG timeout was not given
*/
esp_err_t mdns_query_generic(const char *name, const char *service_type, const char *proto, uint16_t type,
mdns_query_transmission_type_t transmission_type, uint32_t timeout, size_t max_results, mdns_result_t **results);
/**
* @brief Query mDNS for host or service
*
* Note that querying PTR types sends Multicast query, all other types send Unicast queries
*
* @param name service instance or host name (NULL for PTR queries)
* @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries)
* @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries)
* @param type type of query (MDNS_TYPE_*)
* @param timeout time in milliseconds to wait for answers.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
* results must be freed using mdns_query_results_free below
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG timeout was not given
*/
esp_err_t mdns_query(const char *name, const char *service_type, const char *proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t **results);
/**
* @brief Free query results
*
* @param results linked list of results to be freed
*/
void mdns_query_results_free(mdns_result_t *results);
/**
* @brief Query mDNS for service
*
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param max_results maximum results to be collected
* @param results pointer to the results of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_ptr(const char *service_type, const char *proto, uint32_t timeout, size_t max_results, mdns_result_t **results);
/**
* @brief Query mDNS for SRV record
*
* @param instance_name service instance name
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param result pointer to the result of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_srv(const char *instance_name, const char *service_type, const char *proto, uint32_t timeout, mdns_result_t **result);
/**
* @brief Query mDNS for TXT record
*
* @param instance_name service instance name
* @param service_type service type (_http, _arduino, _ftp etc.)
* @param proto service protocol (_tcp, _udp, etc.)
* @param timeout time in milliseconds to wait for answer.
* @param result pointer to the result of the query
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_txt(const char *instance_name, const char *service_type, const char *proto, uint32_t timeout, mdns_result_t **result);
/**
* @brief Look up delegated services.
*
* @param instance instance name (NULL for uncertain instance)
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param max_results maximum results to be collected
* @param result pointer to the result of the search
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_lookup_delegated_service(const char *instance, const char *service_type, const char *proto, size_t max_results,
mdns_result_t **result);
/**
* @brief Look up self hosted services.
*
* @param instance instance name (NULL for uncertain instance)
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param max_results maximum results to be collected
* @param result pointer to the result of the search
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_lookup_selfhosted_service(const char *instance, const char *service_type, const char *proto, size_t max_results,
mdns_result_t **result);
/**
* @brief Query mDNS for A record
*
* @param host_name host name to look for
* @param timeout time in milliseconds to wait for answer.
* @param addr pointer to the resulting IP4 address
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_a(const char *host_name, uint32_t timeout, esp_ip4_addr_t *addr);
#if CONFIG_LWIP_IPV6
/**
* @brief Query mDNS for A record
*
* Please note that hostname must not contain domain name, as mDNS uses '.local' domain.
*
* @param host_name host name to look for
* @param timeout time in milliseconds to wait for answer. If 0, max_results needs to be defined
* @param addr pointer to the resulting IP6 address
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_query_aaaa(const char *host_name, uint32_t timeout, esp_ip6_addr_t *addr);
#endif
/**
* @brief Register custom esp_netif with mDNS functionality
* mDNS service runs by default on preconfigured interfaces (STA, AP, ETH).
* This API enables running the service on any customized interface,
* either using standard WiFi or Ethernet driver or any kind of user defined driver.
*
* @param esp_netif Pointer to esp-netif interface
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running or this netif is already registered
* - ESP_ERR_NO_MEM not enough memory for this in interface in the netif list (see CONFIG_MDNS_MAX_INTERFACES)
*/
esp_err_t mdns_register_netif(esp_netif_t *esp_netif);
/**
* @brief Unregister esp-netif already registered in mDNS service
*
* @param esp_netif Pointer to esp-netif interface
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NOT_FOUND this esp-netif was not registered in mDNS service
*/
esp_err_t mdns_unregister_netif(esp_netif_t *esp_netif);
/**
* @brief Set esp_netif to a desired state, or perform a desired action, such as enable/disable this interface
* or send announcement packets to this netif
*
* * This function is used to enable (probe, resolve conflicts and announce), announce, or disable (send bye) mDNS
* services on the specified network interface.
* * This function must be called if users registers a specific interface using mdns_register_netif()
* to enable mDNS services on that interface.
* * This function could be used in IP/connection event handlers to automatically enable/announce mDNS services
* when network properties change and/or disable them on disconnection.
*
* @param esp_netif Pointer to esp-netif interface
* @param event_action Disable/Enable/Announce on this interface over IPv4/IPv6 protocol.
* Actions enumerated in mdns_event_actions_t type.
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running or this netif is not registered
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_netif_action(esp_netif_t *esp_netif, mdns_event_actions_t event_action);
/**
* @brief Browse mDNS for a service `_service._proto`.
*
* @param service Pointer to the `_service` which will be browsed.
* @param proto Pointer to the `_proto` which will be browsed.
* @param notifier The callback which will be called when the browsing service changed.
* @return mdns_browse_t pointer to new browse object if initiated successfully.
* NULL otherwise.
*/
mdns_browse_t *mdns_browse_new(const char *service, const char *proto, mdns_browse_notify_t notifier);
/**
* @brief Stop the `_service._proto` browse.
* @param service Pointer to the `_service` which will be browsed.
* @param proto Pointer to the `_proto` which will be browsed.
* @return
* - ESP_OK success.
* - ESP_ERR_FAIL mDNS is not running or the browsing of `_service._proto` is never started.
* - ESP_ERR_NO_MEM memory error.
*/
esp_err_t mdns_browse_delete(const char *service, const char *proto);
#ifdef __cplusplus
}
#endif
#endif /* ESP_MDNS_H_ */

View File

@@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MDNS_CONSOLE_H_
#define _MDNS_CONSOLE_H_
/**
* @brief Register MDNS functions with the console component
*/
void mdns_console_register(void);
#endif /* _MDNS_CONSOLE_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "sdkconfig.h"
#include "mdns_private.h"
#include "mdns_mem_caps.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#if CONFIG_MDNS_MEMORY_CUSTOM_IMPL
#define ALLOW_WEAK __attribute__((weak))
#else
#define ALLOW_WEAK
#endif
#if CONFIG_MDNS_TASK_CREATE_FROM_SPIRAM
#define MDNS_TASK_MEMORY_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)
#define MDNS_TASK_MEMORY_LOG "SPIRAM"
#endif
#if CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL
#define MDNS_TASK_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#define MDNS_TASK_MEMORY_LOG "internal RAM"
#endif
#if CONFIG_MDNS_MEMORY_ALLOC_SPIRAM
#define MDNS_MEMORY_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)
#endif
#if CONFIG_MDNS_MEMORY_ALLOC_INTERNAL
#define MDNS_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#endif
// Allocate memory from internal heap as default.
#ifndef MDNS_MEMORY_CAPS
#warning "No memory allocation method defined, using internal memory"
#define MDNS_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#endif
#ifndef MDNS_TASK_MEMORY_CAPS
#define MDNS_TASK_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#define MDNS_TASK_MEMORY_LOG "internal RAM"
#endif
void ALLOW_WEAK *mdns_mem_malloc(size_t size)
{
return heap_caps_malloc(size, MDNS_MEMORY_CAPS);
}
void ALLOW_WEAK *mdns_mem_calloc(size_t num, size_t size)
{
return heap_caps_calloc(num, size, MDNS_MEMORY_CAPS);
}
void ALLOW_WEAK mdns_mem_free(void *ptr)
{
heap_caps_free(ptr);
}
char ALLOW_WEAK *mdns_mem_strdup(const char *s)
{
if (!s) {
return NULL;
}
size_t len = strlen(s) + 1;
char *copy = (char *)heap_caps_malloc(len, MDNS_MEMORY_CAPS);
if (copy) {
memcpy(copy, s, len);
}
return copy;
}
char ALLOW_WEAK *mdns_mem_strndup(const char *s, size_t n)
{
if (!s) {
return NULL;
}
size_t len = strnlen(s, n);
char *copy = (char *)heap_caps_malloc(len + 1, MDNS_MEMORY_CAPS);
if (copy) {
memcpy(copy, s, len);
copy[len] = '\0';
}
return copy;
}
void ALLOW_WEAK *mdns_mem_task_malloc(size_t size)
{
ESP_LOGI("mdns_mem", "mDNS task will be created from %s", MDNS_TASK_MEMORY_LOG);
return heap_caps_malloc(size, MDNS_TASK_MEMORY_CAPS);
}
void ALLOW_WEAK mdns_mem_task_free(void *ptr)
{
heap_caps_free(ptr);
}

View File

@@ -0,0 +1,398 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* MDNS Server Networking
*
*/
#include <string.h>
#include "esp_log.h"
#include "lwip/ip_addr.h"
#include "lwip/pbuf.h"
#include "lwip/igmp.h"
#include "lwip/udp.h"
#include "lwip/mld6.h"
#include "lwip/priv/tcpip_priv.h"
#include "esp_system.h"
#include "esp_event.h"
#include "mdns_networking.h"
#include "esp_netif_net_stack.h"
#include "mdns_mem_caps.h"
/*
* MDNS Server Networking
*
*/
enum interface_protocol {
PROTO_IPV4 = 1 << MDNS_IP_PROTOCOL_V4,
PROTO_IPV6 = 1 << MDNS_IP_PROTOCOL_V6
};
typedef struct interfaces {
bool ready;
int proto;
} interfaces_t;
static interfaces_t s_interfaces[MDNS_MAX_INTERFACES];
static struct udp_pcb *_pcb_main = NULL;
static const char *TAG = "mdns_networking";
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport);
/**
* @brief Low level UDP PCB Initialize
*/
static esp_err_t _udp_pcb_main_init(void)
{
if (_pcb_main) {
return ESP_OK;
}
_pcb_main = udp_new();
if (!_pcb_main) {
return ESP_ERR_NO_MEM;
}
if (udp_bind(_pcb_main, IP_ANY_TYPE, MDNS_SERVICE_PORT) != 0) {
udp_remove(_pcb_main);
_pcb_main = NULL;
return ESP_ERR_INVALID_STATE;
}
_pcb_main->mcast_ttl = 255;
_pcb_main->remote_port = MDNS_SERVICE_PORT;
ip_addr_copy(_pcb_main->remote_ip, *(IP_ANY_TYPE));
udp_recv(_pcb_main, &_udp_recv, NULL);
return ESP_OK;
}
/**
* @brief Low level UDP PCB Free
*/
static void _udp_pcb_main_deinit(void)
{
if (_pcb_main) {
udp_recv(_pcb_main, NULL, NULL);
udp_disconnect(_pcb_main);
udp_remove(_pcb_main);
_pcb_main = NULL;
}
}
/**
* @brief Low level UDP Multicast membership control
*/
static esp_err_t _udp_join_group(mdns_if_t if_inx, mdns_ip_protocol_t ip_protocol, bool join)
{
struct netif *netif = NULL;
esp_netif_t *tcpip_if = _mdns_get_esp_netif(if_inx);
if (!esp_netif_is_netif_up(tcpip_if)) {
// Network interface went down before event propagated, skipping IGMP config
return ESP_ERR_INVALID_STATE;
}
netif = esp_netif_get_netif_impl(tcpip_if);
assert(netif);
#if LWIP_IPV4
if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
ip4_addr_t multicast_addr;
IP4_ADDR(&multicast_addr, 224, 0, 0, 251);
if (join) {
if (igmp_joingroup_netif(netif, &multicast_addr)) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (igmp_leavegroup_netif(netif, &multicast_addr)) {
return ESP_ERR_INVALID_STATE;
}
}
}
#endif // LWIP_IPV4
#if LWIP_IPV6
if (ip_protocol == MDNS_IP_PROTOCOL_V6) {
ip_addr_t multicast_addr = IPADDR6_INIT(0x000002ff, 0, 0, 0xfb000000);
if (join) {
if (mld6_joingroup_netif(netif, ip_2_ip6(&multicast_addr))) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (mld6_leavegroup_netif(netif, ip_2_ip6(&multicast_addr))) {
return ESP_ERR_INVALID_STATE;
}
}
}
#endif // LWIP_IPV6
return ESP_OK;
}
/**
* @brief the receive callback of the raw udp api. Packets are received here
*
*/
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport)
{
uint8_t i;
while (pb != NULL) {
struct pbuf *this_pb = pb;
pb = pb->next;
this_pb->next = NULL;
mdns_rx_packet_t *packet = (mdns_rx_packet_t *)mdns_mem_malloc(sizeof(mdns_rx_packet_t));
if (!packet) {
HOOK_MALLOC_FAILED;
//missed packet - no memory
pbuf_free(this_pb);
continue;
}
packet->tcpip_if = MDNS_MAX_INTERFACES;
packet->pb = this_pb;
packet->src_port = rport;
#if LWIP_IPV4 && LWIP_IPV6
packet->src.type = raddr->type;
memcpy(&packet->src.u_addr, &raddr->u_addr, sizeof(raddr->u_addr));
#elif LWIP_IPV4
packet->src.type = IPADDR_TYPE_V4;
packet->src.u_addr.ip4.addr = raddr->addr;
#elif LWIP_IPV6
packet->src.type = IPADDR_TYPE_V6;
memcpy(&packet->src.u_addr.ip6, raddr, sizeof(ip_addr_t));
#endif
packet->dest.type = packet->src.type;
#if LWIP_IPV4
if (packet->src.type == IPADDR_TYPE_V4) {
packet->ip_protocol = MDNS_IP_PROTOCOL_V4;
struct ip_hdr *iphdr = (struct ip_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP_HLEN);
packet->dest.u_addr.ip4.addr = iphdr->dest.addr;
packet->multicast = ip4_addr_ismulticast(&(packet->dest.u_addr.ip4));
}
#endif // LWIP_IPV4
#if LWIP_IPV6
if (packet->src.type == IPADDR_TYPE_V6) {
packet->ip_protocol = MDNS_IP_PROTOCOL_V6;
struct ip6_hdr *ip6hdr = (struct ip6_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP6_HLEN);
memcpy(&packet->dest.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16);
packet->multicast = ip6_addr_ismulticast(&(packet->dest.u_addr.ip6));
}
#endif // LWIP_IPV6
//lwip does not return the proper pcb if you have more than one for the same multicast address (but different interfaces)
struct netif *netif = NULL;
bool found = false;
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
netif = esp_netif_get_netif_impl(_mdns_get_esp_netif(i));
if (s_interfaces[i].proto && netif && netif == ip_current_input_netif()) {
#if LWIP_IPV4
if (packet->src.type == IPADDR_TYPE_V4) {
if ((packet->src.u_addr.ip4.addr & ip_2_ip4(&netif->netmask)->addr) != (ip_2_ip4(&netif->ip_addr)->addr & ip_2_ip4(&netif->netmask)->addr)) {
//packet source is not in the same subnet
break;
}
}
#endif // LWIP_IPV4
packet->tcpip_if = i;
found = true;
break;
}
}
if (!found || _mdns_send_rx_action(packet) != ESP_OK) {
pbuf_free(this_pb);
mdns_mem_free(packet);
}
}
}
bool mdns_is_netif_ready(mdns_if_t netif, mdns_ip_protocol_t ip_proto)
{
return s_interfaces[netif].ready &&
s_interfaces[netif].proto & (ip_proto == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
}
/**
* @brief Check if any of the interfaces is up
*/
static bool _udp_pcb_is_in_use(void)
{
int i, p;
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
for (p = 0; p < MDNS_IP_PROTOCOL_MAX; p++) {
if (mdns_is_netif_ready(i, p)) {
return true;
}
}
}
return false;
}
/**
* @brief Stop PCB Main code
*/
static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
if (s_interfaces[tcpip_if].proto == 0) {
s_interfaces[tcpip_if].ready = false;
_udp_join_group(tcpip_if, ip_protocol, false);
if (!_udp_pcb_is_in_use()) {
_udp_pcb_main_deinit();
}
}
}
/**
* @brief Start PCB Main code
*/
static esp_err_t _udp_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (mdns_is_netif_ready(tcpip_if, ip_protocol)) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t err = _udp_join_group(tcpip_if, ip_protocol, true);
if (err) {
return err;
}
err = _udp_pcb_main_init();
if (err) {
return err;
}
s_interfaces[tcpip_if].proto |= (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
s_interfaces[tcpip_if].ready = true;
return ESP_OK;
}
typedef struct {
struct tcpip_api_call_data call;
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
struct pbuf *pbt;
const ip_addr_t *ip;
uint16_t port;
esp_err_t err;
} mdns_api_call_t;
/**
* @brief Start PCB from LwIP thread
*/
static err_t _mdns_pcb_init_api(struct tcpip_api_call_data *api_call_msg)
{
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
msg->err = _udp_pcb_init(msg->tcpip_if, msg->ip_protocol) == ESP_OK ? ERR_OK : ERR_IF;
return msg->err;
}
/**
* @brief Stop PCB from LwIP thread
*/
static err_t _mdns_pcb_deinit_api(struct tcpip_api_call_data *api_call_msg)
{
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
_udp_pcb_deinit(msg->tcpip_if, msg->ip_protocol);
msg->err = ESP_OK;
return ESP_OK;
}
/*
* Non-static functions below are
* - _mdns prefixed
* - commented in mdns_networking.h header
*/
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
mdns_api_call_t msg = {
.tcpip_if = tcpip_if,
.ip_protocol = ip_protocol
};
tcpip_api_call(_mdns_pcb_init_api, &msg.call);
return msg.err;
}
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
mdns_api_call_t msg = {
.tcpip_if = tcpip_if,
.ip_protocol = ip_protocol
};
tcpip_api_call(_mdns_pcb_deinit_api, &msg.call);
return msg.err;
}
static err_t _mdns_udp_pcb_write_api(struct tcpip_api_call_data *api_call_msg)
{
void *nif = NULL;
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
nif = esp_netif_get_netif_impl(_mdns_get_esp_netif(msg->tcpip_if));
if (!nif || !mdns_is_netif_ready(msg->tcpip_if, msg->ip_protocol) || _pcb_main == NULL) {
pbuf_free(msg->pbt);
msg->err = ERR_IF;
return ERR_IF;
}
esp_err_t err = udp_sendto_if(_pcb_main, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
pbuf_free(msg->pbt);
msg->err = err;
return err;
}
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len)
{
struct pbuf *pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
if (pbt == NULL) {
return 0;
}
memcpy((uint8_t *)pbt->payload, data, len);
ip_addr_t ip_add_copy;
#if LWIP_IPV6 && LWIP_IPV4
ip_add_copy.type = ip->type;
memcpy(&(ip_add_copy.u_addr), &(ip->u_addr), sizeof(ip_add_copy.u_addr));
#elif LWIP_IPV4
ip_add_copy.addr = ip->u_addr.ip4.addr;
#elif LWIP_IPV6
#if LWIP_IPV6_SCOPES
ip_add_copy.zone = ip->u_addr.ip6.zone;
#endif // LWIP_IPV6_SCOPES
memcpy(ip_add_copy.addr, ip->u_addr.ip6.addr, sizeof(ip_add_copy.addr));
#endif
mdns_api_call_t msg = {
.tcpip_if = tcpip_if,
.ip_protocol = ip_protocol,
.pbt = pbt,
.ip = &ip_add_copy,
.port = port
};
tcpip_api_call(_mdns_udp_pcb_write_api, &msg.call);
if (msg.err) {
return 0;
}
return len;
}
void *_mdns_get_packet_data(mdns_rx_packet_t *packet)
{
return packet->pb->payload;
}
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
{
return packet->pb->len;
}
void _mdns_packet_free(mdns_rx_packet_t *packet)
{
pbuf_free(packet->pb);
mdns_mem_free(packet);
}

View File

@@ -0,0 +1,499 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief MDNS Server Networking module implemented using BSD sockets
*/
#include <string.h>
#include "esp_event.h"
#include "mdns_networking.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/param.h>
#include "esp_log.h"
#include "mdns_mem_caps.h"
#if defined(CONFIG_IDF_TARGET_LINUX)
#include <sys/ioctl.h>
#include <net/if.h>
#endif
enum interface_protocol {
PROTO_IPV4 = 1 << MDNS_IP_PROTOCOL_V4,
PROTO_IPV6 = 1 << MDNS_IP_PROTOCOL_V6
};
typedef struct interfaces {
int sock;
int proto;
} interfaces_t;
static interfaces_t s_interfaces[MDNS_MAX_INTERFACES];
static const char *TAG = "mdns_networking";
static bool s_run_sock_recv_task = false;
static int create_socket(esp_netif_t *netif);
static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol);
#if defined(CONFIG_IDF_TARGET_LINUX)
// Need to define packet buffer struct on linux
struct pbuf {
struct pbuf *next;
void *payload;
size_t tot_len;
size_t len;
};
#else
// Compatibility define to access sock-addr struct the same way for lwip and linux
#define s6_addr32 un.u32_addr
#endif // CONFIG_IDF_TARGET_LINUX
static void __attribute__((constructor)) ctor_networking_socket(void)
{
for (int i = 0; i < sizeof(s_interfaces) / sizeof(s_interfaces[0]); ++i) {
s_interfaces[i].sock = -1;
s_interfaces[i].proto = 0;
}
}
static void delete_socket(int sock)
{
close(sock);
}
bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
return s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
}
void *_mdns_get_packet_data(mdns_rx_packet_t *packet)
{
return packet->pb->payload;
}
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
{
return packet->pb->len;
}
void _mdns_packet_free(mdns_rx_packet_t *packet)
{
mdns_mem_free(packet->pb->payload);
mdns_mem_free(packet->pb);
mdns_mem_free(packet);
}
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
if (s_interfaces[tcpip_if].proto == 0) {
// if the interface for both protocols uninitialized, close the interface socket
if (s_interfaces[tcpip_if].sock >= 0) {
delete_socket(s_interfaces[tcpip_if].sock);
}
}
for (int i = 0; i < MDNS_MAX_INTERFACES; i++) {
if (s_interfaces[i].sock >= 0) {
// If any of the interfaces initialized
return ESP_OK;
}
}
// no interface alive, stop the rx task
s_run_sock_recv_task = false;
vTaskDelay(pdMS_TO_TICKS(500));
return ESP_OK;
}
#if defined(CONFIG_IDF_TARGET_LINUX)
#ifdef CONFIG_LWIP_IPV6
static char *inet6_ntoa_r(struct in6_addr addr, char *ptr, size_t size)
{
inet_ntop(AF_INET6, &(addr.s6_addr32[0]), ptr, size);
return ptr;
}
#endif // CONFIG_LWIP_IPV6
static char *inet_ntoa_r(struct in_addr addr, char *ptr, size_t size)
{
char *res = inet_ntoa(addr);
if (res && strlen(res) < size) {
strcpy(ptr, res);
}
return res;
}
#endif // CONFIG_IDF_TARGET_LINUX
static inline char *get_string_address(struct sockaddr_storage *source_addr)
{
static char address_str[40]; // 40=(8*4+7+term) is the max size of ascii IPv6 addr "XXXX:XX...XX:XXXX"
char *res = NULL;
// Convert ip address to string
#ifdef CONFIG_LWIP_IPV4
if (source_addr->ss_family == PF_INET) {
res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str));
}
#endif
#ifdef CONFIG_LWIP_IPV6
if (source_addr->ss_family == PF_INET6) {
res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str));
}
#endif
if (!res) {
address_str[0] = '\0'; // Returns empty string if conversion didn't succeed
}
return address_str;
}
static inline size_t espaddr_to_inet(const esp_ip_addr_t *addr, const uint16_t port, const mdns_ip_protocol_t ip_protocol, struct sockaddr_storage *in_addr)
{
size_t ss_addr_len = 0;
memset(in_addr, 0, sizeof(struct sockaddr_storage));
#ifdef CONFIG_LWIP_IPV4
if (ip_protocol == MDNS_IP_PROTOCOL_V4 && addr->type == ESP_IPADDR_TYPE_V4) {
in_addr->ss_family = PF_INET;
#if !defined(CONFIG_IDF_TARGET_LINUX)
in_addr->s2_len = sizeof(struct sockaddr_in);
#endif
ss_addr_len = sizeof(struct sockaddr_in);
struct sockaddr_in *in_addr_ip4 = (struct sockaddr_in *) in_addr;
in_addr_ip4->sin_port = port;
in_addr_ip4->sin_addr.s_addr = addr->u_addr.ip4.addr;
}
#endif // CONFIG_LWIP_IPV4
#ifdef CONFIG_LWIP_IPV6
if (ip_protocol == MDNS_IP_PROTOCOL_V6 && addr->type == ESP_IPADDR_TYPE_V6) {
memset(in_addr, 0, sizeof(struct sockaddr_storage));
in_addr->ss_family = PF_INET6;
#if !defined(CONFIG_IDF_TARGET_LINUX)
in_addr->s2_len = sizeof(struct sockaddr_in6);
#endif
ss_addr_len = sizeof(struct sockaddr_in6);
struct sockaddr_in6 *in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
in_addr_ip6->sin6_port = port;
u32_addr[0] = addr->u_addr.ip6.addr[0];
u32_addr[1] = addr->u_addr.ip6.addr[1];
u32_addr[2] = addr->u_addr.ip6.addr[2];
u32_addr[3] = addr->u_addr.ip6.addr[3];
}
#endif // CONFIG_LWIP_IPV6
return ss_addr_len;
}
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len)
{
if (!(s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6))) {
return 0;
}
int sock = s_interfaces[tcpip_if].sock;
if (sock < 0) {
return 0;
}
struct sockaddr_storage in_addr;
size_t ss_size = espaddr_to_inet(ip, htons(port), ip_protocol, &in_addr);
if (!ss_size) {
ESP_LOGE(TAG, "espaddr_to_inet() failed: Mismatch of IP protocols");
return 0;
}
ESP_LOGD(TAG, "[sock=%d]: Sending to IP %s port %d", sock, get_string_address(&in_addr), port);
ssize_t actual_len = sendto(sock, data, len, 0, (struct sockaddr *)&in_addr, ss_size);
if (actual_len < 0) {
ESP_LOGE(TAG, "[sock=%d]: _mdns_udp_pcb_write sendto() has failed\n errno=%d: %s", sock, errno, strerror(errno));
}
return actual_len;
}
static inline void inet_to_espaddr(const struct sockaddr_storage *in_addr, esp_ip_addr_t *addr, uint16_t *port)
{
#ifdef CONFIG_LWIP_IPV4
if (in_addr->ss_family == PF_INET) {
struct sockaddr_in *in_addr_ip4 = (struct sockaddr_in *)in_addr;
memset(addr, 0, sizeof(esp_ip_addr_t));
*port = in_addr_ip4->sin_port;
addr->u_addr.ip4.addr = in_addr_ip4->sin_addr.s_addr;
addr->type = ESP_IPADDR_TYPE_V4;
}
#endif /* CONFIG_LWIP_IPV4 */
#ifdef CONFIG_LWIP_IPV6
if (in_addr->ss_family == PF_INET6) {
struct sockaddr_in6 *in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
memset(addr, 0, sizeof(esp_ip_addr_t));
*port = in_addr_ip6->sin6_port;
uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
if (u32_addr[0] == 0 && u32_addr[1] == 0 && u32_addr[2] == esp_netif_htonl(0x0000FFFFUL)) {
// Mapped IPv4 address, convert directly to IPv4
addr->type = ESP_IPADDR_TYPE_V4;
addr->u_addr.ip4.addr = u32_addr[3];
} else {
addr->type = ESP_IPADDR_TYPE_V6;
addr->u_addr.ip6.addr[0] = u32_addr[0];
addr->u_addr.ip6.addr[1] = u32_addr[1];
addr->u_addr.ip6.addr[2] = u32_addr[2];
addr->u_addr.ip6.addr[3] = u32_addr[3];
}
}
#endif // CONFIG_LWIP_IPV6
}
void sock_recv_task(void *arg)
{
while (s_run_sock_recv_task) {
struct timeval tv = {
.tv_sec = 1,
.tv_usec = 0,
};
fd_set rfds;
FD_ZERO(&rfds);
int max_sock = -1;
for (int i = 0; i < MDNS_MAX_INTERFACES; i++) {
int sock = s_interfaces[i].sock;
if (sock >= 0) {
FD_SET(sock, &rfds);
max_sock = MAX(max_sock, sock);
}
}
if (max_sock < 0) {
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI(TAG, "No sock!");
continue;
}
int s = select(max_sock + 1, &rfds, NULL, NULL, &tv);
if (s < 0) {
ESP_LOGE(TAG, "Select failed. errno=%d: %s", errno, strerror(errno));
break;
} else if (s > 0) {
for (int tcpip_if = 0; tcpip_if < MDNS_MAX_INTERFACES; tcpip_if++) {
int sock = s_interfaces[tcpip_if].sock;
if (sock < 0) {
continue;
}
if (FD_ISSET(sock, &rfds)) {
static char recvbuf[MDNS_MAX_PACKET_SIZE];
uint16_t port = 0;
struct sockaddr_storage raddr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(struct sockaddr_storage);
esp_ip_addr_t addr = {0};
int len = recvfrom(sock, recvbuf, sizeof(recvbuf), 0,
(struct sockaddr *) &raddr, &socklen);
if (len < 0) {
ESP_LOGE(TAG, "multicast recvfrom failed. errno=%d: %s", errno, strerror(errno));
break;
}
ESP_LOGD(TAG, "[sock=%d]: Received from IP:%s", sock, get_string_address(&raddr));
ESP_LOG_BUFFER_HEXDUMP(TAG, recvbuf, len, ESP_LOG_VERBOSE);
inet_to_espaddr(&raddr, &addr, &port);
// Allocate the packet structure and pass it to the mdns main engine
mdns_rx_packet_t *packet = (mdns_rx_packet_t *) mdns_mem_calloc(1, sizeof(mdns_rx_packet_t));
struct pbuf *packet_pbuf = mdns_mem_calloc(1, sizeof(struct pbuf));
uint8_t *buf = mdns_mem_malloc(len);
if (packet == NULL || packet_pbuf == NULL || buf == NULL) {
mdns_mem_free(buf);
mdns_mem_free(packet_pbuf);
mdns_mem_free(packet);
HOOK_MALLOC_FAILED;
ESP_LOGE(TAG, "Failed to allocate the mdns packet");
continue;
}
memcpy(buf, recvbuf, len);
packet_pbuf->next = NULL;
packet_pbuf->payload = buf;
packet_pbuf->tot_len = len;
packet_pbuf->len = len;
packet->tcpip_if = tcpip_if;
packet->pb = packet_pbuf;
packet->src_port = ntohs(port);
memcpy(&packet->src, &addr, sizeof(esp_ip_addr_t));
// TODO(IDF-3651): Add the correct dest addr -- for mdns to decide multicast/unicast
// Currently it's enough to assume the packet is multicast and mdns to check the source port of the packet
memset(&packet->dest, 0, sizeof(esp_ip_addr_t));
packet->multicast = 1;
packet->dest.type = packet->src.type;
packet->ip_protocol =
packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6;
if (_mdns_send_rx_action(packet) != ESP_OK) {
ESP_LOGE(TAG, "_mdns_send_rx_action failed!");
mdns_mem_free(packet->pb->payload);
mdns_mem_free(packet->pb);
mdns_mem_free(packet);
}
}
}
}
}
vTaskDelete(NULL);
}
static void mdns_networking_init(void)
{
if (s_run_sock_recv_task == false) {
s_run_sock_recv_task = true;
xTaskCreate(sock_recv_task, "mdns recv task", 3 * 1024, NULL, 5, NULL);
}
}
static bool create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6)) {
return true;
}
int sock = s_interfaces[tcpip_if].sock;
esp_netif_t *netif = _mdns_get_esp_netif(tcpip_if);
if (sock < 0) {
sock = create_socket(netif);
}
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create the socket!");
return false;
}
int err = join_mdns_multicast_group(sock, netif, ip_protocol);
if (err < 0) {
ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
}
s_interfaces[tcpip_if].proto |= (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
s_interfaces[tcpip_if].sock = sock;
return true;
}
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
ESP_LOGI(TAG, "_mdns_pcb_init(tcpip_if=%lu, ip_protocol=%lu)", (unsigned long)tcpip_if, (unsigned long)ip_protocol);
if (!create_pcb(tcpip_if, ip_protocol)) {
return ESP_FAIL;
}
mdns_networking_init();
return ESP_OK;
}
static int create_socket(esp_netif_t *netif)
{
#ifdef CONFIG_LWIP_IPV6
int sock = socket(PF_INET6, SOCK_DGRAM, 0);
#else
int sock = socket(PF_INET, SOCK_DGRAM, 0);
#endif
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create socket. errno=%d: %s", errno, strerror(errno));
return -1;
}
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
ESP_LOGE(TAG, "Failed setsockopt() to set SO_REUSEADDR. errno=%d: %s\n", errno, strerror(errno));
}
// Bind the socket to any address
#ifdef CONFIG_LWIP_IPV6
struct sockaddr_in6 saddr = { INADDR_ANY };
saddr.sin6_family = AF_INET6;
saddr.sin6_port = htons(5353);
bzero(&saddr.sin6_addr.s6_addr, sizeof(saddr.sin6_addr.s6_addr));
int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6));
if (err < 0) {
ESP_LOGE(TAG, "Failed to bind socket. errno=%d: %s", errno, strerror(errno));
goto err;
}
#else
struct sockaddr_in saddr = { 0 };
saddr.sin_family = AF_INET;
saddr.sin_port = htons(5353);
bzero(&saddr.sin_addr.s_addr, sizeof(saddr.sin_addr.s_addr));
int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (err < 0) {
ESP_LOGE(TAG, "Failed to bind socket. errno=%d: %s", errno, strerror(errno));
goto err;
}
#endif // CONFIG_LWIP_IPV6
struct ifreq ifr;
esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(struct ifreq));
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface. errno=%d: %s", esp_netif_get_desc(netif), errno, strerror(errno));
goto err;
}
return sock;
err:
close(sock);
return -1;
}
#ifdef CONFIG_LWIP_IPV6
static int socket_add_ipv6_multicast_group(int sock, esp_netif_t *netif)
{
int ifindex = esp_netif_get_netif_impl_index(netif);
int err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IPV6_MULTICAST_IF. errno=%d: %s", errno, strerror(errno));
return err;
}
struct ipv6_mreq v6imreq = { 0 };
esp_ip_addr_t multi_addr = ESP_IP6ADDR_INIT(0x000002ff, 0, 0, 0xfb000000);
memcpy(&v6imreq.ipv6mr_multiaddr, &multi_addr.u_addr.ip6.addr, sizeof(v6imreq.ipv6mr_multiaddr));
v6imreq.ipv6mr_interface = ifindex;
err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &v6imreq, sizeof(struct ipv6_mreq));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IPV6_ADD_MEMBERSHIP. errno=%d: %s", errno, strerror(errno));
return err;
}
return err;
}
#endif // CONFIG_LWIP_IPV6
#ifdef CONFIG_LWIP_IPV4
static int socket_add_ipv4_multicast_group(int sock, esp_netif_t *netif)
{
struct ip_mreq imreq = { 0 };
int err = 0;
esp_netif_ip_info_t ip_info = { 0 };
if (esp_netif_get_ip_info(netif, &ip_info) != ESP_OK) {
ESP_LOGE(TAG, "Failed to esp_netif_get_ip_info()");
goto err;
}
imreq.imr_interface.s_addr = ip_info.ip.addr;
esp_ip_addr_t multicast_addr = ESP_IP4ADDR_INIT(224, 0, 0, 251);
imreq.imr_multiaddr.s_addr = multicast_addr.u_addr.ip4.addr;
err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq));
if (err < 0) {
ESP_LOGE(TAG, "[sock=%d] Failed to set IP_ADD_MEMBERSHIP. errno=%d: %s", sock, errno, strerror(errno));
goto err;
}
err:
return err;
}
#endif // CONFIG_LWIP_IPV4
static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol)
{
#ifdef CONFIG_LWIP_IPV4
if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
return socket_add_ipv4_multicast_group(sock, netif);
}
#endif // CONFIG_LWIP_IPV4
#ifdef CONFIG_LWIP_IPV6
if (ip_protocol == MDNS_IP_PROTOCOL_V6) {
return socket_add_ipv6_multicast_group(sock, netif);
}
#endif // CONFIG_LWIP_IPV6
return -1;
}

View File

@@ -0,0 +1,54 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import re
import sys
# Configurable prefix for memory functions
MDNS_MEM_PREFIX = 'mdns_mem_' # Change this to modify the prefix
def add_prefix_to_mem_funcs(content):
# List of memory functions to prefix
mem_funcs = [
'malloc',
'calloc',
'free',
'strdup',
'strndup'
]
# Create regex pattern matching the memory functions but not already prefixed ones
pattern = fr'(?<!{MDNS_MEM_PREFIX})(?<![\w])(' + '|'.join(mem_funcs) + r')(?=\s*\()'
# Replace all occurrences with configured prefix
modified = re.sub(pattern, fr'{MDNS_MEM_PREFIX}\1', content)
return modified
def process_file(filename):
try:
# Read the file
with open(filename, 'r') as f:
content = f.read()
# Add prefixes
modified = add_prefix_to_mem_funcs(content)
# Write back to file
with open(filename, 'w') as f:
f.write(modified)
print(f'Successfully processed {filename}')
except Exception as e:
print(f'Error processing {filename}: {str(e)}')
sys.exit(1)
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: python script.py <filename>')
sys.exit(1)
process_file(sys.argv[1])

View File

@@ -0,0 +1,65 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Allocate memory.
* @param size Number of bytes to allocate.
* @return Pointer to allocated memory, or NULL on failure.
*/
void *mdns_mem_malloc(size_t size);
/**
* @brief Allocate and zero memory.
* @param num Number of elements.
* @param size Size of each element.
* @return Pointer to allocated memory, or NULL on failure.
*/
void *mdns_mem_calloc(size_t num, size_t size);
/**
* @brief Free allocated memory.
* @param ptr Pointer to memory to free.
*/
void mdns_mem_free(void *ptr);
/**
* @brief Duplicate a string.
* @param s String to duplicate.
* @return Pointer to duplicated string, or NULL on failure.
*/
char *mdns_mem_strdup(const char *s);
/**
* @brief Duplicate a string with length limit.
* @param s String to duplicate.
* @param n Maximum number of characters to copy.
* @return Pointer to duplicated string, or NULL on failure.
*/
char *mdns_mem_strndup(const char *s, size_t n);
/**
* @brief Allocate memory for mDNS task.
* @param size Number of bytes to allocate.
* @return Pointer to allocated memory, or NULL on failure.
*/
void *mdns_mem_task_malloc(size_t size);
/**
* @brief Free allocated memory for mDNS task.
* @param ptr Pointer to memory to free.
*/
void mdns_mem_task_free(void *ptr);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_MDNS_NETWORKING_H_
#define ESP_MDNS_NETWORKING_H_
/*
* MDNS Server Networking -- private include
*
*/
#include "mdns.h"
#include "mdns_private.h"
/**
* @brief Queue RX packet action
*/
esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet);
bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
/**
* @brief Start PCB
*/
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
/**
* @brief Stop PCB
*/
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
/**
* @brief send packet over UDP
*
* @param server The server
* @param data byte array containing the packet data
* @param len length of the packet data
*
* @return length of sent packet or 0 on error
*/
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len);
/**
* @brief Gets data pointer to the mDNS packet
*/
void *_mdns_get_packet_data(mdns_rx_packet_t *packet);
/**
* @brief Gets data length of c
*/
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet);
/**
* @brief Free the mDNS packet
*/
void _mdns_packet_free(mdns_rx_packet_t *packet);
#endif /* ESP_MDNS_NETWORKING_H_ */

View File

@@ -0,0 +1,468 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef MDNS_PRIVATE_H_
#define MDNS_PRIVATE_H_
#include "sdkconfig.h"
#include "mdns.h"
#include "esp_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_system.h"
#ifdef CONFIG_MDNS_ENABLE_DEBUG_PRINTS
#define MDNS_ENABLE_DEBUG
#define _mdns_dbg_printf(...) printf(__VA_ARGS__)
#endif
/** Number of predefined interfaces */
#ifndef CONFIG_MDNS_PREDEF_NETIF_STA
#define CONFIG_MDNS_PREDEF_NETIF_STA 0
#endif
#ifndef CONFIG_MDNS_PREDEF_NETIF_AP
#define CONFIG_MDNS_PREDEF_NETIF_AP 0
#endif
#ifndef CONFIG_MDNS_PREDEF_NETIF_ETH
#define CONFIG_MDNS_PREDEF_NETIF_ETH 0
#endif
#define MDNS_MAX_PREDEF_INTERFACES (CONFIG_MDNS_PREDEF_NETIF_STA + CONFIG_MDNS_PREDEF_NETIF_AP + CONFIG_MDNS_PREDEF_NETIF_ETH)
#ifdef CONFIG_LWIP_IPV6_NUM_ADDRESSES
#define NETIF_IPV6_MAX_NUMS CONFIG_LWIP_IPV6_NUM_ADDRESSES
#else
#define NETIF_IPV6_MAX_NUMS 3
#endif
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)
/* CONFIG_LWIP_IPV4 was introduced in IDF v5.1 */
/* For IDF v5.0, set CONFIG_LWIP_IPV4 to 1 by default */
#ifndef CONFIG_LWIP_IPV4
#define CONFIG_LWIP_IPV4 1
#endif // CONFIG_LWIP_IPV4
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)
/** Number of configured interfaces */
#if MDNS_MAX_PREDEF_INTERFACES > CONFIG_MDNS_MAX_INTERFACES
#warning Number of configured interfaces is less then number of predefined interfaces. Please update CONFIG_MDNS_MAX_INTERFACES.
#define MDNS_MAX_INTERFACES (MDNS_MAX_PREDEF_INTERFACES)
#else
#define MDNS_MAX_INTERFACES (CONFIG_MDNS_MAX_INTERFACES)
#endif
/** The maximum number of services */
#define MDNS_MAX_SERVICES CONFIG_MDNS_MAX_SERVICES
#define MDNS_ANSWER_PTR_TTL 4500
#define MDNS_ANSWER_TXT_TTL 4500
#define MDNS_ANSWER_SRV_TTL 120
#define MDNS_ANSWER_A_TTL 120
#define MDNS_ANSWER_AAAA_TTL 120
#define MDNS_FLAGS_QUERY_REPSONSE 0x8000
#define MDNS_FLAGS_AUTHORITATIVE 0x0400
#define MDNS_FLAGS_QR_AUTHORITATIVE (MDNS_FLAGS_QUERY_REPSONSE | MDNS_FLAGS_AUTHORITATIVE)
#define MDNS_FLAGS_DISTRIBUTED 0x0200
#define MDNS_NAME_REF 0xC000
//custom type! only used by this implementation
//to help manage service discovery handling
#define MDNS_TYPE_SDPTR 0x0032
#define MDNS_CLASS_IN 0x0001
#define MDNS_CLASS_ANY 0x00FF
#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001
#define MDNS_ANSWER_ALL 0x3F
#define MDNS_ANSWER_PTR 0x08
#define MDNS_ANSWER_TXT 0x04
#define MDNS_ANSWER_SRV 0x02
#define MDNS_ANSWER_A 0x01
#define MDNS_ANSWER_AAAA 0x10
#define MDNS_ANSWER_NSEC 0x20
#define MDNS_ANSWER_SDPTR 0x80
#define MDNS_ANSWER_AAAA_SIZE 16
#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on
#define MDNS_SERVICE_STACK_DEPTH CONFIG_MDNS_TASK_STACK_SIZE
#define MDNS_TASK_PRIORITY CONFIG_MDNS_TASK_PRIORITY
#if (MDNS_TASK_PRIORITY > ESP_TASK_PRIO_MAX)
#error "mDNS task priority is higher than ESP_TASK_PRIO_MAX"
#elif (MDNS_TASK_PRIORITY > ESP_TASKD_EVENT_PRIO)
#warning "mDNS task priority is higher than ESP_TASKD_EVENT_PRIO, mDNS library might not work correctly"
#endif
#define MDNS_TASK_AFFINITY CONFIG_MDNS_TASK_AFFINITY
#define MDNS_SERVICE_ADD_TIMEOUT_MS CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS
#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing
#define MDNS_ACTION_QUEUE_LEN CONFIG_MDNS_ACTION_QUEUE_LEN // Maximum actions pending to the server
#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record
#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet
#define MDNS_HEAD_LEN 12
#define MDNS_HEAD_ID_OFFSET 0
#define MDNS_HEAD_FLAGS_OFFSET 2
#define MDNS_HEAD_QUESTIONS_OFFSET 4
#define MDNS_HEAD_ANSWERS_OFFSET 6
#define MDNS_HEAD_SERVERS_OFFSET 8
#define MDNS_HEAD_ADDITIONAL_OFFSET 10
#define MDNS_TYPE_OFFSET 0
#define MDNS_CLASS_OFFSET 2
#define MDNS_TTL_OFFSET 4
#define MDNS_LEN_OFFSET 8
#define MDNS_DATA_OFFSET 10
#define MDNS_SRV_PRIORITY_OFFSET 0
#define MDNS_SRV_WEIGHT_OFFSET 2
#define MDNS_SRV_PORT_OFFSET 4
#define MDNS_SRV_FQDN_OFFSET 6
#define MDNS_TIMER_PERIOD_US (CONFIG_MDNS_TIMER_PERIOD_MS*1000)
#define MDNS_SERVICE_LOCK() xSemaphoreTake(_mdns_service_semaphore, portMAX_DELAY)
#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(_mdns_service_semaphore)
#define queueToEnd(type, queue, item) \
if (!queue) { \
queue = item; \
} else { \
type * _q = queue; \
while (_q->next) { _q = _q->next; } \
_q->next = item; \
}
#define queueDetach(type, queue, item) \
if (queue) { \
if (queue == item) { \
queue = queue->next; \
} else { \
type * _q = queue; \
while (_q->next && _q->next != item) { \
_q = _q->next; \
} \
if (_q->next == item) { \
_q->next = item->next; \
item->next = NULL; \
} \
} \
}
#define queueFree(type, queue) while (queue) { type * _q = queue; queue = queue->next; mdns_mem_free(_q); }
#define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1)
#define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING)
#define PCB_STATE_IS_RUNNING(s) (s->state == PCB_RUNNING)
#ifndef HOOK_MALLOC_FAILED
#define HOOK_MALLOC_FAILED ESP_LOGE(TAG, "Cannot allocate memory (line: %d, free heap: %" PRIu32 " bytes)", __LINE__, esp_get_free_heap_size());
#endif
typedef size_t mdns_if_t;
typedef enum {
PCB_OFF, PCB_DUP, PCB_INIT,
PCB_PROBE_1, PCB_PROBE_2, PCB_PROBE_3,
PCB_ANNOUNCE_1, PCB_ANNOUNCE_2, PCB_ANNOUNCE_3,
PCB_RUNNING
} mdns_pcb_state_t;
typedef enum {
MDNS_ANSWER, MDNS_NS, MDNS_EXTRA
} mdns_parsed_record_type_t;
typedef enum {
ACTION_SYSTEM_EVENT,
ACTION_HOSTNAME_SET,
ACTION_INSTANCE_SET,
ACTION_SEARCH_ADD,
ACTION_SEARCH_SEND,
ACTION_SEARCH_END,
ACTION_BROWSE_ADD,
ACTION_BROWSE_SYNC,
ACTION_BROWSE_END,
ACTION_TX_HANDLE,
ACTION_RX_HANDLE,
ACTION_TASK_STOP,
ACTION_DELEGATE_HOSTNAME_ADD,
ACTION_DELEGATE_HOSTNAME_REMOVE,
ACTION_DELEGATE_HOSTNAME_SET_ADDR,
ACTION_MAX
} mdns_action_type_t;
typedef struct {
uint16_t id;
uint16_t flags;
uint16_t questions; //QDCOUNT
uint16_t answers; //ANCOUNT
uint16_t servers; //NSCOUNT
uint16_t additional;//ARCOUNT
} mdns_header_t;
typedef struct {
char host[MDNS_NAME_BUF_LEN]; // hostname for A/AAAA records, instance name for SRV records
char service[MDNS_NAME_BUF_LEN];
char proto[MDNS_NAME_BUF_LEN];
char domain[MDNS_NAME_BUF_LEN];
uint8_t parts;
uint8_t sub;
bool invalid;
} mdns_name_t;
typedef struct mdns_parsed_question_s {
struct mdns_parsed_question_s *next;
uint16_t type;
bool sub;
bool unicast;
char *host;
char *service;
char *proto;
char *domain;
} mdns_parsed_question_t;
typedef struct mdns_parsed_record_s {
struct mdns_parsed_record_s *next;
mdns_parsed_record_type_t record_type;
uint16_t type;
uint16_t clas;
uint8_t flush;
uint32_t ttl;
char *host;
char *service;
char *proto;
char *domain;
uint16_t data_len;
uint8_t *data;
} mdns_parsed_record_t;
typedef struct {
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
esp_ip_addr_t src;
uint16_t src_port;
uint8_t multicast;
uint8_t authoritative;
uint8_t probe;
uint8_t discovery;
uint8_t distributed;
mdns_parsed_question_t *questions;
mdns_parsed_record_t *records;
uint16_t id;
} mdns_parsed_packet_t;
typedef struct {
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
struct pbuf *pb;
esp_ip_addr_t src;
esp_ip_addr_t dest;
uint16_t src_port;
uint8_t multicast;
} mdns_rx_packet_t;
typedef struct mdns_txt_linked_item_s {
const char *key; /*!< item key name */
char *value; /*!< item value string */
uint8_t value_len; /*!< item value length */
struct mdns_txt_linked_item_s *next; /*!< next result, or NULL for the last result in the list */
} mdns_txt_linked_item_t;
typedef struct mdns_subtype_s {
const char *subtype; /*!< subtype */
struct mdns_subtype_s *next; /*!< next result, or NULL for the last result in the list */
} mdns_subtype_t;
typedef struct {
const char *instance;
const char *service;
const char *proto;
const char *hostname;
uint16_t priority;
uint16_t weight;
uint16_t port;
mdns_txt_linked_item_t *txt;
mdns_subtype_t *subtype;
} mdns_service_t;
typedef struct mdns_srv_item_s {
struct mdns_srv_item_s *next;
mdns_service_t *service;
} mdns_srv_item_t;
typedef struct mdns_out_question_s {
struct mdns_out_question_s *next;
uint16_t type;
bool unicast;
const char *host;
const char *service;
const char *proto;
const char *domain;
bool own_dynamic_memory;
} mdns_out_question_t;
typedef struct mdns_host_item_t {
const char *hostname;
mdns_ip_addr_t *address_list;
struct mdns_host_item_t *next;
} mdns_host_item_t;
typedef struct mdns_out_answer_s {
struct mdns_out_answer_s *next;
uint16_t type;
uint8_t bye;
uint8_t flush;
mdns_service_t *service;
mdns_host_item_t *host;
const char *custom_instance;
const char *custom_service;
const char *custom_proto;
} mdns_out_answer_t;
typedef struct mdns_tx_packet_s {
struct mdns_tx_packet_s *next;
uint32_t send_at;
mdns_if_t tcpip_if;
mdns_ip_protocol_t ip_protocol;
esp_ip_addr_t dst;
uint16_t port;
uint16_t flags;
uint8_t distributed;
mdns_out_question_t *questions;
mdns_out_answer_t *answers;
mdns_out_answer_t *servers;
mdns_out_answer_t *additional;
bool queued;
uint16_t id;
} mdns_tx_packet_t;
typedef struct {
mdns_pcb_state_t state;
mdns_srv_item_t **probe_services;
uint8_t probe_services_len;
uint8_t probe_ip;
uint8_t probe_running;
uint16_t failed_probes;
} mdns_pcb_t;
typedef enum {
SEARCH_OFF,
SEARCH_INIT,
SEARCH_RUNNING,
SEARCH_MAX
} mdns_search_once_state_t;
typedef enum {
BROWSE_OFF,
BROWSE_INIT,
BROWSE_RUNNING,
BROWSE_MAX
} mdns_browse_state_t;
typedef struct mdns_search_once_s {
struct mdns_search_once_s *next;
mdns_search_once_state_t state;
uint32_t started_at;
uint32_t sent_at;
uint32_t timeout;
mdns_query_notify_t notifier;
SemaphoreHandle_t done_semaphore;
uint16_t type;
bool unicast;
uint8_t max_results;
uint8_t num_results;
char *instance;
char *service;
char *proto;
mdns_result_t *result;
} mdns_search_once_t;
typedef struct mdns_browse_s {
struct mdns_browse_s *next;
mdns_browse_state_t state;
mdns_browse_notify_t notifier;
char *service;
char *proto;
mdns_result_t *result;
} mdns_browse_t;
typedef struct mdns_browse_result_sync_t {
mdns_result_t *result;
struct mdns_browse_result_sync_t *next;
} mdns_browse_result_sync_t;
typedef struct mdns_browse_sync {
mdns_browse_t *browse;
mdns_browse_result_sync_t *sync_result;
} mdns_browse_sync_t;
typedef struct mdns_server_s {
struct {
mdns_pcb_t pcbs[MDNS_IP_PROTOCOL_MAX];
} interfaces[MDNS_MAX_INTERFACES];
const char *hostname;
const char *instance;
mdns_srv_item_t *services;
QueueHandle_t action_queue;
SemaphoreHandle_t action_sema;
mdns_tx_packet_t *tx_queue_head;
mdns_search_once_t *search_once;
esp_timer_handle_t timer_handle;
mdns_browse_t *browse;
} mdns_server_t;
typedef struct {
mdns_action_type_t type;
union {
struct {
char *hostname;
} hostname_set;
char *instance;
struct {
mdns_if_t interface;
mdns_event_actions_t event_action;
} sys_event;
struct {
mdns_search_once_t *search;
} search_add;
struct {
mdns_tx_packet_t *packet;
} tx_handle;
struct {
mdns_rx_packet_t *packet;
} rx_handle;
struct {
const char *hostname;
mdns_ip_addr_t *address_list;
} delegate_hostname;
struct {
mdns_browse_t *browse;
} browse_add;
struct {
mdns_browse_sync_t *browse_sync;
} browse_sync;
} data;
} mdns_action_t;
/*
* @brief Convert mnds if to esp-netif handle
*
* @param tcpip_if mdns supported interface as internal enum
*
* @return
* - ptr to esp-netif on success
* - NULL if no available netif for current interface index
*/
esp_netif_t *_mdns_get_esp_netif(mdns_if_t tcpip_if);
#endif /* MDNS_PRIVATE_H_ */

View File

@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
if(${IDF_TARGET} STREQUAL "linux")
set(EXTRA_COMPONENT_DIRS "../../../../common_components/linux_compat")
set(COMPONENTS main)
endif()
project(mdns_host)
# Enable sanitizers only without console (we'd see some leaks on argtable when console exits)
if(NOT CONFIG_TEST_CONSOLE AND CONFIG_IDF_TARGET_LINUX)
idf_component_get_property(mdns mdns COMPONENT_LIB)
target_link_options(${mdns} INTERFACE -fsanitize=address -fsanitize=undefined)
endif()

View File

@@ -0,0 +1,33 @@
# Setup dummy network interfaces
Note: Set two addresses so we could use one as source and another as destination
```
sudo ip link add eth2 type dummy
sudo ip addr add 192.168.1.200/24 dev eth2
sudo ip addr add 192.168.1.201/24 dev eth2
sudo ip link set eth2 up
sudo ifconfig eth2 multicast
```
# Dig on a specified interface
```
dig +short -b 192.168.1.200 -p 5353 @224.0.0.251 myesp.local
```
or a reverse query:
```
dig +short -b 192.168.2.200 -p 5353 @224.0.0.251 -x 192.168.1.200
```
# Run avahi to browse services
Avahi needs the netif to have the "multicast" flag set
```bash
david@david-comp:~/esp/idf (feature/mdns_networking_socket)$ avahi-browse -a -r -p
+;eth2;IPv6;myesp-service2;Web Site;local
+;eth2;IPv4;myesp-service2;Web Site;local
=;eth2;IPv6;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password"
=;eth2;IPv4;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password"
```

View File

@@ -0,0 +1,8 @@
idf_build_get_property(idf_target IDF_TARGET)
if(${IDF_TARGET} STREQUAL "linux")
idf_component_register(SRCS esp_netif_linux.c
INCLUDE_DIRS include $ENV{IDF_PATH}/components/esp_netif/include
REQUIRES esp_event)
else()
idf_component_register()
endif()

View File

@@ -0,0 +1,15 @@
menu "LWIP-MOCK-CONFIG"
config LWIP_IPV6
bool "Enable IPv6"
default y
help
Enable/disable IPv6
config LWIP_IPV4
bool "Enable IPv4"
default y
help
Enable/disable IPv4
endmenu

View File

@@ -0,0 +1,197 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include<stdio.h>
#include <stdlib.h>
#include "esp_netif.h"
#include "esp_err.h"
#include <string.h> //strlen
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h>
#include "esp_netif_types.h"
#include "esp_log.h"
#define MAX_NETIFS 4
static const char *TAG = "esp_netif_linux";
static esp_netif_t *s_netif_list[MAX_NETIFS] = { 0 };
struct esp_netif_obj {
const char *if_key;
const char *if_desc;
};
esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key)
{
for (int i = 0; i < MAX_NETIFS; ++i) {
if (s_netif_list[i] && strcmp(s_netif_list[i]->if_key, if_key) == 0) {
return s_netif_list[i];
}
}
return NULL;
}
esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_info)
{
if (esp_netif == NULL) {
return ESP_ERR_INVALID_STATE;
}
struct ifaddrs *addrs, *tmp;
getifaddrs(&addrs);
tmp = addrs;
while (tmp) {
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) {
char addr[20];
struct sockaddr_in *pAddr = (struct sockaddr_in *) tmp->ifa_addr;
inet_ntop(AF_INET, &pAddr->sin_addr, addr, sizeof(addr));
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
ESP_LOGD(TAG, "AF_INET4: %s: %s\n", tmp->ifa_name, addr);
memcpy(&ip_info->ip.addr, &pAddr->sin_addr, 4);
}
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
return ESP_OK;
}
esp_err_t esp_netif_dhcpc_get_status(esp_netif_t *esp_netif, esp_netif_dhcp_status_t *status)
{
return ESP_OK;
}
int esp_netif_get_all_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[])
{
if (esp_netif == NULL) {
return 0;
}
struct ifaddrs *addrs, *tmp;
int addr_count = 0;
getifaddrs(&addrs);
tmp = addrs;
while (tmp) {
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr;
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
memcpy(&if_ip6[addr_count++], &pAddr->sin6_addr, 4 * 4);
}
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
return addr_count;
}
esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6)
{
if (esp_netif == NULL) {
return ESP_ERR_INVALID_STATE;
}
struct ifaddrs *addrs, *tmp;
getifaddrs(&addrs);
tmp = addrs;
while (tmp) {
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) {
char addr[64];
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr;
inet_ntop(AF_INET6, &pAddr->sin6_addr, addr, sizeof(addr));
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
ESP_LOGD(TAG, "AF_INET6: %s: %s\n", tmp->ifa_name, addr);
memcpy(if_ip6->addr, &pAddr->sin6_addr, 4 * 4);
break;
}
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
return ESP_OK;
}
int esp_netif_get_netif_impl_index(esp_netif_t *esp_netif)
{
if (esp_netif == NULL) {
return -1;
}
uint32_t interfaceIndex = if_nametoindex(esp_netif->if_desc);
return interfaceIndex;
}
esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char *name)
{
if (esp_netif == NULL) {
return ESP_ERR_INVALID_STATE;
}
strcpy(name, esp_netif->if_desc);
return ESP_OK;
}
const char *esp_netif_get_desc(esp_netif_t *esp_netif)
{
if (esp_netif == NULL) {
return NULL;
}
return esp_netif->if_desc;
}
esp_netif_t *esp_netif_new(const esp_netif_config_t *config)
{
if (esp_netif_get_handle_from_ifkey(config->base->if_key)) {
return NULL;
}
esp_netif_t *netif = calloc(1, sizeof(struct esp_netif_obj));
if (netif) {
netif->if_desc = config->base->if_desc;
netif->if_key = config->base->if_key;
}
for (int i = 0; i < MAX_NETIFS; ++i) {
if (s_netif_list[i] == NULL) {
s_netif_list[i] = netif;
break;
}
}
return netif;
}
void esp_netif_destroy(esp_netif_t *esp_netif)
{
for (int i = 0; i < MAX_NETIFS; ++i) {
if (s_netif_list[i] == esp_netif) {
s_netif_list[i] = NULL;
break;
}
}
free(esp_netif);
}
const char *esp_netif_get_ifkey(esp_netif_t *esp_netif)
{
return esp_netif->if_key;
}
esp_err_t esp_netif_str_to_ip4(const char *src, esp_ip4_addr_t *dst)
{
if (src == NULL || dst == NULL) {
return ESP_ERR_INVALID_ARG;
}
struct in_addr addr;
if (inet_pton(AF_INET, src, &addr) != 1) {
return ESP_FAIL;
}
dst->addr = addr.s_addr;
return ESP_OK;
}

View File

@@ -0,0 +1,7 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_event.h"

View File

@@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include_next "endian.h"

View File

@@ -0,0 +1,178 @@
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging
import re
import socket
import sys
import dns.message
import dns.query
import dns.rdataclass
import dns.rdatatype
import dns.resolver
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DnsPythonWrapper:
def __init__(self, server='224.0.0.251', port=5353, retries=3):
self.server = server
self.port = port
self.retries = retries
def send_and_receive_query(self, query, timeout=3):
logger.info(f'Sending DNS query to {self.server}:{self.port}')
try:
# Create a UDP socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.settimeout(timeout)
# Send the DNS query
query_data = query.to_wire()
sock.sendto(query_data, (self.server, self.port))
# Receive the DNS response
response_data, _ = sock.recvfrom(512) # 512 bytes is the typical size for a DNS response
# Parse the response
response = dns.message.from_wire(response_data)
return response
except socket.timeout as e:
logger.warning(f'DNS query timed out: {e}')
return None
except dns.exception.DNSException as e:
logger.error(f'DNS query failed: {e}')
return None
def run_query(self, name, query_type='PTR', timeout=3):
logger.info(f'Running DNS query for {name} with type {query_type}')
query = dns.message.make_query(name, dns.rdatatype.from_text(query_type), dns.rdataclass.IN)
# Print the DNS question section
logger.info(f'DNS question section: {query.question}')
# Send and receive the DNS query
response = None
for attempt in range(1, self.retries + 1):
logger.info(f'Attempt {attempt}/{self.retries}')
response = self.send_and_receive_query(query, timeout)
if response:
break
if response:
logger.info(f'DNS query response:\n{response}')
else:
logger.warning('No response received or response was invalid.')
return response
def parse_answer_section(self, response, query_type):
answers = []
if response:
for answer in response.answer:
if dns.rdatatype.to_text(answer.rdtype) == query_type:
for item in answer.items:
full_answer = (
f'{answer.name} {answer.ttl} '
f'{dns.rdataclass.to_text(answer.rdclass)} '
f'{dns.rdatatype.to_text(answer.rdtype)} '
f'{item.to_text()}'
)
answers.append(full_answer)
return answers
def check_record(self, name, query_type, expected=True, expect=None):
output = self.run_query(name, query_type=query_type)
answers = self.parse_answer_section(output, query_type)
logger.info(f'answers: {answers}')
if expect is None:
expect = name
if expected:
assert any(expect in answer for answer in answers), f"Expected record '{expect}' not in answer section"
else:
assert not any(expect in answer for answer in answers), f"Unexpected record '{expect}' found in answer section"
def parse_section(self, response, section: str, rdtype_text: str):
"""Parse a specific response section (answer, authority, additional) for given rdtype.
Returns list of textual records for that rdtype.
"""
out = []
if not response:
return out
rrsets = []
if section == 'answer':
rrsets = response.answer
elif section == 'authority':
rrsets = response.authority
elif section == 'additional':
rrsets = response.additional
else:
raise ValueError('invalid section')
for rr in rrsets:
if dns.rdatatype.to_text(rr.rdtype) != rdtype_text:
continue
for item in rr.items:
full = (
f'{rr.name} {rr.ttl} '
f'{dns.rdataclass.to_text(rr.rdclass)} '
f'{dns.rdatatype.to_text(rr.rdtype)} '
f'{item.to_text()}'
)
out.append(full)
return out
def check_additional(self, response, rdtype_text: str, owner_contains: str, expected: bool = True, expect_substr: str | None = None):
"""Check Additional section for an RR of type rdtype_text whose owner includes owner_contains.
If expect_substr is provided, also require it to appear in the textual RR.
"""
records = self.parse_section(response, 'additional', rdtype_text)
logger.info(f'additional({rdtype_text}): {records}')
def _matches(line: str) -> bool:
in_owner = owner_contains in line
has_val = (expect_substr in line) if expect_substr else True
return in_owner and has_val
found = any(_matches(r) for r in records)
if expected:
assert found, f"Expected {rdtype_text} for {owner_contains} in Additional not found"
else:
assert not found, f"Unexpected {rdtype_text} for {owner_contains} found in Additional"
if __name__ == '__main__':
if len(sys.argv) < 3:
print('Usage: python dns_fixture.py <query_type> <name>')
sys.exit(1)
query_type = sys.argv[1]
name = sys.argv[2]
ip_only = len(sys.argv) > 3 and sys.argv[3] == '--ip_only'
if ip_only:
logger.setLevel(logging.WARNING)
dns_wrapper = DnsPythonWrapper()
if query_type == 'X' and '.' in name:
# Sends an IPv4 reverse query
reversed_ip = '.'.join(reversed(name.split('.')))
name = f'{reversed_ip}.in-addr.arpa'
query_type = 'PTR'
response = dns_wrapper.run_query(name, query_type=query_type)
answers = dns_wrapper.parse_answer_section(response, query_type)
if answers:
for answer in answers:
logger.info(f'DNS query response: {answer}')
if ip_only:
ipv4_pattern = re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b')
ipv4_addresses = ipv4_pattern.findall(answer)
if ipv4_addresses:
print(f"{', '.join(ipv4_addresses)}")
else:
logger.info(f'No response for {name} with query type {query_type}')
exit(9) # Same as dig timeout

View File

@@ -0,0 +1,4 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS
"."
REQUIRES mdns console nvs_flash)

View File

@@ -0,0 +1,21 @@
menu "Test Configuration"
config TEST_HOSTNAME
string "mDNS Hostname"
default "esp32-mdns"
help
mDNS Hostname for example to use
config TEST_NETIF_NAME
string "Network interface name"
default "eth2"
help
Name/ID if the network interface on which we run the mDNS host test
config TEST_CONSOLE
bool "Start console"
default n
help
Test uses esp_console for interactive testing.
endmenu

View File

@@ -0,0 +1,7 @@
dependencies:
idf: ">=5.0"
espressif/mdns:
version: "^1.0.0"
override_path: "../../.."
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@@ -0,0 +1,126 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_console.h"
#include "mdns.h"
#include "mdns_console.h"
static const char *TAG = "mdns-test";
static void mdns_test_app(esp_netif_t *interface);
#ifdef CONFIG_TEST_CONSOLE
static EventGroupHandle_t s_exit_signal = NULL;
static int exit_console(int argc, char **argv)
{
xEventGroupSetBits(s_exit_signal, 1);
return 0;
}
#else
static void query_mdns_host(const char *host_name)
{
ESP_LOGI(TAG, "Query A: %s.local", host_name);
struct esp_ip4_addr addr;
addr.addr = 0;
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
if (err) {
if (err == ESP_ERR_NOT_FOUND) {
ESP_LOGW(TAG, "%x: Host was not found!", (err));
return;
}
ESP_LOGE(TAG, "Query Failed: %x", (err));
return;
}
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
}
#endif // TEST_CONSOLE
#ifndef CONFIG_IDF_TARGET_LINUX
#include "protocol_examples_common.h"
#include "esp_event.h"
#include "nvs_flash.h"
/**
* @brief This is an entry point for the real target device,
* need to init few components and connect to a network interface
*/
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
mdns_test_app(EXAMPLE_INTERFACE);
ESP_ERROR_CHECK(example_disconnect());
}
#else
/**
* @brief This is an entry point for the linux target (simulator on host)
* need to create a dummy WiFi station and use it as mdns network interface
*/
int main(int argc, char *argv[])
{
setvbuf(stdout, NULL, _IONBF, 0);
const esp_netif_inherent_config_t base_cg = { .if_key = "WIFI_STA_DEF", .if_desc = CONFIG_TEST_NETIF_NAME };
esp_netif_config_t cfg = { .base = &base_cg };
esp_netif_t *sta = esp_netif_new(&cfg);
mdns_test_app(sta);
esp_netif_destroy(sta);
return 0;
}
#endif
static void mdns_test_app(esp_netif_t *interface)
{
ESP_ERROR_CHECK(mdns_init());
ESP_ERROR_CHECK(mdns_hostname_set(CONFIG_TEST_HOSTNAME));
ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_TEST_HOSTNAME);
ESP_ERROR_CHECK(mdns_register_netif(interface));
ESP_ERROR_CHECK(mdns_netif_action(interface, MDNS_EVENT_ENABLE_IP4 /*| MDNS_EVENT_ENABLE_IP6 */ | MDNS_EVENT_IP4_REVERSE_LOOKUP | MDNS_EVENT_IP6_REVERSE_LOOKUP));
#ifdef CONFIG_TEST_CONSOLE
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
s_exit_signal = xEventGroupCreate();
repl_config.prompt = "mdns>";
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
const esp_console_cmd_t cmd_exit = {
.command = "exit",
.help = "exit mDNS console application",
.hint = NULL,
.func = exit_console,
.argtable = NULL
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_exit));
mdns_console_register();
ESP_ERROR_CHECK(esp_console_start_repl(repl));
xEventGroupWaitBits(s_exit_signal, 1, pdTRUE, pdFALSE, portMAX_DELAY);
repl->del(repl);
#else
vTaskDelay(pdMS_TO_TICKS(10000));
query_mdns_host("david-work");
vTaskDelay(pdMS_TO_TICKS(1000));
#endif
mdns_free();
ESP_LOGI(TAG, "Exit");
}

View File

@@ -0,0 +1,191 @@
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging
import pexpect
import pytest
from dnsfixture import DnsPythonWrapper
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
ipv6_enabled = False
class MdnsConsole:
def __init__(self, command):
self.process = pexpect.spawn(command, encoding='utf-8')
self.process.logfile = open('mdns_interaction.log', 'w') # Log all interactions
self.process.expect('mdns> ', timeout=10)
def send_input(self, input_data):
logger.info(f'Sending to stdin: {input_data}')
self.process.sendline(input_data)
def get_output(self, expected_data):
logger.info(f'Expecting: {expected_data}')
self.process.expect(expected_data, timeout=10)
output = self.process.before.strip()
logger.info(f'Received from stdout: {output}')
return output
def terminate(self):
self.send_input('exit')
self.get_output('Exit')
self.process.wait()
self.process.close()
assert self.process.exitstatus == 0
@pytest.fixture(scope='module')
def mdns_console():
app = MdnsConsole('./build_linux_console/mdns_host.elf')
yield app
app.terminate()
@pytest.fixture(scope='module')
def dig_app():
return DnsPythonWrapper()
def test_mdns_init(mdns_console, dig_app):
mdns_console.send_input('mdns_init -h hostname')
mdns_console.get_output('MDNS: Hostname: hostname')
dig_app.check_record('hostname.local', query_type='A', expected=True)
if ipv6_enabled:
dig_app.check_record('hostname.local', query_type='AAAA', expected=True)
def test_add_service(mdns_console, dig_app):
mdns_console.send_input('mdns_service_add _http _tcp 80 -i test_service')
mdns_console.get_output('MDNS: Service Instance: test_service')
mdns_console.send_input('mdns_service_lookup _http _tcp')
mdns_console.get_output('PTR : test_service')
dig_app.check_record('_http._tcp.local', query_type='PTR', expected=True)
def test_ptr_additional_records_for_service(dig_app):
# Query PTR for the service type and ensure SRV/TXT are in Additional (RFC 6763 §12.1)
resp = dig_app.run_query('_http._tcp.local', query_type='PTR')
# Answer section should have at least one PTR to the instance
answers = dig_app.parse_answer_section(resp, 'PTR')
assert any('test_service._http._tcp.local' in a for a in answers)
# Additional section should include SRV and TXT for the same instance
dig_app.check_additional(resp, 'SRV', 'test_service._http._tcp.local', expected=True)
dig_app.check_additional(resp, 'TXT', 'test_service._http._tcp.local', expected=True)
def test_remove_service(mdns_console, dig_app):
mdns_console.send_input('mdns_service_remove _http _tcp')
mdns_console.send_input('mdns_service_lookup _http _tcp')
mdns_console.get_output('No results found!')
dig_app.check_record('_http._tcp.local', query_type='PTR', expected=False)
def test_delegate_host(mdns_console, dig_app):
mdns_console.send_input('mdns_delegate_host delegated 1.2.3.4')
dig_app.check_record('delegated.local', query_type='A', expected=True)
def test_undelegate_host(mdns_console, dig_app):
mdns_console.send_input('mdns_undelegate_host delegated')
dig_app.check_record('delegated.local', query_type='A', expected=False)
def test_add_delegated_service(mdns_console, dig_app):
mdns_console.send_input('mdns_delegate_host delegated 1.2.3.4')
dig_app.check_record('delegated.local', query_type='A', expected=True)
mdns_console.send_input('mdns_service_add _test _tcp 80 -i local')
mdns_console.get_output('MDNS: Service Instance: local')
mdns_console.send_input('mdns_service_add _test2 _tcp 80 -i extern -h delegated')
mdns_console.get_output('MDNS: Service Instance: extern')
mdns_console.send_input('mdns_service_lookup _test _tcp')
mdns_console.get_output('PTR : local')
mdns_console.send_input('mdns_service_lookup _test2 _tcp -d')
mdns_console.get_output('PTR : extern')
dig_app.check_record('_test2._tcp.local', query_type='PTR', expected=True)
dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True)
def test_remove_delegated_service(mdns_console, dig_app):
mdns_console.send_input('mdns_service_remove _test2 _tcp -h delegated')
mdns_console.send_input('mdns_service_lookup _test2 _tcp -d')
mdns_console.get_output('No results found!')
dig_app.check_record('_test2._tcp.local', query_type='PTR', expected=False)
# add the delegated service again, would be used in the TXT test
mdns_console.send_input('mdns_service_add _test2 _tcp 80 -i extern -h delegated')
mdns_console.get_output('MDNS: Service Instance: extern')
def check_txt_for_service(instance, service, proto, mdns_console, dig_app, host=None, with_inst=False):
for_host_arg = f'-h {host}' if host is not None else ''
for_inst_arg = f'-i {instance}' if with_inst else ''
mdns_console.send_input(f'mdns_service_txt_set {service} {proto} {for_host_arg} {for_inst_arg} key1 value1')
dig_app.check_record(f'{instance}.{service}.{proto}.local', query_type='SRV', expected=True)
dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key1=value1')
mdns_console.send_input(f'mdns_service_txt_set {service} {proto} {for_host_arg} {for_inst_arg} key2 value2')
dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key2=value2')
mdns_console.send_input(f'mdns_service_txt_remove {service} {proto} {for_host_arg} {for_inst_arg} key2')
dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=False, expect='key2=value2')
dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key1=value1')
mdns_console.send_input(f'mdns_service_txt_replace {service} {proto} {for_host_arg} {for_inst_arg} key3=value3 key4=value4')
dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=False, expect='key1=value1')
dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key3=value3')
dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key4=value4')
def test_update_txt(mdns_console, dig_app):
check_txt_for_service('local', '_test', '_tcp', mdns_console=mdns_console, dig_app=dig_app)
check_txt_for_service('local', '_test', '_tcp', mdns_console=mdns_console, dig_app=dig_app, with_inst=True)
def test_update_delegated_txt(mdns_console, dig_app):
check_txt_for_service('extern', '_test2', '_tcp', mdns_console=mdns_console, dig_app=dig_app, host='delegated')
check_txt_for_service('extern', '_test2', '_tcp', mdns_console=mdns_console, dig_app=dig_app, host='delegated', with_inst=True)
def test_service_port_set(mdns_console, dig_app):
dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True, expect='80')
mdns_console.send_input('mdns_service_port_set _test _tcp 81')
dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True, expect='81')
mdns_console.send_input('mdns_service_port_set _test2 _tcp -h delegated 82')
dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True, expect='82')
mdns_console.send_input('mdns_service_port_set _test2 _tcp -h delegated -i extern 83')
dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True, expect='83')
mdns_console.send_input('mdns_service_port_set _test2 _tcp -h delegated -i invalid_inst 84')
mdns_console.get_output('ESP_ERR_NOT_FOUND')
dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True, expect='83')
def test_service_subtype(mdns_console, dig_app):
dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True)
mdns_console.send_input('mdns_service_subtype _test _tcp _subtest -i local')
dig_app.check_record('_subtest._sub._test._tcp.local', query_type='PTR', expected=True)
mdns_console.send_input('mdns_service_subtype _test2 _tcp _subtest2 -i extern -h delegated')
dig_app.check_record('_subtest2._sub._test2._tcp.local', query_type='PTR', expected=True)
def test_service_set_instance(mdns_console, dig_app):
dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True)
mdns_console.send_input('mdns_service_instance_set _test _tcp local2')
dig_app.check_record('local2._test._tcp.local', query_type='SRV', expected=True)
mdns_console.send_input('mdns_service_instance_set _test2 _tcp extern2 -h delegated')
mdns_console.send_input('mdns_service_lookup _test2 _tcp -d')
mdns_console.get_output('PTR : extern2')
dig_app.check_record('extern2._test2._tcp.local', query_type='SRV', expected=True)
mdns_console.send_input('mdns_service_instance_set _test2 _tcp extern3 -h delegated -i extern')
mdns_console.get_output('ESP_ERR_NOT_FOUND')
def test_service_remove_all(mdns_console, dig_app):
mdns_console.send_input('mdns_service_remove_all')
mdns_console.send_input('mdns_service_lookup _test2 _tcp -d')
mdns_console.get_output('No results found!')
mdns_console.send_input('mdns_service_lookup _test _tcp')
mdns_console.get_output('No results found!')
dig_app.check_record('_test._tcp.local', query_type='PTR', expected=False)
if __name__ == '__main__':
pytest.main(['-s', 'test_mdns.py'])

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="linux"
CONFIG_ESP_EVENT_POST_FROM_ISR=n
CONFIG_MDNS_ENABLE_CONSOLE_CLI=y
CONFIG_TEST_CONSOLE=y

View File

@@ -0,0 +1 @@
CONFIG_IDF_TARGET="esp32"

View File

@@ -0,0 +1,6 @@
CONFIG_TEST_NETIF_NAME="eth0"
CONFIG_ESP_EVENT_POST_FROM_ISR=n
CONFIG_MDNS_NETWORKING_SOCKET=y
CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES=y
CONFIG_MDNS_PREDEF_NETIF_STA=n
CONFIG_MDNS_PREDEF_NETIF_AP=n

Some files were not shown because too many files have changed in this diff Show More