2818 lines
65 KiB
C
2818 lines
65 KiB
C
/****************************************************************************
|
|
* apps/netutils/esp8266/esp8266.c
|
|
*
|
|
* Derives from an application to demo an Arduino connected to the ESPRESSIF
|
|
* ESP8266 with AT command firmware.
|
|
*
|
|
* Copyright (C) 2015 Pierre-Noel Bouteville. All rights reserved.
|
|
* Author: Pierre-Noel Bouteville <pnb990@gmail.com>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <poll.h>
|
|
#include <termios.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "netutils/esp8266.h"
|
|
|
|
#ifdef CONFIG_NETUTILS_ESP8266
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define NETAPP_IPCONFIG_MAC_OFFSET (20)
|
|
|
|
#ifndef CONFIG_NETUTILS_ESP8266_MAXTXLEN
|
|
# define CONFIG_NETUTILS_ESP8266_MAXTXLEN 256
|
|
#endif
|
|
|
|
#ifndef CONFIG_NETUTILS_ESP8266_MAXRXLEN
|
|
# define CONFIG_NETUTILS_ESP8266_MAXRXLEN 256
|
|
#endif
|
|
|
|
#if (CONFIG_NETUTILS_ESP8266_MAXRXLEN < CONFIG_NETUTILS_ESP8266_WORKER_BUF_LEN)
|
|
# error "CONFIG_NETUTILS_ESP8266_WORKER_BUF_LEN whould be bigger than CONFIG_NETUTILS_ESP8266_MAXRXLEN"
|
|
#endif
|
|
|
|
#define BUF_CMD_LEN CONFIG_NETUTILS_ESP8266_MAXTXLEN
|
|
#define BUF_ANS_LEN CONFIG_NETUTILS_ESP8266_MAXRXLEN
|
|
#define BUF_WORKER_LEN CONFIG_NETUTILS_ESP8266_WORKER_BUF_LEN
|
|
|
|
|
|
#define CON_NBR 4
|
|
|
|
#define ESP8266_ACCESS_POINT_NBR_MAX 32
|
|
|
|
#define lespWAITING_OK_POLLING_MS 250
|
|
#define lespTIMEOUT_FLUSH_MS 100
|
|
#define lespTIMEOUT_MS 1000
|
|
#define lespTIMEOUT_MS_SEND 1000
|
|
#define lespTIMEOUT_MS_CONNECTION 30000
|
|
#define lespTIMEOUT_MS_LISP_AP 5000
|
|
#define lespTIMEOUT_FLOODING_OFFSET_S 3
|
|
#define lespTIMEOUT_MS_RECV_S 60
|
|
|
|
#define lespCON_USED_MASK(idx) (1<<(idx))
|
|
#define lespPOLLING_TIME_MS 1000
|
|
|
|
/* Must be a power of 2 */
|
|
|
|
#define SOCKET_FIFO_SIZE 2048
|
|
#define SOCKET_NBR 4
|
|
|
|
#define FLAGS_SOCK_USED (1 << 0)
|
|
#define FLAGS_SOCK_CONNECTED (1 << 1)
|
|
|
|
#define FLAGS_SOCK_TYPE_MASK (3 << 2)
|
|
#define FLAGS_SOCK_TYPE_TCP (0 << 2)
|
|
#define FLAGS_SOCK_TYPE_UDP (1 << 2)
|
|
#define FLAGS_SOCK_TYPE_SSL (2 << 2) /* non standard but useful */
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
typedef enum
|
|
{
|
|
lesp_eERR = -1,
|
|
lesp_eNONE = 0,
|
|
lesp_eOK = 1
|
|
}lesp_ans_t;
|
|
|
|
typedef struct
|
|
{
|
|
sem_t *sem;
|
|
uint8_t flags;
|
|
uint16_t inndx;
|
|
uint16_t outndx;
|
|
struct timespec rcv_timeo;
|
|
uint8_t rxbuf[SOCKET_FIFO_SIZE];
|
|
} lesp_socket_t;
|
|
|
|
typedef struct
|
|
{
|
|
bool running;
|
|
pthread_t thread;
|
|
|
|
char rxbuf[BUF_WORKER_LEN];
|
|
|
|
sem_t sem; /* Inform that something is received */
|
|
char buf[BUF_ANS_LEN]; /* Last complete line received */
|
|
lesp_ans_t ans; /* Last ans received (OK,FAIL or ERROR) */
|
|
pthread_mutex_t mutex;
|
|
} lesp_worker_t;
|
|
|
|
typedef struct
|
|
{
|
|
pthread_mutex_t mutex;
|
|
bool is_initialized;
|
|
int fd;
|
|
lesp_worker_t worker;
|
|
lesp_socket_t sockets[SOCKET_NBR];
|
|
lesp_ans_t ans;
|
|
char bufans[BUF_ANS_LEN];
|
|
char bufcmd[BUF_CMD_LEN];
|
|
struct hostent hostent;
|
|
|
|
/* ESP Got only One ip + 1 for NULL that indicate end of list */
|
|
|
|
in_addr_t * h_addr_list_buf[2];
|
|
|
|
in_addr_t in_addr;
|
|
} lesp_state_t;
|
|
|
|
/****************************************************************************
|
|
* Private Functions prototypes
|
|
****************************************************************************/
|
|
|
|
static int lesp_low_level_read(uint8_t *buf, int size);
|
|
static inline int lesp_read_ipd(int sockfd, int len);
|
|
static lesp_socket_t *get_sock(int sockfd);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
lesp_state_t g_lesp_state =
|
|
{
|
|
.mutex = PTHREAD_MUTEX_INITIALIZER,
|
|
.is_initialized = false,
|
|
.fd = -1,
|
|
.worker.running = false,
|
|
.worker.ans = lesp_eNONE,
|
|
.worker.mutex = PTHREAD_MUTEX_INITIALIZER,
|
|
.ans = lesp_eNONE,
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_clear_read_buffer
|
|
*
|
|
* Description:
|
|
*
|
|
* Input Parmeters:
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lesp_clear_read_buffer(void)
|
|
{
|
|
g_lesp_state.bufans[0] = '\0';
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_clear_read_ans
|
|
*
|
|
* Description:
|
|
*
|
|
* Input Parmeters:
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lesp_clear_read_ans(void)
|
|
{
|
|
g_lesp_state.ans = lesp_eNONE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_str_to_unsigned
|
|
*
|
|
* Description:
|
|
*
|
|
* Input Parmeters:
|
|
*
|
|
* Returned Value:
|
|
* unsigned value
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline int lesp_str_to_unsigned(char **p_ptr, char end)
|
|
{
|
|
int nbr = 0;
|
|
char *ptr = *p_ptr;
|
|
|
|
while (*ptr != end)
|
|
{
|
|
char c = *ptr++ - '0';
|
|
if ((c < 0) || (c >= 10))
|
|
return -1;
|
|
nbr *= 10;
|
|
nbr += c;
|
|
}
|
|
|
|
*p_ptr = ptr+1; /* Pass the end char */
|
|
return nbr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_set_baudrate
|
|
*
|
|
* Description:
|
|
* Set com port to baudrate.
|
|
*
|
|
* Input Parmeters:
|
|
* sockfd : int baudrate
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 incase of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_SERIAL_TERMIOS
|
|
static int lesp_set_baudrate(int baudrate)
|
|
{
|
|
struct termios term;
|
|
|
|
if (ioctl(g_lesp_state.fd, TCGETS, (unsigned long)&term) < 0)
|
|
{
|
|
nerr("ERROR: TCGETS failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
if ((cfsetispeed(&term, baudrate) < 0) ||
|
|
(cfsetospeed(&term, baudrate) < 0))
|
|
{
|
|
nerr("ERROR: Connot set baudrate %0x08X\n", baudrate);
|
|
return -1;
|
|
}
|
|
|
|
if (ioctl(g_lesp_state.fd, TCSETS, (unsigned long)&term) < 0)
|
|
{
|
|
nerr("ERROR: TCSETS failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: get_sock
|
|
*
|
|
* Description:
|
|
* Get socket structure pointer of sockfd.
|
|
*
|
|
* Input Parmeters:
|
|
* sockfd : socket id
|
|
*
|
|
* Returned Value:
|
|
* socket id pointer, NULL on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static lesp_socket_t *get_sock(int sockfd)
|
|
{
|
|
DEBUGASSERT(sockfd >= 0);
|
|
|
|
if (!g_lesp_state.is_initialized)
|
|
{
|
|
errno = ENETDOWN;
|
|
ninfo("Esp8266 not initialized; can't list access points\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (((unsigned int)sockfd) >= SOCKET_NBR)
|
|
{
|
|
errno = EINVAL;
|
|
ninfo("Esp8266 invalid sockfd\n", sockfd);
|
|
return NULL;
|
|
}
|
|
|
|
if ((g_lesp_state.sockets[sockfd].flags & FLAGS_SOCK_USED) == 0)
|
|
{
|
|
errno = EPERM;
|
|
nerr("ERROR: Connection id %d not Created!\n", sockfd);
|
|
return NULL;
|
|
}
|
|
|
|
return &g_lesp_state.sockets[sockfd];
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: get_sock_protected
|
|
*
|
|
* Description:
|
|
* Get socket structure pointer of sockfd with worker.mutex portection.
|
|
*
|
|
* Input Parmeters:
|
|
* sockfd : socket id
|
|
*
|
|
* Returned Value:
|
|
* socket id pointer, NULL on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static lesp_socket_t *get_sock_protected(int sockfd)
|
|
{
|
|
lesp_socket_t *ret;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
ret = get_sock(sockfd);
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: set_sock_closed
|
|
*
|
|
* Description:
|
|
* Set socket sockfd mark as closed without check and don't send message to
|
|
* esp8266.
|
|
*
|
|
* Input Parmeters:
|
|
* sockfd : socket id
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void set_sock_closed(int sockfd)
|
|
{
|
|
sem_t *sem;
|
|
lesp_socket_t *sock;
|
|
|
|
DEBUGASSERT(((unsigned int)sockfd) < SOCKET_NBR);
|
|
|
|
sock = &g_lesp_state.sockets[sockfd];
|
|
sem = sock->sem;
|
|
sock->sem = NULL;
|
|
sock->flags = 0;
|
|
sock->inndx = 0;
|
|
sock->outndx = 0;
|
|
|
|
ninfo("Socket %d closed\n", sockfd);
|
|
|
|
if (sem != NULL)
|
|
{
|
|
sem_post(sem);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_low_level_read
|
|
*
|
|
* Description:
|
|
* put size data from esp8266 into buf.
|
|
*
|
|
* Input Parmeters:
|
|
* buf : buffer to store read data
|
|
* size : size of data to read
|
|
*
|
|
* Returned Value:
|
|
* number of byte read, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_low_level_read(uint8_t *buf, int size)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct pollfd fds[1];
|
|
|
|
memset(fds, 0, sizeof(struct pollfd));
|
|
fds[0].fd = g_lesp_state.fd;
|
|
fds[0].events = POLLIN;
|
|
|
|
/* poll return 1=>even occur 0=>timeout or -1=>error */
|
|
|
|
ret = poll(fds, 1, lespPOLLING_TIME_MS);
|
|
if (ret < 0)
|
|
{
|
|
int errcode = errno;
|
|
nerr("ERROR: worker read Error %d (errno %d)\n", ret, errcode);
|
|
UNUSED(errcode);
|
|
}
|
|
else if ((fds[0].revents & POLLERR) && (fds[0].revents & POLLHUP))
|
|
{
|
|
nerr("ERROR: worker poll read Error %d\n", ret);
|
|
ret = -1;
|
|
}
|
|
else if (fds[0].revents & POLLIN)
|
|
{
|
|
ret = read(g_lesp_state.fd, buf, size);
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_read_ipd
|
|
*
|
|
* Description:
|
|
* Try to treat an '+IPD' command in worker buffer. Worker buffer should
|
|
* already contain '+IPD,<id>,<len>:'
|
|
*
|
|
* Note:
|
|
* g_lesp_state.worker.mutex should be locked.
|
|
*
|
|
* Input Parmeters:
|
|
* sockfd : socker number to put received data.
|
|
* len : data size to read on serial port.
|
|
*
|
|
* Returned Value:
|
|
* 1 was an IPD, 0 is not an IPD, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline int lesp_read_ipd(int sockfd, int len)
|
|
{
|
|
lesp_socket_t *sock;
|
|
|
|
sock = get_sock(sockfd);
|
|
|
|
ninfo("Read %d bytes for socket %d \n", len, sockfd);
|
|
|
|
if (sock == NULL)
|
|
{
|
|
nwarn("socket not opened: drop all data.\n");
|
|
}
|
|
|
|
while (len)
|
|
{
|
|
int size;
|
|
uint8_t *buf = (uint8_t *)g_lesp_state.worker.rxbuf;
|
|
|
|
size = len;
|
|
if (size >= BUF_WORKER_LEN)
|
|
{
|
|
size = BUF_WORKER_LEN;
|
|
}
|
|
|
|
size = lesp_low_level_read(buf, size);
|
|
if (size <= 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
len -= size;
|
|
|
|
if (sock != NULL)
|
|
{
|
|
while (size--)
|
|
{
|
|
int next;
|
|
uint8_t b;
|
|
|
|
/* Read the next byte from the buffer */
|
|
|
|
b = *buf++;
|
|
|
|
/* Pre-calculate the next 'inndx'. We do this so that we can
|
|
* check if the FIFO is full.
|
|
*/
|
|
|
|
next = sock->inndx + 1;
|
|
if (next >= SOCKET_FIFO_SIZE)
|
|
{
|
|
next -= SOCKET_FIFO_SIZE;
|
|
}
|
|
|
|
/* Is there space in the circular buffer for another byte? If
|
|
* the next 'inndx' would be equal to the 'outndx', then the
|
|
* circular buffer is full.
|
|
*/
|
|
|
|
if (next == sock->outndx)
|
|
{
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
usleep(100); /* leave time of aplicative to read buffer */
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
}
|
|
|
|
if (next != sock->outndx)
|
|
{
|
|
/* Yes.. add the byte to the circular buffer */
|
|
|
|
sock->rxbuf[sock->inndx] = b;
|
|
sock->inndx = next;
|
|
}
|
|
else
|
|
{
|
|
/* No.. the we have lost data */
|
|
|
|
nwarn("overflow socket 0x%02X\n", b);
|
|
}
|
|
}
|
|
|
|
if (sock->sem)
|
|
{
|
|
ninfo("post %p \n", sock->sem);
|
|
sem_post(sock->sem);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_vsend_cmd
|
|
*
|
|
* Description:
|
|
* Send cmd with format and argument like sprintf.
|
|
*
|
|
* Input Parmeters:
|
|
* format : null terminated format string to send.
|
|
* ap : format values.
|
|
*
|
|
* Returned Value:
|
|
* len of cmd on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_vsend_cmd(FAR const IPTR char *format, va_list ap)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = vsnprintf(g_lesp_state.bufcmd, BUF_CMD_LEN, format, ap);
|
|
if (ret >= BUF_CMD_LEN)
|
|
{
|
|
g_lesp_state.bufcmd[BUF_CMD_LEN-1] = '\0';
|
|
ninfo("Buffer too small for '%s'...\n", g_lesp_state.bufcmd);
|
|
ret = -1;
|
|
}
|
|
|
|
ninfo("Write:%s\n", g_lesp_state.bufcmd);
|
|
|
|
ret = write(g_lesp_state.fd, g_lesp_state.bufcmd, ret);
|
|
if (ret < 0)
|
|
{
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_send_cmd
|
|
*
|
|
* Description:
|
|
* Send cmd with format and argument like sprintf.
|
|
*
|
|
* Input Parmeters:
|
|
* format : null terminated format string to send.
|
|
* ... : format values.
|
|
*
|
|
* Note it clear AT Rx buffer (not socket buffer)
|
|
*
|
|
* Returned Value:
|
|
* len of cmd on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_send_cmd(FAR const IPTR char *format, ...)
|
|
{
|
|
int ret = 0;
|
|
va_list ap;
|
|
|
|
lesp_clear_read_buffer();
|
|
lesp_clear_read_ans();
|
|
|
|
/* Let lesp_vsend_cmd do the real work */
|
|
|
|
va_start(ap, format);
|
|
ret = lesp_vsend_cmd(format, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_read
|
|
*
|
|
* Description:
|
|
* Read a answer line with timeout.
|
|
*
|
|
* Input Parmeters:
|
|
* timeout_ms : timeout in millisecond.
|
|
*
|
|
* Returned Value:
|
|
* len of line without line return on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_read(int timeout_ms)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct timespec ts;
|
|
|
|
if (! g_lesp_state.is_initialized)
|
|
{
|
|
ninfo("Esp8266 not initialized; can't list access points\n");
|
|
return -1;
|
|
}
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ts.tv_nsec += (timeout_ms%1000) * 1000000;
|
|
if (ts.tv_nsec >= 1000000000)
|
|
{
|
|
ts.tv_nsec -= 1000000000;
|
|
ts.tv_sec += 1;
|
|
}
|
|
|
|
ts.tv_sec += (timeout_ms/1000);
|
|
|
|
do
|
|
{
|
|
if (sem_timedwait(&g_lesp_state.worker.sem, &ts) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
|
|
if (g_lesp_state.worker.ans != lesp_eNONE)
|
|
{
|
|
g_lesp_state.ans = g_lesp_state.worker.ans;
|
|
g_lesp_state.worker.ans = lesp_eNONE;
|
|
}
|
|
|
|
ret = strlen(g_lesp_state.worker.buf);
|
|
if (ret > 0)
|
|
{
|
|
/* +1 to copy null */
|
|
|
|
memcpy(g_lesp_state.bufans, g_lesp_state.worker.buf, ret+1);
|
|
}
|
|
|
|
g_lesp_state.worker.buf[0] = '\0'; /* buffer is read */
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
}
|
|
while ((ret <= 0) && (g_lesp_state.ans == lesp_eNONE));
|
|
|
|
ninfo("lesp_read %d=>%s and ans = %d \n", ret, g_lesp_state.bufans,
|
|
g_lesp_state.ans);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_flush
|
|
*
|
|
* Description:
|
|
* Read and discard all waiting data in rx buffer.
|
|
*
|
|
* Input Parmeters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lesp_flush(void)
|
|
{
|
|
do
|
|
{
|
|
lesp_clear_read_buffer();
|
|
lesp_clear_read_ans();
|
|
}
|
|
while (lesp_read(lespTIMEOUT_FLUSH_MS) >= 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_read_ans_ok
|
|
*
|
|
* Description:
|
|
* Read up to read OK, ERROR, or FAIL.
|
|
*
|
|
* Input Parmeters:
|
|
* timeout_ms : timeout in millisecond.
|
|
*
|
|
* Returned Value:
|
|
* 0 on OK, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_read_ans_ok(int timeout_ms)
|
|
{
|
|
int ret = 0;
|
|
time_t end;
|
|
|
|
end = time(NULL) + (timeout_ms/1000) + lespTIMEOUT_FLOODING_OFFSET_S;
|
|
|
|
while (g_lesp_state.ans != lesp_eOK)
|
|
{
|
|
ret = lesp_read(timeout_ms);
|
|
|
|
if ((ret < 0) || (g_lesp_state.ans == lesp_eERR) || \
|
|
(time(NULL) > end))
|
|
{
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
ninfo("Got:%s\n", g_lesp_state.bufans);
|
|
}
|
|
|
|
lesp_clear_read_ans();
|
|
lesp_clear_read_buffer();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_ask_ans_ok
|
|
*
|
|
* Description:
|
|
* Ask and ignore line start with '+' and except a "OK" answer.
|
|
*
|
|
* Input Parmeters:
|
|
* cmd : command sent
|
|
* timeout_ms : timeout in millisecond
|
|
*
|
|
* Returned Value:
|
|
* len of line without line return on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_ask_ans_ok(int timeout_ms, FAR const IPTR char *format, ...)
|
|
{
|
|
int ret = 0;
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
ret = lesp_vsend_cmd(format, ap);
|
|
va_end(ap);
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read_ans_ok(timeout_ms);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_check
|
|
*
|
|
* Description:
|
|
* check if esp is ready (initialized and AT return OK)
|
|
*
|
|
* Input Parmeters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* 0 : all is ok, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_check(void)
|
|
{
|
|
if (! g_lesp_state.is_initialized)
|
|
{
|
|
nerr("ERROR: ESP8266 not initialized\n");
|
|
return -1;
|
|
}
|
|
|
|
lesp_flush();
|
|
|
|
if (lesp_ask_ans_ok(lespTIMEOUT_MS, "AT\r\n") < 0)
|
|
{
|
|
nerr("ERROR: ESP8266 not answer at AT command\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_parse_cipdomain_ans_line
|
|
*
|
|
* Description:
|
|
* Try to decode line start with:
|
|
* +CIPDOMAIN:
|
|
* see in:
|
|
* https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/
|
|
* or
|
|
* http://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
|
|
*
|
|
* net ip
|
|
* +CIPDOMAIN:"192.168.1.1"
|
|
*
|
|
* Note:
|
|
* - Content of ptr is modified and string in ap point into ptr string.
|
|
* - in current version, only one ip is returned by ESP8266.
|
|
*
|
|
* Input Parmeters:
|
|
* ptr : +CIPDOMAIN line null terminated string pointer.
|
|
* ip : ip of hostname
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_parse_cwdomain_ans_line(const char *ptr, in_addr_t *ip)
|
|
{
|
|
int field_idx;
|
|
char *ptr_next;
|
|
|
|
for (field_idx = 0; field_idx <= 1; field_idx++)
|
|
{
|
|
if (field_idx == 0)
|
|
{
|
|
ptr_next = strchr(ptr, ':');
|
|
}
|
|
else if (field_idx == 1)
|
|
{
|
|
ptr_next = strchr(ptr, '\0');
|
|
}
|
|
|
|
if (ptr_next == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
*ptr_next = '\0';
|
|
|
|
switch (field_idx)
|
|
{
|
|
case 0:
|
|
if (strncmp(ptr, "+CIP", 4) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
/* No '"' for this command ! */
|
|
|
|
if (inet_pton(AF_INET, ptr, ip) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ptr = ptr_next + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_parse_cipxxx_ans_line
|
|
*
|
|
* Description:
|
|
* Try to decode line start with:
|
|
* +CIPAP?
|
|
* +CIPAP_XXX?
|
|
* +CIPSTA?
|
|
* +CIPSTA_XXX?
|
|
* see in:
|
|
* https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/
|
|
* or
|
|
* http://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
|
|
*
|
|
* net ip net mask
|
|
* +CIPxxxx:ip:"192.168.1.1","255.255.255.0","192.168.1.1",
|
|
*
|
|
* Note:
|
|
* - Content of ptr is modified and string in ap point into ptr string.
|
|
* - in current version, only net ip is returned by ESP8266.
|
|
*
|
|
* Input Parmeters:
|
|
* ptr : +CWLAP line null terminated string pointer.
|
|
* ap : ap result of parsing.
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_parse_cipxxx_ans_line(const char *ptr, in_addr_t *ip)
|
|
{
|
|
int field_idx;
|
|
char *ptr_next;
|
|
|
|
for (field_idx = 0; field_idx <= 2; field_idx++)
|
|
{
|
|
if (field_idx <= 1)
|
|
{
|
|
ptr_next = strchr(ptr, ':');
|
|
}
|
|
else if (field_idx == 2)
|
|
{
|
|
ptr_next = strchr(ptr, '\0');
|
|
}
|
|
else
|
|
{
|
|
ptr_next = strchr(ptr, ',');
|
|
}
|
|
|
|
if (ptr_next == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
*ptr_next = '\0';
|
|
|
|
switch (field_idx)
|
|
{
|
|
case 0:
|
|
if (strncmp(ptr, "+CIP", 4) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
/* ip label */
|
|
|
|
break;
|
|
case 2:
|
|
ptr++; /* Remove first '"' */
|
|
*(ptr_next - 1) = '\0';
|
|
if (inet_pton(AF_INET, ptr, ip) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ptr = ptr_next + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_parse_cwjap_ans_line
|
|
*
|
|
* Description:
|
|
* Try to decode @b +CWJAP? or +CWJAP_XXX? line.
|
|
* see in:
|
|
* https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/
|
|
* or
|
|
* http://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
|
|
*
|
|
* SSID BSSID ch RSSI
|
|
* +CWLAP:"FreeWifi","00:07:cb:07:b6:00", 1, -90
|
|
* or
|
|
* +CWJAP_CUR:"WLACTAP","f4:f2:6d:e8:5e:74",6,-59
|
|
*
|
|
* Note: Content of ptr is modified and string in ap point into ptr string.
|
|
*
|
|
* Input Parmeters:
|
|
* ptr : +CWLAP line null terminated string pointer.
|
|
* ap : ap result of parsing.
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_parse_cwjap_ans_line(char *ptr, lesp_ap_t *ap)
|
|
{
|
|
int field_idx;
|
|
char *ptr_next;
|
|
|
|
for (field_idx = 0; field_idx <= 4; field_idx++)
|
|
{
|
|
if (field_idx == 0)
|
|
{
|
|
ptr_next = strchr(ptr, ':');
|
|
}
|
|
else if (field_idx == 4)
|
|
{
|
|
ptr_next = strchr(ptr, '\0');
|
|
}
|
|
else
|
|
{
|
|
ptr_next = strchr(ptr, ',');
|
|
}
|
|
|
|
if (ptr_next == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
*ptr_next = '\0';
|
|
|
|
switch (field_idx)
|
|
{
|
|
case 0:
|
|
if (strncmp(ptr, "+CWJAP", 6) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
ptr++; /* Remove first '"' */
|
|
*(ptr_next - 1) = '\0';
|
|
strncpy(ap->ssid, ptr, lespSSID_SIZE);
|
|
ap->ssid[lespSSID_SIZE] = '\0';
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
int i;
|
|
|
|
ptr++; /* Remove first '"' */
|
|
*(ptr_next - 1) = '\0';
|
|
|
|
for (i = 0; i < lespBSSID_SIZE ; i++)
|
|
{
|
|
ap->bssid[i] = strtol(ptr, &ptr, 16);
|
|
if (*ptr == ':')
|
|
{
|
|
ptr++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
int i = atoi(ptr);
|
|
ap->channel = i;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
{
|
|
int i = atoi(ptr);
|
|
|
|
if (i > 0)
|
|
{
|
|
i = -i;
|
|
}
|
|
|
|
ap->rssi = i;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ptr = ptr_next + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name:
|
|
*
|
|
* Description:
|
|
* Try to decode @b +CWLAP line.
|
|
* see in:
|
|
* https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/
|
|
*
|
|
* +CWLAP:(0,"FreeWifi",-90,"00:07:cb:07:b6:00",1)
|
|
* 0 => security
|
|
* "FreeWifi" => ssid
|
|
* -90 => rssi
|
|
* "00:07:cb:07:b6:00" => mac
|
|
*
|
|
* Note: Content of ptr is modified and string in ap point into ptr string.
|
|
*
|
|
* Input Parmeters:
|
|
* ptr : +CWLAP line null terminated string pointer.
|
|
* ap : ap result of parsing.
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lesp_parse_cwlap_ans_line(char *ptr, lesp_ap_t *ap)
|
|
{
|
|
int field_idx;
|
|
char *ptr_next;
|
|
|
|
for (field_idx = 0; field_idx <= 5; field_idx++)
|
|
{
|
|
if (field_idx == 0)
|
|
{
|
|
ptr_next = strchr(ptr, '(');
|
|
}
|
|
else if (field_idx == 5)
|
|
{
|
|
ptr_next = strchr(ptr, ')');
|
|
}
|
|
else
|
|
{
|
|
ptr_next = strchr(ptr, ',');
|
|
}
|
|
|
|
if (ptr_next == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
*ptr_next = '\0';
|
|
|
|
switch (field_idx)
|
|
{
|
|
case 0:
|
|
if (strcmp(ptr, "+CWLAP:") != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
int i = *ptr - '0';
|
|
|
|
if ((i < 0) || (i >= lesp_eSECURITY_NBR))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ap->security = i;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
ptr++; /* Remove first '"' */
|
|
*(ptr_next - 1) = '\0';
|
|
strncpy(ap->ssid, ptr, lespSSID_SIZE);
|
|
ap->ssid[lespSSID_SIZE] = '\0';
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
int i = atoi(ptr);
|
|
|
|
if (i > 0)
|
|
{
|
|
i = -i;
|
|
}
|
|
|
|
ap->rssi = i;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
{
|
|
int i;
|
|
|
|
ptr++; /* Remove first '"' */
|
|
*(ptr_next - 1) = '\0';
|
|
|
|
for (i = 0; i < lespBSSID_SIZE ; i++)
|
|
{
|
|
ap->bssid[i] = strtol(ptr, &ptr, 16);
|
|
if (*ptr == ':')
|
|
{
|
|
ptr++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
ptr = ptr_next + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_worker
|
|
*
|
|
* Description:
|
|
* Esp8266 worker thread.
|
|
*
|
|
* Input Parmeters:
|
|
* args : unused
|
|
*
|
|
* Returned Value:
|
|
* NULL
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void *lesp_worker(void *args)
|
|
{
|
|
int ret = 0;
|
|
int rxlen = 0;
|
|
|
|
lesp_worker_t *worker = &g_lesp_state.worker;
|
|
|
|
UNUSED(args);
|
|
|
|
ninfo("worker Started \n");
|
|
|
|
while (worker->running)
|
|
{
|
|
uint8_t c;
|
|
|
|
ret = lesp_low_level_read(&c, 1);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: worker read data Error %d\n", ret);
|
|
}
|
|
else if (ret > 0)
|
|
{
|
|
/* ninfo("c:0x%02X (%c)\n", c); */
|
|
|
|
pthread_mutex_lock(&(worker->mutex));
|
|
if (c == '\n')
|
|
{
|
|
if (worker->rxbuf[rxlen-1] == '\r')
|
|
{
|
|
rxlen--;
|
|
}
|
|
|
|
DEBUGASSERT(rxlen >= 0);
|
|
DEBUGASSERT(rxlen < BUF_WORKER_LEN);
|
|
|
|
worker->rxbuf[rxlen] = '\0';
|
|
|
|
if (rxlen != 0)
|
|
{
|
|
if (strcmp(worker->rxbuf, "OK") == 0)
|
|
{
|
|
worker->ans = lesp_eOK;
|
|
}
|
|
else if ((strcmp(worker->rxbuf, "FAIL") == 0) ||
|
|
(strcmp(worker->rxbuf, "ERROR") == 0)
|
|
)
|
|
{
|
|
worker->ans = lesp_eERR;
|
|
}
|
|
else if ((rxlen == 8) &&
|
|
(memcmp(worker->rxbuf+1, ",CLOSED", 7) == 0))
|
|
{
|
|
unsigned int sockid = worker->rxbuf[0] - '0';
|
|
if (sockid < SOCKET_NBR)
|
|
{
|
|
set_sock_closed(sockid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (worker->buf[0] != '\0')
|
|
{
|
|
pthread_mutex_unlock(&(worker->mutex));
|
|
usleep(100); /* leave time of aplicative to read buffer */
|
|
pthread_mutex_lock(&(worker->mutex));
|
|
}
|
|
|
|
/* ninfo("Worker Read data:%s\n", worker->rxbuf); */
|
|
|
|
if (rxlen+1 <= BUF_ANS_LEN)
|
|
{
|
|
memcpy(worker->buf, worker->rxbuf, rxlen+1);
|
|
}
|
|
else
|
|
{
|
|
nerr("Worker ans line is too long:%s\n", worker->rxbuf);
|
|
}
|
|
}
|
|
|
|
sem_post(&worker->sem);
|
|
worker->rxbuf[0] = '\0';
|
|
rxlen = 0;
|
|
}
|
|
}
|
|
else if (rxlen < BUF_WORKER_LEN - 1)
|
|
{
|
|
worker->rxbuf[rxlen++] = c;
|
|
if ((c == ':') && (memcmp(worker->rxbuf, "+IPD,", 5) == 0))
|
|
{
|
|
int sockfd;
|
|
int len;
|
|
char *ptr = worker->rxbuf+5;
|
|
|
|
sockfd = lesp_str_to_unsigned(&ptr, ',');
|
|
if (sockfd >= 0)
|
|
{
|
|
len = lesp_str_to_unsigned(&ptr, ':');
|
|
if (len >= 0)
|
|
{
|
|
lesp_read_ipd(sockfd, len);
|
|
}
|
|
}
|
|
|
|
rxlen = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nerr("Read char overflow:%c\n", c);
|
|
}
|
|
|
|
pthread_mutex_unlock(&(worker->mutex));
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_create_worker
|
|
*
|
|
* Description:
|
|
* start Esp8266 worker thread.
|
|
*
|
|
* Input Parmeters:
|
|
* priority : POSIX priority of worker thread.
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline int lesp_create_worker(int priority)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Thread */
|
|
|
|
pthread_attr_t thread_attr;
|
|
|
|
/* Ihm priority lower than normal */
|
|
|
|
struct sched_param param;
|
|
|
|
ret = pthread_attr_init(&thread_attr);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Cannot Set scheduler parameter thread (%d)\n", ret);
|
|
}
|
|
else
|
|
{
|
|
ret = pthread_attr_getschedparam(&thread_attr, ¶m);
|
|
if (ret >= 0)
|
|
{
|
|
param.sched_priority += priority;
|
|
ret = pthread_attr_setschedparam(&thread_attr, ¶m);
|
|
}
|
|
else
|
|
{
|
|
nerr("ERROR: Cannot Get/Set scheduler parameter thread (%d)\n", ret);
|
|
}
|
|
|
|
g_lesp_state.worker.running = true;
|
|
|
|
ret = pthread_create(&g_lesp_state.worker.thread,
|
|
(ret < 0)?NULL:&thread_attr, lesp_worker, NULL);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Cannot Create thread return (%d)\n", ret);
|
|
g_lesp_state.worker.running = false;
|
|
}
|
|
|
|
#if CONFIG_TASK_NAME_SIZE > 0
|
|
if (ret >= 0)
|
|
{
|
|
pthread_setname_np(g_lesp_state.worker.thread, "ESP8266");
|
|
}
|
|
#endif
|
|
|
|
if (pthread_attr_destroy(&thread_attr) < 0)
|
|
{
|
|
nerr("ERROR: Cannot destroy thread attribute (%d)\n", ret);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_initialize
|
|
*
|
|
* Description:
|
|
* initialise Esp8266 class.
|
|
* - open port
|
|
* - configure port
|
|
* - etc...
|
|
*
|
|
* Input Parmeters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_initialize(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
if (g_lesp_state.is_initialized)
|
|
{
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
ninfo("Esp8266 already initialized\n");
|
|
return 0;
|
|
}
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
|
|
ninfo("Initializing Esp8266...\n");
|
|
|
|
memset(g_lesp_state.sockets, 0, SOCKET_NBR * sizeof(lesp_socket_t));
|
|
|
|
if (sem_init(&g_lesp_state.worker.sem, 0, 0) < 0)
|
|
{
|
|
ninfo("Cannot create semaphore\n");
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret >= 0 && g_lesp_state.fd < 0)
|
|
{
|
|
g_lesp_state.fd = open(CONFIG_NETUTILS_ESP8266_DEV_PATH, O_RDWR);
|
|
}
|
|
|
|
if (ret >= 0 && g_lesp_state.fd < 0)
|
|
{
|
|
nerr("ERROR: Cannot open %s\n", CONFIG_NETUTILS_ESP8266_DEV_PATH);
|
|
ret = -1;
|
|
}
|
|
|
|
#ifdef CONFIG_SERIAL_TERMIOS
|
|
if (ret >= 0 && lesp_set_baudrate(CONFIG_NETUTILS_ESP8266_BAUDRATE) < 0)
|
|
{
|
|
nerr("ERROR: Cannot set baud rate %d\n", CONFIG_NETUTILS_ESP8266_BAUDRATE);
|
|
ret = -1;
|
|
}
|
|
#endif
|
|
|
|
if ((ret >= 0) && (g_lesp_state.worker.running == false))
|
|
{
|
|
ret = lesp_create_worker(CONFIG_NETUTILS_ESP8266_THREADPRIO);
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
ninfo("Esp8266 initialisation failed!\n");
|
|
ret = -1;
|
|
}
|
|
else
|
|
{
|
|
g_lesp_state.is_initialized = true;
|
|
ninfo("Esp8266 initialized\n");
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_soft_reset
|
|
*
|
|
* Description:
|
|
* reset esp8266 (command "AT+RST");
|
|
*
|
|
* Input Parmeters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_soft_reset(void)
|
|
{
|
|
int ret = 0;
|
|
int i;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
/* Rry to close opened reset */
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
|
|
for (i = 0; i < SOCKET_NBR; i++)
|
|
{
|
|
if ((g_lesp_state.sockets[i].flags & FLAGS_SOCK_USED) != 0)
|
|
{
|
|
set_sock_closed(i);
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
|
|
/* Leave time to close socket */
|
|
|
|
sleep(1);
|
|
|
|
/* Send reset */
|
|
|
|
lesp_send_cmd("AT+RST\r\n");
|
|
|
|
/* Leave time to reset */
|
|
|
|
sleep(1);
|
|
|
|
lesp_flush();
|
|
|
|
while (lesp_ask_ans_ok(lespTIMEOUT_MS, "ATE0\r\n") < 0)
|
|
{
|
|
sleep(1);
|
|
lesp_flush();
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+GMR\r\n");
|
|
}
|
|
|
|
/* Enable the module to act as a “Station” */
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CWMODE_CUR=1\r\n");
|
|
}
|
|
|
|
/* Enable the multi connection */
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPMUX=1\r\n");
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
ret = -1;
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_soft_reset
|
|
*
|
|
* Description:
|
|
* reset esp8266 (command "AT+RST");
|
|
*
|
|
* Input Parmeters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_ap_connect(const char *ssid_name, const char *ap_key, int timeout_s)
|
|
{
|
|
int ret = 0;
|
|
|
|
ninfo("Starting manual connect...\n");
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(timeout_s*1000, "AT+CWJAP=\"%s\",\"%s\"\r\n",
|
|
ssid_name, ap_key);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ninfo("Wifi connected\n");
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_ap_get
|
|
*
|
|
* Description:
|
|
* Read the current connected Access Point.
|
|
*
|
|
* Input Parmeters:
|
|
* None
|
|
*
|
|
* Output Parmeters:
|
|
* ap : details of connected acces point
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_ap_get(lesp_ap_t *ap)
|
|
{
|
|
int ret = 0;
|
|
ninfo("Get Access Point info...\n");
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_send_cmd("AT+CWJAP_CUR?\r\n");
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read(lespTIMEOUT_MS);
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ninfo("Read:%s\n", g_lesp_state.bufans);
|
|
ret = lesp_parse_cwjap_ans_line(g_lesp_state.bufans, ap);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Line badly formed.\n");
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read_ans_ok(lespTIMEOUT_MS);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Get access point information.\n");
|
|
return -1;
|
|
}
|
|
|
|
ninfo("Connected to %s\n", ap->ssid);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_get_net
|
|
*
|
|
* Description:
|
|
* Read the current network details.
|
|
*
|
|
* Input Parmeters:
|
|
* mode : lesp_eMODE_AP or lesp_eMODE_STATION
|
|
*
|
|
* Output Parmeters:
|
|
* ip : ip of interface
|
|
* mask : mask of interface (not implemented by esp8266)
|
|
* gw : gateway of interface (not implemented by esp8266)
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 in case of error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_get_net(lesp_mode_t mode, in_addr_t *ip, in_addr_t *mask, in_addr_t *gw)
|
|
{
|
|
int ret = 0;
|
|
ninfo("Get IP info...\n");
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_send_cmd("AT+CIP%s_CUR?\r\n",
|
|
(mode == lesp_eMODE_STATION)?"STA":"AP");
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read(lespTIMEOUT_MS);
|
|
}
|
|
if (ret >= 0)
|
|
{
|
|
ninfo("Read:%s\n", g_lesp_state.bufans);
|
|
|
|
ret = lesp_parse_cipxxx_ans_line(g_lesp_state.bufans, ip);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Line badly formed.\n");
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read(lespTIMEOUT_MS);
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ninfo("Read:%s\n", g_lesp_state.bufans);
|
|
|
|
ret = lesp_parse_cipxxx_ans_line(g_lesp_state.bufans, mask);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Line badly formed.\n");
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read(lespTIMEOUT_MS);
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ninfo("Read:%s\n", g_lesp_state.bufans);
|
|
|
|
ret = lesp_parse_cipxxx_ans_line(g_lesp_state.bufans, mask);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Line badly formed.\n");
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read_ans_ok(lespTIMEOUT_MS);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Get network information.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_set_net
|
|
*
|
|
* Description:
|
|
* It will set network ip of mode.
|
|
* Warning: use lesp_eMODE_STATION or lesp_eMODE_AP.
|
|
*
|
|
* Input Parmeters:
|
|
* mode : mode to configure.
|
|
* ip : ip of interface.
|
|
* mask : network mask of interface.
|
|
* gateway : gateway ip of network.
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_set_net(lesp_mode_t mode, in_addr_t ip, in_addr_t mask, in_addr_t gateway)
|
|
{
|
|
int ret = 0;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIP%s_CUR=\"%d.%d.%d.%d\","
|
|
"\"%d.%d.%d.%d\",\"%d.%d.%d.%d\"\r\n",
|
|
(mode == lesp_eMODE_STATION)?"STA":"AP",
|
|
*((uint8_t *)&(ip)+0), *((uint8_t *)&(ip)+1),
|
|
*((uint8_t *)&(ip)+2), *((uint8_t *)&(ip)+3),
|
|
*((uint8_t *)&(gateway)+0),
|
|
*((uint8_t *)&(gateway)+1),
|
|
*((uint8_t *)&(gateway)+2),
|
|
*((uint8_t *)&(gateway)+3),
|
|
*((uint8_t *)&(mask)+0), *((uint8_t *)&(mask)+1),
|
|
*((uint8_t *)&(mask)+2), *((uint8_t *)&(mask)+3));
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_set_dhcp
|
|
*
|
|
* Description:
|
|
* It will Enable or disable DHCP of mode.
|
|
*
|
|
* Input Parmeters:
|
|
* mode : mode to configure.
|
|
* enable : true for enable, false for disable.
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_set_dhcp(lesp_mode_t mode, bool enable)
|
|
{
|
|
int ret = 0;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CWDHCP_CUR=%d,%c\r\n",
|
|
mode, (enable)?'1':'0');
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_get_dhcp
|
|
*
|
|
* Description:
|
|
* It will get if DHCP is Enable or disable for each mode.
|
|
*
|
|
* Input Parmeters:
|
|
* ap_enable : true DHCP is enable in Access Point mode, false otherwise.
|
|
* sta_enable : true DHCP is enable in Station mode, false otherwise.
|
|
*
|
|
* Returned Value:
|
|
* 0 on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_get_dhcp(bool *ap_enable, bool *sta_enable)
|
|
{
|
|
int ret = 0;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("Get DHCP State...\n");
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_send_cmd("AT+CWDHCP_CUR?\r\n");
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read(lespTIMEOUT_MS);
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
char *ptr = g_lesp_state.bufans;
|
|
|
|
ninfo("Read:%s\n", ptr);
|
|
|
|
ptr = strchr(ptr, ':');
|
|
ptr++;
|
|
switch (*ptr - '0')
|
|
{
|
|
case 0:
|
|
*ap_enable = 0;
|
|
*sta_enable = 0;
|
|
break;
|
|
case 1:
|
|
*ap_enable = 1;
|
|
*sta_enable = 0;
|
|
break;
|
|
case 2:
|
|
*ap_enable = 0;
|
|
*sta_enable = 1;
|
|
break;
|
|
case 3:
|
|
*ap_enable = 1;
|
|
*sta_enable = 1;
|
|
break;
|
|
default:
|
|
nerr("ERROR: Line badly formed.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read_ans_ok(lespTIMEOUT_MS);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Get DHCP.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_list_access_points
|
|
*
|
|
* Description:
|
|
* Search all access points.
|
|
*
|
|
* Input Parmeters:
|
|
* cb : call back call for each access point found.
|
|
*
|
|
* Returned Value:
|
|
* Number access point(s) found on success, -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_list_access_points(lesp_cb_t cb)
|
|
{
|
|
lesp_ap_t ap;
|
|
int ret = 0;
|
|
int number = 0;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("List access point(s)...\n");
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT\r\n");
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_send_cmd("AT+CWLAP\r\n");
|
|
}
|
|
|
|
while (ret >= 0)
|
|
{
|
|
ret = lesp_read(lespTIMEOUT_MS_LISP_AP);
|
|
if (ret < 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ninfo("Read:%s\n", g_lesp_state.bufans);
|
|
|
|
if (strcmp(g_lesp_state.bufans, "OK") == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ret = lesp_parse_cwlap_ans_line(g_lesp_state.bufans, &ap);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Line badly formed.");
|
|
}
|
|
|
|
cb(&ap);
|
|
number++;
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: list access points.");
|
|
return -1;
|
|
}
|
|
|
|
ninfo("Access Point list finished with %d ap founds\n", number);
|
|
|
|
return number;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_security_to_str
|
|
*
|
|
* Description:
|
|
* return corresponding string of security enum.
|
|
*
|
|
* Input Parmeters:
|
|
* security : enum value of string
|
|
*
|
|
* Returned Value:
|
|
* String corresponding to security value.
|
|
*
|
|
****************************************************************************/
|
|
|
|
const char *lesp_security_to_str(lesp_security_t security)
|
|
{
|
|
switch (security)
|
|
{
|
|
case lesp_eSECURITY_NONE:
|
|
return "NONE";
|
|
case lesp_eSECURITY_WEP:
|
|
return "WEP";
|
|
case lesp_eSECURITY_WPA_PSK:
|
|
return "WPA_PSK";
|
|
case lesp_eSECURITY_WPA2_PSK:
|
|
return "WPA2_PSK";
|
|
case lesp_eSECURITY_WPA_WPA2_PSK:
|
|
return "WPA_WPA2_PSK";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_socket
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX socket for esp8266.
|
|
* socket() creates an endpoint for communication and returns a descriptor.
|
|
*
|
|
* Input Parameters:
|
|
* domain : only PF_INET is supported
|
|
* type : only SOCK_STREAM is supported
|
|
* protocol : only IPPROTO_TCP is supported
|
|
*
|
|
* Returned Value:
|
|
* A non-negative socket descriptor on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_socket(int domain, int type, int protocol)
|
|
{
|
|
int ret = 0;
|
|
int i = CON_NBR;
|
|
int flags = 0;
|
|
|
|
if (domain != PF_INET)
|
|
{
|
|
nerr("ERROR: only PF_INET Implemented!\n");
|
|
return -1;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case SOCK_STREAM:
|
|
flags |= FLAGS_SOCK_TYPE_TCP;
|
|
break;
|
|
case SOCK_DGRAM:
|
|
flags |= FLAGS_SOCK_TYPE_UDP;
|
|
break;
|
|
case -1:
|
|
flags |= FLAGS_SOCK_TYPE_SSL;
|
|
break;
|
|
default:
|
|
nerr("ERROR: Only SOCK_DGRAM and SOCK_STREAM Implemented!\n");
|
|
errno = ESOCKTNOSUPPORT;
|
|
return -1;
|
|
}
|
|
|
|
flags |= FLAGS_SOCK_USED;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
|
|
ret = -1;
|
|
if (!g_lesp_state.is_initialized)
|
|
{
|
|
ninfo("Esp8266 not initialized; can't list access points\n");
|
|
errno = ENETDOWN;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < CON_NBR; i++)
|
|
{
|
|
if ((g_lesp_state.sockets[i].flags & FLAGS_SOCK_USED) == 0)
|
|
{
|
|
g_lesp_state.sockets[i].flags = flags;
|
|
g_lesp_state.sockets[i].rcv_timeo.tv_sec = lespTIMEOUT_MS_RECV_S;
|
|
g_lesp_state.sockets[i].rcv_timeo.tv_nsec = 0;
|
|
ret = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
errno = EAGAIN;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_closesocket
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX close for esp8266.
|
|
* close socket creates with lesp_socket.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd : socket indentifer.
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_closesocket(int sockfd)
|
|
{
|
|
int ret = 0;
|
|
lesp_socket_t *sock = NULL;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("List access point(s)...\n");
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
sock = get_sock_protected(sockfd);
|
|
if (sock == NULL)
|
|
{
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPCLOSE=%d\r\n", sockfd);
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
set_sock_closed(sockfd);
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Close socket %d.\n", sockfd);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_bind
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX bind for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor of the socket to bind
|
|
* addr Socket local address
|
|
* addrlen Length of 'addr'
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_bind(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen)
|
|
{
|
|
int ret = 0;
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("Bind socket %d...\n", sockfd);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
nerr("ERROR: Not implemented %s\n", __func__);
|
|
errno = EIO;
|
|
ret = -1;
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_connect
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX connect for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor returned by socket()
|
|
* addr Server address (form depends on type of socket)
|
|
* addrlen Length of actual 'addr'
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen)
|
|
{
|
|
int ret = 0;
|
|
const char *proto_str;
|
|
lesp_socket_t *sock;
|
|
struct sockaddr_in *in;
|
|
unsigned short port;
|
|
in_addr_t ip;
|
|
|
|
in = (struct sockaddr_in *)addr;
|
|
port = ntohs(in->sin_port); /* e.g. htons(3490) */
|
|
ip = in->sin_addr.s_addr;
|
|
|
|
DEBUGASSERT(in->sin_family == AF_INET);
|
|
DEBUGASSERT(addrlen == sizeof(struct sockaddr_in));
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("Connect %d...\n", sockfd);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
sock = get_sock(sockfd);
|
|
if (sock == NULL)
|
|
{
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
switch (FLAGS_SOCK_TYPE_MASK & sock->flags)
|
|
{
|
|
case FLAGS_SOCK_TYPE_TCP:
|
|
proto_str = "TCP";
|
|
break;
|
|
case FLAGS_SOCK_TYPE_UDP:
|
|
proto_str = "UDP";
|
|
break;
|
|
case FLAGS_SOCK_TYPE_SSL:
|
|
proto_str = "SSL";
|
|
break;
|
|
default:
|
|
errno = ESOCKTNOSUPPORT;
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPSTART=%d,\"%s\","
|
|
"\"%d.%d.%d.%d\",%d\r\n", sockfd, proto_str,
|
|
*((uint8_t *)&(ip)+0), *((uint8_t *)&(ip)+1),
|
|
*((uint8_t *)&(ip)+2), *((uint8_t *)&(ip)+3), port);
|
|
if (ret < 0)
|
|
{
|
|
errno = EIO;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Connect socket %d.\n", sockfd);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_listen
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX listen for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor returned by socket()
|
|
* backlog The maximum length the queue of pending connections may grow.
|
|
* If a connection request arrives with the queue full, the client
|
|
* may receive an error with an indication of ECONNREFUSED or,
|
|
* if the underlying protocol supports retransmission, the request
|
|
* may be ignored so that retries succeed.
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_listen(int sockfd, int backlog)
|
|
{
|
|
int ret = 0;
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("Connect %d...\n", sockfd);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
nerr("ERROR: Not implemented %s\n", __func__);
|
|
errno = EIO;
|
|
ret = -1;
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_accept
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX accept for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor returned by socket()
|
|
* addr Receives the address of the connecting client
|
|
* addrlen Input: allocated size of 'addr', Return: returned size of 'addr'
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|
{
|
|
int ret = 0;
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("Connect %d...\n", sockfd);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
nerr("ERROR: Not implemented %s\n", __func__);
|
|
errno = EIO;
|
|
ret = -1;
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_send
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX send for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor returned by socket()
|
|
* buf Data to send
|
|
* len Length of data to send
|
|
* flags Send flags
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
ssize_t lesp_send(int sockfd, FAR const uint8_t *buf, size_t len, int flags)
|
|
{
|
|
int ret = 0;
|
|
lesp_socket_t *sock = NULL;
|
|
|
|
UNUSED(flags);
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("Send %d bytes in %d socket...\n", len, sockfd);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
sock = get_sock_protected(sockfd);
|
|
if (sock == NULL)
|
|
{
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPSEND=%d,%d\r\n", sockfd, len);
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ninfo("Sending in socket %d, %d bytes\n", sockfd, len);
|
|
ret = write(g_lesp_state.fd, buf, len);
|
|
}
|
|
|
|
while (ret >= 0)
|
|
{
|
|
char * ptr = g_lesp_state.bufans;
|
|
ret = lesp_read(lespTIMEOUT_MS);
|
|
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
while ((*ptr != 0) && (*ptr != 'S'))
|
|
{
|
|
ptr++;
|
|
}
|
|
|
|
if (*ptr == 'S')
|
|
{
|
|
if (strcmp(ptr, "SEND OK") == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Cannot send in socket %d, %d bytes\n", sockfd, len);
|
|
return -1;
|
|
}
|
|
|
|
ninfo("Sent\n");
|
|
|
|
return len;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_recv
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX recv for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor returned by socket()
|
|
* buf Data to receive
|
|
* len Length of data to receive
|
|
* flags Send flags
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
ssize_t lesp_recv(int sockfd, FAR uint8_t *buf, size_t len, int flags)
|
|
{
|
|
int ret = 0;
|
|
lesp_socket_t *sock;
|
|
sem_t sem;
|
|
|
|
if (sem_init(&sem, 0, 0) < 0)
|
|
{
|
|
ninfo("Cannot create semaphore\n");
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
|
|
UNUSED(flags);
|
|
|
|
sock = get_sock(sockfd);
|
|
if (sock == NULL)
|
|
{
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret >= 0 && sock->inndx == sock->outndx)
|
|
{
|
|
struct timespec ts;
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
|
|
{
|
|
ret = -1;
|
|
}
|
|
else
|
|
{
|
|
ts.tv_sec += sock->rcv_timeo.tv_sec;
|
|
ts.tv_nsec += sock->rcv_timeo.tv_nsec;
|
|
if (ts.tv_nsec >= 1000000000)
|
|
{
|
|
ts.tv_sec++;
|
|
ts.tv_nsec -= 1000000000;
|
|
}
|
|
|
|
sock->sem = &sem;
|
|
|
|
while (ret >= 0 && sock->inndx == sock->outndx)
|
|
{
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
ret = sem_timedwait(&sem, &ts);
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
}
|
|
|
|
sock->sem = NULL;
|
|
}
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = 0;
|
|
while (ret < len && sock->outndx != sock->inndx)
|
|
{
|
|
/* Remove one byte from the circular buffer */
|
|
|
|
int ndx = sock->outndx;
|
|
*buf++ = sock->rxbuf[ndx];
|
|
|
|
/* Increment the circular buffer 'outndx' */
|
|
|
|
if (++ndx >= SOCKET_FIFO_SIZE)
|
|
{
|
|
ndx -= SOCKET_FIFO_SIZE;
|
|
}
|
|
|
|
sock->outndx = ndx;
|
|
|
|
/* Increment the count of bytes returned */
|
|
|
|
ret++;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_setsockopt
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX setsockopt for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor returned by socket()
|
|
* level Protocol level to set the option
|
|
* option identifies the option to set
|
|
* value Points to the argument value
|
|
* value_len The length of the argument value
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_setsockopt(int sockfd, int level, int option,
|
|
FAR const void *value, socklen_t value_len)
|
|
{
|
|
int ret = 0;
|
|
lesp_socket_t *sock;
|
|
|
|
if (level != SOL_SOCKET)
|
|
{
|
|
nerr("ERROR: %s:Not implemented level:%d\n", __func__, level);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&g_lesp_state.worker.mutex);
|
|
|
|
sock = get_sock(sockfd);
|
|
if (sock == NULL)
|
|
{
|
|
ret = -1;
|
|
}
|
|
else
|
|
{
|
|
switch (option)
|
|
{
|
|
case SO_RCVTIMEO:
|
|
if (value_len == sizeof(struct timeval))
|
|
{
|
|
sock->rcv_timeo.tv_sec = ((struct timeval *)(value))->tv_sec;
|
|
sock->rcv_timeo.tv_nsec = ((struct timeval *)(value))->tv_usec;
|
|
sock->rcv_timeo.tv_nsec *= 1000; /* tv_usec to tv_nsec */
|
|
}
|
|
else
|
|
{
|
|
nerr("ERROR: Wrong size:%d\n", level);
|
|
errno = EINVAL;
|
|
ret = -1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
nerr("ERROR: Not implemented %s\n", __func__);
|
|
errno = EINVAL;
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.worker.mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_getsockopt
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX getsockopt for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* sockfd Socket descriptor returned by socket()
|
|
* level Protocol level to set the option
|
|
* option identifies the option to set
|
|
* value Points to the argument value
|
|
* value_len The length of the argument value
|
|
*
|
|
* Returned Value:
|
|
* A 0 on success; -1 on error.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int lesp_getsockopt(int sockfd, int level, int option, FAR void *value,
|
|
FAR socklen_t *value_len)
|
|
{
|
|
int ret = 0;
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("getsockopt on %d socket...\n", sockfd);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
nerr("ERROR: Not implemented %s\n", __func__);
|
|
errno = EIO;
|
|
ret = -1;
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lesp_gethostbyname
|
|
*
|
|
* Description:
|
|
* Equivalent of POSIX gethostbyname for esp8266.
|
|
*
|
|
* Input Parameters:
|
|
* name - The name of the host to find.
|
|
*
|
|
* Returned Value:
|
|
* Upon successful completion, this function will return a pointer to a
|
|
* hostent structure if the requested entry was found, and a null pointer
|
|
* if the end of the database was reached or the requested entry was not
|
|
* found.
|
|
*
|
|
* Upon unsuccessful completion, gethostbyname() return NULL.
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR struct hostent *lesp_gethostbyname(FAR const char *hostname)
|
|
{
|
|
int ret = 0;
|
|
|
|
memset(&g_lesp_state.hostent, 0, sizeof(g_lesp_state.hostent));
|
|
|
|
g_lesp_state.hostent.h_addr_list = (char**)&g_lesp_state.h_addr_list_buf;
|
|
g_lesp_state.hostent.h_addrtype = AF_INET;
|
|
g_lesp_state.hostent.h_length = sizeof(struct sockaddr_in);
|
|
|
|
g_lesp_state.h_addr_list_buf[0] = &g_lesp_state.in_addr;
|
|
g_lesp_state.h_addr_list_buf[1] = NULL;
|
|
|
|
pthread_mutex_lock(&g_lesp_state.mutex);
|
|
|
|
ninfo("Get host by name '%s' ...\n", hostname);
|
|
|
|
ret = lesp_check();
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_send_cmd("AT+CIPDOMAIN=\"%s\"\r\n", hostname);
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read(lespTIMEOUT_MS);
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
ninfo("Read:%s\n", g_lesp_state.bufans);
|
|
|
|
ret = lesp_parse_cwdomain_ans_line(g_lesp_state.bufans,
|
|
&g_lesp_state.in_addr);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Line badly formed.\n");
|
|
errno = EIO;
|
|
}
|
|
}
|
|
if (ret >= 0)
|
|
{
|
|
ret = lesp_read_ans_ok(lespTIMEOUT_MS);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_lesp_state.mutex);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Get host by name.\n");
|
|
return NULL;
|
|
}
|
|
|
|
return &g_lesp_state.hostent;
|
|
}
|
|
|
|
#endif /* CONFIG_NETUTILS_ESP8266 */
|