much further progress :)

Able to have sustained and queued websockets connections at ~500 Hz.  Commands pass over said connection.
This commit is contained in:
cnlohr 2015-07-30 02:45:02 -04:00
parent bba28c688f
commit 666d8077ab
11 changed files with 535 additions and 114 deletions

View file

@ -61,17 +61,25 @@ static void ICACHE_FLASH_ATTR scandone(void *arg, STATUS status)
void ICACHE_FLASH_ATTR issue_command(void *arg, char *pusrdata, unsigned short len)
int ICACHE_FLASH_ATTR issue_command(char * buffer, int retsize, char *pusrdata, unsigned short len)
{
pespconn = (struct espconn *)arg;
//We accept commands on this.
char * buffend = buffer;
switch( pusrdata[0] )
{
case 'e': case 'E': //Echo
if( retsize > len )
{
ets_memcpy( buffend, pusrdata, len );
return len;
}
else
{
return -1;
}
case 'f': case 'F': //Flashing commands (F_)
{
flashchip->chip_size = 0x01000000;
// ets_printf( "PC%p\n", &pusrdata[2] );
const char * colon = (const char *) ets_strstr( (char*)&pusrdata[2], ":" );
int nr = my_atoi( &pusrdata[2] );
@ -79,51 +87,33 @@ void ICACHE_FLASH_ATTR issue_command(void *arg, char *pusrdata, unsigned short l
{
case 'e': case 'E': //(FE#\n) <- # = sector.
{
char __attribute__ ((aligned (16))) buffer[50];
char * buffend;
buffend = buffer;
EnterCritical();
spi_flash_erase_sector( nr ); //SPI_FLASH_SEC_SIZE 4096
ExitCritical();
buffend += ets_sprintf(buffend, "FE%d\r\n", nr );
espconn_sent( pespconn, buffer, ets_strlen( buffer ) );
break;
}
case 'b': case 'B': //(FE#\n) <- # = sector.
{
char __attribute__ ((aligned (16))) buffer[50];
char * buffend;
buffend = buffer;
//spi_flash_erase_sector( nr ); //SPI_FLASH_SEC_SIZE 4096
EnterCritical();
SPIEraseBlock( nr );
ExitCritical();
buffend += ets_sprintf(buffend, "FB%d\r\n", nr );
espconn_sent( pespconn, buffer, ets_strlen( buffer ) );
break;
}
case 'm': case 'M': //Execute the flash re-writer
{
char ret[128];
int n = ets_sprintf( ret, "FM" );
espconn_sent( pespconn, ret, n );
int r = (*GlobalRewriteFlash)( &pusrdata[2], len-2 );
n = ets_sprintf( ret, "!FM%d", r );
espconn_sent( pespconn, ret, n );
buffend += ets_sprintf( buffend, "!FM%d\r\n", r );
break;
}
case 'w': case 'W': //Flash Write (FW#\n) <- # = byte pos.
if( colon )
{
char __attribute__ ((aligned (32))) buffer[1300];
char * buffend;
buffend = buffer;
buffer[0] = 0;
colon++;
const char * colon2 = (const char *) ets_strstr( (char*)colon, ":" );
if( colon2 )
@ -136,50 +126,37 @@ void ICACHE_FLASH_ATTR issue_command(void *arg, char *pusrdata, unsigned short l
spi_flash_write( nr, (uint32*)buffer, (datlen/4)*4 );
ExitCritical();
//SPIWrite( nr, (uint32_t*)buffer, (datlen/4)*4 );
buffend = buffer;
buffend += ets_sprintf(buffend, "FW%d\r\n", nr );
if( ets_strlen( buffer ) )
espconn_sent( pespconn, buffer, ets_strlen( buffer ) );
break;
}
}
buffend += ets_sprintf(buffend, "!FW\r\n" );
break;
case 'r': case 'R': //Flash Read (FR#\n) <- # = sector.
if( colon )
{
char __attribute__ ((aligned (16))) buffer[1300];
char * buffend;
buffend = buffer;
buffer[0] = 0;
colon++;
int datlen = my_atoi( colon );
datlen = (datlen/4)*4; //Must be multiple of 4 bytes
if( datlen <= 1280 )
{
buffend += ets_sprintf(buffend, "FR%08d:%04d:", nr, datlen );
int frontlen = buffend - buffer;
spi_flash_read( nr, (uint32*)buffend, (datlen/4)*4 );
espconn_sent( pespconn, buffer, frontlen + datlen );
buffer[0] = 0;
if( ets_strlen( buffer ) )
espconn_sent( pespconn, buffer, ets_strlen( buffer ) );
buffend += ets_sprintf(buffend, "FR%08d:%04d:", nr, datlen ); //Caution: This string must be a multiple of 4 bytes.
spi_flash_read( nr, (uint32*)buffend, datlen );
break;
}
}
buffend += ets_sprintf(buffend, "!FR\r\n" );
break;
}
flashchip->chip_size = 0x00080000;
break;
return buffend - buffer;
}
case 'w': case 'W': // (W1:SSID:PASSWORD) (To connect) or (W2) to be own base station. ...or WI, to get info... or WS to scan.
{
char * colon = (char *) ets_strstr( (char*)&pusrdata[2], ":" );
char * colon2 = (colon)?((char *)ets_strstr( (char*)(colon+1), ":" )):0;
char * extra = colon2;
char __attribute__ ((aligned (16))) buffer[1300];
char * buffend;
buffend = buffer;
buffer[0] = 0;
if( extra )
{
@ -196,59 +173,65 @@ void ICACHE_FLASH_ATTR issue_command(void *arg, char *pusrdata, unsigned short l
switch (pusrdata[1])
{
case '1': //Station mode
case '2': //AP Mode
if( colon && colon2 )
{
int c1l = 0;
int c2l = 0;
struct station_config stationConf;
*colon = 0; colon++;
*colon2 = 0; colon2++;
c1l = ets_strlen( colon );
c2l = ets_strlen( colon2 );
if( c1l > 32 ) c1l = 32;
if( c2l > 32 ) c2l = 32;
os_bzero( &stationConf, sizeof( stationConf ) );
printf( "Switching to: \"%s\"/\"%s\".\n", colon, colon2 );
os_memcpy(&stationConf.ssid, colon, c1l);
os_memcpy(&stationConf.password, colon2, c2l);
if( pusrdata[1] == '1' )
{
struct station_config stationConf;
os_bzero( &stationConf, sizeof( stationConf ) );
wifi_set_opmode( 1 );
wifi_station_set_config(&stationConf);
wifi_station_connect();
espconn_sent( pespconn, "W1\r\n", 4 );
os_memcpy(&stationConf.ssid, colon, c1l);
os_memcpy(&stationConf.password, colon2, c2l);
printf( "Switching.\n" );
}
break;
case '2':
{
wifi_set_opmode_current( 1 );
wifi_set_opmode( 2 );
espconn_sent( pespconn, "W2\r\n", 4 );
wifi_set_opmode( 1 );
wifi_station_set_config(&stationConf);
wifi_station_connect();
buffend += ets_sprintf( buffend, "W1\r\n" );
printf( "Switching.\n" );
}
else
{
wifi_set_opmode_current( 1 );
wifi_set_opmode( 2 );
buffend += ets_sprintf( buffend, "W2\r\n" );
}
}
break;
case 'I':
{
struct station_config sc;
int mode = wifi_get_opmode();
char buffer[200];
char * buffend = &buffer[0];
buffend += ets_sprintf( buffend, "WI%d", mode );
wifi_station_get_config( &sc );
buffend += ets_sprintf( buffend, ":%s:%s", sc.ssid, sc.password );
espconn_sent( pespconn, buffer, buffend - buffer );
if( mode == 2 )
{
struct softap_config ap;
wifi_softap_get_config( &ap );
buffend += ets_sprintf( buffend, "\t%s\t%s\t%s\t%d", ap.ssid, ap.password, enctypes[ap.authmode], ap.channel );
}
else
{
struct station_config sc;
wifi_station_get_config( &sc );
buffend += ets_sprintf( buffend, "\t%s\t%s\tna\t%d", sc.ssid, sc.password, wifi_get_channel() );
}
}
break;
case 'S': case 's':
{
char buffer[1024];
char * buffend = &buffer[0];
int i, r;
scanplace = 0;
@ -267,34 +250,41 @@ void ICACHE_FLASH_ATTR issue_command(void *arg, char *pusrdata, unsigned short l
buffend += ets_sprintf( buffend, "WS%d\n", r );
uart0_sendStr(buffer);
espconn_sent( pespconn, buffer, buffend - buffer );
break;
}
break;
case 'R': case 'r':
{
char buffer[1024];
char * buffend = &buffer[0];
int i, r;
buffend += ets_sprintf( buffend, "WR%d\n", scanplace );
for( i = 0; i < scanplace; i++ )
for( i = 0; i < scanplace && buffend - buffer < retsize - 64; i++ )
{
buffend += ets_sprintf( buffend, "#%s\t%s\t%d\t%d\t%s\n",
totalscan[i].name, totalscan[i].mac, totalscan[i].rssi, totalscan[i].channel, enctypes[totalscan[i].encryption] );
}
espconn_sent( pespconn, buffer, buffend - buffer );
break;
}
break;
}
break;
return buffend - buffer;
}
default:
break;
}
return -1;
}
void ICACHE_FLASH_ATTR issue_command_udp(void *arg, char *pusrdata, unsigned short len)
{
char __attribute__ ((aligned (32))) retbuf[1300];
int r = issue_command( retbuf, 1300, pusrdata, len );
if( r > 0 )
{
espconn_sent( (struct espconn *)arg, retbuf, r );
}
}
void ICACHE_FLASH_ATTR CSInit()
@ -304,7 +294,7 @@ void ICACHE_FLASH_ATTR CSInit()
pUdpServer->type = ESPCONN_UDP;
pUdpServer->proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp));
pUdpServer->proto.udp->local_port = 7878;
espconn_regist_recvcb(pUdpServer, issue_command);
espconn_regist_recvcb(pUdpServer, issue_command_udp);
espconn_create( pUdpServer );
pHTTPServer = (struct espconn *)os_zalloc(sizeof(struct espconn));

View file

@ -6,6 +6,12 @@
#include "c_types.h"
//Returns nr bytes to return. You must allocate retdata.
//It MUST be at least 1,300 bytes large and it MUST be 32-bit aligned.
//NOTE: It is SAFE to use pusrdata and retdata as the same buffer.
int ICACHE_FLASH_ATTR issue_command(char * retdata, int retsize, char *pusrdata, unsigned short len);
//Includes UDP Control + HTTP Interfaces
void ICACHE_FLASH_ATTR CSInit();
void ICACHE_FLASH_ATTR CSTick( int slowtick );

View file

@ -11,6 +11,9 @@ struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS];
struct HTTPConnection * curhttp;
uint8 * curdata;
uint16 curlen;
uint8 wsmask[4];
uint8 wsmaskplace;
ICACHE_FLASH_ATTR void InternalStartHTTP( );
ICACHE_FLASH_ATTR void HTTPHandleInternalCallback( );
@ -29,7 +32,7 @@ void ICACHE_FLASH_ATTR HTTPGotData( )
while( curlen-- )
{
c = POP;
c = HTTPPOP;
// sendhex2( h->state ); sendchr( ' ' );
switch( curhttp->state )
@ -97,8 +100,15 @@ void ICACHE_FLASH_ATTR HTTPGotData( )
WebSocketGotData( c );
break;
case HTTP_WAIT_CLOSE:
//printf( "__HTTPCLose1\n" );
HTTPClose( );
if( curhttp->keep_alive )
{
curhttp->state = HTTP_STATE_WAIT_METHOD;
}
else
{
//printf( "__HTTPCLose1\n" );
HTTPClose( );
}
break;
default:
break;
@ -130,8 +140,15 @@ static void DoHTTP( uint8_t timed )
case HTTP_WAIT_CLOSE:
if( TCPDoneSend( curhttp->socket ) )
{
//printf( "HTTPCLose2\n");
HTTPClose( );
if( curhttp->keep_alive )
{
curhttp->state = HTTP_STATE_WAIT_METHOD;
}
else
{
//printf( "HTTPCLose2\n");
HTTPClose( );
}
}
break;
case HTTP_STATE_DATA_WEBSOCKET:
@ -187,14 +204,21 @@ void ICACHE_FLASH_ATTR HTTPHandleInternalCallback( )
START_PACK;
//TODO: Content Length? MIME-Type?
PushString("HTTP/1.1 200 Ok\r\nConnection: close");
PushString("HTTP/1.1 200 Ok\r\n");
if( curhttp->bytesleft < 0xfffffffe )
{
PushString("\r\nContent-Length: ");
PushString("Connection: keep-alive\r\nContent-Length: ");
Uint32To10Str( stto, curhttp->bytesleft );
PushBlob( stto, os_strlen( stto ) );
curhttp->keep_alive = 1;
}
else
{
PushString("Connection: close\r\n");
curhttp->keep_alive = 0;
}
PushString( "\r\nContent-Type: " );
//Content-Type?
while( slen && ( curhttp->pathbuffer[--slen] != '.' ) );
@ -203,6 +227,10 @@ void ICACHE_FLASH_ATTR HTTPHandleInternalCallback( )
{
PushString( "audio/mpeg3" );
}
else if( strcmp( k, "gz" ) == 0 )
{
PushString( "text/plain\r\nContent-Encoding: gzip\r\nCache-Control: public, max-age=3600" );
}
else if( curhttp->bytesleft == 0xfffffffe )
{
PushString( "text/plain" );
@ -410,6 +438,7 @@ void ICACHE_FLASH_ATTR WebSocketGotData( uint8_t c )
#define WS_KEY_LEN 36
#define WS_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_RETKEY_SIZEM1 32
while( curlen > 1 )
{
@ -449,14 +478,16 @@ void ICACHE_FLASH_ATTR WebSocketGotData( uint8_t c )
SHA1_Update( &c, inkey, i );
SHA1_Final( hash, &c );
if( SHA1_HASH_LEN * 2 > MAX_PATHLEN )
{
HTDEBUG( "Pathlen too short.\n" );
curhttp->state = HTTP_WAIT_CLOSE;
return;
}
#if (WS_RETKEY_SIZE > MAX_PATHLEN - 10 )
#error MAX_PATHLEN too short.
#endif
my_base64_encode( hash, SHA1_HASH_LEN, curhttp->pathbuffer );
my_base64_encode( hash, SHA1_HASH_LEN, curhttp->pathbuffer + (MAX_PATHLEN-WS_RETKEY_SIZEM1) );
curhttp->bytessofar = 0;
curhttp->bytesleft = 0;
NewWebSocket();
//Respond...
curhttp->state_deets = 1;
@ -507,7 +538,16 @@ void ICACHE_FLASH_ATTR WebSocketGotData( uint8_t c )
{
payloadlen &= 0x7f;
}
WebSocketData( curdata, payloadlen );
wsmask[0] = curdata[0];
wsmask[1] = curdata[1];
wsmask[2] = curdata[2];
wsmask[3] = curdata[3];
curdata += 4;
curlen -= 4;
wsmaskplace = 0;
WebSocketData( payloadlen );
curlen -= payloadlen;
curdata += payloadlen;
@ -525,13 +565,11 @@ void ICACHE_FLASH_ATTR WebSocketTickInternal()
case 4: //Has key full HTTP header, etc. wants response.
START_PACK;
PushString( "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " );
PushString( curhttp->pathbuffer );
PushString( curhttp->pathbuffer + (MAX_PATHLEN-WS_RETKEY_SIZEM1) );
PushString( "\r\n\r\n" );
EndTCPWrite( curhttp->socket );
curhttp->state_deets = 5;
curhttp->bytessofar = 0;
curhttp->bytesleft = 0;
NewWebSocket();
curhttp->keep_alive = 0;
break;
case 5:
WebSocketTick();
@ -557,4 +595,11 @@ void ICACHE_FLASH_ATTR WebSocketSend( uint8_t * data, int size )
EndTCPWrite( curhttp->socket );
}
uint8_t WSPOPMASK()
{
uint8_t mask = wsmask[wsmaskplace];
wsmaskplace = (wsmaskplace+1)&3;
return (*curdata++)^(mask);
}

View file

@ -22,15 +22,18 @@
void ICACHE_FLASH_ATTR HTTPCustomStart( );
void ICACHE_FLASH_ATTR HTTPCustomCallback( ); //called when we can send more data
void ICACHE_FLASH_ATTR WebSocketData( uint8_t * data, int len );
void ICACHE_FLASH_ATTR WebSocketData( int len );
void ICACHE_FLASH_ATTR WebSocketTick( );
void ICACHE_FLASH_ATTR WebSocketNew();
extern struct HTTPConnection * curhttp;
extern uint8 * curdata;
extern uint16 curlen;
extern uint8 wsmask[4];
extern uint8 wsmaskplace;
#define POP (*curdata++)
uint8_t WSPOPMASK();
#define HTTPPOP (*curdata++)
#define HTTP_STATE_NONE 0
#define HTTP_STATE_WAIT_METHOD 1
@ -49,7 +52,10 @@ struct HTTPConnection
{
uint8_t state:4;
uint8_t state_deets;
uint8_t pathbuffer[MAX_PATHLEN]; //Also used for temporary and handshake information when using websockets.
//Provides path, i.e. "/index.html" but, for websockets, the last
//32 bytes of the buffer are used for the websockets key.
uint8_t pathbuffer[MAX_PATHLEN];
uint8_t is_dynamic:1;
uint16_t timeout;
@ -60,6 +66,7 @@ struct HTTPConnection
} data;
void * rcb;
void * rcbDat; //For websockets primarily.
uint32_t bytesleft;
uint32_t bytessofar;
@ -67,6 +74,7 @@ struct HTTPConnection
uint8_t is404:1;
uint8_t isdone:1;
uint8_t isfirst:1;
uint8_t keep_alive:1;
uint8_t need_resend:1;
struct espconn * socket;

View file

@ -34,11 +34,16 @@ static ICACHE_FLASH_ATTR void echo()
static ICACHE_FLASH_ATTR void issue()
{
char mydat[512];
int len = URLDecode( mydat, 512, curhttp->pathbuffer+9 );
issue_command(curhttp->socket, mydat, len );
uint8_t __attribute__ ((aligned (32))) buf[1300];
int len = URLDecode( buf, 1300, curhttp->pathbuffer+9 );
int r = issue_command(buf, 1300, buf, len );
if( r > 0 )
{
START_PACK;
PushBlob( buf, r );
EndTCPWrite( curhttp->socket );
}
curhttp->state = HTTP_WAIT_CLOSE;
}
@ -72,6 +77,7 @@ void ICACHE_FLASH_ATTR HTTPCustomStart( )
}
void ICACHE_FLASH_ATTR HTTPCustomCallback( )
{
if( curhttp->rcb )
@ -80,18 +86,91 @@ void ICACHE_FLASH_ATTR HTTPCustomCallback( )
curhttp->isdone = 1;
}
void ICACHE_FLASH_ATTR WebSocketTick()
static void ICACHE_FLASH_ATTR WSEchoData( int len )
{
char cbo[len];
int i;
for( i = 0; i < len; i++ )
{
cbo[i] = WSPOPMASK();
}
WebSocketSend( cbo, len );
}
void ICACHE_FLASH_ATTR WebSocketData( uint8_t * data, int len )
static void ICACHE_FLASH_ATTR WSEvalData( int len )
{
char stout[128];
int stl = ets_sprintf( stout, "output.innerHTML = %d; doSend('x' );\n", curhttp->bytessofar++ );
WebSocketSend( stout, stl );
char cbo[128];
int l = ets_sprintf( cbo, "output.innerHTML = %d; doSend('x' );", curhttp->bytessofar++ );
WebSocketSend( cbo, l );
}
static void ICACHE_FLASH_ATTR WSCommandData( int len )
{
uint8_t __attribute__ ((aligned (32))) buf[1300];
int i;
for( i = 0; i < len; i++ )
{
buf[i] = WSPOPMASK();
}
i = issue_command(buf, 1300, buf, len );
if( i < 0 ) i = 0;
WebSocketSend( buf, i );
}
// output.innerHTML = msg++ + " " + lasthz;
// doSend('x' );
void ICACHE_FLASH_ATTR NewWebSocket()
{
if( strcmp( (const char*)curhttp->pathbuffer, "/d/ws/echo" ) == 0 )
{
curhttp->rcb = 0;
curhttp->rcbDat = (void*)&WSEchoData;
}
else if( strcmp( (const char*)curhttp->pathbuffer, "/d/ws/evaltest" ) == 0 )
{
curhttp->rcb = 0;
curhttp->rcbDat = (void*)&WSEvalData;
}
else if( strcmp( (const char*)curhttp->pathbuffer, "/d/ws/issue" ) == 0 )
{
curhttp->rcb = 0;
curhttp->rcbDat = (void*)&WSCommandData;
}
else
{
curhttp->is404 = 1;
}
}
void ICACHE_FLASH_ATTR WebSocketTick()
{
if( curhttp->rcb )
{
((void(*)())curhttp->rcb)();
}
}
void ICACHE_FLASH_ATTR WebSocketData( int len )
{
if( curhttp->rcbDat )
{
((void(*)( int ))curhttp->rcbDat)( len );
}
}