mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
Ok, now we have a working-ish server and client
This commit is contained in:
parent
46f097dce5
commit
cee2f638a1
@ -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!
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user