ESP32 obtaining IP twice from a single command

Vilius
Posts: 26
Joined: Mon Nov 13, 2023 9:22 am

ESP32 obtaining IP twice from a single command

Postby Vilius » Mon Feb 05, 2024 11:24 am

Hi,

I am working on a fairly complex project that involves data transmission via TCP. ESP32 acts as a client and the python script on PC emulates TCP server. I have already developed the algorithm for my application using static or automatically assigned IP addresses, and now I want to move to DHCP.

Despite the fact I am calling the IP assignment function only once, I still get 2 IP assignments: first one is made by windows autoconfig, and the second one is real DHCP provided IP. What is more, there is no clear time pattern or relation when the second assignment will be executed (this is serious consideration for my project). I can not develop this project further without solving this. Earlier I was using a second network adapter configured on windows not to interfere with my main network. Now I have removed the mentioned adapter and moved the ESP32 client to my main network. As far as I know, autoconfig should only kick in if the DHCP is not available, which is not the case here... I am using an ethernet switch (not a hub) just to be clear. (for port expansion) Any ideas how get only the DHCP assigned IP?

ESP32 code: (DHCP part is in the main function)

Code: Select all

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "freertos/timers.h"
#include "soc/soc.h"
#include "soc/gpio_struct.h"
#include "soc/gpio_reg.h"
#include "esp32/rom/ets_sys.h"
#include "soc/rtc.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "esp_transport.h"
#include "esp_transport_tcp.h"
#include "esp_transport_socks_proxy.h"
#include <sys/time.h>
#include <errno.h>
#include <arpa/inet.h>
#include <math.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define MAX_SOCKETS 10000

#ifdef CONFIG_EXAMPLE_ENABLE_PROXY
#define PROXY_ADDR CONFIG_EXAMPLE_PROXY_ADDR
#define PROXY_PORT CONFIG_EXAMPLE_PROXY_PORT
#endif

#define MAX_RETRY_COUNT 5
#define MAX_POLL_TIMEOUT_ERRORS 5
#define INTERRUPT_INPUT 15
#define GPIO_CLOCK 14
#define NUM_GPIO_PINS 12
#define NUM_CYCLES 64

#define GPIO_PIN_12 12
#define GPIO_PIN_13 13
#define GPIO_PIN_2 2
#define GPIO_PIN_4 4
#define GPIO_PIN_16 16
#define GPIO_PIN_17 17
#define GPIO_PIN_32 32
#define GPIO_PIN_33 33
#define GPIO_PIN_34 34
#define GPIO_PIN_35 35
#define GPIO_PIN_36 36
#define GPIO_PIN_39 39

typedef struct {
    int index;
    bool is_used;
} Socket;

Socket sockets[MAX_SOCKETS];

uint64_t get_time_us() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}

static const char *TAG = "tcp_transport_client";
esp_transport_handle_t transport = NULL;
static bool is_connected = false;
static int unsuccessful_retries = 0;
static int consecutive_poll_timeout_errors = 0;
static bool received_from_server = false; // Flag to indicate if a message has been received from the server

static const gpio_num_t GPIO_PINS[NUM_GPIO_PINS] = {
    GPIO_PIN_12, GPIO_PIN_13, GPIO_PIN_2, GPIO_PIN_4,
    GPIO_PIN_16, GPIO_PIN_17, GPIO_PIN_32, GPIO_PIN_33,
    GPIO_PIN_34, GPIO_PIN_35, GPIO_PIN_36, GPIO_PIN_39
};

bool trigger = false;
uint32_t data_array[NUM_CYCLES] = {0};

TaskHandle_t tcpTaskHandle;
SemaphoreHandle_t tcpMutex;
SemaphoreHandle_t dataReceivedSemaphore;
QueueHandle_t tcpDataQueue;
TimerHandle_t timerHandle;

int counter = 0; // Global variable to count interrupts
bool global_digit = false; // Global variable to store the least significant integer bit
int received_integer = 0;
bool timer_started = false;

static char target_addr[16]; // IPv4 address has a maximum of 15 characters + null terminator

// Function prototypes
void establish_connection();
void disconnect_if_connected();
void Impulse_Count();
void close_connection();
void send_data_over_tcp(uint32_t *data_array, size_t num_elements);
void send_data_task(void *pvParameters);
void receive_data_task(void *pvParameters);
static void IRAM_ATTR gpio_interrupt_handler(void *args);

