Ok, now we have a working-ish server and client

This commit is contained in:
Gordon Williams 2015-01-14 13:58:33 +00:00
parent 46f097dce5
commit cee2f638a1

View File

@ -21,13 +21,17 @@
#define INVALID_SOCKET ((SOCKET)(-1))
#define SOCKET_ERROR (-1)
#define ESP8266_IPD_NAME JS_HIDDEN_CHAR_STR"IPD"
#define ESP8266_IPD_NAME JS_HIDDEN_CHAR_STR"ID"
#define ESP8266_IPD_NAME_LEN 3 // strlen(ESP8266_IPD_NAME)
#define ESP8266_DEBUG
// ---------------------------------------------------------------------------
typedef enum {
ESP8266_IDLE,
ESP8266_DELAY, // just delay and execute after the timeout
ESP8266_WAIT_OK,
ESP8266_WAIT_OK_THEN_CWMODE_THEN_RESET_THEN_READY_THEN_CIPMUX,
ESP8266_WAIT_CWMODE_THEN_RESET_THEN_READY_THEN_CIPMUX,
ESP8266_WAIT_RESET_THEN_READY_THEN_CIPMUX,
ESP8266_WAIT_READY_THEN_CIPMUX,
@ -36,6 +40,7 @@ typedef enum {
ESP8266_WAIT_LINKED,
ESP8266_WAIT_OK_THEN_WAIT_CONNECTED,
ESP8266_WAIT_SEND_OK,
ESP8266_WAIT_SEND_OK_THEN_CLOSE,
ESP8266_WAIT_CONNECTED_RESPONSE, // wait until we're connected (we can get an IP)
ESP8266_DELAY_1S_WAIT_CONNECTED, // after ESP8266_WAIT_CONNECTED_RESPONSE fails, it waits 1s and tried again
@ -88,7 +93,9 @@ ESP8266State net_esp8266_getState() {
// ---------------------------------------------------------------------------
void esp8266_send(JsVar *msg) {
#ifdef ESP8266_DEBUG
jsiConsolePrintf(">>> %q\n", msg);
#endif
JsvStringIterator it;
jsvStringIteratorNew(&it, msg, 0);
while (jsvStringIteratorHasChar(&it)) {
@ -100,9 +107,30 @@ void esp8266_send(JsVar *msg) {
}
void esp8266_got_data(int sckt, JsVar *data) {
JsVar *ipd = jsvObjectGetChild(execInfo.hiddenRoot, ESP8266_IPD_NAME, 0);
#ifdef ESP8266_DEBUG
jsiConsolePrintf("%d<<< %q\n", sckt, data);
#endif
if (sckt<0 || sckt>=SOCKET_COUNT) { // sanity check
assert(0);
return;
}
if (socketUsage & (1<<SOCKET_COUNT) && // is a server
net_esp8266_getState() == ESP8266_WAIT_SEND_OK_THEN_CLOSE &&
currentSocket==sckt) {
/* Whoa. So we are a server, we served up a webpage, were waiting
* to close the socket after we'd finished sending data, but it
* looks like the client got there first *and then started another request*
* which also went onto socket #0. So now we just try not to close the socket
* and pray.
*/
esp8266State = ESP8266_WAIT_SEND_OK;
}
char ipdName[ESP8266_IPD_NAME_LEN+1] = ESP8266_IPD_NAME"x";
ipdName[ESP8266_IPD_NAME_LEN] = (char)('0' + sckt); // make ipdName different for each socket
JsVar *ipd = jsvObjectGetChild(execInfo.hiddenRoot, ipdName, 0);
if (!ipd) {
jsvObjectSetChild(execInfo.hiddenRoot, ESP8266_IPD_NAME, data);
jsvObjectSetChild(execInfo.hiddenRoot, ipdName, data);
} else {
jsvAppendStringVar(ipd, data, 0, JSVAPPENDSTRINGVAR_MAXLENGTH);
jsvUnLock(ipd);
@ -116,15 +144,18 @@ bool esp8266_idle(JsVar *usartClass) {
if (jsvIsString(buf)) {
if (esp8266_ipd_buffer_size) {
size_t nChars = jsvGetStringLength(buf);
if (nChars > esp8266_ipd_buffer_size)
nChars = esp8266_ipd_buffer_size;
esp8266_ipd_buffer_size -= nChars;
JsVar *data = jsvNewFromStringVar(buf,0,(size_t)nChars);
JsVar *newBuf = jsvNewFromStringVar(buf, (size_t)(nChars+1), JSVAPPENDSTRINGVAR_MAXLENGTH);
jsvUnLock(buf);
buf = newBuf;
hasChanged = true;
esp8266_got_data(esp8266_ipd_buffer_sckt, data);
if (nChars>0) {
if (nChars > esp8266_ipd_buffer_size)
nChars = esp8266_ipd_buffer_size;
esp8266_ipd_buffer_size -= nChars;
JsVar *data = jsvNewFromStringVar(buf,0,(size_t)nChars);
JsVar *newBuf = jsvNewFromStringVar(buf, (size_t)(nChars+1), JSVAPPENDSTRINGVAR_MAXLENGTH);
//jsiConsolePrintf("%d,%d %q -> %q + %q\n", nChars,esp8266_ipd_buffer_size, buf, data, newBuf);
jsvUnLock(buf);
buf = newBuf;
hasChanged = true;
esp8266_got_data(esp8266_ipd_buffer_sckt, data);
}
} else {
char chars[20];
jsvGetStringChars(buf, 0, chars, sizeof(chars));
@ -140,12 +171,13 @@ bool esp8266_idle(JsVar *usartClass) {
esp8266_ipd_buffer_size = (size_t)stringToInt(&chars[sckt_idx+1]);
size_t len = jsvGetStringLength(buf);
if (len > len_idx) {
size_t nChars = len-len_idx;
size_t nChars = len-(len_idx+1);
if (nChars > esp8266_ipd_buffer_size)
nChars = esp8266_ipd_buffer_size;
esp8266_ipd_buffer_size -= nChars;
JsVar *data = jsvNewFromStringVar(buf,(size_t)(len_idx+1),(size_t)nChars);
JsVar *newBuf = jsvNewFromStringVar(buf, (size_t)(len_idx+nChars+1), JSVAPPENDSTRINGVAR_MAXLENGTH);
//jsiConsolePrintf("%d,%d %q -> %q + %q\n", nChars,esp8266_ipd_buffer_size, buf, data, newBuf);
jsvUnLock(buf);
buf = newBuf;
hasChanged = true;
@ -165,11 +197,15 @@ bool esp8266_idle(JsVar *usartClass) {
int idx = jsvGetStringIndexOf(buf, '\n');
if (idx<0 && esp8266_idle_compare && esp8266_idle_compare[0]=='>' && esp8266_idle_compare[1]==0 && buf && buf->varData.str[0]=='>')
idx=2; // pretend we had /r/n - this only works because right now we're only expecting one char
while (!found && idx>0/* because we want a \r before it */) {
while ((!found) && idx>=0) {
hasChanged = true;
JsVar *line = jsvNewFromStringVar(buf,0,(size_t)(idx-1)); // \r\n - so idx is of '\n' and we want to remove '\r' too
JsVar *line = idx ?
jsvNewFromStringVar(buf,0,(size_t)(idx - 1)) : // \r\n - so idx is of '\n' and we want to remove '\r' too
jsvNewFromEmptyString();
#ifdef ESP8266_DEBUG
jsiConsoleRemoveInputLine();
jsiConsolePrintf("ESP8266> %q\n", line);
#endif
switch (net_esp8266_getState()) {
case ESP8266_IDLE: break; // ignore?
case ESP8266_DELAY: break; // ignore here - see stateTimeout below
@ -179,6 +215,14 @@ bool esp8266_idle(JsVar *usartClass) {
net_esp8266_execute_and_set_idle();
}
break;
case ESP8266_WAIT_OK_THEN_CWMODE_THEN_RESET_THEN_READY_THEN_CIPMUX:
if (jsvIsStringEqualOrStartsWith(line, "OK", false)) {
JsVar *cmd = jsvNewFromString("AT+CWMODE=1\r\n"); // set mode to 1 (client). 3 (both) works too
esp8266_send(cmd);
jsvUnLock(cmd);
net_esp8266_setState(ESP8266_WAIT_CWMODE_THEN_RESET_THEN_READY_THEN_CIPMUX, 500, stateSuccessCallback, stateTimeoutMessage);
}
break;
case ESP8266_WAIT_CWMODE_THEN_RESET_THEN_READY_THEN_CIPMUX:
if (jsvIsStringEqualOrStartsWith(line, "OK", false) || jsvIsStringEqualOrStartsWith(line, "no change", false)) {
JsVar *cmd = jsvNewFromString("AT+RST\r\n"); // sent reset command
@ -186,10 +230,12 @@ bool esp8266_idle(JsVar *usartClass) {
jsvUnLock(cmd);
net_esp8266_setState(ESP8266_WAIT_RESET_THEN_READY_THEN_CIPMUX, 500, stateSuccessCallback, stateTimeoutMessage);
}
break;
case ESP8266_WAIT_RESET_THEN_READY_THEN_CIPMUX:
if (jsvIsStringEqualOrStartsWith(line, "OK", false)) {
net_esp8266_setState(ESP8266_WAIT_READY_THEN_CIPMUX, 6000, stateSuccessCallback, stateTimeoutMessage);
}
break;
case ESP8266_WAIT_READY_THEN_CIPMUX:
if (jsvIsStringEqualOrStartsWith(line, "ready", false)) {
JsVar *cmd = jsvNewFromString("AT+CIPMUX=1\r\n"); // sent reset command
@ -237,11 +283,14 @@ bool esp8266_idle(JsVar *usartClass) {
net_esp8266_execute_and_set_idle();
}
break;
/* case ESP8266_WAIT_OK_THEN_READY:
if (jsvIsStringEqualOrStartsWith(line, "OK", false)) {
net_esp8266_setState(ESP8266_WAIT_READY, 6000, stateSuccessCallback, "Module not ready");
case ESP8266_WAIT_SEND_OK_THEN_CLOSE:
if (jsvIsStringEqualOrStartsWith(line, "SEND OK", false)) {
JsVar *msg = jsvVarPrintf("AT+CIPCLOSE=%d\r\n", currentSocket);
esp8266_send(msg);
jsvUnLock(msg);
net_esp8266_setState(ESP8266_WAIT_OK, 500, 0, "No acknowledgement");
}
break;*/
break;
}
if (esp8266_idle_compare && jsvIsStringEqualOrStartsWith(line, esp8266_idle_compare, esp8266_idle_compare_only_start))
@ -271,7 +320,7 @@ bool esp8266_idle(JsVar *usartClass) {
net_esp8266_setState(ESP8266_WAIT_CONNECTED_RESPONSE, 1000, stateSuccessCallback, stateTimeoutMessage);
} else {
if (currentSocket>=0) {
socketErrors |= 1<<currentSocket;
socketErrors = (unsigned char)(socketErrors | 1<<currentSocket);
currentSocket = -1;
}
jsExceptionHere(JSET_ERROR, stateTimeoutMessage ? stateTimeoutMessage : "ESP8266 timeout");
@ -313,10 +362,10 @@ bool esp8266_wait_for(const char *text, int milliseconds, bool justTheStart) {
bool net_esp8266_initialise(JsVar *callback) {
JsVar *cmd = jsvNewFromString("AT+CWMODE=3\r\n");
JsVar *cmd = jsvNewFromString("AT+CWJAP=\"\",\"\"\r\n");
esp8266_send(cmd);
jsvUnLock(cmd);
net_esp8266_setState(ESP8266_WAIT_CWMODE_THEN_RESET_THEN_READY_THEN_CIPMUX, 500, callback, "No Acknowledgement");
net_esp8266_setState(ESP8266_WAIT_OK_THEN_CWMODE_THEN_RESET_THEN_READY_THEN_CIPMUX, 500, callback, "No Acknowledgement");
socketUsage = 0; // no sockets should be used now
socketErrors = 0;
return true;
@ -352,37 +401,84 @@ bool net_esp8266_checkError(JsNetwork *net) {
/// if host=0, creates a server otherwise creates a client (and automatically connects). Returns >=0 on success
int net_esp8266_createsocket(JsNetwork *net, uint32_t host, unsigned short port) {
int sckt = 0;
while (sckt<SOCKET_COUNT && (socketUsage&(1<<sckt))) sckt++;
if (sckt>=SOCKET_COUNT) {
jsError("No available sockets");
return -1;
char sckt;
if (host!=0) {
sckt = 0;
if (socketUsage&(1<<SOCKET_COUNT)) {
jsError("Can't create a client socket while we have a server");
return -1;
}
while (sckt<SOCKET_COUNT && (socketUsage&(1<<sckt))) sckt++;
if (sckt>=SOCKET_COUNT) {
jsError("No available sockets");
return -1;
}
socketErrors = (unsigned char)(socketErrors & ~(1<<sckt));
socketUsage = (unsigned char)(socketUsage | 1<<sckt);
JsVar *hostStr = networkGetAddressAsString((unsigned char *)&host, 4,10,'.');
JsVar *msg = jsvVarPrintf("AT+CIPSTART=%d,\"TCP\",%q,%d\r\n", sckt, hostStr, port);
jsvUnLock(hostStr);
esp8266_send(msg);
jsvUnLock(msg);
} else { // server
sckt = SOCKET_COUNT;
if (socketUsage&(1<<sckt)) {
jsError("Only one server allowed");
return -1;
}
socketErrors = (unsigned char)(socketErrors & ~(1<<sckt));
socketUsage = (unsigned char)(socketUsage | 1<<sckt);
JsVar *msg = jsvVarPrintf("AT+CIPSERVER=1,%d\r\n", port);
esp8266_send(msg);
jsvUnLock(msg);
}
socketErrors &= ~(1<<sckt);
socketUsage |= 1<<sckt;
JsVar *hostStr = networkGetAddressAsString((unsigned char *)&host, 4,10,'.');
JsVar *msg = jsvVarPrintf("AT+CIPSTART=%d,\"TCP\",%q,%d\r\n", sckt, hostStr, port);
jsvUnLock(hostStr);
esp8266_send(msg);
jsvUnLock(msg);
currentSocket = sckt;
net_esp8266_setState(ESP8266_WAIT_OK, 60000, 0, "Couldn't create connection");
net_esp8266_setState(ESP8266_WAIT_OK, 10000, 0, "Couldn't create connection");
return sckt;
}
/// destroys the given socket
void net_esp8266_closesocket(JsNetwork *net, int sckt) {
JsVar *msg = jsvVarPrintf("AT+CIPCLOSE=%d\r\n", sckt);
esp8266_send(msg);
jsvUnLock(msg);
net_esp8266_setState(ESP8266_WAIT_OK, 100, 0, "No Acknowledgement");
socketErrors &= ~(1<<sckt); // clear socket error
socketUsage &= ~(1<<sckt);
JsVar *msg = 0;
if (sckt!=SOCKET_COUNT) {
if (net_esp8266_getState() == ESP8266_WAIT_SEND_OK && currentSocket==sckt) {
esp8266State = ESP8266_WAIT_SEND_OK_THEN_CLOSE;
} else {
msg = jsvVarPrintf("AT+CIPCLOSE=%d\r\n", sckt);
}
} else { // server
msg = jsvNewFromString("AT+CIPSERVER=0\r\n");
}
if (msg) {
esp8266_send(msg);
jsvUnLock(msg);
net_esp8266_setState(ESP8266_WAIT_OK, 100, 0, "No Acknowledgement");
}
socketErrors = (unsigned char)(socketErrors & ~(1<<sckt)); // clear socket error
socketUsage = (unsigned char)(socketUsage & ~(1<<sckt));
}
/// If the given server socket can accept a connection, return it (or return < 0)
int net_esp8266_accept(JsNetwork *net, int sckt) {
int net_esp8266_accept(JsNetwork *net, int serverSckt) {
NOT_USED(serverSckt); // only one server allowed, so no need to check
assert(serverSckt==SOCKET_COUNT);
char ipdName[ESP8266_IPD_NAME_LEN+1] = ESP8266_IPD_NAME"x";
char sckt;
// Basically we only have a connection if there's been data on it
for (sckt=0;sckt<SOCKET_COUNT;sckt++) {
if (socketUsage & (1<<sckt)) continue;
ipdName[ESP8266_IPD_NAME_LEN] = (char)('0' + sckt); // make ipdName different for each socket
JsVar *ipd = jsvObjectGetChild(execInfo.hiddenRoot, ipdName, 0);
size_t l = ipd ? jsvGetStringLength(ipd) : 0;
jsvUnLock(ipd);
if (l) {
//jsiConsolePrintf("Socket %d accepted\n", sckt);
socketUsage = (unsigned char)(socketUsage | 1<<sckt);
return sckt; // if we have data, return the socket
}
}
return -1;
}
@ -390,7 +486,10 @@ int net_esp8266_accept(JsNetwork *net, int sckt) {
int net_esp8266_recv(JsNetwork *net, int sckt, void *buf, size_t len) {
if (socketErrors & (1<<sckt)) return -1; // socket error
JsVar *ipd = jsvObjectGetChild(execInfo.hiddenRoot, ESP8266_IPD_NAME, 0);
char ipdName[ESP8266_IPD_NAME_LEN+1] = ESP8266_IPD_NAME"x";
ipdName[ESP8266_IPD_NAME_LEN] = (char)('0' + sckt); // make ipdName different for each socket
JsVar *ipd = jsvObjectGetChild(execInfo.hiddenRoot, ipdName, 0);
size_t chars = 0;
if (ipd) {
chars = jsvGetStringLength(ipd);
@ -398,9 +497,9 @@ int net_esp8266_recv(JsNetwork *net, int sckt, void *buf, size_t len) {
JsVar *newIpd = (chars>len) ? jsvNewFromStringVar(ipd, len, JSVAPPENDSTRINGVAR_MAXLENGTH) : 0;
jsvUnLock(ipd);
jsvUnLock(jsvObjectSetChild(execInfo.hiddenRoot, ESP8266_IPD_NAME, newIpd));
jsvUnLock(jsvObjectSetChild(execInfo.hiddenRoot, ipdName, newIpd));
}
return (chars>len) ? len : chars;
return (int)((chars>len) ? len : chars);
}
/// Send data if possible. returns nBytes on success, 0 on no data, or -1 on failure
@ -413,12 +512,13 @@ int net_esp8266_send(JsNetwork *net, int sckt, const void *buf, size_t len) {
jsvUnLock(msg);
// doesn't seem to return this until it's done, but we need to wait anyway
if (esp8266_wait_for(">",10000,true)) {
if (esp8266_wait_for(">",1000,true)) {
size_t i;
for (i=0;i<len;i++)
jshTransmit(networkGetCurrent()->data.device, ((unsigned char *)buf)[i]);
net_esp8266_setState(ESP8266_WAIT_SEND_OK, 10000, 0, "Send failed");
return len;
currentSocket = sckt;
net_esp8266_setState(ESP8266_WAIT_SEND_OK, 2000, 0, "Send failed");
return (int)len;
}
return -1; // broken!
}