/*- * Copyright (c) 2012 Darran Hunt (darran [at] hunt dot net dot nz) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * @file WiFly RN-XV Library */ #include "WiFlyHQ.h" /* For free memory check */ extern unsigned int __bss_end; extern unsigned int __heap_start; extern void *__brkval; #undef DEBUG #ifdef DEBUG #define DPRINT(item) debug.print(item) #define DPRINTLN(item) debug.println(item) #else #define DPRINT(item) #define DPRINTLN(item) #endif #define WIFLY_STATUS_TCP_MASK 0x000F #define WIFLY_STATUS_TCP_OFFSET 0 #define WIFLY_STATUS_ASSOC_MASK 0x0001 #define WIFLY_STATUS_ASSOC_OFFSET 4 #define WIFLY_STATUS_AUTHEN_MASK 0x0001 #define WIFLY_STATUS_AUTHEN_OFFSET 5 #define WIFLY_STATUS_DNS_SERVER_MASK 0x0001 #define WIFLY_STATUS_DNS_SERVER_OFFSET 6 #define WIFLY_STATUS_DNS_FOUND_MASK 0x0001 #define WIFLY_STATUS_DNS_FOUND_OFFSET 7 #define WIFLY_STATUS_CHAN_MASK 0x000F #define WIFLY_STATUS_CHAN_OFFSET 9 #define WIFLY_TCP_IDLE 0 #define WIFLY_TCP_CONNECTED 1 #define WIFLY_TCP_NOIP 3 #define WIFLY_TCP_CONNECTING 4 /* WiFi data rates */ #define WIFLY_RATE_1MBPS 0 #define WIFLY_RATE_2MBPS 1 #define WIFLY_RATE_5_5MBPS 2 /* 5.5 MBps */ #define WIFLY_RATE_6MBPS 8 #define WIFLY_RATE_9MBPS 9 #define WIFLY_RATE_11MBPS 3 #define WIFLY_RATE_12MBPS 10 #define WIFLY_RATE_18MBPS 11 #define WIFLY_RATE_24MBPS 12 /* Default */ #define WIFLY_RATE_36MBPS 13 #define WIFLY_RATE_48MBPS 14 #define WIFLY_RATE_54MBPS 15 /* Work around a bug with PROGMEM and PSTR where the compiler always * generates warnings. */ #undef PROGMEM #define PROGMEM __attribute__(( section(".progmem.data") )) #undef PSTR #define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];})) /* Request and response strings in PROGMEM */ const prog_char req_GetIP[] PROGMEM = "get ip\r"; const prog_char resp_IP[] PROGMEM = "IP="; const prog_char resp_NM[] PROGMEM = "NM="; const prog_char resp_GW[] PROGMEM = "GW="; const prog_char resp_Host[] PROGMEM = "HOST="; const prog_char resp_DHCP[] PROGMEM = "DHCP="; const prog_char req_GetMAC[] PROGMEM = "get mac\r"; const prog_char resp_MAC[] PROGMEM = "Mac Addr="; const prog_char req_GetWLAN[] PROGMEM = "get wlan\r"; const prog_char resp_SSID[] PROGMEM = "SSID="; const prog_char resp_Chan[] PROGMEM = "Chan="; const prog_char req_GetOpt[] PROGMEM = "get opt\r"; const prog_char resp_DeviceID[] PROGMEM = "DeviceId="; const prog_char req_GetUart[] PROGMEM = "get u\r"; const prog_char resp_Baud[] PROGMEM = "Baudrate="; const prog_char req_GetTime[] PROGMEM = "get time\r"; const prog_char resp_Zone[] PROGMEM = "Zone="; const prog_char req_ShowTime[] PROGMEM = "show time\r"; const prog_char resp_Uptime[] PROGMEM = "UpTime="; const prog_char resp_Time[] PROGMEM = "Time="; const prog_char req_GetDNS[] PROGMEM = "get dns\r"; const prog_char resp_DNSAddr[] PROGMEM = "Address="; const prog_char req_ShowTimeT[] PROGMEM = "show t t\r"; const prog_char resp_RTC[] PROGMEM = "RTC="; const prog_char resp_Mode[] PROGMEM = "Mode="; const prog_char req_GetComm[] PROGMEM = "get comm\r"; const prog_char resp_FlushTimeout[] PROGMEM = "FlushTimer="; const prog_char resp_FlushChar[] PROGMEM = "MatchChar="; const prog_char resp_FlushSize[] PROGMEM = "FlushSize="; const prog_char req_GetRSSI[] PROGMEM = "show rssi\r"; const prog_char resp_RSSI[] PROGMEM = "RSSI=(-"; const prog_char resp_Flags[] PROGMEM = "FLAGS=0x"; const prog_char resp_Protocol[] PROGMEM = "PROTO="; const prog_char req_GetAdhoc[] PROGMEM = "get adhoc\r"; const prog_char resp_Beacon[] PROGMEM = "Beacon="; const prog_char resp_Probe[] PROGMEM = "Probe="; const prog_char resp_Reboot[] PROGMEM = "Reboot="; const prog_char resp_Join[] PROGMEM = "Join="; const prog_char resp_Rate[] PROGMEM = "Rate="; const prog_char resp_Power[] PROGMEM = "TxPower="; const prog_char req_Scan[] PROGMEM = "scan\r"; const prog_char resp_Scan[] PROGMEM = "Scan:"; /* Request and response for specific info */ static struct { const prog_char *req; const prog_char *resp; } requests[] = { { req_GetIP, resp_IP }, /* 0 */ { req_GetIP, resp_NM }, /* 1 */ { req_GetIP, resp_GW }, /* 2 */ { req_GetMAC, resp_MAC }, /* 3 */ { req_GetWLAN, resp_SSID }, /* 4 */ { req_GetOpt, resp_DeviceID }, /* 5 */ { req_GetUart, resp_Baud }, /* 6 */ { req_ShowTime, resp_Time }, /* 7 */ { req_ShowTime, resp_Uptime }, /* 8 */ { req_GetTime, resp_Zone }, /* 9 */ { req_GetDNS, resp_DNSAddr }, /* 10 */ { req_ShowTimeT, resp_RTC }, /* 11 */ { req_GetIP, resp_DHCP }, /* 12 */ { req_GetUart, resp_Mode }, /* 13 */ { req_GetComm, resp_FlushTimeout }, /* 14 */ { req_GetComm, resp_FlushChar }, /* 15 */ { req_GetComm, resp_FlushSize }, /* 16 */ { req_GetRSSI, resp_RSSI }, /* 17 */ { req_GetIP, resp_Flags }, /* 18 */ { req_GetIP, resp_Host }, /* 19 */ { req_GetIP, resp_Protocol }, /* 20 */ { req_GetAdhoc, resp_Beacon }, /* 21 */ { req_GetAdhoc, resp_Probe }, /* 22 */ { req_GetAdhoc, resp_Reboot }, /* 23 */ { req_GetWLAN, resp_Join }, /* 24 */ { req_GetWLAN, resp_Rate }, /* 25 */ { req_GetWLAN, resp_Power }, /* 26 */ }; /* Request indices, must match table above */ typedef enum { WIFLY_GET_IP = 0, WIFLY_GET_NETMASK = 1, WIFLY_GET_GATEWAY = 2, WIFLY_GET_MAC = 3, WIFLY_GET_SSID = 4, WIFLY_GET_DEVICEID = 5, WIFLY_GET_BAUD = 6, WIFLY_GET_TIME = 7, WIFLY_GET_UPTIME = 8, WIFLY_GET_ZONE = 9, WIFLY_GET_DNS = 10, WIFLY_GET_RTC = 11, WIFLY_GET_DHCP = 12, WIFLY_GET_UART_MODE = 13, WIFLY_GET_FLUSHTIMEOUT = 14, WIFLY_GET_FLUSHCHAR = 15, WIFLY_GET_FLUSHSIZE = 16, WIFLY_GET_RSSI = 17, WIFLY_GET_IP_FLAGS = 18, WIFLY_GET_HOST = 19, WIFLY_GET_PROTOCOL = 20, WIFLY_GET_BEACON = 21, WIFLY_GET_PROBE = 22, WIFLY_GET_REBOOT = 23, WIFLY_GET_JOIN = 24, WIFLY_GET_RATE = 25, WIFLY_GET_POWER = 26, } e_wifly_requests; /** Convert a unsigned int to a string */ static int simple_utoa(uint32_t val, uint8_t base, char *buf, int size) { char tmpbuf[16]; int ind=0; uint32_t nval; int fsize=0; if (base == DEC) { do { nval = val / 10; tmpbuf[ind++] = '0' + val - (nval * 10); val = nval; } while (val); } else { do { nval = val & 0x0F; tmpbuf[ind++] = nval + ((nval < 10) ? '0' : 'A'); val >>= 4; } while (val); tmpbuf[ind++] = 'x'; tmpbuf[ind++] = '0'; } ind--; do { buf[fsize++] = tmpbuf[ind]; } while ((ind-- > 0) && (fsize < (size-1))); buf[fsize] = '\0'; return fsize; } /** Simple hex string to uint32_t */ static uint32_t atoh(char *buf) { uint32_t res=0; char ch; bool gotX = false; while ((ch=*buf++) != 0) { if (ch >= '0' && ch <= '9') { res = (res << 4) + ch - '0'; } else if (ch >= 'a' && ch <= 'f') { res = (res << 4) + ch - 'a' + 10; } else if (ch >= 'A' && ch <= 'F') { res = (res << 4) + ch - 'A' + 10; } else if ((ch == 'x') && !gotX) { /* Ignore 0x at start */ gotX = true; } else { break; } } return res; } /** Simple ASCII to unsigned int */ static uint32_t atou(const char *buf) { uint32_t res=0; while (*buf) { if ((*buf < '0') || (*buf > '9')) { break; } res = res * 10 + *buf - '0'; buf++; } return res; } /** * Convert an IPAdress to an ASCIIZ string * @param addr - the IP Address to convert * @param buf - the buffer to write the result to * @param size - the size of the buffer * @returns pointer to the result */ char *WiFly::iptoa(IPAddress addr, char *buf, int size) { uint8_t fsize=0; uint8_t ind; for (ind=0; ind<3; ind++) { fsize += simple_utoa(addr[ind], 10, &buf[fsize], size-fsize); if (fsize < (size-1)) { buf[fsize++] = '.'; } } simple_utoa(addr[ind], 10, &buf[fsize], size-fsize); return buf; } /** * Convert a dotquad IP address string to an IPAddress. * E.g. "192.168.1.100" -> { 192, 168, 1, 100 }. * @param buf - the string to convert * @returns the IPAddress form of the string */ IPAddress WiFly::atoip(char *buf) { IPAddress ip; for (uint8_t ind=0; ind<3; ind++) { ip[ind] = atou(buf); while (*buf >= '0' && *buf <= '9') { buf++; } if (*buf == '\0') break; } return ip; } WiFly::WiFly() { inCommandMode = false; exitCommand = 0; connected = false; connecting = false; dhcp = true; restoreHost = true; #ifdef DEBUG debugOn = true; #else debugOn = false; #endif dbgBuf = NULL; dbgInd = 0; dbgMax = 0; } /** * Get WiFly ready to handle commands, and determine * some initial status. */ void WiFly::init() { int8_t dhcpMode=0; lastPort = 0; lastHost[0] = 0; if (!setopt(PSTR("set u m 1"), (char *)NULL)) { debug.println(F("Failed to turn off echo")); } if (!setopt(PSTR("set sys printlvl 0"), (char *)NULL)) { debug.println(F("Failed to turn off sys print")); } if (!setopt(PSTR("set comm remote 0"), (char *)NULL)) { debug.println(F("Failed to set comm remote")); } /* update connection status */ getConnection(); DPRINT(F("tcp status: ")); DPRINT(status.tcp); DPRINT("\n\r"); DPRINT(F("assoc status: ")); DPRINT(status.assoc); DPRINT("\n\r"); DPRINT(F("authen status: ")); DPRINT(status.authen); DPRINT("\n\r"); DPRINT(F("dns status: ")); DPRINT(status.dnsServer); DPRINT("\n\r"); DPRINT(F("dns found status: ")); DPRINT(status.dnsFound); DPRINT("\n\r"); DPRINT(F("channel status: ")); DPRINT(status.channel); DPRINT("\n\r"); dhcpMode = getDHCPMode(); dhcp = !((dhcpMode == WIFLY_DHCP_MODE_OFF) || (dhcpMode == WIFLY_DHCP_MODE_SERVER)); } /** * Start the WiFly device, set it up to handle commands, obtain * some initial status (TCP connection status, WiFi association, etc). * @param serialdev - the serial stream to use to talk to the WiFly. * @param debugPrint - optional debug stream for errors and status. * @retval true - WiFly ready for use * @retval false - failed to initialise WiFly */ boolean WiFly::begin(Stream *serialdev, Stream *debugPrint) { debug.begin(debugPrint); serial = serialdev; if (!enterCommandMode()) { debug.println(F("Failed to enter command mode")); return false; } init(); if (!exitCommandMode()) { debug.println(F("Failed to exit command mode")); return false; } return true; } /** * Return number of bytes of memory available. * @returns number of bytes of free memory */ int WiFly::getFreeMemory() { int free; if ((int)__brkval == 0) free = ((int)&free) - ((int)&__bss_end); else free = ((int)&free) - ((int)__brkval); return free; } /** * Flush the incoming data from the WiFly. * @param timeout - the number of milliseconds to wait for additional data to flush. Default is 500msecs. */ void WiFly::flushRx(int timeout) { char ch; DPRINT(F("flush\n\r")); while (readTimeout(&ch,timeout)); DPRINT(F("flushed\n\r")); } /** * Write a byte to the WiFly. * @param byte - the byte to write. * @return the number of bytes written (1). */ size_t WiFly::write(uint8_t byte) { if (dbgInd < dbgMax) { dbgBuf[dbgInd++] = byte; } return serial->write(byte); } /* Read-ahead for checking for TCP stream close * A circular buffer is used to keep read-ahead bytes and * feed them back to the user. */ static char peekBuf[8]; static uint8_t peekHead = 0; /* head of buffer; new characters stored here */ static uint8_t peekTail = 0; /* Tail of buffer; characters read from here */ static uint8_t peekCount = 0; /* Number of characters in peek buffer */ /** * Return the next byte that a read() would return, but leave the * byte in the receive buffer. * @returns the next byte that would be read * @retval -1 - no data in receive buffer */ int WiFly::peek() { if (peekCount == 0) { return serial->peek(); } else { return peekBuf[peekTail]; } } /** Check for a state change on the stream * @param str progmem string to check stream input for * @param peeked true if the caller peeked the first char of the string, * false if the caller read the character already * @retval true - the state change was matched * @retval false - state change not matched * @note A side effect of this function is that it will store * the unmatched string in the read-ahead peek buffer since * it has to read the characters from the WiFly to check for * the match. The peek buffer is used to feed those characters * to the user ahead of reading any more characters from the WiFly. */ boolean WiFly::checkStream(const prog_char *str, boolean peeked) { char next; #ifdef DEBUG debug.print(F("checkStream: ")); debug.println((const __FlashStringHelper *)str); #endif if (peekCount > 0) { uint8_t ind=0; if (peeked) { str++; ind = 1; } for (; ind sizeof(peekBuf)) { pind = 0; } if (peekBuf[pind] != next) { /* Not a match */ return false; } /* peeked characters match */ } /* string matched so far, keep reading */ } else if (!peeked) { /* Already read and matched the first character before being called */ str++; } next = pgm_read_byte(str++); while (readTimeout(&peekBuf[peekHead]),50) { if (peekBuf[peekHead] != next) { if (++peekHead > sizeof(peekBuf)) { peekHead = 0; } peekCount++; if (peekCount > sizeof(peekBuf)) { debug.println(F("ERROR peek.1 buffer overlow")); } break; } if (++peekHead > sizeof(peekBuf)) { peekHead = 0; } peekCount++; if (peekCount > sizeof(peekBuf)) { debug.println(F("ERROR peek.2 buffer overlow")); } next = pgm_read_byte(str++); if (next == '\0') { /* Done - got a match */ peekCount = 0; // discard peeked bytes peekTail = 0; peekHead = 0; return true; } } return false; } /** Check for stream close, if its closed * we will quickly receive *CLOS* from the WiFly * @param peeked - set to true if first character of *CLOS* was peeked, or false if it has been read. * @retval true - stream closed * @retval false - stream not closed */ boolean WiFly::checkClose(boolean peeked) { if (checkStream(PSTR("*CLOS*"), peeked)) { connected = false; DPRINTLN(F("Stream closed")); return true; } return false; } /** Check for stream open. * @param peeked - set to true if first character of *OPEN* was peeked, or false if it has been read. * @retval true - stream opened * @retval false - stream not opened */ boolean WiFly::checkOpen(boolean peeked) { if (checkStream(PSTR("*OPEN*"), peeked)) { connected = true; DPRINTLN(F("Stream opened")); return true; } return false; } /** Read the next byte from the WiFly. * @returns the byte read * @retval -1 - nothing in the receive buffer to read */ int WiFly::read() { int data = -1; /* Any data in peek buffer? */ if (peekCount) { data = (uint8_t)peekBuf[peekTail++]; if (peekTail > sizeof(peekBuf)) { peekTail = 0; } peekCount--; } else { data = serial->read(); /* TCP connected? Check for close */ if (connected && data == '*') { if (checkClose(false)) { return -1; } else { data = (uint8_t)peekBuf[peekTail++]; if (peekTail > sizeof(peekBuf)) { peekTail = 0; } peekCount--; } } } return data; } /** Check to see if data is available to be read. * @returns the number of bytes that are available to read. * @retval 0 - no data available * @retval -1 - active TCP connection was closed, */ int WiFly::available() { int count; count = serial->available(); if (count > 0) { if (debugOn) { debug.print(F("available: peek = ")); debug.println((char)serial->peek()); } /* Check for TCP stream closure */ if (serial->peek() == '*') { if (connected) { if (checkClose(true)) { return -1; } else { return peekCount + serial->available(); } } else { checkOpen(true); return peekCount + serial->available(); } } } return count+peekCount; } void WiFly::flush() { serial->flush(); } /** Hex dump a string */ void WiFly::dump(const char *str) { while (*str) { debug.print(*str,HEX); debug.print(' '); str++; } debug.println(); } /** Send a string to the WiFly */ void WiFly::send(const char *str) { DPRINT(F("send: ")); DPRINT(str); DPRINT("\n\r"); print(str); //serial->print(str); } /** Send a character to the WiFly */ void WiFly::send(const char ch) { write(ch); //serial->write(ch); } /** Send a string from PROGMEM to the WiFly */ void WiFly::send_P(const prog_char *str) { DPRINT(F("send_P: ")); DPRINTLN((const __FlashStringHelper *)str); print((const __FlashStringHelper *)str); } /** * Start a capture of all the characters recevied from the WiFly. * @param size - the size of the capture buffer. This will be malloced. */ void WiFly::dbgBegin(int size) { if (dbgBuf != NULL) { free(dbgBuf); } dbgBuf = (char *)malloc(size); dbgInd = 0; dbgMax = size; } /** Stop debug capture and free buffer */ void WiFly::dbgEnd() { if (dbgBuf != NULL) { free(dbgBuf); dbgBuf = NULL; } dbgInd = 0; dbgMax = 0; } /** Do a hex and ASCII dump of the capture buffer, and free the buffer. */ void WiFly::dbgDump() { int ind; if (dbgBuf == NULL) { return; } if (dbgInd > 0) { debug.println(F("debug dump")); for (ind=0; indavailable() > 0) { ch = serial->read(); *chp = ch; if (dbgInd < dbgMax) { dbgBuf[dbgInd++] = ch; } if (debugOn) { debug.print(ind++); debug.print(F(": ")); debug.print(ch,HEX); if (isprint(ch)) { debug.print(' '); debug.print(ch); } debug.println(); } return true; } } if (debugOn) { debug.println(F("readTimeout - timed out")); } return false; } static char prompt[16]; static boolean gotPrompt = false; /** Scan the input data for the WiFLy prompt. This is a string starting with a '<' and * ending with a '>'. Store the prompt for future use. */ boolean WiFly::setPrompt() { char ch; while (readTimeout(&ch,500)) { if (ch == '<') { uint8_t ind = 1; prompt[0] = ch; while (ind < (sizeof(prompt)-4)) { if (readTimeout(&ch,500)) { prompt[ind++] = ch; if (ch == '>') { if (readTimeout(&ch,500)) { if (ch == ' ') { prompt[ind++] = ch; //prompt[ind++] = '\r'; //prompt[ind++] = '\n'; prompt[ind] = 0; DPRINT(F("setPrompt: ")); DPRINT(prompt); DPRINT("\n\r"); gotPrompt = true; gets(NULL,0); return true; } else { /* wrong character */ return false; } } else { /* timeout */ return false; } } } else { /* timeout */ return false; } } return false; } } return false; } /** See if the prompt is somewhere in the string */ boolean WiFly::checkPrompt(const char *str) { if (strstr(str, prompt) != NULL) { return true; } else { return false; } } /** * Read characters from the WiFly and match them against the * string. Ignore any leading characters that don't match. Keep * reading, discarding the input, until the string is matched * or until no characters are received for the timeout duration. * @param str The string to match * @param timeout fail if no data received for this period (in milliseconds). * @retval true - a match was found * @retval false - no match found, timeout reached */ boolean WiFly::match(const char *str, uint16_t timeout) { const char *match = str; char ch; #ifdef DEBUG if (debugOn) { debug.print(F("match: ")); debug.println(str); } #endif if ((match == NULL) || (*match == '\0')) { return true; } /* find first character */ while (readTimeout(&ch,timeout)) { if (ch == *match) { match++; } else { match = str; if (ch == *match) { match++; } } if (*match == '\0') { DPRINT(F("match: true\n\r")); return true; } } DPRINT(F("match: false\n\r")); return false; } /** * Read characters from the WiFly and match them against the * progmem string. Ignore any leading characters that don't match. Keep * reading, discarding the input, until the string is matched * or until no characters are received for the timeout duration. * @param str The string to match, in progmem. * @param timeout fail if no data received for this period (in milliseconds). * @retval true - a match was found * @retval false - no match found, timeout reached */ boolean WiFly::match_P(const prog_char *str, uint16_t timeout) { const prog_char *match = str; char ch, ch_P; if (debugOn) { debug.print(F("match_P: ")); debug.println((const __FlashStringHelper *)str); } ch_P = pgm_read_byte(match); if (ch_P == '\0') { /* Null string always matches */ return true; } while (readTimeout(&ch,timeout)) { if (ch == ch_P) { match++; } else { /* Restart match */ match = str; if (ch == pgm_read_byte(match)) { match++; } } ch_P = pgm_read_byte(match); if (ch_P == '\0') { DPRINT(F("match_P: true\n\r")); return true; } } DPRINT(F("match_P: false\n\r")); return false; } /** * Read characters from the WiFly and match them against the set of * progmem strings. Ignore any leading characters that don't match. Keep * reading, discarding the input, until one of the strings is matched * or until no characters are received for the timeout duration.
* Example: res = multiMatch_P(500, 3, F("first="), F("second="), F("closed"));
* Will return 0 if "first=" is matched, 1 if "second=" is matched, 2 if "closed" is * matched, or -1 if nothing is matched and no data is received for 500 milliseconds. * @param timeout - fail if no data received for this period (in milliseconds). * @param count - the number of strings in the str array * @param ... A list of count progmem strings to match * @returns the index of the matching string * @retval -1 - no match found, timeout reached */ int WiFly::multiMatch_P(uint16_t timeout, uint8_t count, ...) { const prog_char *str[20]; int ind; va_list ap; va_start(ap, count); if (count > 20) { count = 20; } for (ind=0; indwrite('\r'); if (getPrompt()) { inCommandMode = true; return true; } } } } /* See if we're already in command mode */ DPRINT(F("Check in command mode\n\r")); serial->write('\r'); if (getPrompt()) { inCommandMode = true; DPRINT(F("Already in command mode\n\r")); return true; } for (retry=0; retry<5; retry++) { DPRINT(F("send $$$ ")); DPRINT(retry); DPRINT("\n\r"); delay(250); send_P(PSTR("$$$")); delay(250); if (match_P(PSTR("CMD\r\n"), 500)) { inCommandMode = true; return true; } } return false; } /** Take the WiFly out of command mode */ boolean WiFly::exitCommandMode() { if (!inCommandMode) { return true; } send_P(PSTR("exit\r")); if (match_P(PSTR("EXIT\r\n"), 500)) { inCommandMode = false; return true; } else { debug.println(F("Failed to exit\n\r")); return false; } } /*read data into the buffer until its full or the next character takes more than timeout millis to arrive. return the number of byted read*/ int WiFly::readBufTimeout(char* buf, int size, uint16_t timeout){ int pos=0; DPRINTLN("reading from serial.."); while(readTimeout(buf+pos, timeout)&& pos"; } /* Get WiFly connection status */ uint16_t WiFly::getConnection() { char buf[16]; uint16_t res; int len; if (!startCommand()) { debug.println(F("getCon: failed to start")); return 0; } //dbgBegin(256); DPRINT(F("getCon\n\r")); DPRINT(F("show c\n\r")); send_P(PSTR("show c\r")); len = gets(buf, sizeof(buf)); if (checkPrompt(buf)) { /* Got prompt first */ len = gets(buf, sizeof(buf)); } else { getPrompt(); } if (len <= 4) { res = (uint16_t)atoh(buf); } else { res = (uint16_t)atoh(&buf[len-4]); } status.tcp = (res >> WIFLY_STATUS_TCP_OFFSET) & WIFLY_STATUS_TCP_MASK; status.assoc = (res >> WIFLY_STATUS_ASSOC_OFFSET) & WIFLY_STATUS_ASSOC_MASK; status.authen = (res >> WIFLY_STATUS_AUTHEN_OFFSET) & WIFLY_STATUS_AUTHEN_MASK; status.dnsServer = (res >> WIFLY_STATUS_DNS_SERVER_OFFSET) & WIFLY_STATUS_DNS_SERVER_MASK; status.dnsFound = (res >> WIFLY_STATUS_DNS_FOUND_OFFSET) & WIFLY_STATUS_DNS_FOUND_MASK; status.channel = (res >> WIFLY_STATUS_CHAN_OFFSET) & WIFLY_STATUS_CHAN_MASK; finishCommand(); if (status.tcp == WIFLY_TCP_CONNECTED) { connected = true; if (debugOn) debug.println(F("getCon: TCP connected")); } else { connected = false; if (debugOn) debug.println(F("getCon: TCP disconnected")); } return res; } /** Get local IP address */ char *WiFly::getIP(char *buf, int size) { char *chp = buf; if (getopt(WIFLY_GET_IP, buf, size)) { /* Trim off port */ while (*chp && *chp != ':') { chp++; } } *chp = '\0'; return buf; } /** Get local port */ uint16_t WiFly::getPort() { char buf[22]; uint8_t ind; if (getopt(WIFLY_GET_IP, buf, sizeof(buf))) { /* Trim off IP */ for (ind=0; buf[ind]; ind++) { if (buf[ind] == ':') { ind++; break; } } return (uint16_t)atou(&buf[ind]); } return 0; } /** Get remote IP address */ char *WiFly::getHostIP(char *buf, int size) { char *chp = buf; if (getopt(WIFLY_GET_HOST, buf, size)) { /* Trim off port */ while (*chp && *chp != ':') { chp++; } } *chp = '\0'; return buf; } /** Get remote port */ uint16_t WiFly::getHostPort() { char buf[22]; uint8_t ind; if (getopt(WIFLY_GET_HOST, buf, sizeof(buf))) { /* Trim off IP */ for (ind=0; buf[ind]; ind++) { if (buf[ind] == ':') { ind++; break; } } return (uint16_t)atou(&buf[ind]); } return 0; } char *WiFly::getNetmask(char *buf, int size) { return getopt(WIFLY_GET_NETMASK, buf, size); } char *WiFly::getGateway(char *buf, int size) { return getopt(WIFLY_GET_GATEWAY, buf, size); } char *WiFly::getDNS(char *buf, int size) { return getopt(WIFLY_GET_DNS, buf, size); } char *WiFly::getMAC(char *buf, int size) { return getopt(WIFLY_GET_MAC, buf, size); } char *WiFly::getSSID(char *buf, int size) { return getopt(WIFLY_GET_SSID, buf, size); } uint8_t WiFly::getJoin() { return getopt(WIFLY_GET_JOIN); } char *WiFly::getDeviceID(char *buf, int size) { return getopt(WIFLY_GET_DEVICEID, buf, size); } uint32_t WiFly::getopt(int opt, uint8_t base) { char buf[11]; if (!getopt(opt, buf, sizeof(buf))) { return 0; } if (base == DEC) { return atou(buf); } else { return atoh(buf); } } uint8_t WiFly::getIpFlags() { return getopt(WIFLY_GET_IP_FLAGS, HEX); } uint32_t WiFly::getBaud() { return getopt(WIFLY_GET_BAUD); } char *WiFly::getTime(char *buf, int size) { return getopt(WIFLY_GET_TIME, buf, size); } uint32_t WiFly::getRTC() { return getopt(WIFLY_GET_RTC); } /** * Do a DNS lookup to find the ip address of the specified hostname * @param hostname - host to lookup * @param buf - buffer to return the ip address in * @param size - size of the buffer * @return true on success, false on failure */ bool WiFly::getHostByName(const char *hostname, char *buf, int size) { if (startCommand()) { send_P(PSTR("lookup ")); send(hostname); send("\r"); if (match(hostname, 5000)) { char ch; readTimeout(&ch); // discard '=' gets(buf, size); getPrompt(); finishCommand(); return true; } getPrompt(); finishCommand(); } /* lookup failed */ return false; } uint32_t WiFly::getUptime() { return getopt(WIFLY_GET_UPTIME); } uint8_t WiFly::getTimezone() { return getopt(WIFLY_GET_ZONE); } uint8_t WiFly::getUartMode() { return getopt(WIFLY_GET_UART_MODE, HEX); } int8_t WiFly::getDHCPMode() { char buf[16]; int8_t mode; if (!getopt(WIFLY_GET_DHCP, buf, sizeof(buf))) { return -1; } if (strncmp_P(buf, PSTR("OFF"), 3) == 0) { mode = 0; } else if (strncmp_P(buf, PSTR("ON"), 2) == 0) { mode = 1; } else if (strncmp_P(buf, PSTR("AUTOIP"), 6) == 0) { mode = 2; } else if (strncmp_P(buf, PSTR("CACHE"), 5) == 0) { mode = 3; } else if (strncmp_P(buf, PSTR("SERVER"), 6) == 0) { mode = 4; } else { mode = -1; // unknown } return mode; } static struct { uint8_t protocol; char name[6]; } protmap[] __attribute__((__progmem__)) = { { WIFLY_PROTOCOL_UDP, "UDP," }, { WIFLY_PROTOCOL_TCP, "TCP," }, { WIFLY_PROTOCOL_SECURE, "SECUR" }, { WIFLY_PROTOCOL_TCP_CLIENT, "TCP_C" }, { WIFLY_PROTOCOL_HTTP, "HTTP," }, { WIFLY_PROTOCOL_RAW, "RAW," }, { WIFLY_PROTOCOL_SMTP, "SMTP," } }; uint8_t WiFly::getProtocol() { char buf[50]; int8_t prot=0; if (!getopt(WIFLY_GET_PROTOCOL, buf, sizeof(buf))) { return -1; } for (uint8_t ind=0; ind < (sizeof(protmap)/sizeof(protmap[0])); ind++) { if (strstr_P(buf, protmap[ind].name) != NULL) { prot |= pgm_read_byte(&protmap[ind].protocol); } } return prot; } uint16_t WiFly::getFlushTimeout() { return getopt(WIFLY_GET_FLUSHTIMEOUT); } uint16_t WiFly::getFlushSize() { return getopt(WIFLY_GET_FLUSHSIZE); } uint8_t WiFly::getFlushChar() { return getopt(WIFLY_GET_FLUSHCHAR, HEX); } int8_t WiFly::getRSSI() { return -(int8_t)getopt(WIFLY_GET_RSSI); } const prog_char res_AOK[] PROGMEM = "AOK\r\n"; const prog_char res_ERR[] PROGMEM = "ERR: "; /* Get the result from a set operation * Should be AOK or ERR */ boolean WiFly::getres(char *buf, int size) { const prog_char *setResult[] = { PSTR("ERR: "), PSTR("AOK\r\n") }; int8_t res; DPRINTLN(F("getres")); res = multiMatch_P(setResult, 2); if (res == 1) { return true; } else if (res == 0) { gets(buf, size); debug.print(F("ERR: ")); debug.println(buf); } else { /* timeout */ DPRINTLN(F("timeout")); strncpy_P(buf, PSTR(""), size); } return false; } /** Set an option to an unsigned integer value */ boolean WiFly::setopt(const prog_char *opt, const uint32_t value, uint8_t base) { char buf[11]; simple_utoa(value, base, buf, sizeof(buf)); return setopt(opt, buf); } /* Set an option, confirm ok status */ boolean WiFly::setopt(const prog_char *cmd, const char *buf, const __FlashStringHelper *buf_P) { char rbuf[16]; boolean res; if (!startCommand()) { return false; } send_P(cmd); if (buf_P != NULL) { send(' '); send_P((const prog_char *)buf_P); } else if (buf != NULL) { send(' '); send(buf); } send('\r'); res = getres(rbuf, sizeof(rbuf)); getPrompt(); finishCommand(); return res; } /* Save current configuration */ boolean WiFly::save() { bool res = false; if (!startCommand()) { return false; } send_P(PSTR("save\r")); if (match_P(PSTR("Storing"))) { getPrompt(); res = true; } finishCommand(); return res; } /** Reboots the WiFly. * @note Depending on the shield, this may also reboot the Arduino. */ boolean WiFly::reboot() { if (!startCommand()) { return false; } send_P(PSTR("reboot\r")); if (!match_P(PSTR("*Reboot*"))) { finishCommand(); return false; } delay(5000); inCommandMode = false; exitCommand = 0; init(); return true; } /** Restore factory default settings */ boolean WiFly::factoryRestore() { bool res = false; if (!startCommand()) { return false; } send_P(PSTR("factory RESTORE\r")); if (match_P(PSTR("Set Factory Defaults"))) { getPrompt(); res = true; } finishCommand(); return res; } boolean WiFly::setDeviceID(const char *buf) { return setopt(PSTR("set o d"), buf); } bool WiFly::setJoin(uint8_t join) { return setopt(PSTR("set wlan join"), join); } boolean WiFly::setIP(const char *buf) { return setopt(PSTR("set ip address"), buf); } boolean WiFly::setIP(const __FlashStringHelper *buf) { return setopt(PSTR("set ip address"), NULL, buf); } /** Set local port */ boolean WiFly::setPort(const uint16_t port) { return setopt(PSTR("set ip localport"), port); } boolean WiFly::setHostIP(const __FlashStringHelper *buf) { return setopt(PSTR("set ip host"), NULL, buf); } boolean WiFly::setHostIP(const char *buf) { return setopt(PSTR("set ip host"), buf); } boolean WiFly::setHostPort(const uint16_t port) { return setopt(PSTR("set ip remote"), port); } boolean WiFly::setHost(const char *buf, uint16_t port) { bool res; if (!startCommand()) { debug.println(F("SetHost: failed to start command")); return false; } res = setHostIP(buf); res = res && setHostPort(port); finishCommand(); return res; } boolean WiFly::setNetmask(const char *buf) { return setopt(PSTR("set ip netmask"), buf); } boolean WiFly::setNetmask(const __FlashStringHelper *buf) { return setopt(PSTR("set ip netmask"), NULL, buf); } boolean WiFly::setGateway(const char *buf) { return setopt(PSTR("set ip gateway"), buf); } boolean WiFly::setDNS(const char *buf) { return setopt(PSTR("set dns address"), buf); } boolean WiFly::setDHCP(const uint8_t mode) { char buf[2]; if (mode > 9) { return false; } buf[0] = '0' + mode; buf[1] = 0; return setopt(PSTR("set ip dhcp"), buf); } boolean WiFly::setProtocol(const uint8_t protocol) { return setopt(PSTR("set ip protocol"), protocol, HEX); } boolean WiFly::setIpProtocol(const uint8_t protocol) { return setProtocol(protocol); } boolean WiFly::setIpFlags(const uint8_t protocol) { return setopt(PSTR("set ip protocol"), protocol, HEX); } /** Set NTP server IP address */ boolean WiFly::setTimeAddress(const char *buf) { return setopt(PSTR("set time address"), buf); } /** Set NTP server port */ boolean WiFly::setTimePort(const uint16_t port) { return setopt(PSTR("set time port"), port); } /** Set timezone for calculating local time based on NTP time. */ boolean WiFly::setTimezone(const uint8_t zone) { return setopt(PSTR("set time zone"), zone); } /** Set the NTP update period */ boolean WiFly::setTimeEnable(const uint16_t period) { return setopt(PSTR("set time enable"), period); } boolean WiFly::setUartMode(const uint8_t mode) { /* Always set NOECHO, need to keep echo off for library to function correctly */ return setopt(PSTR("set uart mode"), mode | WIFLY_UART_MODE_NOECHO, HEX); } /** Set the UDP broadcast time interval. * @param seconds the number of seconds between broadcasts. * Set this to zero to disable broadcasts. * @return true if sucessful, else false. */ boolean WiFly::setBroadcastInterval(const uint8_t seconds) { return setopt(PSTR("set broadcast interval"), seconds, HEX); } /** * Enable the UDP auto-pair functionality. * The WiFly will automatically set the Host IP and port * to match the sender of the last UDP packet. */ boolean WiFly::enableUdpAutoPair() { udpAutoPair = true; setHostIP(F("0.0.0.0")); setIpFlags(getIpFlags() | WIFLY_FLAG_UDP_AUTO_PAIR); disableHostRestore(); return true; } boolean WiFly::disableUdpAutoPair() { udpAutoPair = false; setIpFlags(getIpFlags() & ~WIFLY_FLAG_UDP_AUTO_PAIR); return true; } /** * Set comms flush timeout. When using data trigger mode, * this timer defines how long the WiFly will wait for * the next character from the sketch before sending what * it has collected so far as a packet. * @param timeout number of milliseconds to wait before * flushing the packet. * @note the flush timeout change does not actually work * unless the config is saved and the wifly is rebooted. */ boolean WiFly::setFlushTimeout(const uint16_t timeout) { return setopt(PSTR("set comm time"), timeout); } /** Set the comms flush character. 0 disables the feature. * A packet will be sent whenever this character is sent * to the WiFly. Used for auto connect mode or UDP packet sending. * @param flushChar send a packet when this character is sent. * Set to 0 to disable character based flush. */ boolean WiFly::setFlushChar(const char flushChar) { return setopt(PSTR("set comm match"), (uint8_t)flushChar, HEX); } /** Set the comms flush size. * A packet will be sent whenever this many characters are sent. * @param size number of characters to buffer before sending a packet */ boolean WiFly::setFlushSize(uint16_t size) { if (size > 1460) { /* Maximum size */ size = 1460; } return setopt(PSTR("set comm size"), size); } /** Set the WiFly IO function option */ boolean WiFly::setIOFunc(const uint8_t func) { return setopt(PSTR("set sys iofunc"), func, HEX); } /** * Enable data trigger mode. This mode will automatically send a new packet based on several conditions: * 1. If no characters are send to the WiFly for at least the flushTimeout period. * 2. If the character defined by flushChar is sent to the WiFly. * 3. If the number of characters sent to the WiFly reaches flushSize. * @param flushTimeout Send a packet if no more characters are sent within this many milliseconds. Set * to 0 to disable. * @param flushChar Send a packet when this character is sent to the WiFly. Set to 0 to disable. * @param flushSize Send a packet when this many characters have been sent to the WiFly. * @returns true on success, else false. * @note as of 2.32 firmware, the flushTimeout parameter does not take affect until after a save and reboot. */ boolean WiFly::enableDataTrigger(const uint16_t flushTimeout, const char flushChar, const uint16_t flushSize) { bool res=true; res = res && setUartMode(getUartMode() | WIFLY_UART_MODE_DATA_TRIGGER); res = res && setFlushTimeout(flushTimeout); res = res && setFlushChar(flushChar); res = res && setFlushSize(flushSize); return res; } boolean WiFly::disableDataTrigger() { bool res=true; res = res && setUartMode(getUartMode() & ~WIFLY_UART_MODE_DATA_TRIGGER); res = res && setFlushTimeout(10); res = res && setFlushChar(0); res = res && setFlushSize(64); return res; } /** Hide passphrase and key */ boolean WiFly::hide() { return setopt(PSTR("set wlan hide 1"), (char *)NULL); } boolean WiFly::disableDHCP() { return setDHCP(0); } boolean WiFly::enableDHCP() { return setDHCP(1); } boolean WiFly::setSSID(const char *buf) { return setopt(PSTR("set wlan ssid"), buf); } /** Set the WiFi channel. * @param channel the wifi channel from 0 to 13. 0 means auto channel scan. * @returns true if successful, else false. */ boolean WiFly::setChannel(uint8_t channel) { if (channel > 13) { channel = 13; } return setopt(PSTR("set wlan chan"), channel); } /** Set WEP key */ boolean WiFly::setKey(const char *buf) { boolean res; if ((buf[1] == 'x') || (buf[1] == 'X')) { /* Skip over the 0x leader */ buf += 2; } res = setopt(PSTR("set wlan key"), buf); hide(); /* hide the key */ return res; } /** * Set WPA passphrase. * Use '$' instead of spaces. * Use setSpaceReplacement() to change the replacement character. */ boolean WiFly::setPassphrase(const char *buf) { boolean res; res = setopt(PSTR("set wlan phrase"), buf); hide(); /* hide the key */ return res; } /** * Set the space replacement character in WPA passphrase. * Default is '$'. */ boolean WiFly::setSpaceReplace(const char *buf) { return setopt(PSTR("set opt replace"), buf); } /* data rates to register setting */ static struct { uint32_t rate; uint8_t setting; } rateMap[] __attribute__((__progmem__)) = { { 1000000, WIFLY_RATE_1MBPS }, { 2000000, WIFLY_RATE_2MBPS }, { 5500000, WIFLY_RATE_5_5MBPS }, { 6000000, WIFLY_RATE_6MBPS }, { 9000000, WIFLY_RATE_9MBPS }, { 11000000, WIFLY_RATE_11MBPS }, { 12000000, WIFLY_RATE_12MBPS }, { 18000000, WIFLY_RATE_18MBPS }, { 24000000, WIFLY_RATE_24MBPS }, { 36000000, WIFLY_RATE_36MBPS }, { 48000000, WIFLY_RATE_48MBPS }, { 54000000, WIFLY_RATE_54MBPS } }; /** * Set WiFi data rate * @param rate the data rate to set in bits per second. * valid values are 1000000, 2000000, 5500000, * 6000000, 9000000, 11000000, 12000000, 18000000, * 24000000, 36000000, 48000000, 54000000. * @returns true on success, false on failure. * @note rates are rounded up to the nearest valid value */ boolean WiFly::setRate(uint32_t rate) { uint8_t setting = WIFLY_RATE_54MBPS; for (uint8_t ind=0; ind < (sizeof(rateMap)/sizeof(rateMap[0])); ind++) { if (rate <= pgm_read_dword(&rateMap[ind].rate)) { setting = pgm_read_byte(&rateMap[ind].setting); break; } } return setopt(PSTR("set wlan rate"), setting); } /** * Return the current WiFi data rate in bits/sec * @returns current data rate in bits/sec */ uint32_t WiFly::getRate() { uint8_t rate = getopt(WIFLY_GET_RATE); debug.print("rate: "); debug.println(rate); for (uint8_t ind=0; ind < (sizeof(rateMap)/sizeof(rateMap[0])); ind++) { if (rate == pgm_read_byte(&rateMap[ind].setting)) { return pgm_read_dword(&rateMap[ind].rate); } } return 0; /* Unknown */ } /** * Set the transmit power level. * @param dBm power level from 1 to 12 dBm * @returns true on success, false on failure. * @ Note: a setting of 0 means max power which is 12 dBm. */ boolean WiFly::setTxPower(uint8_t dBm) { if (dBm > 12) { dBm = 12; } return setopt(PSTR("set wlan tx"), dBm); } /** * Get the current transmit power. * @returns tx power in dBm */ uint8_t WiFly::getTxPower() { uint8_t power = getopt(WIFLY_GET_POWER); if (power == 0) { /* 0 means max power, or 12 dBm */ power = 12; } return power; } /** * Set the ad hoc beacon period in milliseconds. * The beacon is a management frame needed to keep the network alive. * Default is 100 milliseconds. * @param msecs the number of milliseconds between beacon * @returns true on success, false on failure. */ boolean WiFly::setAdhocBeacon(const uint16_t msecs) { return setopt(PSTR("set adhoc beacon"), msecs); } /** * Set the ad hoc network probe period. When this number of seconds * passes since last receiving a beacon then the network is declared lost. * Default is 5 seconds.. * @param secs the number of seconds in the probe period. * @returns true on success, false on failure. */ boolean WiFly::setAdhocProbe(const uint16_t secs) { return setopt(PSTR("set adhoc probe"), secs); } uint16_t WiFly::getAdhocBeacon() { return getopt(WIFLY_GET_BEACON); } uint16_t WiFly::getAdhocProbe() { return getopt(WIFLY_GET_PROBE); } uint16_t WiFly::getAdhocReboot() { return getopt(WIFLY_GET_REBOOT); } /** join a wireless network */ boolean WiFly::join(const char *ssid, uint16_t timeout) { int8_t res; const prog_char *joinResult[] = { PSTR("FAILED"), PSTR("Associated!") }; if (!startCommand()) { return false; } send_P(PSTR("join ")); if (ssid != NULL) { send(ssid); } send_P(PSTR("\r")); res = multiMatch_P(joinResult,2,timeout); flushRx(100); if (res == 1) { status.assoc = 1; if (dhcp) { // need some time to complete DHCP request match_P(PSTR("GW="), 15000); flushRx(100); } gets(NULL,0); finishCommand(); return true; } finishCommand(); return false; } /** join a wireless network */ boolean WiFly::join(uint16_t timeout) { char ssid[64]; getSSID(ssid, sizeof(ssid)); return join(ssid); } boolean WiFly::join(const char *ssid, const char *password, bool dhcp, uint8_t mode, uint16_t timeout) { setSSID(ssid); if (mode == WIFLY_MODE_WPA) { setPassphrase(password); } else { setKey(password); } if (dhcp) { enableDHCP(); } return join(ssid, timeout); } /** leave the wireless network */ boolean WiFly::leave() { send_P(PSTR("leave\r")); /* Don't care about result, it either succeeds with a * "DeAuth" reponse and prompt, or fails with just a * prompt because we're already de-associated. * So discard the result. */ flushRx(100); status.assoc = 0; return true; } /** Check to see if the WiFly is connected to a wireless network */ boolean WiFly::isAssociated() { return (status.assoc == 1); } boolean WiFly::setBaud(uint32_t baud) { char buf[16]; simple_utoa(baud, 10, buf, sizeof(buf)); DPRINT(F("set baud ")); DPRINT(buf); DPRINT("\n\r"); /* Go into command mode, since "set uart instant" will exit command mode */ //edit: we now use permanent setting for baud rates startCommand(); if (setopt(PSTR("set u b"), buf)) { //serial->begin(baud); // Sketch will need to do this return true; } return false; } /** See if the string is a valid dot quad IP address */ boolean WiFly::isDotQuad(const char *addr) { uint32_t value; for (uint8_t ind=0; ind<3; ind++) { value = atou(addr); if (value > 255) { return false; } while (*addr >= '0' && *addr <= '9') { addr++; } if (ind == 3) { /* Got a match if this is the end of the string */ return *addr == '\0'; } if (*addr != '.') { return false; } } return false; } /** Send final chunk, end of HTTP message */ void WiFly::sendChunkln() { serial->println('0'); serial->println(); } /** * Send a string as an HTTP chunk with newline * An HTTP chunk is the length of the string in HEX followed * by the string. * @param str the string to send */ void WiFly::sendChunkln(const char *str) { serial->println(strlen(str)+2,HEX); serial->println(str); serial->println(); } /** * Send a progmame string as an HTTP chunk with newline * An HTTP chunk is the length of the string in HEX followed * by the string. * @param str the string to send */ void WiFly::sendChunkln(const __FlashStringHelper *str) { serial->println(strlen_P((const prog_char *)str)+2,HEX); serial->println(str); serial->println(); } /** * Send a string as an HTTP chunk without a newline * An HTTP chunk is the length of the string in HEX followed * by the string. * @param str the string to send */ void WiFly::sendChunk(const char *str) { serial->println(strlen(str),HEX); serial->println(str); } /** * Send a progmem string as an HTTP chunk without a newline. * An HTTP chunk is the length of the string in HEX followed * by the string. * @param str the string to send */ void WiFly::sendChunk(const __FlashStringHelper *str) { serial->println(strlen_P((const prog_char *)str),HEX); serial->println(str); } /** * Ping the specified host. Return true if a ping response * is received, else false. * @param host the host or IP address to ping * @retval true - received ping response * @retval false - no response from host */ boolean WiFly::ping(const char *host) { char ip[16]; const char *addr = host; if (!isDotQuad(host)) { /* do a DNS lookup to get the IP address */ if (!getHostByName(host, ip, sizeof(ip))) { return false; } addr = ip; } startCommand(); send_P(PSTR("ping ")); send(addr); send('\r'); match_P(PSTR("Ping try")); gets(NULL,0); if (!getPrompt()) { finishCommand(); return false; } if (match_P(PSTR("64 bytes"), 5000)) { gets(NULL,0); gets(NULL,0, 5000); finishCommand(); return true; } finishCommand(); return false; } /** * Create an Adhoc WiFi network. * The WiFly is assigned IP address 169.254.1.1. * @param ssid the SSID to use for the network * @param channel the WiFi channel to use; 1 to 13. * @retval true - successfully create Ad Hoc network * @retval false - failed * @note the WiFly is rebooted as the final step of this command. */ boolean WiFly::createAdhocNetwork(const char *ssid, uint8_t channel) { startCommand(); setDHCP(WIFLY_DHCP_MODE_OFF); setIP(F("169.254.1.1")); setNetmask(F("255.255.0.0")); setJoin(WIFLY_WLAN_JOIN_ADHOC); setSSID(ssid); setChannel(channel); save(); finishCommand(); reboot(); return true; } /** * Open a TCP connection. * If there is already an open connection then that is closed first. * @param addr - the IP address or hostname to connect. A DNS lookup will be peformed * for the hostname. * @param port - the TCP port to connect to * @param block - true = wait for the connection to complete * false = start the connection and return. Use openComplete() * to determine the result. * @retval true - success, the connection is open * @retval false - failed, or connection already in progress */ boolean WiFly::open(const char *addr, int port, boolean block) { char buf[20]; char ch; if (connecting) { /* an open is already in progress */ return false; } startCommand(); /* Already connected? Close the connection first */ if (connected) { close(); } simple_utoa(port, 10, buf, sizeof(buf)); debug.print(F("open ")); debug.print(addr); debug.print(' '); debug.println(buf); send_P(PSTR("open ")); send(addr); send(" "); send(buf); send_P(PSTR("\r")); if (!getPrompt()) { debug.println(F("Failed to get prompt")); debug.println(F("WiFly has crashed and will reboot...")); while (1); /* wait for the reboot */ return false; } if (!block) { /* Non-blocking connect, user will poll for result */ connecting = true; return true; } /* Expect "*OPEN*" or "Connect FAILED" */ while (readTimeout(&ch,10000)) { switch (ch) { case '*': if (match_P(PSTR("OPEN*"))) { DPRINT(F("Connected\n\r")); connected = true; /* successful connection exits command mode */ inCommandMode = false; return true; } else { finishCommand(); return false; } break; case 'C': { buf[0] = ch; gets(&buf[1], sizeof(buf)-1); debug.print(F("Failed to connect: ")); debug.println(buf); finishCommand(); return false; } default: if (debugOn) { debug.print(F("Unexpected char: ")); debug.print(ch,HEX); if (isprint(ch)) { debug.print(' '); debug.print(ch); } debug.println(); } break; } } debug.println(F("")); finishCommand(); return false; } /** * Open a TCP connection. * If there is already an open connection then that is closed first. * @param addr - the IP address to connect to * @param port - the TCP port to connect to * @param block - true = wait for the connection to complete * false = start the connection and return. Use openComplete() * to determine the result. * @retval true - success, the connection is open * @retval false - failed, or connection already in progress */ boolean WiFly::open(IPAddress addr, int port, boolean block) { char buf[16]; return open(iptoa(addr, buf, sizeof(buf)), port, block); } /** Check to see if there is a tcp connection. */ boolean WiFly::isConnected() { if (!connected) { /* Check for a connection */ available(); } return connected; } boolean WiFly::isInCommandMode() { return inCommandMode; } /** Internal UPD sendto function */ boolean WiFly::sendto( const uint8_t *data, uint16_t size, const __FlashStringHelper *flashData, const char *host, uint16_t port) { bool restore = true; if (!startCommand()) { debug.println(F("sendto: failed to start command")); return false; } if (udpAutoPair || (port != lastPort) || (strcmp(host, lastHost) != 0)) { setHost(host,port); if (!restoreHost) { /* Keep a copy of this for reference for the next call */ lastPort = port; strncpy(lastHost, host, sizeof(lastHost)); restore = false; } #ifdef DEBUG debug.print(F("sendto: set host and port ")); debug.print(host); debug.print(':'); debug.println(port); #endif } else { /* same host and port, no need to restore */ restore = false; #ifdef DEBUG debug.print(F("sendto: same host and port ")); debug.print(host); debug.print(':'); debug.println(port); #endif } finishCommand(); if (data) { write(data, size); } else if (flashData) { print(flashData); } /* Restore original host and port */ if (restore) { setHost(lastHost,lastPort); #ifdef DEBUG debug.print(F("sendto: restored ")); debug.print(lastHost); debug.print(':'); debug.println(lastPort); #endif } else if (udpAutoPair) { setHostIP(F("0.0.0.0")); } #ifdef DEBUG debug.print(F("sendto: send ")); if (data) { debug.println((char *)data); } else if (flashData) { debug.println(flashData); } #endif return true; } /** * Send binary data as a UDP packet to a host. * @param data - pointer to an array of data to send * @param size - then number of bytes of data to send * @param host - the IP or hostname to send the packet to. If this is a hostname * then a DNS lookup will be performed to find the IP address. * @param port - the UDP port to send the packet to * @retval true - packet send successfully * @retval false - failed to send packet */ boolean WiFly::sendto(const uint8_t *data, uint16_t size, const char *host, uint16_t port) { return sendto(data, size, NULL, host, port); } /** * Send binary data as a UDP packet to a host. * @param data - pointer to an array of data to send * @param size - then number of bytes of data to send * @param host - the IP address to send the packet to. * @param port - the UDP port to send the packet to * @retval true - packet send successfully * @retval false - failed to send packet */ boolean WiFly::sendto(const uint8_t *data, uint16_t size, IPAddress host, uint16_t port) { char buf[16]; return sendto(data, size, iptoa(host, buf, sizeof(buf)) , port); } /** * Send a string as a UDP packet to a host. * @param data - the null terminated string to send * @param host - the IP or hostname to send the packet to. If this is a hostname * then a DNS lookup will be performed to find the IP address. * @param port - the UDP port to send the packet to * @retval true - packet send successfully * @retval false - failed to send packet */ boolean WiFly::sendto(const char *data, const char *host, uint16_t port) { return sendto((uint8_t *)data, strlen(data), host, port); } /** * Send a string as a UDP packet to a host. * @param data - the null terminated string to send * @param host - the IP address to send the packet to. * @param port - the UDP port to send the packet to * @retval true - packet send successfully * @retval false - failed to send packet */ boolean WiFly::sendto(const char *data, IPAddress host, uint16_t port) { return sendto((uint8_t *)data, strlen(data), host, port); } /** * Send a string as a UDP packet to a host. * @param data - the null terminated flash string to send * @param host - the IP or hostname to send the packet to. If this is a hostname * then a DNS lookup will be performed to find the IP address. * @param port - the UDP port to send the packet to * @retval true - packet send successfully * @retval false - failed to send packet */ boolean WiFly::sendto(const __FlashStringHelper *flashData, const char *host, uint16_t port) { return sendto(NULL, 0, flashData, host, port); } /** * Send a string as a UDP packet to a host. * @param data - the null terminated flash string to send * @param host - the IP address to send the packet to. * @param port - the UDP port to send the packet to * @retval true - packet send successfully * @retval false - failed to send packet */ boolean WiFly::sendto(const __FlashStringHelper *flashData, IPAddress host, uint16_t port) { char buf[16]; return sendto(NULL, 0, flashData, iptoa(host, buf, sizeof(buf)), port); } /** * Preserve the IP and Port set via setIP() and setPort() when using * sendto() function. */ void WiFly::enableHostRestore() { restoreHost = true; getHostIP(lastHost, sizeof(lastHost)); lastPort = getHostPort(); debug.print(F("enableHostRestore: stored ")); debug.print(lastHost); debug.print(':'); debug.println(lastPort); } /** * Don't preserve the IP and Port set via setIP() and setPort() when using * sendto() function. The IP and Port will be left set by the last sendto() call. */ void WiFly::disableHostRestore() { restoreHost = false; } /** * Check to see if the non-blocking open has completed. * When this returns true the open has finished with either * success or failure. You can use isConnected() to see * if the open was successful. * @retval true - the open operation has completed * @retval false - the open is still in progress */ boolean WiFly::openComplete() { char buf[20]; if (!connecting) { return true; } if (serial->available()) { char ch = serial->read(); switch (ch) { case '*': if (match_P(PSTR("OPEN*"))) { DPRINT(F("Connected\n\r")); connected = true; connecting = false; /* successful connection exits command mode */ inCommandMode = false; DPRINT(F("openComplete: true\n\r")); return true; } else { /* Failed to connected */ connecting = false; finishCommand(); DPRINT(F("openComplete: true\n\r")); return true; } break; case 'C': { buf[0] = ch; gets(&buf[1], sizeof(buf)-1); debug.print(F("Failed to connect: ")); debug.println(buf); connecting = false; finishCommand(); DPRINT(F("openComplete: true\n\r")); return true; } default: buf[0] = ch; gets(&buf[1], sizeof(buf)-1); debug.print(F("Unexpected resp: ")); debug.println(buf); connecting = false; finishCommand(); DPRINT(F("openComplete: true\n\r")); return true; } } DPRINT(F("openComplete: false\n\r")); return false; } /** * Start a simple terminal that connects the debug stream * to the WiFly. * Useful for debugging and manually setting and reading * WiFly options. */ void WiFly::terminal() { debug.println(F("Terminal ready")); while (1) { if (serial->available() > 0) { debug.write(serial->read()); } if (debug.available()) { // Outgoing data serial->write(debug.read()); } } } /** * Close the TCP connection * @retval true - connection closed * @retval false - failed to close */ boolean WiFly::close() { if (!connected) { return true; } flushRx(); startCommand(); send_P(PSTR("close\r")); if (match_P(PSTR("*CLOS*"))) { finishCommand(); debug.println(F("close: got *CLOS*")); connected = false; return true; } else { debug.println(F("close: failed, no *CLOS*")); } /* Check connection state */ getConnection(); finishCommand(); return !connected; } WFDebug::WFDebug() { debug = NULL; } void WFDebug::begin(Stream *debugPrint) { debug = debugPrint; } size_t WFDebug::write(uint8_t data) { if (debug != NULL) { return debug->write(data); } return 0; }