void send_broadcast_message(const char *broadcast_address, int port) {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        ESP_LOGE(TAG, "Failed to create socket");
        return;
    }

    int broadcast_enable = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast_enable, sizeof(broadcast_enable)) < 0) {
        ESP_LOGE(TAG, "Failed to enable broadcast");
        close(sockfd);
        return;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(broadcast_address);

    const char *message = "Scintilator";
    if (sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        ESP_LOGE(TAG, "Failed to send broadcast message");
    }
    else{
        ESP_LOGI(TAG, "Broadcast message sent");
    }

    close(sockfd);
}

// Initialize the array of sockets
void initialize_sockets() {
    for (int i = 0; i < MAX_SOCKETS; i++) {
        sockets[i].index = i;
        sockets[i].is_used = false;
    }
}

// Mark a socket as used
void mark_socket_used(int index) {
    if (index >= 0 && index < MAX_SOCKETS) {
        sockets[index].is_used = true;
    }
}

// Mark a socket as free
void mark_socket_free(int index) {
    if (index >= 0 && index < MAX_SOCKETS) {
        sockets[index].is_used = false;
    }
}

// Find a free socket and return its index, or -1 if no free socket is available
int find_free_socket() {
    for (int i = 0; i < MAX_SOCKETS; i++) {
        if (!sockets[i].is_used) {
            return i;
        }
    }
    return -1; // No free socket found
}


void timerCallback(TimerHandle_t xTimer) {
    gpio_intr_disable(GPIO_NUM_15);
    vTaskDelay(100 / portTICK_PERIOD_MS); 
    ESP_LOGI(TAG, "Timer expired. Disconnecting from server...");
    vTaskDelay(5000 / portTICK_PERIOD_MS);  
    close_connection();
    ESP_LOGI(TAG, "Interrupt count: %d", counter);
}

void Impulse_Count(){
    vTaskDelay(50 / portTICK_PERIOD_MS); 
    ESP_LOGI(TAG, "Impulse target was reached. Disconnecting from server...");
    vTaskDelay(5000 / portTICK_PERIOD_MS);  
    close_connection();
    ESP_LOGI(TAG, "Interrupt count: %d", counter); 
}

float ntohf(float netfloat) {
    union {
        uint32_t n;
        float f;
    } tmp;

    tmp.n = htonl(*(uint32_t *)&netfloat);

    global_digit = (int)tmp.f & 1;

    int intPart = (int)tmp.f;
    float fracPart = tmp.f - intPart;

    if (intPart != 0) {
        intPart >>= 1; 
    } else {
        fracPart /= 2.0;
    }

    // Reconstruct the float
    tmp.f = intPart + fracPart;

    return tmp.f;
}

void establish_connection(int socket_index) {
    if (transport == NULL) {
        ESP_LOGE(TAG, "Error: TCP transport not initialized");
        return;
}
    const int connection_timings[MAX_RETRY_COUNT] = {1, 1, 1, 2, 3, 3, 5, 5, 5, 0}; // 0 indicates no further attempts

    for (int i = 0; i < MAX_RETRY_COUNT; i++) {
        if (!is_connected) {
            vTaskDelay(connection_timings[i] * 1000 / portTICK_PERIOD_MS); // Convert seconds to milliseconds
            int err = esp_transport_connect(transport, target_addr, /*TARGET_PORT +*/ socket_index, 30000); // 30 second timeout
            if (err == 0) {
                ESP_LOGI(TAG, "Connected successfully");
                is_connected = true; // Mark the connection as successful
                break; // Exit loop on successful connection
            } else {
                ESP_LOGE(TAG, "Connection failed: errno %d", errno);
            }
        }
    }
}


void disconnect_if_connected() {
    if (is_connected) {
        int err = esp_transport_close(transport);
        if (err == 0) {
            ESP_LOGI(TAG, "Closed the connection successfully");
        } else {
            ESP_LOGE(TAG, "Error closing the connection: errno %d", errno);
        }
        is_connected = false;  // Mark the connection as unsuccessful
    }
}

void close_connection() {
    if (is_connected) {
        int err = esp_transport_close(transport);
        if (err == 0) {
            ESP_LOGI(TAG, "Closed the connection successfully");
        } else {
            ESP_LOGE(TAG, "Error closing the connection: errno %d", errno);
        }
        is_connected = false;  // Mark the connection as unsuccessful
    }
}


