arduino/libraries/Adafruit_ESP8266/Adafruit_ESP8266.cpp
2018-11-03 20:21:33 +00:00

270 lines
9.4 KiB
C++

/*------------------------------------------------------------------------
An Arduino library for the ESP8266 WiFi-serial bridge
https://www.adafruit.com/product/2282
The ESP8266 is a 3.3V device. Safe operation with 5V devices (most
Arduino boards) requires a logic-level shifter for TX and RX signals.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried and Phil Burgess for Adafruit Industries.
MIT license, all text above must be included in any redistribution.
------------------------------------------------------------------------*/
#include "Adafruit_ESP8266.h"
// Constructor
Adafruit_ESP8266::Adafruit_ESP8266(Stream *s, Stream *d, int8_t r) :
stream(s), debug(d), reset_pin(r), host(NULL), writing(false) {
setTimeouts();
};
// Override various timings. Passing 0 for an item keeps current setting.
void Adafruit_ESP8266::setTimeouts(
uint32_t rcv, uint32_t rst, uint32_t con, uint32_t ipd) {
if(rcv) {
stream->setTimeout(rcv);
receiveTimeout = rcv;
}
if(rst) resetTimeout = rst;
if(con) connectTimeout = con;
if(ipd) ipdTimeout = ipd;
}
// Override boot marker string, or pass NULL to restore default.
void Adafruit_ESP8266::setBootMarker(Fstr *s) {
bootMarker = s ? s : defaultBootMarker;
}
// Anything printed to the EPS8266 object will be split to both the WiFi
// and debug streams. Saves having to print everything twice in debug code.
size_t Adafruit_ESP8266::write(uint8_t c) {
if(debug) {
if(!writing) {
debug->print(F("---> "));
writing = true;
}
debug->write(c);
}
return stream->write(c);
}
// Equivalent to Arduino Stream find() function, but with search string in
// flash/PROGMEM rather than RAM-resident. Returns true if string found
// (any further pending input remains in stream), false if timeout occurs.
// Can optionally pass NULL (or no argument) to read/purge the OK+CR/LF
// returned by most AT commands. The ipd flag indicates this call follows
// a CIPSEND request and might be broken into multiple sections with +IPD
// delimiters, which must be parsed and handled (as the search string may
// cross these delimiters and/or contain \r or \n itself).
boolean Adafruit_ESP8266::find(Fstr *str, boolean ipd) {
uint8_t stringLength, matchedLength = 0;
int c;
boolean found = false;
uint32_t t, save;
uint16_t bytesToGo = 0;
if(ipd) { // IPD stream stalls really long occasionally, what gives?
save = receiveTimeout;
setTimeouts(ipdTimeout);
}
if(str == NULL) str = F("OK\r\n");
stringLength = strlen_P((Pchr *)str);
if(debug && writing) {
debug->print(F("<--- '"));
writing = false;
}
for(t = millis();;) {
if(ipd && !bytesToGo) { // Expecting next IPD marker?
if(find(F("+IPD,"))) { // Find marker in stream
for(;;) {
if((c = stream->read()) > 0) { // Read subsequent chars...
if(debug) debug->write(c);
if(c == ':') break; // ...until delimiter.
bytesToGo = (bytesToGo * 10) + (c - '0'); // Keep count
t = millis(); // Timeout resets w/each byte received
} else if(c < 0) { // No data on stream, check for timeout
if((millis() - t) > receiveTimeout) goto bail;
} else goto bail; // EOD on stream
}
} else break; // OK (EOD) or ERROR
}
if((c = stream->read()) > 0) { // Character received?
if(debug) debug->write(c); // Copy to debug stream
bytesToGo--;
if(c == pgm_read_byte((Pchr *)str +
matchedLength)) { // Match next byte?
if(++matchedLength == stringLength) { // Matched whole string?
found = true; // Winner!
break;
}
} else { // Character mismatch; reset match pointer+counter
matchedLength = 0;
}
t = millis(); // Timeout resets w/each byte received
} else if(c < 0) { // No data on stream, check for timeout
if((millis() - t) > receiveTimeout) break; // You lose, good day sir
} else break; // End-of-data on stream
}
bail: // Sorry, dreaded goto. Because nested loops.
if(debug) debug->println('\'');
if(ipd) setTimeouts(save);
return found;
}
// Read from ESP8266 stream into RAM, up to a given size. Max number of
// chars read is 1 less than this, so NUL can be appended on string.
int Adafruit_ESP8266::readLine(char *buf, int bufSiz) {
if(debug && writing) {
debug->print(F("<--- '"));
writing = false;
}
int bytesRead = stream->readBytesUntil('\r', buf, bufSiz-1);
buf[bytesRead] = 0;
if(debug) {
debug->print(buf);
debug->println('\'');
}
while(stream->read() != '\n'); // Discard thru newline
return bytesRead;
}
// ESP8266 is reset by momentarily connecting RST to GND. Level shifting is
// not necessary provided you don't accidentally set the pin to HIGH output.
// It's generally safe-ish as the default Arduino pin state is INPUT (w/no
// pullup) -- setting to LOW provides an open-drain for reset.
// Returns true if expected boot message is received (or if RST is unused),
// false otherwise.
boolean Adafruit_ESP8266::hardReset(void) {
if(reset_pin < 0) return true;
digitalWrite(reset_pin, LOW);
pinMode(reset_pin, OUTPUT); // Open drain; reset -> GND
delay(10); // Hold a moment
pinMode(reset_pin, INPUT); // Back to high-impedance pin state
return find(bootMarker); // Purge boot message from stream
}
// Soft reset. Returns true if expected boot message received, else false.
boolean Adafruit_ESP8266::softReset(void) {
boolean found = false;
uint32_t save = receiveTimeout; // Temporarily override recveive timeout,
receiveTimeout = resetTimeout; // reset time is longer than normal I/O.
println(F("AT+RST")); // Issue soft-reset command
if(find(bootMarker)) { // Wait for boot message
println(F("ATE0")); // Turn off echo
found = find(); // OK?
}
receiveTimeout = save; // Restore normal receive timeout
return found;
}
// For interactive debugging...shuttle data between Serial Console <-> WiFi
void Adafruit_ESP8266::debugLoop(void) {
if(!debug) for(;;); // If no debug connection, nothing to do.
debug->println(F("\n========================"));
for(;;) {
if(debug->available()) stream->write(debug->read());
if(stream->available()) debug->write(stream->read());
}
}
// Connect to WiFi access point. SSID and password are flash-resident
// strings. May take several seconds to execute, this is normal.
// Returns true on successful connection, false otherwise.
boolean Adafruit_ESP8266::connectToAP(Fstr *ssid, Fstr *pass) {
char buf[256];
println(F("AT+CWMODE=1")); // WiFi mode = Sta
readLine(buf, sizeof(buf));
if(!(strstr_P(buf, (Pchr *)F("OK")) ||
strstr_P(buf, (Pchr *)F("no change")))) return false;
print(F("AT+CWJAP=\"")); // Join access point
print(ssid);
print(F("\",\""));
print(pass);
println('\"');
uint32_t save = receiveTimeout; // Temporarily override recv timeout,
receiveTimeout = connectTimeout; // connection time is much longer!
boolean found = find(); // Await 'OK' message
receiveTimeout = save; // Restore normal receive timeout
if(found) {
println(F("AT+CIPMUX=0")); // Set single-client mode
found = find(); // Await 'OK'
}
return found;
}
void Adafruit_ESP8266::closeAP(void) {
println(F("AT+CWQAP")); // Quit access point
find(); // Purge 'OK'
}
// Open TCP connection. Hostname is flash-resident string.
// Returns true on successful connection, else false.
boolean Adafruit_ESP8266::connectTCP(Fstr *h, int port) {
print(F("AT+CIPSTART=\"TCP\",\""));
print(h);
print(F("\","));
println(port);
if(find(F("Linked"))) {
host = h;
return true;
}
return false;
}
void Adafruit_ESP8266::closeTCP(void) {
println(F("AT+CIPCLOSE"));
find(F("Unlink\r\n"));
}
// Requests page from currently-open TCP connection. URL is
// flash-resident string. Returns true if request issued successfully,
// else false. Calling function should then handle data returned, may
// need to parse IPD delimiters (see notes in find() function.
// (Can call find(F("Unlink"), true) to dump to debug.)
boolean Adafruit_ESP8266::requestURL(Fstr *url) {
print(F("AT+CIPSEND="));
println(25 + strlen_P((Pchr *)url) + strlen_P((Pchr *)host));
if(find(F("> "))) { // Wait for prompt
print(F("GET ")); // 4
print(url);
print(F(" HTTP/1.1\r\nHost: ")); // 17
print(host);
print(F("\r\n\r\n")); // 4
return(find()); // Gets 'SEND OK' line
}
return false;
}
// Requests page from currently-open TCP connection. URL is
// character string in SRAM. Returns true if request issued successfully,
// else false. Calling function should then handle data returned, may
// need to parse IPD delimiters (see notes in find() function.
// (Can call find(F("Unlink"), true) to dump to debug.)
boolean Adafruit_ESP8266::requestURL(char* url) {
print(F("AT+CIPSEND="));
println(25 + strlen(url) + strlen_P((Pchr *)host));
if(find(F("> "))) { // Wait for prompt
print(F("GET ")); // 4
print(url);
print(F(" HTTP/1.1\r\nHost: ")); // 17
print(host);
print(F("\r\n\r\n")); // 4
return(find()); // Gets 'SEND OK' line
}
return false;
}