diff --git a/components/esp32/smartconfig.c b/components/esp32/smartconfig.c new file mode 100644 index 000000000..a0636d078 --- /dev/null +++ b/components/esp32/smartconfig.c @@ -0,0 +1,154 @@ +// Copyright 2010-2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/* + * After station connects to AP and gets IP address by smartconfig, + * it will use UDP to send 'ACK' to cellphone. + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "lwip/sockets.h" +#include "tcpip_adapter.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_smartconfig.h" +#include "smartconfig.h" + +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); + + getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen); + + return sock_errno; +} + +static void sc_ack_send_task(void *pvParameters) +{ + sc_ack_t *ack = (sc_ack_t *)pvParameters; + tcpip_adapter_ip_info_t local_ip; + uint8_t *remote_ip = ack->ctx.ip; + int remote_port = (ack->type == SC_ACK_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_SERVER_PORT : SC_ACK_AIRKISS_SERVER_PORT; + struct sockaddr_in server_addr; + socklen_t sin_size = sizeof(server_addr); + int send_sock = 0; + int optval = 1; + int sendlen; + int ack_len = (ack->type == SC_ACK_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_LEN : SC_ACK_AIRKISS_LEN; + uint8_t packet_count = 1; + int err; + int ret; + + bzero(&server_addr, sizeof(struct sockaddr_in)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr((const char*)remote_ip); + server_addr.sin_port = htons(remote_port); + + esp_wifi_get_mac(WIFI_IF_STA, ack->ctx.mac); + + vTaskDelay(200 / portTICK_RATE_MS); + + while (s_sc_ack_send) { + /* Get local IP address of station */ + ret = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &local_ip); + if ((ESP_OK == ret) && (local_ip.ip.addr != INADDR_ANY)) { + /* If ESP touch, smartconfig ACK contains local IP address. */ + if (ack->type == SC_ACK_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 < 0) { + ESP_LOGE(TAG, "Creat udp socket failed"); + free(ack); + vTaskDelete(NULL); + } + + setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST | SO_REUSEADDR, &optval, sizeof(int)); + + while (s_sc_ack_send) { + /* Send smartconfig ACK every 100ms. */ + vTaskDelay(100 / portTICK_RATE_MS); + + sendlen = sendto(send_sock, &ack->ctx, ack_len, 0, (struct sockaddr*) &server_addr, sin_size); + if (sendlen > 0) { + /* Totally send 30 smartconfig ACKs. Then smartconfig is successful. */ + if (packet_count++ >= SC_ACK_MAX_COUNT) { + if (ack->link_flag) { + *ack->link_flag = 1; + } + if (ack->cb) { + ack->cb(SC_STATUS_LINK_OVER, remote_ip); + } + close(send_sock); + free(ack); + vTaskDelete(NULL); + } + } + else { + err = sc_ack_send_get_errno(send_sock); + if (err == ENOMEM || err == EAGAIN) { + ESP_LOGD(TAG, "send failed, errno %d", err); + continue; + } + ESP_LOGE(TAG, "send failed, errno %d", err); + close(send_sock); + free(ack); + vTaskDelete(NULL); + } + } + } + else { + vTaskDelay((portTickType)(100 / portTICK_RATE_MS)); + } + } + + free(ack); + vTaskDelete(NULL); +} + +void sc_ack_send(sc_ack_t *param) +{ + sc_ack_t *ack = NULL; + + if (param == NULL) { + ESP_LOGE(TAG, "Smart config ack parameter error"); + return; + } + + ack = malloc(sizeof(sc_ack_t)); + if (ack == NULL) { + ESP_LOGE(TAG, "Smart config ack parameter malloc fail"); + return; + } + memcpy(ack, param, sizeof(sc_ack_t)); + + s_sc_ack_send = true; + + xTaskCreate(sc_ack_send_task, "sc_ack_send_task", SC_ACK_TASK_STACK_SIZE, ack, SC_ACK_TASK_PRIORITY, NULL); +} + +void sc_ack_send_stop(void) +{ + s_sc_ack_send = false; +} diff --git a/components/esp32/smartconfig.h b/components/esp32/smartconfig.h new file mode 100644 index 000000000..1723a775b --- /dev/null +++ b/components/esp32/smartconfig.h @@ -0,0 +1,74 @@ +// Copyright 2010-2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef SMARTCONFIG_H +#define SMARTCONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#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_SERVER_PORT 18266 /*!< ESP touch UDP port of server on cellphone */ +#define SC_ACK_AIRKISS_SERVER_PORT 10000 /*!< Airkiss UDP port of server on cellphone */ + +#define SC_ACK_TOUCH_LEN 11 /*!< Length of ESP touch 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 ACK type. + */ +typedef enum { + SC_ACK_TYPE_ESPTOUCH = 0, /*!< ESP touch ACK type */ + SC_ACK_TYPE_AIRKISS, /*!< Airkiss ACK type */ +} sc_ack_type_t; + +/** + * @brief Smartconfig parameters passed to sc_ack_send call. + */ +typedef struct sc_ack { + sc_ack_type_t type; /*!< Smartconfig ACK type */ + uint8_t *link_flag; /*!< Smartconfig link flag */ + sc_callback_t cb; /*!< Smartconfig callback function */ + struct { + uint8_t token; /*!< Smartconfig token to be sent */ + uint8_t mac[6]; /*!< MAC address of station */ + uint8_t ip[4]; /*!< IP address of cellphone */ + } ctx; +} sc_ack_t; + +/** + * @brief Send smartconfig ACK to cellphone. + * + * @attention The API is only used in libsmartconfig.a. + * + * @param param: smartconfig parameters; + */ +void sc_ack_send(sc_ack_t *param); + +/** + * @brief Stop sending smartconfig ACK to cellphone. + * + * @attention The API is only used in libsmartconfig.a. + */ +void sc_ack_send_stop(void); + +#ifdef __cplusplus +} +#endif +#endif