void send_data_over_tcp(uint32_t *data_array, size_t num_elements) {
    if (transport == NULL) {
        ESP_LOGE(TAG, "Error: TCP transport not initialized");
        return;
    }

    if (!is_connected || !received_from_server) { // Check if not connected or no message received from server
        ESP_LOGE(TAG, "Error: Not connected to server or message not received from server");
        return;
    }

    size_t total_bytes_sent = 0;
    size_t remaining_elements = num_elements;
    uint8_t *data_ptr = (uint8_t *)data_array;

    int retry_count = 0;
    bool retry_exceeded = false;

    while (remaining_elements > 0 && !retry_exceeded) {
        int bytes_written = esp_transport_write(transport, (char *)data_ptr, sizeof(uint32_t) * remaining_elements, 0);

        if (bytes_written < 0) {
            int error_code = errno;
            ESP_LOGE(TAG, "Error occurred during sending: esp_transport_write() returned %d, errno %d", bytes_written, error_code);

            if (error_code == ECONNRESET) {
                ESP_LOGE(TAG, "Connection reset by peer. Attempting to reconnect...");
                is_connected = false;
                esp_transport_close(transport);
            } else {
                ESP_LOGE(TAG, "Unhandled error. Closing the transport.");
                esp_transport_close(transport);
            }

            consecutive_poll_timeout_errors++;  // Increment the error counter

            // Reset retry variables
            vTaskDelay(1000 / portTICK_PERIOD_MS);  // Add a small delay before retrying
            continue;  // Retry without decrementing remaining_elements
        }

        if (bytes_written == 0) {
            ESP_LOGW(TAG, "Zero bytes written to TCP. Retrying...");
            vTaskDelay(1000 / portTICK_PERIOD_MS);  // Add a small delay before retrying
            retry_count++;

            if (retry_count >= MAX_RETRY_COUNT) {
                ESP_LOGE(TAG, "Maximum retry count exceeded. Disconnecting...");
                disconnect_if_connected();
                retry_exceeded = true;  // Set the flag to true
            }

            continue;  // Retry without decrementing remaining_elements
        }

        remaining_elements -= bytes_written / sizeof(uint32_t);
        data_ptr += bytes_written;
        total_bytes_sent += bytes_written;

        consecutive_poll_timeout_errors = 0;  // Reset the error counter on successful write

        ESP_LOGI(TAG, "Sent %d bytes over TCP", bytes_written);  // Log only on successful send

         if(global_digit && (received_integer == counter)){
            //gpio_intr_disable(GPIO_NUM_15);
            Impulse_Count();   
        }
        counter++;
        //if(((received_integer > counter) && global_digit) || timer_started)
        //{
        //counter++;
        //}

    }
}

void receive_data_task(void *pvParameters) {
    char recv_buffer[256];
    int recv_len;
    //bool timer_started = false;

    while (1) {
        if (is_connected) {
            recv_len = esp_transport_read(transport, recv_buffer, sizeof(recv_buffer), 0);
            if (recv_len > 0) {
                float received_float;
                memcpy(&received_float, recv_buffer, sizeof(float));
                received_float = ntohf(received_float); // Convert float from network byte order to host byte order
                
                 // Adjust integer part
                //int intPart = (int)received_float;
                //intPart >>= 1; // Reduce by one bit
                //received_float = intPart + (received_float - intPart); // Reconstruct the float


                received_from_server = true;
                //counter = 0;
                
                if(!global_digit){
                ESP_LOGI(TAG, "Received measurement time from server: %.3f s", received_float);

                uint32_t seconds = (uint32_t)received_float;  // Integer part represents seconds
                float fraction = received_float - seconds;    // Fractional part represents fractions of a second
                uint32_t microseconds = fraction * 1000000;  // Convert fraction to microseconds
                
                    if (!timer_started) {
                        if (timerHandle != NULL) {
                            xTimerDelete(timerHandle, 0);
                        }
                        timerHandle = xTimerCreate("Timer", pdMS_TO_TICKS(seconds * 1000 + microseconds / 1000), pdFALSE, NULL, timerCallback);
                        if (timerHandle != NULL) {
                            xTimerStart(timerHandle, 0);
                            timer_started = true; 
                        }
                    }
                }

                else {
                    received_integer = (int)received_float; // Directly cast to integer
                    ESP_LOGI(TAG, "Received impulse target from server: %d ", received_integer);
                }
                 xSemaphoreGive(dataReceivedSemaphore);
            }
        }
        vTaskDelay(100 / portTICK_PERIOD_MS);  // Delay before retrying to receive data
    }
}


