diff --git a/NinjaBlockWiFly/NinjaBlockWiFly.cpp b/NinjaBlockWiFly/NinjaBlockWiFly.cpp new file mode 100644 index 0000000..ebcbe8f --- /dev/null +++ b/NinjaBlockWiFly/NinjaBlockWiFly.cpp @@ -0,0 +1,290 @@ +#include "NinjaBlockWiFly.h" + +#undef DEBUG_SERIAL +#define DEBUG_SERIAL + +#ifdef DEBUG_SERIAL +#define DPRINT(A) debugSerial->print(A) +#define DPRINTLN(A) debugSerial->println(A) +#else +#define DPRINT(A) +#define DPRINTLN(A) +#endif + +#define ALLNOTNULL(A, B, C) ((A!=NULL) && (B!=NULL) && (C!=NULL)) +#define kEthernetBytes 4 + +int NinjaBlockClass::begin() +{ + DPRINTLN(F("$$$")); + int result = 0; + if (ALLNOTNULL(host, nodeID, token)) + { + if( !client.open(host, port) ) { + //it can't connect + result = 0; + DPRINTLN(F("!26")); + } + else { + result = 1; + } + } else { + DPRINTLN(F("!32")); + } + return result; +} + +void NinjaBlockClass::httppost(char *postData) +{ + DPRINT(F("->")); + if( client.open(host,port) ) { + sendHeaders(true,client); + client.print(F("Content-Length: ")); + client.println(strlen(postData)); + client.println(); + client.println(postData); + DPRINTLN(F("P")); + //DPRINTLN(postData); + client.flush(); + client.close(); + } else { + DPRINTLN(F("!51")); + } + return; +} + +void NinjaBlockClass::sendHeaders(bool isPOST, WiFly hclient) { + char strData[DATA_LEN]; + if (isPOST) + strcpy_P(strData,PSTR("POST")); + else + strcpy_P(strData,PSTR("GET")); + strcat_P(strData,PSTR(" /rest/v0/block/")); + strcat_P(strData, nodeID); + if (isPOST) + strcat_P(strData,PSTR("/data")); + else + strcat_P(strData, PSTR("/commands")); + strcat_P(strData, PSTR(" HTTP/1.1\r\n")); + hclient.print(strData); + strcpy_P(strData,PSTR("Host: ")); + strcat(strData ,host); + strcat_P(strData, PSTR("\r\n")); + hclient.print(strData); + hclient.print(F("User-Agent: Ninja Arduino 1.1\r\n\ +Content-Type: application/json\r\n\ +Accept: application/json\r\n")); + strcpy_P(strData,PSTR("X-Ninja-Token: ")); + strcat_P(strData, token); + strcat_P(strData,PSTR("\r\n")); + hclient.print(strData); +} + +void NinjaBlockClass::send(char *data) +{ + ninjaMessage(false, 0, data); +} + +void NinjaBlockClass::send(int data) +{ + ninjaMessage(true, data, 0); +} + +//Most Arduinos, 2-byte int range -32,768 to 32,767 (max 6 chars) +//Arduino Duo, 4-byte range -2,147,483,648 to 2,147,483,647 (max 11 chars) +const char kNumLength = sizeof(int) * 3; +char strNumber[kNumLength]; +//return reference to strNumber +char *int2str(int num) { + return itoa(num, strNumber, 10); // base 10 +} +char strSend[DATA_SIZE]; +void addStringAndUnderscore(char * str) { + strcat(strSend, str); + strcat_P(strSend, PSTR("_")); +} +void addStringAndUnderscore_P(prog_char * str) { + strcat_P(strSend, str); + strcat_P(strSend, PSTR("_")); +} +void NinjaBlockClass::ninjaMessage(bool isInt, int intData, char *charData) { + if (guid != NULL) { + strcpy_P(strSend,PSTR("{\"GUID\": \"")); + addStringAndUnderscore_P(nodeID); + addStringAndUnderscore(guid); + addStringAndUnderscore(int2str(vendorID)); + strcat(strSend, int2str(deviceID)); + strcat_P(strSend, PSTR("\",\"G\": \"")); + strcat(strSend, guid); + strcat_P(strSend, PSTR("\",\"V\": ")); + strcat(strSend, int2str(vendorID)); + strcat_P(strSend, PSTR(",\"D\": ")); + strcat(strSend, int2str(deviceID)); + strcat_P(strSend, PSTR(",\"DA\": ")); + if (isInt) { + strcat(strSend, int2str(intData)); + } else { + strcat_P(strSend, PSTR("\"")); + strcat(strSend, charData); + strcat_P(strSend, PSTR("\"")); + } + strcat_P(strSend, PSTR("}")); + httppost(strSend); + } +} + + +const char kStrHeaderEnd[] = {'\r', '\n', '\r', '\n'}; +const byte kHeaderLength = sizeof(kStrHeaderEnd); +const char kCharInvertedCommas = '\"'; +bool NinjaBlockClass::receive(void) { + bool gotData = false; + if(!client.isConnected()) + { + // connect if not connected + DPRINT(F("-")); + //client.close(); + if(client.open(host,port)==1) + { + DPRINTLN(F("-")); + sendHeaders(false, client); + client.println(); + } else { + DPRINTLN(F("!161")); + } + } + if (client.isConnected()) + { + gotData = receiveConnected(); + } + return gotData; +} + +// giving a name prefix, eg. -> G":" <-, skip past the value after it, and insert a string terminator +// returns NULL, or the beginning of a string within data that has been null-terminated +char * valueString(const char *name, char *data, int &index, const int length) { + char *result = NULL; + uint8_t nameLength = strlen(name); + for (uint8_t matching=0 + ; (matching < nameLength) && (index < length) + ; index++) { + matching = ((data[index] == name[matching]) ? matching+1 : 0); + } + if (index < length) { + //if searching for a string seek end of string ("), otherwise (,) ends an int + char endChar = (data[index-1]==kCharInvertedCommas) ? kCharInvertedCommas : ','; + int start = index; + while ((index < length) && (data[index] != endChar)) { + index++; + } + if (index < length) { + data[index] = '\0'; // insert string terminator after value (string or int) + result = &data[start]; + } + } + return result; +} + +bool NinjaBlockClass::receiveConnected(void) { + bool gotHeader = false; + bool gotData = false; + int bytesRead = 0; + int index = 0; + char data[DATA_SIZE]; + IsTick = false; + + //skip past header + uint8_t matching=0; + char ch; + while(client.available() ) { + delayMicroseconds(100); + ch = client.read(); + //DPRINT(ch); + + if( !gotHeader ) { + //skip header first + matching = 0; + while( ch == kStrHeaderEnd[matching] ) { + matching++; + if( matching == kHeaderLength ) { + //DPRINT(F("EOH")); + gotHeader = true; + break; + } + delayMicroseconds(100); + ch = client.read(); + //DPRINT(ch); + } + } + else { + //skipped header; now get the message + //DPRINT("index="); DPRINTLN(index); + //message still coming + //read data into array eg. {"DEVICE":[{"G":"0","V":0,"D":1000,"DA":"FFFFFF"}]} + bytesRead++; + if( index < (DATA_SIZE - 1) ) { + data[index++] = ch; + } + } + } + if( gotHeader ) { + char *strVal; + bytesRead = 0; + data[index] = 0;//null terminate it + DPRINT(F("<-")); + strVal = valueString("G\":\"", data, bytesRead, index); + if (strVal) { + strcpy(strGUID, strVal); + strVal = valueString("V\":", data, bytesRead, index); + if (strVal != NULL) { + intVID = atoi(strVal); + strVal = valueString("D\":", data, bytesRead, index); + if (strVal != NULL) { + intDID = atoi(strVal); + + // DPRINT(F(" strGUID=")); + // DPRINTLN(strGUID); + // DPRINT(F(" intVID=")); + // DPRINTLN(intVID); + // DPRINT(F(" intDID=")); + // DPRINTLN(intDID); + + int start = bytesRead; + strVal = valueString("DA\":\"", data, bytesRead, index); + if (strVal != NULL) { + strcpy(strDATA, strVal); + IsDATAString = true; + gotData = true; + DPRINTLN(F("sD")); + // DPRINTLN(strDATA); + } + else { // may be an int value + bytesRead = start; // reset to where we were before attempting (data is unmodified if NULL was returned) + strVal = valueString("DA\":", data, bytesRead, index); + if (strVal) { + intDATA = atoi(strVal); + IsDATAString = false; + gotData = true; + DPRINTLN(F("iD")); + // DPRINTLN(intDATA); + } + } + } + } + } + if( !gotData ) { + //this is a tick from the cloud + DPRINTLN(F("T")); + IsTick = true; + } + //if a header was received, there was some data after (either json, or some html etc) + //purge and close the stream + client.flush(); + delay(100); + client.close(); + delay(100); + } + return gotData || IsTick; +} + +NinjaBlockClass NinjaBlock; diff --git a/NinjaBlockWiFly/NinjaBlockWiFly.h b/NinjaBlockWiFly/NinjaBlockWiFly.h new file mode 100644 index 0000000..9efbccd --- /dev/null +++ b/NinjaBlockWiFly/NinjaBlockWiFly.h @@ -0,0 +1,57 @@ +/* + * NinjaBlock ninjablocks.com API wrapper + * Author: JP Liew, Pete Moore @askpete + * Source: git://github.com/ninjablocks/arduino + * This library provides a basic interface to the Ninja Blocks API. + * Use it to send sensor data and/or listen for commands. See exampes + * for usage or http://help.ninjablocks.com and search for Arduino library. + */ + +#ifndef ninjablockwifly_h +#define ninjablockwifly_h + +#include "WiFlyHQ.h" + +#define DATA_SIZE 128 +#define GUID_LEN 36 +#define DATA_LEN 96 + +class NinjaBlockClass { + +public: + char *host; + prog_char *nodeID; + prog_char *token; + char *guid; + int port; + int vendorID; + int deviceID; + WiFly client; + Stream* debugSerial; + + char strGUID[GUID_LEN]; + int intVID; + int intDID; + int intDATA; + char strDATA[DATA_LEN]; + bool IsDATAString; + bool IsTick; + + int begin(); + void send(int data); + void send(char *data); + bool receive(void); + void httppost(char *postData); + bool decodeJSON(); + void setClient(WiFly client); + void setDebugSerial(Stream* debugSerial); + +private: + void ninjaMessage(bool, int intData, char *charData); + void sendHeaders(bool isPOST, WiFly hclient); + bool receiveConnected(void); +}; + +extern NinjaBlockClass NinjaBlock; + +#endif diff --git a/NinjaBlockWiFly/RCSwitch.cpp b/NinjaBlockWiFly/RCSwitch.cpp new file mode 100755 index 0000000..4093d1d --- /dev/null +++ b/NinjaBlockWiFly/RCSwitch.cpp @@ -0,0 +1,514 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + - Andreas Steinel / A.(at)gmail(dot)com + + Project home: http://code.google.com/p/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "RCSwitch.h" + +#if not defined( RCSwitchDisableReceiving ) +unsigned long RCSwitch::nReceivedValue = NULL; +unsigned int RCSwitch::nReceivedBitlength = 0; +unsigned int RCSwitch::nReceivedDelay = 0; +unsigned int RCSwitch::nReceivedProtocol = 0; +int RCSwitch::nReceiveTolerance = 60; +#endif +unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES]; + +RCSwitch::RCSwitch() { + this->nTransmitterPin = -1; + this->setPulseLength(350); + this->setRepeatTransmit(10); + this->setProtocol(1); + #if not defined( RCSwitchDisableReceiving ) + this->nReceiverInterrupt = -1; + this->setReceiveTolerance(60); + RCSwitch::nReceivedValue = NULL; + #endif +} + +/** + * Sets the protocol to send. + */ +void RCSwitch::setProtocol(int nProtocol) { + this->nProtocol = nProtocol; + if (nProtocol == 1){ + this->setPulseLength(350); + } + else if (nProtocol == 2) { + this->setPulseLength(650); + } + else if (nProtocol == 3) { + this->setPulseLength(100); + } +} + +/** + * Sets the protocol to send with pulse length in microseconds. + */ +void RCSwitch::setProtocol(int nProtocol, int nPulseLength) { + this->nProtocol = nProtocol; + this->setPulseLength(nPulseLength); +} + + +/** + * Sets pulse length in microseconds + */ +void RCSwitch::setPulseLength(int nPulseLength) { + this->nPulseLength = nPulseLength; +} + +/** + * Sets Repeat Transmits + */ +void RCSwitch::setRepeatTransmit(int nRepeatTransmit) { + this->nRepeatTransmit = nRepeatTransmit; +} + +/** + * Set Receiving Tolerance + */ +#if not defined( RCSwitchDisableReceiving ) +void RCSwitch::setReceiveTolerance(int nPercent) { + RCSwitch::nReceiveTolerance = nPercent; +} +#endif + + +/** + * Enable transmissions + * + * @param nTransmitterPin Arduino Pin to which the sender is connected to + */ +void RCSwitch::enableTransmit(int nTransmitterPin) { + this->nTransmitterPin = nTransmitterPin; + pinMode(this->nTransmitterPin, OUTPUT); +} + +/** + * Disable transmissions + */ +void RCSwitch::disableTransmit() { + this->nTransmitterPin = -1; +} + +/** + * @param sCodeWord /^[10FS]*$/ -> see getCodeWord + */ +void RCSwitch::sendTriState(char* sCodeWord) { + for (int nRepeat=0; nRepeatsendT0(); + break; + case 'F': + this->sendTF(); + break; + case '1': + this->sendT1(); + break; + } + i++; + } + this->sendSync(); + } +} + +void RCSwitch::send(unsigned long Code, unsigned int length) { + this->send( this->dec2binWzerofill(Code, length) ); +} + +void RCSwitch::send(char* sCodeWord) { + for (int nRepeat=0; nRepeatsend0(); + break; + case '1': + this->send1(); + break; + } + i++; + } + this->sendSync(); + } +} + +void RCSwitch::transmit(int nHighPulses, int nLowPulses) { + #if not defined ( RCSwitchDisableReceiving ) + boolean disabled_Receive = false; + int nReceiverInterrupt_backup = nReceiverInterrupt; + #endif + if (this->nTransmitterPin != -1) { + #if not defined( RCSwitchDisableReceiving ) + if (this->nReceiverInterrupt != -1) { + this->disableReceive(); + disabled_Receive = true; + } + #endif + digitalWrite(this->nTransmitterPin, HIGH); + delayMicroseconds( this->nPulseLength * nHighPulses); + digitalWrite(this->nTransmitterPin, LOW); + delayMicroseconds( this->nPulseLength * nLowPulses); + + #if not defined( RCSwitchDisableReceiving ) + if(disabled_Receive){ + this->enableReceive(nReceiverInterrupt_backup); + } + #endif + } +} +/** + * Sends a "0" Bit + * _ + * Waveform Protocol 1: | |___ + * _ + * Waveform Protocol 2: | |__ + */ +void RCSwitch::send0() { + if (this->nProtocol == 1){ + this->transmit(1,3); + } + else if (this->nProtocol == 2) { + this->transmit(1,2); + } + else if (this->nProtocol == 3) { + this->transmit(4,11); + } +} + +/** + * Sends a "1" Bit + * ___ + * Waveform Protocol 1: | |_ + * __ + * Waveform Protocol 2: | |_ + */ +void RCSwitch::send1() { + if (this->nProtocol == 1){ + this->transmit(3,1); + } + else if (this->nProtocol == 2) { + this->transmit(2,1); + } + else if (this->nProtocol == 3) { + this->transmit(9,6); + } +} + + +/** + * Sends a Tri-State "0" Bit + * _ _ + * Waveform: | |___| |___ + */ +void RCSwitch::sendT0() { + this->transmit(1,3); + this->transmit(1,3); +} + +/** + * Sends a Tri-State "1" Bit + * ___ ___ + * Waveform: | |_| |_ + */ +void RCSwitch::sendT1() { + this->transmit(3,1); + this->transmit(3,1); +} + +/** + * Sends a Tri-State "F" Bit + * _ ___ + * Waveform: | |___| |_ + */ +void RCSwitch::sendTF() { + this->transmit(1,3); + this->transmit(3,1); +} + +/** + * Sends a "Sync" Bit + * _ + * Waveform Protocol 1: | |_______________________________ + * _ + * Waveform Protocol 2: | |__________ + */ +void RCSwitch::sendSync() { + + if (this->nProtocol == 1){ + this->transmit(1,31); + } + else if (this->nProtocol == 2) { + this->transmit(1,10); + } + else if (this->nProtocol == 3) { + this->transmit(1,71); + } +} + +#if not defined( RCSwitchDisableReceiving ) +/** + * Enable receiving data + */ +void RCSwitch::enableReceive(int interrupt) { + this->nReceiverInterrupt = interrupt; + this->enableReceive(); +} + +void RCSwitch::enableReceive() { + if (this->nReceiverInterrupt != -1) { + RCSwitch::nReceivedValue = NULL; + RCSwitch::nReceivedBitlength = NULL; + attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE); + } +} + +/** + * Disable receiving data + */ +void RCSwitch::disableReceive() { + detachInterrupt(this->nReceiverInterrupt); + this->nReceiverInterrupt = -1; +} + +bool RCSwitch::available() { + return RCSwitch::nReceivedValue != NULL; +} + +void RCSwitch::resetAvailable() { + RCSwitch::nReceivedValue = NULL; +} + +unsigned long RCSwitch::getReceivedValue() { + return RCSwitch::nReceivedValue; +} + +unsigned int RCSwitch::getReceivedBitlength() { + return RCSwitch::nReceivedBitlength; +} + +unsigned int RCSwitch::getReceivedDelay() { + return RCSwitch::nReceivedDelay; +} + +unsigned int RCSwitch::getReceivedProtocol() { + return RCSwitch::nReceivedProtocol; +} + +unsigned int* RCSwitch::getReceivedRawdata() { + return RCSwitch::timings; +} + +/** + * + */ +bool RCSwitch::receiveProtocol1(unsigned int changeCount){ + + unsigned long code = 0; + unsigned long delay = RCSwitch::timings[0] / 31; + unsigned long delayTolerance = delay * RCSwitch::nReceiveTolerance * 0.01; + + for (int i = 1; i delay-delayTolerance && RCSwitch::timings[i] < delay+delayTolerance && RCSwitch::timings[i+1] > delay*3-delayTolerance && RCSwitch::timings[i+1] < delay*3+delayTolerance) { + code = code << 1; + } else if (RCSwitch::timings[i] > delay*3-delayTolerance && RCSwitch::timings[i] < delay*3+delayTolerance && RCSwitch::timings[i+1] > delay-delayTolerance && RCSwitch::timings[i+1] < delay+delayTolerance) { + code+=1; + code = code << 1; + } else { + // Failed + i = changeCount; + code = 0; + } + } + code = code >> 1; + if (changeCount > 6) { // ignore < 4bit values as there are no devices sending 4bit values => noise + RCSwitch::nReceivedValue = code; + RCSwitch::nReceivedBitlength = changeCount / 2; + RCSwitch::nReceivedDelay = delay; + RCSwitch::nReceivedProtocol = 1; + } + + if (code == 0){ + return false; + }else if (code != 0){ + return true; + } + + +} + +bool RCSwitch::receiveProtocol2(unsigned int changeCount){ + + unsigned long code = 0; + unsigned long delay = RCSwitch::timings[0] / 10; + unsigned long delayTolerance = delay * RCSwitch::nReceiveTolerance * 0.01; + + for (int i = 1; i delay-delayTolerance && RCSwitch::timings[i] < delay+delayTolerance && RCSwitch::timings[i+1] > delay*2-delayTolerance && RCSwitch::timings[i+1] < delay*2+delayTolerance) { + code = code << 1; + } else if (RCSwitch::timings[i] > delay*2-delayTolerance && RCSwitch::timings[i] < delay*2+delayTolerance && RCSwitch::timings[i+1] > delay-delayTolerance && RCSwitch::timings[i+1] < delay+delayTolerance) { + code+=1; + code = code << 1; + } else { + // Failed + i = changeCount; + code = 0; + } + } + code = code >> 1; + if (changeCount > 6) { // ignore < 4bit values as there are no devices sending 4bit values => noise + RCSwitch::nReceivedValue = code; + RCSwitch::nReceivedBitlength = changeCount / 2; + RCSwitch::nReceivedDelay = delay; + RCSwitch::nReceivedProtocol = 2; + } + + if (code == 0){ + return false; + }else if (code != 0){ + return true; + } + +} + +/** Protocol 3 is used by BL35P02. + * + */ +bool RCSwitch::receiveProtocol3(unsigned int changeCount){ + + unsigned long code = 0; + unsigned long delay = RCSwitch::timings[0] / PROTOCOL3_SYNC_FACTOR; + unsigned long delayTolerance = delay * RCSwitch::nReceiveTolerance * 0.01; + + for (int i = 1; i delay*PROTOCOL3_0_HIGH_CYCLES - delayTolerance + && RCSwitch::timings[i] < delay*PROTOCOL3_0_HIGH_CYCLES + delayTolerance + && RCSwitch::timings[i+1] > delay*PROTOCOL3_0_LOW_CYCLES - delayTolerance + && RCSwitch::timings[i+1] < delay*PROTOCOL3_0_LOW_CYCLES + delayTolerance) { + code = code << 1; + } else if (RCSwitch::timings[i] > delay*PROTOCOL3_1_HIGH_CYCLES - delayTolerance + && RCSwitch::timings[i] < delay*PROTOCOL3_1_HIGH_CYCLES + delayTolerance + && RCSwitch::timings[i+1] > delay*PROTOCOL3_1_LOW_CYCLES - delayTolerance + && RCSwitch::timings[i+1] < delay*PROTOCOL3_1_LOW_CYCLES + delayTolerance) { + code+=1; + code = code << 1; + } else { + // Failed + i = changeCount; + code = 0; + } + } + code = code >> 1; + if (changeCount > 6) { // ignore < 4bit values as there are no devices sending 4bit values => noise + RCSwitch::nReceivedValue = code; + RCSwitch::nReceivedBitlength = changeCount / 2; + RCSwitch::nReceivedDelay = delay; + RCSwitch::nReceivedProtocol = 3; + } + + if (code == 0){ + return false; + }else if (code != 0){ + return true; + } +} + +void RCSwitch::handleInterrupt() { + + static unsigned int duration; + static unsigned int changeCount; + static unsigned long lastTime; + static unsigned int repeatCount; + + + long time = micros(); + duration = time - lastTime; + + if (duration > 5000 && duration > RCSwitch::timings[0] - 200 && duration < RCSwitch::timings[0] + 200) { + repeatCount++; + changeCount--; + if (repeatCount == 2) { + if (receiveProtocol1(changeCount) == false){ + if (receiveProtocol2(changeCount) == false){ + if (receiveProtocol3(changeCount) == false){ + //failed + } + } + } + repeatCount = 0; + } + changeCount = 0; + } else if (duration > 5000) { + changeCount = 0; + } + + if (changeCount >= RCSWITCH_MAX_CHANGES) { + changeCount = 0; + repeatCount = 0; + } + RCSwitch::timings[changeCount++] = duration; + lastTime = time; +} + +/** + * Turns a decimal value to its binary representation + */ +char* RCSwitch::dec2binWzerofill(unsigned long Dec, unsigned int bitLength){ + return dec2binWcharfill(Dec, bitLength, '0'); +} + +char* RCSwitch::dec2binWcharfill(unsigned long Dec, unsigned int bitLength, char fill){ + static char bin[64]; + unsigned int i=0; + + while (Dec > 0) { + bin[32+i++] = ((Dec & 1) > 0) ? '1' : fill; + Dec = Dec >> 1; + } + + for (unsigned int j = 0; j< bitLength; j++) { + if (j >= bitLength - i) { + bin[j] = bin[ 31 + i - (j - (bitLength - i)) ]; + }else { + bin[j] = fill; + } + } + bin[bitLength] = '\0'; + + return bin; +} + +#endif + diff --git a/NinjaBlockWiFly/RCSwitch.h b/NinjaBlockWiFly/RCSwitch.h new file mode 100755 index 0000000..2ce517c --- /dev/null +++ b/NinjaBlockWiFly/RCSwitch.h @@ -0,0 +1,144 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + + Project home: http://code.google.com/p/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _RCSwitch_h +#define _RCSwitch_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific + #include "Energia.h" +#else + #include "WProgram.h" +#endif + + +// At least for the ATTiny X4/X5, receiving has to be disabled due to +// missing libm depencies (udivmodhi4) +//#if defined( __AVR_ATtinyX5__ ) or defined ( __AVR_ATtinyX4__ ) +//#define RCSwitchDisableReceiving +//#endif + +// Number of maximum High/Low changes per packet. +// We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync +#define RCSWITCH_MAX_CHANGES 67 + +#define PROTOCOL3_SYNC_FACTOR 71 +#define PROTOCOL3_0_HIGH_CYCLES 4 +#define PROTOCOL3_0_LOW_CYCLES 11 +#define PROTOCOL3_1_HIGH_CYCLES 9 +#define PROTOCOL3_1_LOW_CYCLES 6 + +class RCSwitch { + + public: + RCSwitch(); + + void switchOn(int nGroupNumber, int nSwitchNumber); + void switchOff(int nGroupNumber, int nSwitchNumber); + void switchOn(char* sGroup, int nSwitchNumber); + void switchOff(char* sGroup, int nSwitchNumber); + void switchOn(char sFamily, int nGroup, int nDevice); + void switchOff(char sFamily, int nGroup, int nDevice); + void switchOn(char* sGroup, char* sDevice); + void switchOff(char* sGroup, char* sDevice); + void switchOn(char sGroup, int nDevice); + void switchOff(char sGroup, int nDevice); + + void sendTriState(char* Code); + void send(unsigned long Code, unsigned int length); + void send(char* Code); + + #if not defined( RCSwitchDisableReceiving ) + void enableReceive(int interrupt); + void enableReceive(); + void disableReceive(); + bool available(); + void resetAvailable(); + + unsigned long getReceivedValue(); + unsigned int getReceivedBitlength(); + unsigned int getReceivedDelay(); + unsigned int getReceivedProtocol(); + unsigned int* getReceivedRawdata(); + #endif + + void enableTransmit(int nTransmitterPin); + void disableTransmit(); + void setPulseLength(int nPulseLength); + void setRepeatTransmit(int nRepeatTransmit); + #if not defined( RCSwitchDisableReceiving ) + void setReceiveTolerance(int nPercent); + #endif + void setProtocol(int nProtocol); + void setProtocol(int nProtocol, int nPulseLength); + + private: + char* getCodeWordB(int nGroupNumber, int nSwitchNumber, boolean bStatus); + char* getCodeWordA(char* sGroup, int nSwitchNumber, boolean bStatus); + char* getCodeWordA(char* sGroup, char* sDevice, boolean bStatus); + char* getCodeWordC(char sFamily, int nGroup, int nDevice, boolean bStatus); + char* getCodeWordD(char group, int nDevice, boolean bStatus); + void sendT0(); + void sendT1(); + void sendTF(); + void send0(); + void send1(); + void sendSync(); + void transmit(int nHighPulses, int nLowPulses); + + static char* dec2binWzerofill(unsigned long dec, unsigned int length); + static char* dec2binWcharfill(unsigned long dec, unsigned int length, char fill); + + #if not defined( RCSwitchDisableReceiving ) + static void handleInterrupt(); + static bool receiveProtocol1(unsigned int changeCount); + static bool receiveProtocol2(unsigned int changeCount); + static bool receiveProtocol3(unsigned int changeCount); + int nReceiverInterrupt; + #endif + int nTransmitterPin; + int nPulseLength; + int nRepeatTransmit; + char nProtocol; + + #if not defined( RCSwitchDisableReceiving ) + static int nReceiveTolerance; + static unsigned long nReceivedValue; + static unsigned int nReceivedBitlength; + static unsigned int nReceivedDelay; + static unsigned int nReceivedProtocol; + #endif + /* + * timings[0] contains sync timing, followed by a number of bits + */ + static unsigned int timings[RCSWITCH_MAX_CHANGES]; + + +}; + +#endif diff --git a/NinjaBlockWiFly/README.md b/NinjaBlockWiFly/README.md new file mode 100644 index 0000000..f87c27a --- /dev/null +++ b/NinjaBlockWiFly/README.md @@ -0,0 +1,39 @@ +NinjaBlockWiFly +==================== + +Arduino library for connecting to the Ninja API via Wi-Fly. + +*NinjaBlockWiFly* is based on the original *NinjaBlocksEthernet* minimal library for connecting directly to the Ninja API from any board compatible, but with the WiFly HQ library. The code has also been improved to utilize PROGMEM library to save memory space and allow for other libraries to be utilized as part of your sketch. + + +Ninja Params +------------ +For reference the NinjaBlock params are: + +1. *token* - For hacking use the virtual block token from https://a.ninja.is/hacking , there are other ways to get a token but hard to squeeze into a library for a 328. +2. *nodeID* - This is the boards ID. Can be any 12+char string. Its used in REST so its a good idea to make it easy to remember, e.g. ARDUINOBLOCK. +3. *vendorID* - Use 0 with the device IDs here http://ninjablocks.com/docs/device-ids and you get pretty widgets for "free". Create your own by pinging help@ninjablocks.com. +4. *deviceID* - Identifies each device and maps to UI elements and properties that allow other apps to interact with your devices without knowing about them, see link above. +5. *guid* - Think of this as port - it is used to differentiate between multiple devices of the same type. + + +You need a Ninja Blocks account to get tokens, signup at https://a.ninja.is + + +Usage Notes +----------- + +1. Hit the forums http://forums.ninjablocks.com if something isn't clear. +2. Copy your libraries into the Arduino IDEs library folder +3. Get a virtual_block_token from https://a.ninja.is/hacking +4. Configure Wi-Fly (using an Xbee USB breakout board and CoolTerm/Terminal) to your desired baud rate, flush timeout, and flush size. It hasn't worked for more than 9600 baud while using the SoftwareSerial, however, I am willing to learn. You can also try to utilize the hardware Serial and switch the debugging around (of course, you will also need to disconnect and re-connect to re-program the Uno) to see if you can get it any faster. +5. Use the Ninja Dashboard, REST interface, or helper libs to pissfart to your hearts content! + + +curl -H 'Content-Type: application/json' \\ + -X 'PUT' \\ + -d '{ "DA" : "Arduino REST FTW" }' \\ + -i https://api.ninja.is/rest/v0/device/ARDUINOBLOCK_0_0_7000?user_access_token=YOURTOKEN + + +NB: A user access token is not the same as a block token, from https://a.ninja.is/hacking diff --git a/NinjaBlockWiFly/WiFlyHQ.cpp b/NinjaBlockWiFly/WiFlyHQ.cpp new file mode 100644 index 0000000..dff8daf --- /dev/null +++ b/NinjaBlockWiFly/WiFlyHQ.cpp @@ -0,0 +1,2840 @@ +/*- + * 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 +//#define 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="; + +/* 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; + } +} + +int WiFly::getsTerm(char *buf, int size, char term, uint16_t timeout) +{ + char ch; + int ind=0; + + DPRINTLN(F("getsTerm:")); + + while (readTimeout(&ch, timeout)) { + if (ch == term) { + if (buf) { + buf[ind] = 0; + } + return ind; + } + + /* Truncate to buffer size */ + if ((ind < (size-1)) && buf) { + buf[ind++] = ch; + } + } + + if (buf) { + buf[ind] = 0; + } + return 0; +} + +/** + * Read characters into the buffer until a carriage-return and newline is reached. + * If the buffer is too small, the remaining characters in the line are discarded. + * @param buf - the buffer to read into. If this is NULL then all characters in the line are discarded. + * @param size - the size of the buffer (max number of characters it can store) + * @param timeout - the number of milliseconds to wait for a new character to arrive + * @returns the number of characters read into the buffer. + * @retval 0 - timeout reading newline + * @note The buffer will be null terminated, and in effect can hold size-1 characters. + */ +int WiFly::gets(char *buf, int size, uint16_t timeout) +{ + char ch; + int ind=0; + + DPRINTLN(F("gets:")); + + while (readTimeout(&ch, timeout)) { + if (ch == '\r') { + readTimeout(&ch, timeout); + if (ch == '\n') { + if (buf) { + buf[ind] = 0; + } + return ind; + } + if (buf) { + if (ind < (size-2)) { + buf[ind++] = '\r'; + buf[ind++] = ch; + } else if (ind < (size - 1)) { + buf[ind++] = '\r'; + } + } + } + /* Truncate to buffer size */ + if ((ind < (size-1)) && buf) { + buf[ind++] = ch; + } + } + + if (buf) { + buf[ind] = 0; + } + return 0; +} + +/* Get the WiFly ready to receive a command. */ +boolean WiFly::startCommand() +{ + if (!inCommandMode) { + if (!enterCommandMode()) { + return false; + } + /* If we're already in command mode, then we don't exit it in finishCommand(). + * This is an optimisation to avoid switching in and out of command mode + * when using several commands to implement another command. + */ + } else { + DPRINT(F("Already in command mode\n\r")); + } + exitCommand++; + return true; +} + +/* Finished with command */ +boolean WiFly::finishCommand() +{ + if (--exitCommand == 0) { + return exitCommandMode(); + } + return true; +} + +/* Get the value of an option */ +char *WiFly::getopt(int opt, char *buf, int size) +{ + if (startCommand()) { + send_P(requests[opt].req); + + if (match_P(requests[opt].resp, 500)) { + gets(buf, size); + getPrompt(); + finishCommand(); + return buf; + } + + finishCommand(); + } + return (char *)""; +} + +/* 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) +{ + String temp = ssid; + temp.replace(' ', '$'); + char escapedSSID[strlen(ssid) + 1]; + temp.toCharArray(escapedSSID, strlen(ssid) + 1); + setSSID(escapedSSID); + + if (mode == WIFLY_MODE_WPA) { + temp = password; + temp.replace(' ', '$'); + char escapedPassphrase[strlen(password) + 1]; + temp.toCharArray(escapedPassphrase, strlen(password) + 1); + setPassphrase(escapedPassphrase); + } 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 */ + startCommand(); + if (setopt(PSTR("set u i"), 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; +} diff --git a/NinjaBlockWiFly/WiFlyHQ.h b/NinjaBlockWiFly/WiFlyHQ.h new file mode 100644 index 0000000..c84caef --- /dev/null +++ b/NinjaBlockWiFly/WiFlyHQ.h @@ -0,0 +1,367 @@ +/*- + * 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. + */ + +/* Release history + * + * Version Date Description + * 0.1 25-Mar-2012 First release. + * 0.2 09-Apr-2012 Added features to support http servers. + * - added an httpserver.ino example. + * - added sendChunk() and sendChunkln() to send chunked HTTP bodies. + * - added terminal() method for simple terminal access via debug stream + * - replaced getFreeMemory() with simpler version that works with 0 bytes + * - turned peek buffer into a circular buffer to fix bug with detecting + * *CLOS* and *OPEN* after a partial match. + * - Added new TCP connection detection via *OPEN* match from available(). + * isConnected() can now be polled until a client connects. + * - made the match() function public, handy for matching text in a stream. + * - Added a getProtocol() function to get current set of protocols. + * 0.3 21-Apr-2012 Added createAdhocNetwork() to create an Ad Hoc WiFi network. + * Optimised the setopt() and getopt() function so they handle + * integer conversions and refactored all of the set and get functions. + * Added a multMatch_P() function to match serial data against multiple + * progmem strings. + * Added failure detection to the join() function to quickly detect + * a failure rather than relying on a timeout. + * Added setJoin() and getJoin() function for access to the wlan join parameter. + * Refactored getres() to use the new multiMatch_P() function. + * + */ + +#ifndef _WIFLYHQ_H_ +#define _WIFLYHQ_H_ + +#include +#include +#include +#include + +/* IP Protocol bits */ +#define WIFLY_PROTOCOL_UDP 0x01 +#define WIFLY_PROTOCOL_TCP 0x02 +#define WIFLY_PROTOCOL_SECURE 0x04 +#define WIFLY_PROTOCOL_TCP_CLIENT 0x08 +#define WIFLY_PROTOCOL_HTTP 0x10 /* HTTP Client mode */ +#define WIFLY_PROTOCOL_RAW 0x20 +#define WIFLY_PROTOCOL_SMTP 0x40 + +/* IP Flag bits */ +#define WIFLY_FLAG_TCP_KEEP 0x01 /* Keep TCP connection alive when wifi lost */ +#define WIFLY_FLAG_TCP_NODELAY 0x02 +#define WIFLY_FLAG_TCP_RETRY 0x04 +#define WIFLY_FLAG_UDP_RETRY 0x08 +#define WIFLY_FLAG_DNS_CACHING 0x10 +#define WIFLY_FLAG_ARP_CACHING 0x20 +#define WIFLY_FLAG_UDP_AUTO_PAIR 0x40 +#define WIFLY_FLAG_ADD_TIMESTAMP 0x80 + +/* UART mode bits */ +#define WIFLY_UART_MODE_NOECHO 0x01 +#define WIFLY_UART_MODE_DATA_TRIGGER 0x02 +#define WIFLY_UART_MODE_SLEEP_RX_BREAK 0x08 +#define WIFLY_UART_MODE_RX_BUFFER 0x10 + +/* DHCP modes */ +#define WIFLY_DHCP_MODE_OFF 0x00 /* No DHCP, static IP mode */ +#define WIFLY_DHCP_MODE_ON 0x01 /* get IP, Gateway, and DNS from AP */ +#define WIFLY_DHCP_MODE_AUTOIP 0x02 /* Used with Adhoc networks */ +#define WIFLY_DHCP_MODE_CACHE 0x03 /* Use previous DHCP address based on lease */ +#define WIFLY_DHCP_MODE_SERVER 0x04 /* Server DHCP IP addresses? */ + +/* WLAN Join modes */ +#define WIFLY_WLAN_JOIN_MANUAL 0x00 /* Don't auto-join a network */ +#define WIFLY_WLAN_JOIN_AUTO 0x01 /* Auto-join network set in SSID, passkey, and channel. */ +#define WIFLY_WLAN_JOIN_ANY 0x02 /* Ignore SSID and join strongest network using passkey. */ +#define WIFLY_WLAN_JOIN_ADHOC 0x04 /* Create an Adhoc network using SSID, Channel, IP and NetMask */ + +#define WIFLY_DEFAULT_TIMEOUT 500 /* 500 milliseconds */ + +#define WIFLY_MODE_WPA 0 +#define WIFLY_MODE_WEP 1 + +class WFDebug : public Stream { +public: + WFDebug(); + void begin(Stream *debugPrint); + + virtual size_t write(uint8_t byte); + virtual int read() { return debug->read(); } + virtual int available() { return debug->available(); } + virtual void flush() { return debug->flush(); } + virtual int peek() { return debug->peek(); } + + using Print::write; +private: + Stream *debug; +}; + +class WiFly : public Stream { +public: + WiFly(); + + boolean begin(Stream *serialdev, Stream *debugPrint = NULL); + + char *getSSID(char *buf, int size); + uint8_t getJoin(); + char *getDeviceID(char *buf, int size); + char *getIP(char *buf, int size); + uint16_t getPort(); + char *getNetmask(char *buf, int size); + char *getGateway(char *buf, int size); + char *getDNS(char *buf, int size); + char *getMAC(char *buf, int size); + int8_t getDHCPMode(); + uint32_t getRate(); + uint8_t getTxPower(); + + uint16_t getConnection(); + int8_t getRSSI(); + + bool setJoin(uint8_t join); + boolean setDeviceID(const char *buf); + boolean setBaud(uint32_t baud); + uint32_t getBaud(); + uint8_t getUartMode(); + uint8_t getIpFlags(); + uint8_t getProtocol(); + + uint8_t getFlushChar(); + uint16_t getFlushSize(); + uint16_t getFlushTimeout(); + + char *getHostIP(char *buf, int size); + uint16_t getHostPort(); + + boolean setSSID(const char *buf); + boolean setIP(const char *buf); + boolean setIP(const __FlashStringHelper *buf); + boolean setPort(const uint16_t port); + boolean setNetmask(const char *buf); + boolean setNetmask(const __FlashStringHelper *buf); + boolean setGateway(const char *buf); + boolean setDNS(const char *buf); + boolean setChannel(uint8_t channel); + boolean setKey(const char *buf); + boolean setPassphrase(const char *buf); + boolean setSpaceReplace(const char *buf); + boolean setDHCP(const uint8_t mode); + boolean setRate(uint32_t rate); + boolean setTxPower(uint8_t dBm); + + boolean setHostIP(const char *buf); + boolean setHostIP(const __FlashStringHelper *buf); + boolean setHostPort(const uint16_t port); + boolean setHost(const char *buf, uint16_t port); + + boolean setProtocol(const uint8_t protocol); + boolean setIpProtocol(const uint8_t protocol); /* obsolete */ + boolean setIpFlags(const uint8_t flags); + boolean setUartMode(const uint8_t mode); + + boolean setBroadcastInterval(const uint8_t seconds); + + boolean setTimeAddress(const char *buf); + boolean setTimePort(const uint16_t port); + boolean setTimezone(const uint8_t zone); + boolean setTimeEnable(const uint16_t enable); + + boolean setAdhocBeacon(const uint16_t msecs); + boolean setAdhocProbe(const uint16_t secs); + uint16_t getAdhocBeacon(); + uint16_t getAdhocProbe(); + uint16_t getAdhocReboot(); + + boolean setFlushTimeout(const uint16_t timeout); + boolean setFlushChar(const char flushChar); + boolean setFlushSize(uint16_t size); + boolean enableDataTrigger(const uint16_t flushtime=10, const char flushChar=0, const uint16_t flushSize=64); + boolean disableDataTrigger(); + boolean enableUdpAutoPair(); + boolean disableUdpAutoPair(); + + boolean setIOFunc(const uint8_t func); + + char *getTime(char *buf, int size); + uint32_t getUptime(); + uint8_t getTimezone(); + uint32_t getRTC(); + + bool getHostByName(const char *hostname, char *buf, int size); + boolean ping(const char *host); + + boolean enableDHCP(); + boolean disableDHCP(); + + boolean createAdhocNetwork(const char *ssid, uint8_t channel); + boolean join(const char *ssid, uint16_t timeout=20000); + boolean join(uint16_t timeout=20000); + boolean join(const char *ssid, const char *password, bool dhcp=true, uint8_t mode=WIFLY_MODE_WPA, uint16_t timeout=20000); + boolean leave(); + boolean isAssociated(); + + boolean save(); + boolean reboot(); + boolean factoryRestore(); + + boolean sendto(const uint8_t *data, uint16_t size, const char *host, uint16_t port); + boolean sendto(const uint8_t *data, uint16_t size, IPAddress host, uint16_t port); + boolean sendto(const char *data, const char *host, uint16_t port); + boolean sendto(const char *data, IPAddress host, uint16_t port); + boolean sendto(const __FlashStringHelper *data, const char *host, uint16_t port); + boolean sendto(const __FlashStringHelper *data, IPAddress host, uint16_t port); + + void enableHostRestore(); + void disableHostRestore(); + + boolean open(const char *addr, int port=80, boolean block=true); + boolean open(IPAddress addr, int port=80, boolean block=true); + boolean close(); + boolean openComplete(); + boolean isConnected(); + boolean isInCommandMode(); + + virtual size_t write(uint8_t byte); + virtual int read(); + virtual int available(); + virtual void flush(); + virtual int peek(); + + char *iptoa(IPAddress addr, char *buf, int size); + IPAddress atoip(char *buf); + boolean isDotQuad(const char *addr); + + void sendChunk(const char *str); + void sendChunk(const __FlashStringHelper *str); + void sendChunkln(const char *str); + void sendChunkln(const __FlashStringHelper *str); + void sendChunkln(void); + + int getFreeMemory(); + void terminal(); + + using Print::write; + + void dbgBegin(int size=256); + void dbgDump(); + void dbgEnd(); + boolean debugOn; + + boolean match(const char *str, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + boolean match(const __FlashStringHelper *str, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + int multiMatch_P(uint16_t timeout, uint8_t count, ...); + int gets(char *buf, int size, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + int getsTerm(char *buf, int size, char term, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + void flushRx(int timeout=WIFLY_DEFAULT_TIMEOUT); + + boolean setFtpDefaults(void); + boolean setFtpAddress(const char *addr); + boolean setFtpPort(uint16_t port); + boolean setFtpDirectory(const char *dir); + boolean setFtpUser(const char *user); + boolean setFtpPassword(const char *password); + boolean setFtpFilename(const char *filename); + boolean setFtpTimer(uint16_t msecs); + boolean setFtpMode(uint8_t mode); + + boolean ftpGet( + const char *addr, + const char *dir, + const char *user, + const char *password, + const char *filename); + WFDebug debug; /* Internal debug channel. */ + + private: + void init(void); + + void dump(const char *str); + + boolean sendto( + const uint8_t *data, + uint16_t size, + const __FlashStringHelper *flashData, + const char *host, + uint16_t port); + + boolean match_P(const prog_char *str, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + int8_t multiMatch_P(const prog_char *str[], uint8_t count, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + + void send_P(const prog_char *str); + void send(const char *str); + void send(const char ch); + boolean enterCommandMode(); + boolean exitCommandMode(); + boolean setPrompt(); + boolean getPrompt(uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + boolean checkPrompt(const char *str); + int getResponse(char *buf, int size, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + boolean readTimeout(char *ch, uint16_t timeout=WIFLY_DEFAULT_TIMEOUT); + boolean startCommand(); + boolean finishCommand(); + char *getopt(int opt, char *buf, int size); + uint32_t getopt(int opt, uint8_t base=DEC); + boolean setopt(const prog_char *cmd, const char *buf=NULL, const __FlashStringHelper *buf_P=NULL); + boolean setopt(const prog_char *opt, const uint32_t value, uint8_t base=DEC); + boolean getres(char *buf, int size); + + boolean checkStream(const prog_char *str, boolean peeked); + boolean checkClose(boolean peeked); + boolean checkOpen(boolean peeked); + + boolean hide(); + + boolean inCommandMode; + int exitCommand; + boolean dhcp; + bool restoreHost; + bool restoreHostStored; + char lastHost[32]; + uint16_t lastPort; + + boolean tcpMode; + boolean udpAutoPair; + + boolean connected; + boolean connecting; + struct { + uint8_t tcp; + uint8_t assoc; + uint8_t authen; + uint8_t dnsServer; + uint8_t dnsFound; + uint8_t channel; + } status; + + Stream *serial; /* Serial interface to WiFly */ + + + + /* for dbgDump() */ + char *dbgBuf; + int dbgInd; + int dbgMax; +}; + +#endif diff --git a/NinjaBlockWiFly/examples/Simple/Simple.ino b/NinjaBlockWiFly/examples/Simple/Simple.ino new file mode 100644 index 0000000..6b63b16 --- /dev/null +++ b/NinjaBlockWiFly/examples/Simple/Simple.ino @@ -0,0 +1,247 @@ +/* + NinjaBlock Basic Comms Example - @ozziegurkan + + This sketch uses the NinjaBlockWiFly library to create a Tri-State RF433 actuator + and a simple led actuator. This is awesome because it gives your Arduino project a + REST interface in a couple of minutes. + + It utilizes Wi-Fly card by Roving Networks supplied by Sparkfun.com through the use of + SoftwareSerial library built into Arduino. + + Ninja Params + ------------ + + You need a Ninja Blocks account to use the library, use the invite code 1012BB to signup + at https://a.ninja.is if you don't have one. + + For reference the NinjaBlock params are: + + token For hacking use the virtual block token from https://a.ninja.is/hacking , there + are other ways to get a token but hard to squeeze into a library for a 328. + nodeID This is the board's ID. Can be any 12+char string. It's used in REST so it's a + good idea to make it easy to remember, e.g. ARDUINOBLOCK. + vendorID Use 0 with the device IDs here http://ninjablocks.com/docs/device-ids and + you get pretty widgets for "free". Create your own by pinging help@ninjablocks.com. + deviceID Identifies each device and maps to UI elements and properties that allow other apps + to interact with your devices without knowing about them, see link above. + guid Think of this as port - it is used to differentiate between multiple devices of + the same type. + + How to use this example + ----------------------- + + 1) I've tested this with a RN-XV Wi-Fly module from Sparkfun. I am using the 433 transmitter + from Seeed Studio. + + Connect the RX pin of the Arduino to the TX pin of the 433 transmitter. + Connect the anode (long lead, +ve) of a LED to pin 7, and connect that LED's cathode (short lead, -ve) to GND through a 330R-1K resistor. + + 2) Copy the NinjaBlockWiFly library into your Arduino libraries dir. + + 3) Upload and plug in the intwertubes + + 4) Your OnBoard RGB LED and RF433 widgets will show up automatically. Turn your led on by + clicking on white and off by clicking on black. "Listen" to 433 signals by clicking on your remote + and then send it back down to trigger something. + + 5) The real fun is using REST interface, e.g. turn your light on with + + curl -H 'Content-Type: application/json' \ + -X 'PUT' \ + -d '{ "DA" : "FFFFFF" }' \ + -i https://api.ninja.is/rest/v0/device/ARDUINOBLOCK_0_0_1000?user_access_token=YOURTOKEN + + NB: Access tokens are not the same as block tokens, get yours from https://a.ninja.is/hacking + + You can also add a callback for your button that pings a uri of your choice whenever its + pressed, or create a rule that sends an sms, posts to facebook, hits a webhook or whatever. + I hope you do something interesting, be sure and let me know in the forums + http://ninjablocks.com/forums + +*/ + +#include "NinjaBlockWiFly.h" +#include "RCSwitch.h" +#include "WiFlyHQ.h" +#include + +#undef DEBUG_SERIAL +#define DEBUG_SERIAL + +#ifdef DEBUG_SERIAL +#define DSBEGIN() Serial.begin(57600) +#define DPRINT(item) Serial.print(item) +#define DPRINTLN(item) Serial.println(item) +#else +#define DSBEGIN() +#define DPRINT(item) +#define DPRINTLN(item) +#endif + +#define DEFAULT_VENDOR_ID 0 +#define LED_DEVICE_ID 1000 +#define RF_DEVICE_ID 11 +#define BUTTON_DEVICE_ID 5 + +const byte led = 7; // Connect the anode (long lead, +ve) of a LED to this pin, and connect that LED's cathode (short lead, -ve) to GND through a 330R-1K resistor. +const byte rfTx = 4; // transmit pin + +SoftwareSerial WiFlySerial(2,3); +WiFly wifly; +RCSwitch mySwitch = RCSwitch(); + +char LED_VALUE[] = "000000"; +const char mySSID[] = "SSID"; +const char myPassword[] = "PASS"; + +void setup(){ + DSBEGIN(); + DPRINTLN(F("Starting")); + + pinMode(led, OUTPUT); + + WiFlySerial.begin(9600); + if( !wifly.begin(&WiFlySerial) ) { + DPRINTLN(F("Init Ethernet failed")); + wifly.terminal(); + } + if( !wifly.isAssociated() ) { + DPRINTLN(F("Joining Network")); + if (wifly.join(mySSID, myPassword, true)) { + wifly.save(); + DPRINTLN(F("Joined wifi network")); + } + else { + DPRINTLN(F("Failed to join wifi network")); + wifly.terminal(); + } + } + else { + DPRINTLN(F("Already joined network")); + } + + NinjaBlock.client = wifly; + #ifdef DEBUG_SERIAL + NinjaBlock.debugSerial = &Serial; + #endif + NinjaBlock.host = "api.ninja.is"; + NinjaBlock.port = 80; + NinjaBlock.nodeID = PSTR("ARDUINOBLOCK"); + NinjaBlock.token = PSTR("YOUR_TOKEN"); + if (NinjaBlock.begin()==0) { + DPRINTLN(F("Init failed")); + wifly.terminal(); + } + + //Register LED + NinjaBlock.guid="0"; + NinjaBlock.vendorID=DEFAULT_VENDOR_ID; + NinjaBlock.deviceID=LED_DEVICE_ID; + NinjaBlock.send(LED_VALUE); + + //Register RF device + NinjaBlock.guid="0"; + NinjaBlock.vendorID=DEFAULT_VENDOR_ID; + NinjaBlock.deviceID = RF_DEVICE_ID; + NinjaBlock.send("110111010101011100000000"); //sample RF string +} + +void loop() { + sendObjects(); + + if(NinjaBlock.receive()) { + // If this function returns true, there are commands (data) from the server + // Return values are: + // NinjaBlock.strGUID + // NinjaBlock.intVID + // NinjaBlock.intDID + // NinjaBlock.intDATA - if data is integer + // NinjaBlock.strDATA - if data is string (note char[64]) + + if( NinjaBlock.IsTick ) { + //update sender + NinjaBlock.guid="0"; + NinjaBlock.vendorID=DEFAULT_VENDOR_ID; + NinjaBlock.deviceID=LED_DEVICE_ID; + NinjaBlock.send(LED_VALUE); + } + else if (NinjaBlock.IsDATAString) { + + // Serial.print("strDATA="); + // DPRINTLN(NinjaBlock.strDATA); + + if (NinjaBlock.intDID == 1000) { + // FFFFFF is "white" in the RGB widget we identified as + if (strcmp(NinjaBlock.strDATA,"FFFFFF") == 0) { + DPRINTLN(F("LED ON")); + digitalWrite(led, HIGH); + } else if (strcmp(NinjaBlock.strDATA,"000000") == 0) { + DPRINTLN(F("LED OFF")); + digitalWrite(led, LOW); + } + + //update sender + strcpy(LED_VALUE, NinjaBlock.strDATA); + NinjaBlock.guid="0"; + NinjaBlock.vendorID=DEFAULT_VENDOR_ID; + NinjaBlock.deviceID=LED_DEVICE_ID; + NinjaBlock.send(LED_VALUE); + } + else if( NinjaBlock.intDID == 11 ) { + //convert to tri-state + //receiving binary, convert to hex string + // DPRINT(F("strData=")); + // DPRINTLN(NinjaBlock.strDATA); + char* tsString = bin2tristate(NinjaBlock.strDATA); + DPRINT(F("tri-state-string=")); DPRINTLN(tsString); + mySwitch.enableTransmit(rfTx); + mySwitch.setProtocol(1, 200); //change this for your plugs + mySwitch.sendTriState(tsString); + mySwitch.disableTransmit(); + + //update sender + NinjaBlock.guid="0"; + NinjaBlock.vendorID=DEFAULT_VENDOR_ID; + NinjaBlock.deviceID=RF_DEVICE_ID; + NinjaBlock.send(NinjaBlock.strDATA); + } + } else { + // Do something with int data + // Serial.print("intDATA="); + // DPRINTLN(NinjaBlock.intDATA); + } + + } + +} + +void sendObjects() { + //write any code to send data up to Ninja servers +} + +/** +** Utility method from RCSwitch to convert the binary from tri-state remote +** into actual tri-state. The Ninja server converts the Hex into Binary +** so it has to be converted back to Tri-State HEX (which isn't the same as just +** converting it to Hex). +**/ +static char* bin2tristate(char* bin) { + char returnValue[50]; + int pos = 0; + int pos2 = 0; + while (bin[pos]!='\0' && bin[pos+1]!='\0') { + if (bin[pos]=='0' && bin[pos+1]=='0') { + returnValue[pos2] = '0'; + } else if (bin[pos]=='1' && bin[pos+1]=='1') { + returnValue[pos2] = '1'; + } else if (bin[pos]=='0' && bin[pos+1]=='1') { + returnValue[pos2] = 'F'; + } else { + return "not applicable"; + } + pos = pos+2; + pos2++; + } + returnValue[pos2] = '\0'; + return returnValue; +}