/* * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ /* * After station connects to AP and gets IP address by smartconfig, * it will use UDP to send 'ACK' to cellphone. */ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_netif.h" #include "esp_log.h" #include "esp_wifi.h" #include "esp_event.h" #if CONFIG_ESP_NETIF_TCPIP_LWIP #include #include "lwip/sockets.h" #include "esp_smartconfig.h" #include "smartconfig_ack.h" #define SC_ACK_TASK_PRIORITY 2 /*!< Priority of sending smartconfig ACK task */ #define SC_ACK_TASK_STACK_SIZE 2048 /*!< Stack size of sending smartconfig ACK task */ #define SC_ACK_TOUCH_DEVICE_PORT 7001 /*!< ESPTouch UDP port of server on device */ #define SC_ACK_TOUCH_SERVER_PORT 18266 /*!< ESPTouch UDP port of server on cellphone */ #define SC_ACK_TOUCH_V2_SERVER_PORT(i) (18266+i*10000) /*!< ESPTouch v2 UDP port of server on cellphone */ #define SC_ACK_AIRKISS_SERVER_PORT 10000 /*!< Airkiss UDP port of server on cellphone */ #define SC_ACK_AIRKISS_DEVICE_PORT 10001 /*!< Airkiss UDP port of server on device */ #define SC_ACK_AIRKISS_TIMEOUT 1500 /*!< Airkiss read data timout millisecond */ #define SC_ACK_TOUCH_LEN 11 /*!< Length of ESPTouch ACK context */ #define SC_ACK_AIRKISS_LEN 7 /*!< Length of Airkiss ACK context */ #define SC_ACK_MAX_COUNT 30 /*!< Maximum count of sending smartconfig ACK */ /** * @brief Smartconfig parameters passed to sc_ack_send call. */ typedef struct sc_ack { smartconfig_type_t type; /*!< Smartconfig type(ESPTouch or AirKiss) */ struct { uint8_t token; /*!< Smartconfig token from the cellphone */ uint8_t mac[6]; /*!< MAC address of station */ uint8_t ip[4]; /*!< IP address of cellphone */ } ctx; } sc_ack_t; static const char *TAG = "smartconfig"; /* Flag to indicate sending smartconfig ACK or not. */ static bool s_sc_ack_send = false; static int sc_ack_send_get_errno(int fd) { int sock_errno = 0; u32_t optlen = sizeof(sock_errno); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen) < 0) { return sock_errno; } return 0; } static void sc_ack_send_task(void *pvParameters) { sc_ack_t *ack = (sc_ack_t *)pvParameters; esp_netif_ip_info_t local_ip; uint8_t remote_ip[4]; memcpy(remote_ip, ack->ctx.ip, sizeof(remote_ip)); struct sockaddr_in server_addr; socklen_t sin_size = sizeof(server_addr); int send_sock = -1; int optval = 1; int sendlen; int ack_len = (ack->type == SC_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_LEN : SC_ACK_AIRKISS_LEN; uint8_t packet_count = 1; int err; int ret; int remote_port = 0; if (ack->type == SC_TYPE_ESPTOUCH) { remote_port = SC_ACK_TOUCH_SERVER_PORT; } else if (ack->type == SC_TYPE_ESPTOUCH_V2) { uint8_t port_bit = ack->ctx.token; if(port_bit > 3) { port_bit = 0; } remote_port = SC_ACK_TOUCH_V2_SERVER_PORT(port_bit); } else { remote_port = SC_ACK_AIRKISS_SERVER_PORT; } bzero(&server_addr, sizeof(struct sockaddr_in)); server_addr.sin_family = AF_INET; memcpy(&server_addr.sin_addr.s_addr, remote_ip, sizeof(remote_ip)); server_addr.sin_port = htons(remote_port); esp_wifi_get_mac(WIFI_IF_STA, ack->ctx.mac); vTaskDelay(200 / portTICK_PERIOD_MS); while (s_sc_ack_send) { /* Get local IP address of station */ ret = esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &local_ip); if ((ESP_OK == ret) && (local_ip.ip.addr != INADDR_ANY)) { /* If ESPTouch, smartconfig ACK contains local IP address. */ if (ack->type == SC_TYPE_ESPTOUCH) { memcpy(ack->ctx.ip, &local_ip.ip.addr, 4); } /* Create UDP socket. */ send_sock = socket(AF_INET, SOCK_DGRAM, 0); if ((send_sock < LWIP_SOCKET_OFFSET) || (send_sock > (FD_SETSIZE - 1))) { ESP_LOGE(TAG, "Creat udp socket failed"); goto _end; } if (setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(int)) < 0) { ESP_LOGE(TAG, "setsockopt SO_BROADCAST failed"); goto _end; } if (setsockopt(send_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)) < 0) { ESP_LOGE(TAG, "setsockopt SO_REUSEADDR failed"); goto _end; } if (ack->type == SC_TYPE_AIRKISS) { char data = 0; struct sockaddr_in local_addr, from; socklen_t sockadd_len = sizeof(struct sockaddr); struct timeval timeout = { SC_ACK_AIRKISS_TIMEOUT / 1000, SC_ACK_AIRKISS_TIMEOUT % 1000 * 1000 }; bzero(&local_addr, sizeof(struct sockaddr_in)); bzero(&from, sizeof(struct sockaddr_in)); local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = INADDR_ANY; if (ack->type == SC_TYPE_AIRKISS) { local_addr.sin_port = htons(SC_ACK_AIRKISS_DEVICE_PORT); } else { local_addr.sin_port = htons(SC_ACK_TOUCH_DEVICE_PORT); } if (bind(send_sock, (struct sockaddr *)&local_addr, sockadd_len) < 0) { ESP_LOGE(TAG, "socket bind failed"); goto _end; } if (setsockopt(send_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { goto _end; } if (recvfrom(send_sock, &data, 1, 0, (struct sockaddr *)&from, &sockadd_len) < 0) { goto _end; } if (from.sin_addr.s_addr != INADDR_ANY) { memcpy(remote_ip, &from.sin_addr, 4); server_addr.sin_addr.s_addr = from.sin_addr.s_addr; } else { goto _end; } } while (s_sc_ack_send) { /* Send smartconfig ACK every 100ms. */ vTaskDelay(100 / portTICK_PERIOD_MS); sendlen = sendto(send_sock, &ack->ctx, ack_len, 0, (struct sockaddr*) &server_addr, sin_size); if (sendlen <= 0) { err = sc_ack_send_get_errno(send_sock); ESP_LOGD(TAG, "send failed, errno %d", err); vTaskDelay(100 / portTICK_PERIOD_MS); } /* Send 30 smartconfig ACKs. Then smartconfig is successful. */ if (packet_count++ >= SC_ACK_MAX_COUNT) { esp_event_post(SC_EVENT, SC_EVENT_SEND_ACK_DONE, NULL, 0, portMAX_DELAY); goto _end; } } } else { vTaskDelay((TickType_t)(100 / portTICK_PERIOD_MS)); } } _end: close(send_sock); free(ack); vTaskDelete(NULL); } esp_err_t sc_send_ack_start(smartconfig_type_t type, uint8_t token, uint8_t *cellphone_ip) { sc_ack_t *ack = NULL; if (cellphone_ip == NULL) { ESP_LOGE(TAG, "Cellphone IP address is NULL"); return ESP_ERR_INVALID_ARG; } ack = malloc(sizeof(sc_ack_t)); if (ack == NULL) { ESP_LOGE(TAG, "ACK parameter malloc fail"); return ESP_ERR_NO_MEM; } ack->type = type; ack->ctx.token = token; memcpy(ack->ctx.ip, cellphone_ip, 4); s_sc_ack_send = true; if (xTaskCreate(sc_ack_send_task, "sc_ack_send_task", SC_ACK_TASK_STACK_SIZE, ack, SC_ACK_TASK_PRIORITY, NULL) != pdPASS) { ESP_LOGE(TAG, "Create sending smartconfig ACK task fail"); free(ack); return ESP_ERR_NO_MEM; } return ESP_OK; } void sc_send_ack_stop(void) { s_sc_ack_send = false; } #endif