void send_data_task(void *pvParameters) {
    uint32_t received_data[NUM_CYCLES];
    int retry_delay = 1000;  // Initial retry delay in milliseconds

    while (1) {
        if (xSemaphoreTake(tcpMutex, portMAX_DELAY)) {
            if (xQueueReceive(tcpDataQueue, received_data, portMAX_DELAY)) {
                int retries = 0;
                int retry_delay = 0;  // Initial retry delay

                while (retries < 5) {
                    send_data_over_tcp(received_data, NUM_CYCLES);

                    if (is_connected) {
                        //ESP_LOGI(TAG, "Sent %d bytes over TCP", sizeof(uint32_t) * NUM_CYCLES);
                        retry_delay = 0;  // Reset retry delay on successful send
                        break;  // Data sent successfully
                    } else {
                        // Connection failed, wait and retry with the specified delays
                        vTaskDelay(retry_delay / portTICK_PERIOD_MS);
                        retries++;

                        // Set the next retry delay according to the pattern
                        if (retries == 1) {
                            retry_delay = 1000;  // 1 second
                        } else if (retries == 2) {
                            retry_delay = 2000;  // 2 seconds
                        } else if (retries == 3) {
                            retry_delay = 3000;  // 3 seconds
                        } else if (retries == 4) {
                            retry_delay = 5000;  // 5 seconds
                        }
                    }
                }

                if (retries == 5) {
                    ESP_LOGE(TAG, "Failed to send data after 5 retries. Performing a software reset...");

                    // Log the intentional reset
                    ESP_LOGW(TAG, "Intentional reset triggered by software (esp_restart())");

                    esp_restart();  // Perform a software reset
                }
            } else {
                if (is_connected) {
                    ESP_LOGI(TAG, "Connection lost. Attempting to reconnect...");
                    esp_transport_close(transport);
                    is_connected = false;
                    //last_reconnection_attempt_time = get_time_us();  // Record the time of the last reconnection attempt
                }
            }

            xSemaphoreGive(tcpMutex);
        }

    }
}

static void IRAM_ATTR gpio_interrupt_handler(void *args) {

        BaseType_t xHigherPriorityTaskWoken = pdFALSE;

        uint32_t local_data_array[NUM_CYCLES];
        for (int i = 0; i < NUM_CYCLES; ++i) {
            local_data_array[i] = data_array[i];
        }

        xQueueSendFromISR(tcpDataQueue, local_data_array, &xHigherPriorityTaskWoken);
        //counter++; // Increment the counter each time an interrupt occurs

        if (xHigherPriorityTaskWoken == pdTRUE) {
            portYIELD_FROM_ISR();
        }
}

void app_main() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);  // Initial delay

    initialize_sockets();

    int free_socket_index = find_free_socket();
    if (free_socket_index == -1) {
        ESP_LOGE(TAG, "No free socket available");
        return;
    }

    esp_log_level_set("transport", ESP_LOG_INFO);
    esp_log_level_set("transport_base", ESP_LOG_INFO);
    esp_log_level_set("transport_proxy", ESP_LOG_INFO);

    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());

    transport = esp_transport_tcp_init();

    esp_netif_ip_info_t ip_info;
    esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("ETH"), &ip_info); 
    inet_ntoa_r(ip_info.ip,target_addr, 16);

    vTaskDelay(1000 / portTICK_PERIOD_MS);  // Initial delay

    ESP_LOGI(TAG, "Using socket index %d to establish connection", free_socket_index);
    send_broadcast_message("255.255.255.255", free_socket_index); 
    //establish_connection(free_socket_index);

#ifdef CONFIG_EXAMPLE_ENABLE_PROXY
    esp_transport_handle_t parent = transport;
    esp_transport_socks_proxy_config_t proxy_config = {.port = PROXY_PORT, .address = PROXY_ADDR, .version = SOCKS4};
    transport = esp_transport_socks_proxy_init(parent, &proxy_config);
