diff --git a/components/esp8266/include/esp_smartconfig.h b/components/esp8266/include/esp_smartconfig.h new file mode 100644 index 00000000..ed5dff8f --- /dev/null +++ b/components/esp8266/include/esp_smartconfig.h @@ -0,0 +1,121 @@ +// Copyright 2018 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 __ESP_SMARTCONFIG_H__ +#define __ESP_SMARTCONFIG_H__ + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SC_STATUS_WAIT = 0, /**< Waiting to start connect */ + SC_STATUS_FIND_CHANNEL, /**< Finding target channel */ + SC_STATUS_GETTING_SSID_PSWD, /**< Getting SSID and password of target AP */ + SC_STATUS_LINK, /**< Connecting to target AP */ + SC_STATUS_LINK_OVER, /**< Connected to AP successfully */ +} smartconfig_status_t; + +typedef enum { + SC_TYPE_ESPTOUCH = 0, /**< protocol: ESPTouch */ + SC_TYPE_AIRKISS, /**< protocol: AirKiss */ + SC_TYPE_ESPTOUCH_AIRKISS, /**< protocol: ESPTouch and AirKiss */ +} smartconfig_type_t; + +/** + * @brief The callback of SmartConfig, executed when smart-config status changed. + * + * @param status Status of SmartConfig: + * - SC_STATUS_GETTING_SSID_PSWD : pdata is a pointer of smartconfig_type_t, means config type. + * - SC_STATUS_LINK : pdata is a pointer of struct station_config. + * - SC_STATUS_LINK_OVER : pdata is a pointer of phone's IP address(4 bytes) if pdata unequal NULL. + * - otherwise : parameter void *pdata is NULL. + * @param pdata According to the different status have different values. + * + */ +typedef void (*sc_callback_t)(smartconfig_status_t status, void *pdata); + +/** + * @brief Get the version of SmartConfig. + * + * @return + * - SmartConfig version const char. + */ +const char *esp_smartconfig_get_version(void); + +/** + * @brief Start SmartConfig, config ESP device to connect AP. You need to broadcast information by phone APP. + * Device sniffer special packets from the air that containing SSID and password of target AP. + * + * @attention 1. This API can be called in station or softAP-station mode. + * @attention 2. Can not call esp_smartconfig_start twice before it finish, please call + * esp_smartconfig_stop first. + * + * @param cb SmartConfig callback function. + * @param ... log 1: UART output logs; 0: UART only outputs the result. + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t esp_smartconfig_start(sc_callback_t cb, ...); + +/** + * @brief Stop SmartConfig, free the buffer taken by esp_smartconfig_start. + * + * @attention Whether connect to AP succeed or not, this API should be called to free + * memory taken by smartconfig_start. + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t esp_smartconfig_stop(void); + +/** + * @brief Set timeout of SmartConfig process. + * + * @attention Timing starts from SC_STATUS_FIND_CHANNEL status. SmartConfig will restart if timeout. + * + * @param time_s range 15s~255s, offset:45s. + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t esptouch_set_timeout(uint8_t time_s); + +/** + * @brief Set protocol type of SmartConfig. + * + * @attention If users need to set the SmartConfig type, please set it before calling + * esp_smartconfig_start. + * + * @param type Choose from the smartconfig_type_t. + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t esp_smartconfig_set_type(smartconfig_type_t type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/esp8266/include/smartconfig.h b/components/esp8266/include/smartconfig.h deleted file mode 100644 index 7971a142..00000000 --- a/components/esp8266/include/smartconfig.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * ESPRSSIF MIT License - * - * Copyright (c) 2015 - * - * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __SMARTCONFIG_H__ -#define __SMARTCONFIG_H__ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** \defgroup WiFi_APIs WiFi Related APIs - * @brief WiFi APIs - */ - -/** @addtogroup WiFi_APIs - * @{ - */ - -/** \defgroup Smartconfig_APIs Smartconfig APIs - * @brief SmartConfig APIs - * - * SmartConfig can only be enabled in station only mode. - * Please make sure the target AP is enabled before enable SmartConfig. - * - */ - -/** @addtogroup Smartconfig_APIs - * @{ - */ - -typedef enum { - SC_STATUS_WAIT = 0, /**< waiting, do not start connection in this phase */ - SC_STATUS_FIND_CHANNEL, /**< find target channel, start connection by APP in this phase */ - SC_STATUS_GETTING_SSID_PSWD, /**< getting SSID and password of target AP */ - SC_STATUS_LINK, /**< connecting to target AP */ - SC_STATUS_LINK_OVER, /**< got IP, connect to AP successfully */ -} sc_status; - -typedef enum { - SC_TYPE_ESPTOUCH = 0, /**< protocol: ESPTouch */ - SC_TYPE_AIRKISS, /**< protocol: AirKiss */ - SC_TYPE_ESPTOUCH_AIRKISS, /**< protocol: ESPTouch and AirKiss */ -} sc_type; - -/** - * @brief The callback of SmartConfig, executed when smart-config status changed. - * - * @param sc_status status : status of SmartConfig: - * - if status == SC_STATUS_GETTING_SSID_PSWD, parameter void *pdata is a pointer - of sc_type, means SmartConfig type: AirKiss or ESP-TOUCH. - * - if status == SC_STATUS_LINK, parameter void *pdata is a pointer of struct station_config; - * - if status == SC_STATUS_LINK_OVER, parameter void *pdata is a pointer of mobile - * phone's IP address, 4 bytes. This is only available in ESPTOUCH, otherwise, - * it is NULL. - * - otherwise, parameter void *pdata is NULL. - * @param void *pdata : data of SmartConfig - * - * @return null - */ -typedef void (*sc_callback_t)(sc_status status, void *pdata); - -/** - * @brief Get the version of SmartConfig. - * - * @param null - * - * @return SmartConfig version - */ -const char *smartconfig_get_version(void); - -/** - * @brief Start SmartConfig mode. - * - * Start SmartConfig mode, to connect ESP8266 station to AP, by sniffing - * for special packets from the air, containing SSID and password of desired AP. - * You need to broadcast the SSID and password (e.g. from mobile device or computer) - * with the SSID and password encoded. - * - * @attention 1. This api can only be called in station mode. - * @attention 2. During SmartConfig, ESP8266 station and soft-AP are disabled. - * @attention 3. Can not call smartconfig_start twice before it finish, please call - * smartconfig_stop first. - * @attention 4. Don't call any other APIs during SmartConfig, please call smartconfig_stop first. - * - * @param sc_callback_t cb : SmartConfig callback; executed when SmartConfig status changed; - * @param uint8 log : 1, UART output logs; otherwise, UART only outputs the result. - * - * @return true : succeed - * @return false : fail - */ -bool smartconfig_start(sc_callback_t cb, ...); - -/** - * @brief Stop SmartConfig, free the buffer taken by smartconfig_start. - * - * @attention Whether connect to AP succeed or not, this API should be called to free - * memory taken by smartconfig_start. - * - * @param null - * - * @return true : succeed - * @return false : fail - */ -bool smartconfig_stop(void); - -/** - * @brief Set timeout of SmartConfig. - * - * @attention SmartConfig timeout start at SC_STATUS_FIND_CHANNEL, SmartConfig will - * restart if timeout. - * - * @param uint8 time_s : range 15s~255s, offset:45s. - * - * @return true : succeed - * @return false : fail - */ -bool esptouch_set_timeout(uint8_t time_s); - -/** - * @brief Set protocol type of SmartConfig. - * - * @attention If users need to set the SmartConfig type, please set it before calling - * smartconfig_start. - * - * @param sc_type type : AirKiss, ESP-TOUCH or both. - * - * @return true : succeed - * @return false : fail - */ -bool smartconfig_set_type(sc_type type); - -/** - * @} - */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/components/smartconfig_ack/component.mk b/components/smartconfig_ack/component.mk new file mode 100644 index 00000000..58eac9a5 --- /dev/null +++ b/components/smartconfig_ack/component.mk @@ -0,0 +1,2 @@ +# +# Component Makefile diff --git a/components/smartconfig_ack/include/smartconfig_ack.h b/components/smartconfig_ack/include/smartconfig_ack.h new file mode 100644 index 00000000..6bee2383 --- /dev/null +++ b/components/smartconfig_ack/include/smartconfig_ack.h @@ -0,0 +1,74 @@ +// Copyright 2018 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_ACK_H +#define SMARTCONFIG_ACK_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 diff --git a/components/smartconfig_ack/smartconfig_ack.c b/components/smartconfig_ack/smartconfig_ack.c new file mode 100644 index 00000000..0bdaa48c --- /dev/null +++ b/components/smartconfig_ack/smartconfig_ack.c @@ -0,0 +1,157 @@ +// Copyright 2018 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 +#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_ack.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 = -1; + 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 < LWIP_SOCKET_OFFSET) || (send_sock > (FD_SETSIZE - 1))) { + ESP_LOGE(TAG, "Creat udp socket failed"); + goto _end; + } + + 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); + } + goto _end; + } + } + 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); + goto _end; + } + } + } + else { + vTaskDelay((portTickType)(100 / portTICK_RATE_MS)); + } + } + +_end: + if ((send_sock >= LWIP_SOCKET_OFFSET) && (send_sock <= (FD_SETSIZE - 1))) { + close(send_sock); + } + 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; + + 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); + } +} + +void sc_ack_send_stop(void) +{ + s_sc_ack_send = false; +}