diff --git a/.gitignore b/.gitignore index 5e14a6fa..b72a1113 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ espfs/espfstest/*.o espfs/espfstest/espfstest *.DS_Store html_compressed/ -libwebpages-espfs.a \ No newline at end of file +libwebpages-espfs.a +*~ diff --git a/Makefile b/Makefile index 59a6db62..5d465bd3 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,8 @@ XTENSA_TOOLS_ROOT ?= # base directory of the ESP8266 SDK package, absolute # Only used for the non-FreeRTOS build -SDK_BASE ?= /opt/Espressif/ESP8266_SDK +#SDK_BASE ?= /opt/Espressif/ESP8266_SDK +SDK_BASE ?= $(abspath ../esp_iot_sdk_v2.0.0.p1) # Base directory of the ESP8266 FreeRTOS SDK package, absolute # Only used for the FreeRTOS build diff --git a/core/httpd-freertos.c b/core/httpd-freertos.c index a1de29fa..128cbf21 100644 --- a/core/httpd-freertos.c +++ b/core/httpd-freertos.c @@ -32,7 +32,7 @@ struct RtosConnType{ static RtosConnType rconn[HTTPD_MAX_CONNECTIONS]; -int ICACHE_FLASH_ATTR httpdPlatSendData(ConnTypePtr conn, char *buff, int len) { +int ICACHE_FLASH_ATTR httpdPlatSendData(ConnTypePtr conn, const char *buff, int len) { conn->needWriteDoneNotif=1; return (write(conn->fd, buff, len)>=0); } diff --git a/core/httpd-nonos.c b/core/httpd-nonos.c index 2fc73145..080b8f34 100644 --- a/core/httpd-nonos.c +++ b/core/httpd-nonos.c @@ -15,7 +15,8 @@ static esp_tcp httpdTcp; static void ICACHE_FLASH_ATTR platReconCb(void *arg, sint8 err) { - //Yeah, this is pretty useless... + ConnTypePtr conn=arg; + httpdReconCb(conn, (char*)conn->proto.tcp->remote_ip, conn->proto.tcp->remote_port, err); } static void ICACHE_FLASH_ATTR platDisconCb(void *arg) { @@ -46,7 +47,7 @@ static void ICACHE_FLASH_ATTR platConnCb(void *arg) { } -int ICACHE_FLASH_ATTR httpdPlatSendData(ConnTypePtr conn, char *buff, int len) { +int ICACHE_FLASH_ATTR httpdPlatSendData(ConnTypePtr conn, const char *buff, int len) { int r; r=espconn_sent(conn, (uint8_t*)buff, len); return (r>=0); @@ -73,4 +74,4 @@ void ICACHE_FLASH_ATTR httpdPlatInit(int port, int maxConnCt) { } -#endif \ No newline at end of file +#endif diff --git a/core/httpd-platform.h b/core/httpd-platform.h index e3e5140a..e2a8bb63 100644 --- a/core/httpd-platform.h +++ b/core/httpd-platform.h @@ -1,7 +1,7 @@ #ifndef HTTPD_PLATFORM_H #define HTTPD_PLATFORM_H -int httpdPlatSendData(ConnTypePtr conn, char *buff, int len); +int httpdPlatSendData(ConnTypePtr conn, const char *buff, int len); void httpdPlatDisconnect(ConnTypePtr conn); void httpdPlatDisableTimeout(ConnTypePtr conn); void httpdPlatInit(int port, int maxConnCt); diff --git a/core/httpd.c b/core/httpd.c index 28a1ad03..3f6b9c34 100644 --- a/core/httpd.c +++ b/core/httpd.c @@ -51,6 +51,7 @@ struct HttpdPriv { int headPos; char *sendBuff; int sendBuffLen; + int sendBuffMax; char *chunkHdr; HttpSendBacklogItem *sendBacklog; int sendBacklogSize; @@ -98,23 +99,11 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { return mimeTypes[i].mimetype; } -//Looks up the connData info for a specific connection -static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(ConnTypePtr conn, char *remIp, int remPort) { - for (int i=0; iremote_port == remPort && - memcmp(connData[i]->remote_ip, remIp, 4) == 0) { - connData[i]->conn=conn; - return connData[i]; - } - } - //Shouldn't happen. - httpd_printf("*** Unknown connection %d.%d.%d.%d:%d\n", remIp[0]&0xff, remIp[1]&0xff, remIp[2]&0xff, remIp[3]&0xff, remPort); - httpdPlatDisconnect(conn); - return NULL; -} - //Retires a connection for re-use static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) { + if (conn==NULL) return; + if (conn->conn && conn->conn->reverse == conn) + conn->conn->reverse = NULL; // break reverse link if (conn->priv->sendBacklog!=NULL) { HttpSendBacklogItem *i, *j; i=conn->priv->sendBacklog; @@ -124,13 +113,15 @@ static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) { free(j); } while (i!=NULL); } - if (conn->post->buff!=NULL) free(conn->post->buff); - if (conn->post!=NULL) free(conn->post); + if (conn->post!=NULL) { + if (conn->post->buff!=NULL) free(conn->post->buff); + free(conn->post); + } if (conn->priv!=NULL) free(conn->priv); - if (conn) free(conn); for (int i=0; ipriv->flags&=~HFL_CHUNKED; } +//Setup a send buffer +void ICACHE_FLASH_ATTR httpdSetSendBuffer(HttpdConnData *conn, char *buff, short max) +{ + conn->priv->sendBuff = buff; + conn->priv->sendBuffLen = 0; + conn->priv->sendBuffMax = max; +} + //Start the response headers. void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code) { char buff[256]; int l; - l=sprintf(buff, "HTTP/1.%d %d OK\r\nServer: esp8266-httpd/"HTTPDVER"\r\n%s\r\n", + l=sprintf(buff, "HTTP/1.%d %d OK\r\nServer: esp8266-httpd/"HTTPDVER"\r\n", (conn->priv->flags&HFL_HTTP11)?1:0, - code, - (conn->priv->flags&HFL_CHUNKED)?"Transfer-Encoding: chunked":"Connection: close"); + code); httpdSend(conn, buff, l); + if (code != 101) { + l=sprintf(buff, "%s\r\n", + (conn->priv->flags&HFL_CHUNKED)?"Transfer-Encoding: chunked":"Connection: close"); + httpdSend(conn, buff, l); + } } //Send a http header. @@ -350,15 +353,15 @@ int ICACHE_FLASH_ATTR cgiRedirectApClientToHostname(HttpdConnData *connData) { int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) { if (conn->conn==NULL) return 0; if (len<0) len=strlen(data); - if (len==0) return 0; + if (len==0) return 1; if (conn->priv->flags&HFL_CHUNKED && conn->priv->flags&HFL_SENDINGBODY && conn->priv->chunkHdr==NULL) { - if (conn->priv->sendBuffLen+len+6>MAX_SENDBUFF_LEN) return 0; + if (conn->priv->sendBuffLen+len+6>conn->priv->sendBuffMax) return 0; //Establish start of chunk conn->priv->chunkHdr=&conn->priv->sendBuff[conn->priv->sendBuffLen]; strcpy(conn->priv->chunkHdr, "0000\r\n"); conn->priv->sendBuffLen+=6; } - if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) return 0; + if (conn->priv->sendBuffLen+len>conn->priv->sendBuffMax) return 0; memcpy(conn->priv->sendBuff+conn->priv->sendBuffLen, data, len); conn->priv->sendBuffLen+=len; return 1; @@ -374,7 +377,7 @@ static char ICACHE_FLASH_ATTR httpdHexNibble(int val) { //are doing! Also, if you do set conn->cgi to NULL to indicate the connection is closed, do it BEFORE //calling this. void ICACHE_FLASH_ATTR httpdFlushSendBuffer(HttpdConnData *conn) { - int r, len; + int len; if (conn->conn==NULL) return; if (conn->priv->chunkHdr!=NULL) { //We're sending chunked data, and the chunk needs fixing up. @@ -395,22 +398,28 @@ void ICACHE_FLASH_ATTR httpdFlushSendBuffer(HttpdConnData *conn) { strcpy(&conn->priv->sendBuff[conn->priv->sendBuffLen], "0\r\n\r\n"); conn->priv->sendBuffLen+=5; } - if (conn->priv->sendBuffLen!=0) { - r=httpdPlatSendData(conn->conn, conn->priv->sendBuff, conn->priv->sendBuffLen); - if (!r) { + if (!httpdUnbufferedSend(conn, conn->priv->sendBuff, conn->priv->sendBuffLen)) { + httpd_printf("Httpd: UnbufferedSend failed!\n"); + } + conn->priv->sendBuffLen=0; +} + +int ICACHE_FLASH_ATTR httpdUnbufferedSend(HttpdConnData *conn, const char *data, int len) { + if (len!=0) { + if (!httpdPlatSendData(conn->conn, data, len)) { + httpd_printf("Httpd: queuing %d byte buffer\n", len); //Can't send this for some reason. Dump packet in backlog, we can send it later. - if (conn->priv->sendBacklogSize+conn->priv->sendBuffLen>MAX_BACKLOG_SIZE) { - httpd_printf("Httpd: Backlog: Exceeded max backlog size, dropped %d bytes instead of sending them.\n", conn->priv->sendBuffLen); - conn->priv->sendBuffLen=0; - return; + if (conn->priv->sendBacklogSize+len>MAX_BACKLOG_SIZE) { + httpd_printf("Httpd: Backlog: Exceeded max backlog size, dropped %d bytes instead of sending them.\n", len); + return 0; } - HttpSendBacklogItem *i=malloc(sizeof(HttpSendBacklogItem)+conn->priv->sendBuffLen); + HttpSendBacklogItem *i=malloc(sizeof(HttpSendBacklogItem)+len); if (i==NULL) { httpd_printf("Httpd: Backlog: malloc failed, out of memory!\n"); - return; + return 0; } - memcpy(i->data, conn->priv->sendBuff, conn->priv->sendBuffLen); - i->len=conn->priv->sendBuffLen; + memcpy(i->data, data, len); + i->len=len; i->next=NULL; if (conn->priv->sendBacklog==NULL) { conn->priv->sendBacklog=i; @@ -419,10 +428,10 @@ void ICACHE_FLASH_ATTR httpdFlushSendBuffer(HttpdConnData *conn) { while (e->next!=NULL) e=e->next; e->next=i; } - conn->priv->sendBacklogSize+=conn->priv->sendBuffLen; + conn->priv->sendBacklogSize+=len; } - conn->priv->sendBuffLen=0; } + return 1; } void ICACHE_FLASH_ATTR httpdCgiIsDone(HttpdConnData *conn) { @@ -445,16 +454,24 @@ void ICACHE_FLASH_ATTR httpdCgiIsDone(HttpdConnData *conn) { } } +// store the cgi callback reason and call the user's function +static int ICACHE_FLASH_ATTR callCGI(HttpdConnData *connData, int reason, int value) { + connData->cgiReason = reason; + connData->cgiValue = value; + return connData->cgi(connData); +} + //Callback called when the data on a socket has been successfully //sent. void ICACHE_FLASH_ATTR httpdSentCb(ConnTypePtr rconn, char *remIp, int remPort) { int r; - HttpdConnData *conn=httpdFindConnData(rconn, remIp, remPort); + HttpdConnData *conn=rconn->reverse; char *sendBuff; if (conn==NULL) return; if (conn->priv->sendBacklog!=NULL) { + httpd_printf("Httpd: sending %d byte queued buffer\n", conn->priv->sendBacklog->len); //We have some backlog to send first. HttpSendBacklogItem *next=conn->priv->sendBacklog->next; httpdPlatSendData(conn->conn, conn->priv->sendBacklog->data, conn->priv->sendBacklog->len); @@ -476,7 +493,9 @@ void ICACHE_FLASH_ATTR httpdSentCb(ConnTypePtr rconn, char *remIp, int remPort) sendBuff=malloc(MAX_SENDBUFF_LEN); conn->priv->sendBuff=sendBuff; conn->priv->sendBuffLen=0; - r=conn->cgi(conn); //Execute cgi fn. + conn->priv->sendBuffMax = MAX_SENDBUFF_LEN; + + r=callCGI(conn, CGI_CB_SENT, 0); //Execute cgi fn. if (r==HTTPD_CGI_DONE) { httpdCgiIsDone(conn); } @@ -510,7 +529,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) { if (builtInUrls[i].url[strlen(builtInUrls[i].url)-1]=='*' && strncmp(builtInUrls[i].url, conn->url, strlen(builtInUrls[i].url)-1)==0) match=1; if (match) { - httpd_printf("Is url index %d\n", i); +// httpd_printf("Is url index %d\n", i); conn->cgiData=NULL; conn->cgi=builtInUrls[i].cgiCb; conn->cgiArg=builtInUrls[i].cgiArg; @@ -527,7 +546,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) { //Okay, we have a CGI function that matches the URL. See if it wants to handle the //particular URL we're supposed to handle. - r=conn->cgi(conn); + r=callCGI(conn, CGI_CB_RECV, 0); if (r==HTTPD_CGI_MORE) { //Yep, it's happy to do so and has more data to send. if (conn->recvHdl) { @@ -581,7 +600,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { e++; //Skip to protocol indicator while (*e==' ') e++; //Skip spaces. //If HTTP/1.1, note that and set chunked encoding - if (strcasecmp(e, "HTTP/1.1")==0) conn->priv->flags|=HFL_HTTP11|HFL_CHUNKED; + if (strcasecmp(e, "HTTP/1.1")==0) conn->priv->flags|=HFL_HTTP11; // BUG -- dbetz |HFL_CHUNKED; httpd_printf("URL = %s\n", conn->url); //Parse out the URL part before the GET parameters. @@ -631,14 +650,15 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { //Callback called when there's data available on a socket. -void httpdRecvCb(ConnTypePtr rconn, char *remIp, int remPort, char *data, unsigned short len) { +void ICACHE_FLASH_ATTR httpdRecvCb(ConnTypePtr rconn, char *remIp, int remPort, char *data, unsigned short len) { int x, r; char *p, *e; char *sendBuff=malloc(MAX_SENDBUFF_LEN); - HttpdConnData *conn=httpdFindConnData(rconn, remIp, remPort); + HttpdConnData *conn=rconn->reverse; if (conn==NULL) return; conn->priv->sendBuff=sendBuff; conn->priv->sendBuffLen=0; + conn->priv->sendBuffMax = MAX_SENDBUFF_LEN; //This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are: //<0 (-1): Post len unknown because we're still receiving headers @@ -688,7 +708,7 @@ void httpdRecvCb(ConnTypePtr rconn, char *remIp, int remPort, char *data, unsign conn->post->buff[conn->post->buffLen]=0; //zero-terminate, in case the cgi handler knows it can use strings //Process the data if (conn->cgi) { - r=conn->cgi(conn); + r=callCGI(conn, CGI_CB_RECV, 0); if (r==HTTPD_CGI_DONE) { httpdCgiIsDone(conn); } @@ -721,14 +741,23 @@ void httpdRecvCb(ConnTypePtr rconn, char *remIp, int remPort, char *data, unsign //The platform layer should ALWAYS call this function, regardless if the connection is closed by the server //or by the client. void ICACHE_FLASH_ATTR httpdDisconCb(ConnTypePtr rconn, char *remIp, int remPort) { - HttpdConnData *hconn=httpdFindConnData(rconn, remIp, remPort); + HttpdConnData *hconn=rconn->reverse; if (hconn==NULL) return; httpd_printf("Pool slot %d: socket closed.\n", hconn->slot); hconn->conn=NULL; //indicate cgi the connection is gone - if (hconn->cgi) hconn->cgi(hconn); //Execute cgi fn if needed + if (hconn->cgi) callCGI(hconn, CGI_CB_DISCONNECT, 0); //Execute cgi fn if needed httpdRetireConn(hconn); } +//This seems to be called on connection failure +void ICACHE_FLASH_ATTR httpdReconCb(ConnTypePtr rconn, char *remIp, int remPort, int err) { + HttpdConnData *hconn=rconn->reverse; + if (hconn==NULL) return; + httpd_printf("Pool slot %d: socket closed.\n", hconn->slot); + hconn->conn=NULL; //indicate cgi the connection is gone + if (hconn->cgi) callCGI(hconn, CGI_CB_RECONNECT, err); //Execute cgi fn if needed + httpdRetireConn(hconn); +} int ICACHE_FLASH_ATTR httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort) { int i; @@ -744,6 +773,7 @@ int ICACHE_FLASH_ATTR httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort) connData[i]->priv=malloc(sizeof(HttpdPriv)); memset(connData[i]->priv, 0, sizeof(HttpdPriv)); connData[i]->conn=conn; + conn->reverse = connData[i]; connData[i]->slot=i; connData[i]->priv->headPos=0; connData[i]->post=malloc(sizeof(HttpdPostData)); diff --git a/espfs/espfs.c b/espfs/espfs.c index b5cf35ee..de8c11c4 100644 --- a/espfs/espfs.c +++ b/espfs/espfs.c @@ -148,7 +148,7 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { return NULL; } if (h.flags&FLAG_LASTFILE) { - httpd_printf("End of image.\n"); +// httpd_printf("End of image.\n"); return NULL; } //Grab the name of the file. diff --git a/include/cgiwifi.h b/include/cgiwifi.h index 21ef2fc5..a3a9da6e 100644 --- a/include/cgiwifi.h +++ b/include/cgiwifi.h @@ -3,6 +3,15 @@ #include "httpd.h" +//WiFi access point data +typedef struct { + char ssid[32]; + char bssid[8]; + int channel; + char rssi; + char enc; +} ApData; + int cgiWiFiScan(HttpdConnData *connData); int tplWlan(HttpdConnData *connData, char *token, void **arg); int cgiWiFi(HttpdConnData *connData); @@ -10,4 +19,11 @@ int cgiWiFiConnect(HttpdConnData *connData); int cgiWiFiSetMode(HttpdConnData *connData); int cgiWiFiConnStatus(HttpdConnData *connData); -#endif \ No newline at end of file +int wifiJoin(char *ssid, char *passwd); + +int cgiWiFiStartScan(void (*callback)(void *data, int count), void *data); +int cgiWiFiScanDone(void); +ApData *cgiWiFiScanResult(int n); +int cgiWifiScanResultCount(void); + +#endif diff --git a/include/esp8266.h b/include/esp8266.h index 7b685e45..bc7bc310 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -31,4 +31,18 @@ #include "platform.h" #include "espmissingincludes.h" +#if 0 +void *ets_memcpy(void *dest, const void *src, size_t n); +void *ets_memset(void *s, int c, size_t n); +int ets_strcmp(const char *s1, const char *s2); +int ets_strncmp(const char *s1, const char *s2, int len); +size_t ets_strlen(const char *s); +char *ets_strcpy(char *dest, const char *src); +char *ets_strstr(const char *haystack, const char *needle); +int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +int strcasecmp(const char *a, const char *b); +void *pvPortMalloc(size_t xWantedSize, const char *file, int line); +void vPortFree(void *ptr, const char *file, int line); +#endif diff --git a/include/espmissingincludes.h b/include/espmissingincludes.h index 3126c95c..b6aaa0c2 100644 --- a/include/espmissingincludes.h +++ b/include/espmissingincludes.h @@ -2,6 +2,7 @@ #define ESPMISSINGINCLUDES_H #include +#include #include @@ -14,8 +15,8 @@ int strcasecmp(const char *a, const char *b); typedef struct espconn espconn; int atoi(const char *nptr); -void ets_install_putc1(void *routine); -void ets_isr_attach(int intr, void *handler, void *arg); +//void ets_install_putc1(void *routine); +//void ets_isr_attach(int intr, void *handler, void *arg); void ets_isr_mask(unsigned intr); void ets_isr_unmask(unsigned intr); int ets_memcmp(const void *s1, const void *s2, size_t n); @@ -25,11 +26,11 @@ int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (pri int ets_str2macaddr(void *, void *); int ets_strcmp(const char *s1, const char *s2); char *ets_strcpy(char *dest, const char *src); -size_t ets_strlen(const char *s); -int ets_strncmp(const char *s1, const char *s2, int len); +//size_t ets_strlen(const char *s); +//int ets_strncmp(const char *s1, const char *s2, int len); char *ets_strncpy(char *dest, const char *src, size_t n); char *ets_strstr(const char *haystack, const char *needle); -void ets_timer_arm_new(os_timer_t *a, int b, int c, int isMstimer); +//void ets_timer_arm_new(os_timer_t *a, int b, int c, int isMstimer); void ets_timer_disarm(os_timer_t *a); void ets_timer_setfn(os_timer_t *t, ETSTimerFunc *fn, void *parg); void ets_update_cpu_frequency(int freqmhz); @@ -37,12 +38,12 @@ void *os_memmove(void *dest, const void *src, size_t n); int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4))); int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -void uart_div_modify(int no, unsigned int freq); +//void uart_div_modify(int no, unsigned int freq); uint8 wifi_get_opmode(void); uint32 system_get_time(); int rand(void); void ets_bzero(void *s, size_t n); -void ets_delay_us(int ms); +//void ets_delay_us(int ms); //Hack: this is defined in SDK 1.4.0 and undefined in 1.3.0. It's only used for this, the symbol itself //has no meaning here. @@ -54,9 +55,9 @@ void vPortFree(void *ptr); void *vPortMalloc(size_t xWantedSize); void pvPortFree(void *ptr); #else -void *pvPortMalloc(size_t xWantedSize, const char *file, int line); -void *pvPortZalloc(size_t, const char *file, int line); -void vPortFree(void *ptr, const char *file, int line); +//void *pvPortMalloc(size_t xWantedSize, const char *file, int line); +//void *pvPortZalloc(size_t, const char *file, int line); +//void vPortFree(void *ptr, const char *file, int line); void *vPortMalloc(size_t xWantedSize, const char *file, int line); void pvPortFree(void *ptr, const char *file, int line); #endif @@ -72,6 +73,11 @@ void pvPortFree(void *ptr, const char *file, int line); } while (0) #endif +int ets_vsprintf(char *str, const char *format, va_list argptr); +int ets_vsnprintf(char *buffer, size_t sizeOfBuffer, const char *format, va_list argptr); +int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__((format(printf, 3, 4))); + #endif #endif + diff --git a/include/httpd.h b/include/httpd.h index 20f6ac58..b26c2aec 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -15,6 +15,13 @@ typedef struct HttpdPriv HttpdPriv; typedef struct HttpdConnData HttpdConnData; typedef struct HttpdPostData HttpdPostData; +enum { + CGI_CB_RECV, + CGI_CB_SENT, + CGI_CB_DISCONNECT, + CGI_CB_RECONNECT // connection failed? +}; + typedef int (* cgiSendCallback)(HttpdConnData *connData); typedef int (* cgiRecvHandler)(HttpdConnData *connData, char *data, int len); @@ -29,7 +36,9 @@ struct HttpdConnData { void *cgiData; // Opaque data pointer for the CGI function char *hostName; // Host name field of request HttpdPriv *priv; // Opaque pointer to data for internal httpd housekeeping - cgiSendCallback cgi; // CGI function pointer + int cgiReason; // Reason cgi is being called + int cgiValue; // Value associated with the reason (usually an error code) + cgiSendCallback cgi; // CGI function pointer cgiRecvHandler recvHdl; // Handler for data received after headers, if any HttpdPostData *post; // POST data structure int remote_port; // Remote TCP port @@ -69,13 +78,17 @@ void httpdHeader(HttpdConnData *conn, const char *field, const char *val); void httpdEndHeaders(HttpdConnData *conn); int httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen); int httpdSend(HttpdConnData *conn, const char *data, int len); +int httpdUnbufferedSend(HttpdConnData *conn, const char *data, int len); +void httpdSetSendBuffer(HttpdConnData *conn, char *buff, short max); void httpdFlushSendBuffer(HttpdConnData *conn); +void httpdCgiIsDone(HttpdConnData *conn); //Platform dependent code should call these. void httpdSentCb(ConnTypePtr conn, char *remIp, int remPort); void httpdRecvCb(ConnTypePtr conn, char *remIp, int remPort, char *data, unsigned short len); void httpdDisconCb(ConnTypePtr conn, char *remIp, int remPort); +void httpdReconCb(ConnTypePtr conn, char *remIp, int remPort, int err); int httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort); -#endif \ No newline at end of file +#endif diff --git a/util/cgiwebsocket.c b/util/cgiwebsocket.c index adf6c189..dc555a02 100644 --- a/util/cgiwebsocket.c +++ b/util/cgiwebsocket.c @@ -126,19 +126,20 @@ int ICACHE_FLASH_ATTR cgiWebsockBroadcast(char *resource, char *data, int len, i //will not have an associated send buffer. This means httpdSend will write to a dangling pointer! //Disabled for now. If you really need this, open an issue on github or otherwise poke me and I'll //see what I can do. -/* - Websock *lw=llStart; int ret=0; +#if 1 + Websock *lw=llStart; while (lw!=NULL) { + char sendBuff[1024]; + httpdSetSendBuffer(lw->conn, sendBuff, sizeof(sendBuff)); if (strcmp(lw->conn->url, resource)==0) { cgiWebsocketSend(lw, data, len, flags); ret++; } lw=lw->priv->next; } +#endif return ret; -*/ - return 0; } diff --git a/util/cgiwifi.c b/util/cgiwifi.c index 4e064325..c7004cca 100644 --- a/util/cgiwifi.c +++ b/util/cgiwifi.c @@ -18,15 +18,6 @@ Cgi/template routines for the /wifi url. //Enable this to disallow any changes in AP settings //#define DEMO_MODE -//WiFi access point data -typedef struct { - char ssid[32]; - char bssid[8]; - int channel; - char rssi; - char enc; -} ApData; - //Scan result typedef struct { char scanInProgress; //if 1, don't access the underlying stuff from the webpage. @@ -37,6 +28,23 @@ typedef struct { //Static scan status storage. static ScanResultData cgiWifiAps; +//Callback for outside scan clients +static void (*scanCallback)(void *data, int count) = NULL; +static void *scanCallbackData; + +#ifndef DEMO_MODE + +typedef struct { + os_timer_t timer; + char inProgress; + char newMode; + char needScan; +} NewModeData; + +static NewModeData newModeData; + +#endif + #define CONNTRY_IDLE 0 #define CONNTRY_WORKING 1 #define CONNTRY_SUCCESS 2 @@ -48,8 +56,9 @@ static os_timer_t resetTimer; //Callback the code calls when a wlan ap scan is done. Basically stores the result in //the cgiWifiAps struct. void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { - int n; + int noAps, n, i; struct bss_info *bss_link = (struct bss_info *)arg; + httpd_printf("wifiScanDoneCb %d\n", status); if (status!=OK) { cgiWifiAps.scanInProgress=0; @@ -63,53 +72,117 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { } //Count amount of access points found. - n=0; + noAps=0; while (bss_link != NULL) { bss_link = bss_link->next.stqe_next; - n++; + noAps++; } + //Allocate memory for access point data - cgiWifiAps.apData=(ApData **)malloc(sizeof(ApData *)*n); - cgiWifiAps.noAps=n; - httpd_printf("Scan done: found %d APs\n", n); + cgiWifiAps.apData=(ApData **)malloc(sizeof(ApData *)*noAps); + memset(cgiWifiAps.apData, 0, sizeof(ApData *)*noAps); //Copy access point data to the static struct n=0; bss_link = (struct bss_info *)arg; while (bss_link != NULL) { - if (n>=cgiWifiAps.noAps) { + if (n>=noAps) { //This means the bss_link changed under our nose. Shouldn't happen! //Break because otherwise we will write in unallocated memory. - httpd_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); + httpd_printf("Huh? I have more than the allocated %d aps!\n", noAps); break; } + + // check for duplicate SSIDs and keep the one with the strongest signal + for (i = 0; i < n; ++i) { + if (strncmp((char*)bss_link->ssid, cgiWifiAps.apData[i]->ssid, 32) == 0) { + if (bss_link->rssi > cgiWifiAps.apData[i]->rssi) { + cgiWifiAps.apData[i]->rssi=bss_link->rssi; + cgiWifiAps.apData[i]->channel=bss_link->channel; + cgiWifiAps.apData[i]->enc=bss_link->authmode; + strncpy(cgiWifiAps.apData[i]->bssid, (char*)bss_link->bssid, 6); + } + break; + } + } + //Save the ap data. - cgiWifiAps.apData[n]=(ApData *)malloc(sizeof(ApData)); - cgiWifiAps.apData[n]->rssi=bss_link->rssi; - cgiWifiAps.apData[n]->channel=bss_link->channel; - cgiWifiAps.apData[n]->enc=bss_link->authmode; - strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); - strncpy(cgiWifiAps.apData[n]->bssid, (char*)bss_link->bssid, 6); + if (i >= n) { + cgiWifiAps.apData[n]=(ApData *)malloc(sizeof(ApData)); + cgiWifiAps.apData[n]->rssi=bss_link->rssi; + cgiWifiAps.apData[n]->channel=bss_link->channel; + cgiWifiAps.apData[n]->enc=bss_link->authmode; + strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); + strncpy(cgiWifiAps.apData[n]->bssid, (char*)bss_link->bssid, 6); + n++; + } bss_link = bss_link->next.stqe_next; - n++; } + cgiWifiAps.noAps = n; + //We're done. + httpd_printf("Scan done: found %d APs\n", cgiWifiAps.noAps); + if (scanCallback) { + (*scanCallback)(scanCallbackData, cgiWifiAps.noAps); + scanCallback = NULL; + } cgiWifiAps.scanInProgress=0; } - //Routine to start a WiFi access point scan. static void ICACHE_FLASH_ATTR wifiStartScan() { // int x; if (cgiWifiAps.scanInProgress) return; - cgiWifiAps.scanInProgress=1; - wifi_station_scan(NULL, wifiScanDoneCb); + if (newModeData.inProgress) { + if (newModeData.newMode == STATIONAP_MODE) + newModeData.needScan = 1; + else + httpd_printf("Must be in STA+AP mode to start AP scan: mode=%d\n", newModeData.newMode); + } + else { + if (wifi_get_opmode() == STATIONAP_MODE) { + httpd_printf("Starting scan...\n"); + wifi_station_scan(NULL, wifiScanDoneCb); + cgiWifiAps.scanInProgress=1; + } + else + httpd_printf("Must be in STA+AP mode to start AP scan: mode=%d\n", wifi_get_opmode()); + } +} + +//Routine to start a WiFi access point scan. +int ICACHE_FLASH_ATTR cgiWiFiStartScan(void (*callback)(void *data, int count), void *data) { + if (scanCallback) return 0; + scanCallback = callback; + scanCallbackData = data; + wifiStartScan(); + return 1; +} + +//Routine to check the status of a WiFi access point scan. +int ICACHE_FLASH_ATTR cgiWiFiScanDone(void) { + return cgiWifiAps.scanInProgress == 0; +} + +//Routine to return a scan result. +ApData ICACHE_FLASH_ATTR *cgiWiFiScanResult(int n) { + if (cgiWifiAps.scanInProgress) + return NULL; + else if (n < 0 || n >= cgiWifiAps.noAps) + return NULL; + return cgiWifiAps.apData[n]; } -//This CGI is called from the bit of AJAX-code in wifi.tpl. It will initiate a +//Routine to return the scan result count. +int cgiWifiScanResultCount(void) +{ + return cgiWifiAps.scanInProgress ? -1 : cgiWifiAps.noAps; +} + +//This CGI is called from the bit of AJAX-code in wifi.html. It will initiate a //scan for access points and if available will return the result of an earlier scan. -//The result is embedded in a bit of JSON parsed by the javascript in wifi.tpl. +//The result is embedded in a bit of JSON parsed by the javascript in wifi.html. int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { int pos=(int)connData->cgiData; int len; @@ -158,19 +231,29 @@ int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { //Temp store for new ap info. static struct station_config stconf; +//#define SWITCH_TO_STA_MODE_AFTER_CONNECT + //This routine is ran some time after a connection attempt to an access point. If //the connect succeeds, this gets the module in STA-only mode. static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { int x=wifi_station_get_connect_status(); if (x==STATION_GOT_IP) { + httpd_printf("Got IP address.\n"); +#ifdef SWITCH_TO_STA_MODE_AFTER_CONNECT //Go to STA mode. This needs a reset, so do that. - httpd_printf("Got IP. Going into STA mode..\n"); - wifi_set_opmode(1); - system_restart(); + if (x!=STATION_MODE) { + httpd_printf("Going into STA mode..\n"); + wifi_set_opmode(STATION_MODE); + } +#endif } else { connTryStatus=CONNTRY_FAIL; - httpd_printf("Connect fail. Not going into STA-only mode.\n"); +#ifdef SWITCH_TO_STA_MODE_AFTER_CONNECT + httpd_printf("Connect failed. Not going into STA-only mode.\n"); //Maybe also pass this through on the webpage? +#else + httpd_printf("Connect failed.\n"); +#endif } } @@ -181,13 +264,12 @@ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { //but I can't be arsed to put the code back :P static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { int x; - httpd_printf("Try to connect to AP....\n"); wifi_station_disconnect(); wifi_station_set_config(&stconf); wifi_station_connect(); x=wifi_get_opmode(); connTryStatus=CONNTRY_WORKING; - if (x!=1) { + if (x!=STATION_MODE) { //Schedule disconnect/connect os_timer_disarm(&resetTimer); os_timer_setfn(&resetTimer, resetTimerCb, NULL); @@ -215,6 +297,8 @@ int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { strncpy((char*)stconf.password, passwd, 64); httpd_printf("Try to connect to AP %s pw %s\n", essid, passwd); + connTryStatus=CONNTRY_IDLE; + //Schedule disconnect/connect os_timer_disarm(&reassTimer); os_timer_setfn(&reassTimer, reassTimerCb, NULL); @@ -228,6 +312,48 @@ int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { return HTTPD_CGI_DONE; } +int ICACHE_FLASH_ATTR wifiJoin(char *ssid, char *passwd) +{ + static os_timer_t reassTimer; + + strncpy((char*)stconf.ssid, ssid, 32); + strncpy((char*)stconf.password, passwd, 64); + httpd_printf("Try to connect to AP %s pw %s\n", ssid, passwd); + + connTryStatus=CONNTRY_IDLE; + + //Schedule disconnect/connect + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, reassTimerCb, NULL); + os_timer_arm(&reassTimer, 500, 0); + + return 0; +} + +#ifndef DEMO_MODE + +static void ICACHE_FLASH_ATTR setModeCb(void *arg) { + if (!newModeData.inProgress) return; + switch (newModeData.newMode) { + case STATION_MODE: + case SOFTAP_MODE: + case STATIONAP_MODE: +httpd_printf("setModeCb: %d\n", newModeData.newMode); + wifi_set_opmode(newModeData.newMode); + break; + default: + httpd_printf("setModeCb: invalid mode %d\n", newModeData.newMode); + break; + } + if (newModeData.needScan) { + httpd_printf("Starting deferred scan...\n"); + wifi_station_scan(NULL, wifiScanDoneCb); + cgiWifiAps.scanInProgress=1; + } + newModeData.inProgress = 0; +} +#endif + //This cgi uses the routines above to connect to a specific access point with the //given ESSID using the given password. int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { @@ -241,16 +367,36 @@ int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); if (len!=0) { - httpd_printf("cgiWifiSetMode: %s\n", buff); #ifndef DEMO_MODE - wifi_set_opmode(atoi(buff)); - system_restart(); + if (os_strcmp(buff, "STA") == 0) + newModeData.newMode = STATION_MODE; + else if (os_strcmp(buff, "AP") == 0) + newModeData.newMode = SOFTAP_MODE; + else if (os_strcmp(buff, "STA+AP") == 0) + newModeData.newMode = STATIONAP_MODE; + else + newModeData.newMode = atoi(buff); +httpd_printf("cgiWiFiSetMode: '%s' (%d)\n", buff, newModeData.newMode); + newModeData.inProgress = 1; + newModeData.needScan = 0; + os_timer_disarm(&newModeData.timer); + os_timer_setfn(&newModeData.timer, setModeCb, NULL); + os_timer_arm(&newModeData.timer, 1000, 0); #endif } httpdRedirect(connData, "/wifi"); return HTTPD_CGI_DONE; } +/* + STATION_IDLE = 0, + STATION_CONNECTING, + STATION_WRONG_PASSWORD, + STATION_NO_AP_FOUND, + STATION_CONNECT_FAIL, + STATION_GOT_IP +*/ + int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) { char buff[1024]; int len; @@ -293,9 +439,10 @@ int ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) strcpy(buff, "Unknown"); if (strcmp(token, "WiFiMode")==0) { x=wifi_get_opmode(); - if (x==1) strcpy(buff, "Client"); - if (x==2) strcpy(buff, "SoftAP"); - if (x==3) strcpy(buff, "STA+AP"); + if (x==1) strcpy(buff, "Station"); + else if (x==2) strcpy(buff, "SoftAP"); + else if (x==3) strcpy(buff, "Station+SoftAP"); + else strcpy(buff, "(unknown)"); } else if (strcmp(token, "currSsid")==0) { strcpy(buff, (char*)stconf.ssid); } else if (strcmp(token, "WiFiPasswd")==0) {