#endif
    
    dataReceivedSemaphore = xSemaphoreCreateBinary();
    
    xTaskCreatePinnedToCore(receive_data_task, "TCP_Task", 8192, NULL, 3, &tcpTaskHandle, APP_CPU_NUM);  // Increased priority

    if (xSemaphoreTake(dataReceivedSemaphore, portMAX_DELAY) == pdTRUE) {
        tcpDataQueue = xQueueCreate(1, sizeof(data_array));

        xTaskCreatePinnedToCore(send_data_task, "TCP_Task", 8192, NULL, 4, &tcpTaskHandle, APP_CPU_NUM);  // Increased priority

        tcpMutex = xSemaphoreCreateMutex();

        gpio_config_t io_conf_output = {
            .pin_bit_mask = (1ULL << GPIO_CLOCK),
            .mode = GPIO_MODE_OUTPUT,
        };
        gpio_config(&io_conf_output);

        gpio_config_t io_conf;
        for (int i = 0; i < NUM_GPIO_PINS; ++i) {
            io_conf = (gpio_config_t){
                .pin_bit_mask = (1ULL << GPIO_PINS[i]),
                .mode = GPIO_MODE_INPUT,
                .intr_type = GPIO_INTR_DISABLE,
                .pull_up_en = GPIO_PULLUP_DISABLE,
                .pull_down_en = GPIO_PULLDOWN_ENABLE,
            };
            gpio_config(&io_conf);
        }

    esp_rom_gpio_pad_select_gpio(INTERRUPT_INPUT);
    gpio_set_direction(INTERRUPT_INPUT, GPIO_MODE_INPUT);
    gpio_pulldown_en(INTERRUPT_INPUT);
    gpio_pullup_dis(INTERRUPT_INPUT);
    gpio_set_intr_type(INTERRUPT_INPUT, GPIO_INTR_NEGEDGE);

    gpio_install_isr_service(0);
    gpio_isr_handler_add(INTERRUPT_INPUT, gpio_interrupt_handler, (void *)INTERRUPT_INPUT);
        
        if(global_digit){
        counter = 0;
        }
    
    }

    uint8_t k = 0;
    while (1) {
        REG_WRITE(GPIO_OUT_W1TS_REG, (1 << GPIO_CLOCK));
        data_array[k] = REG_READ(GPIO_IN_REG);
        data_array[k + 1] = REG_READ(GPIO_IN1_REG);
        k = (k + 2) % NUM_CYCLES;
        REG_WRITE(GPIO_OUT_W1TC_REG, (1 << GPIO_CLOCK));
    }

    close_connection();

#ifdef CONFIG_EXAMPLE_ENABLE_PROXY
    esp_transport_destroy(parent);
#endif
}
Logs:
I (0) cpu_start: App cpu up.
I (335) cpu_start: Pro cpu start user code
I (335) cpu_start: cpu freq: 240000000 Hz
I (335) cpu_start: Application information:
I (340) cpu_start: Project name: ESPTCP
I (345) cpu_start: App version: 1
I (349) cpu_start: Compile time: Jan 25 2024 09:46:21
I (355) cpu_start: ELF file SHA256: 48bd67937baa4ded...
I (361) cpu_start: ESP-IDF: v5.1.1
I (366) cpu_start: Min chip rev: v0.0
I (371) cpu_start: Max chip rev: v3.99
I (375) cpu_start: Chip rev: v3.1
I (380) heap_init: Initializing. RAM available for dynamic allocation:
I (388) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (393) heap_init: At 3FFCB988 len 00014678 (81 KiB): DRAM
I (400) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (406) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (412) heap_init: At 4008DA58 len 000125A8 (73 KiB): IRAM
I (420) spi_flash: detected chip: generic
I (423) spi_flash: flash io: dio
I (428) app_start: Starting scheduler on CPU0
I (432) app_start: Starting scheduler on CPU1
I (432) main_task: Started on CPU0
I (442) main_task: Calling app_main()
I (1502) esp_eth.netif.netif_glue: 08:3a:8d:13:5a:73
I (1502) esp_eth.netif.netif_glue: ethernet attached to netif
I (3602) ethernet_connect: Waiting for IP(s).
I (3602) ethernet_connect: Ethernet Link Up
I (5462) ethernet_connect: Got IPv6 event: Interface "example_netif_eth" address: fe80:0000:0000:0000:0a3a:8dff:fe13:5a73, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (8162) esp_netif_handlers: example_netif_eth ip: 169.254.116.90, mask: 255.255.0.0, gw: 0.0.0.0
I (8162) ethernet_connect: Got IPv4 event: Interface "example_netif_eth" address: 169.254.116.90
I (8162) example_common: Connected to example_netif_eth
I (8172) example_common: - IPv4 address: 169.254.116.90,
I (8182) example_common: - IPv6 address: fe80:0000:0000:0000:0a3a:8dff:fe13:5a73, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (9192) tcp_transport_client: Using socket index 0 to establish connection
E (9192) tcp_transport_client: Failed to send broadcast message
I (12162) esp_netif_handlers: example_netif_eth ip: 10.10.10.171, mask: 255.255.254.0, gw: 10.10.10.254
I (12162) ethernet_connect: Got IPv4 event: Interface "example_netif_eth" address: 10.10.10.171

Also I noticed one strange thing: the ESP32 is supposed to send a broadcast message as soon as it obtains the dynamic IP. But wireshark depict exact same sender and receiver IP adresses as if the ESP32 was sending a message to itself (picture in the attachments). On the other hand, Destination is marked as "Broadcast". Should it be like that?
Attachments
Capture.PNG
Capture.PNG (87.39 KiB) Viewed 611 times

Who is online

Users browsing this forum: No registered users and 81 guests