From d063469e030dce23ceeb8a4db025215edb0f1250 Mon Sep 17 00:00:00 2001 From: Tony DiCola Date: Tue, 9 Jun 2015 21:22:36 -0700 Subject: [PATCH] Add Ladyada's FONA implementation. --- Adafruit_MQTT_FONA.h | 112 +++++++++++++++++++++ examples/mqtt_fona/fonahelper.cpp | 45 +++++++++ examples/mqtt_fona/mqtt_fona.ino | 155 ++++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 Adafruit_MQTT_FONA.h create mode 100644 examples/mqtt_fona/fonahelper.cpp create mode 100644 examples/mqtt_fona/mqtt_fona.ino diff --git a/Adafruit_MQTT_FONA.h b/Adafruit_MQTT_FONA.h new file mode 100644 index 0000000..afdb71a --- /dev/null +++ b/Adafruit_MQTT_FONA.h @@ -0,0 +1,112 @@ +#ifndef _ADAFRUIT_MQTT_FONA_H_ +#define _ADAFRUIT_MQTT_FONA_H_ + +#include +#include "Adafruit_MQTT.h" + +#define MQTT_FONA_INTERAVAILDELAY 100 +#define MQTT_FONA_QUERYDELAY 500 + +class Adafruit_MQTT_FONA : public Adafruit_MQTT { + public: + Adafruit_MQTT_FONA(Adafruit_FONA *f, const char *server, uint16_t port, + const char *cid, const char *user, const char *pass): + Adafruit_MQTT(server, port, cid, user, pass), + fona(f) + {} + + bool connectServer() { + char server[40]; + strncpy_P(server, servername, 40); + Watchdog.reset(); + + // connect to server + DEBUG_PRINTLN(F("Connecting to TCP")); + return fona->TCPconnect(server, portnum); + } + + bool disconnect() { + return fona->TCPclose(); + } + + uint16_t readPacket(uint8_t *buffer, uint8_t maxlen, int16_t timeout, + bool checkForValidPubPacket = false) { + uint8_t *buffp = buffer; + DEBUG_PRINTLN(F("Reading a packet..")); + + if (!fona->TCPconnected()) return 0; + + + /* Read data until either the connection is closed, or the idle timeout is reached. */ + uint16_t len = 0; + int16_t t = timeout; + uint16_t avail; + + while (fona->TCPconnected() && (timeout >= 0)) { + DEBUG_PRINT('.'); + while (avail = fona->TCPavailable()) { + DEBUG_PRINT('!'); + + if (len + avail > maxlen) { + // oof we cant read more of the available data in this buffer + return len; + } + + // try to read the data into the end of the pointer + if (! fona->TCPread(buffp, avail)) return len; + + // read it! advance pointer + buffp += avail; + len += avail; + timeout = t; // reset the timeout + + //DEBUG_PRINTLN((uint8_t)c, HEX); + + if (len == maxlen) { // we read all we want, bail + DEBUG_PRINT(F("Read packet:\t")); + DEBUG_PRINTBUFFER(buffer, len); + return len; + } + + // special case where we just one one publication packet at a time + if (checkForValidPubPacket) { + if ((buffer[0] == (MQTT_CTRL_PUBLISH << 4)) && (buffer[1] == len-2)) { + // oooh a valid publish packet! + DEBUG_PRINT(F("Read PUBLISH packet:\t")); + DEBUG_PRINTBUFFER(buffer, len); + return len; + } + } + + } + Watchdog.reset(); + timeout -= MQTT_FONA_INTERAVAILDELAY; + timeout -= MQTT_FONA_QUERYDELAY; // this is how long it takes to query the FONA for avail() + delay(MQTT_FONA_INTERAVAILDELAY); + } + + return len; + } + + bool sendPacket(uint8_t *buffer, uint8_t len) { + DEBUG_PRINTLN(F("Writing packet")); + if (fona->TCPconnected()) { + boolean ret = fona->TCPsend((char *)buffer, len); + //DEBUG_PRINT(F("sendPacket returned: ")); DEBUG_PRINTLN(ret); + if (!ret) { + DEBUG_PRINTLN("Failed to send packet.") + return false; + } + } else { + DEBUG_PRINTLN(F("Connection failed!")); + return false; + } + return true; + } + + private: + uint32_t serverip; + Adafruit_FONA *fona; +}; + +#endif diff --git a/examples/mqtt_fona/fonahelper.cpp b/examples/mqtt_fona/fonahelper.cpp new file mode 100644 index 0000000..d808604 --- /dev/null +++ b/examples/mqtt_fona/fonahelper.cpp @@ -0,0 +1,45 @@ +#include +#include "Adafruit_FONA.h" + +#define halt(s) { Serial.println(F( s )); while(1); } + +extern Adafruit_FONA fona; +extern SoftwareSerial fonaSS; + +boolean FONAconnect(const __FlashStringHelper *apn, const __FlashStringHelper *username, const __FlashStringHelper *password) { + Watchdog.enable(8000); + Watchdog.reset(); + + Serial.println(F("Initializing FONA....(May take 3 seconds)")); + + fonaSS.begin(4800); // if you're using software serial + + if (! fona.begin(fonaSS)) { // can also try fona.begin(Serial1) + Serial.println(F("Couldn't find FONA")); + return false; + } + fonaSS.println("AT+CMEE=2"); + Serial.println(F("FONA is OK")); + Watchdog.reset(); + Serial.println(F("Checking for network...")); + while (fona.getNetworkStatus() != 1) { + delay(500); + } + Watchdog.reset(); + + fona.setGPRSNetworkSettings(apn, username, password); + + Serial.println(F("Disabling GPRS")); + fona.enableGPRS(false); + + Watchdog.reset(); + Serial.println(F("Enabling GPRS")); + if (!fona.enableGPRS(true)) { + Serial.println(F("Failed to turn GPRS on")); + return false; + } + Watchdog.reset(); + + return true; +} + diff --git a/examples/mqtt_fona/mqtt_fona.ino b/examples/mqtt_fona/mqtt_fona.ino new file mode 100644 index 0000000..3ac1d35 --- /dev/null +++ b/examples/mqtt_fona/mqtt_fona.ino @@ -0,0 +1,155 @@ +#include +#include +#include "Adafruit_FONA.h" +#include "Adafruit_MQTT.h" +#include "Adafruit_MQTT_FONA.h" + +/*************************** FONA Pins ***********************************/ + +#define FONA_RX 2 +#define FONA_TX 3 +#define FONA_RST 4 +SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX); + +Adafruit_FONA fona = Adafruit_FONA(FONA_RST); + +/************************* WiFi Access Point *********************************/ + + // Optionally configure a GPRS APN, username, and password. + // You might need to do this to access your network's GPRS/data + // network. Contact your provider for the exact APN, username, + // and password values. Username and password are optional and + // can be removed, but APN is required. +#define FONA_APN "" +#define FONA_USERNAME "" +#define FONA_PASSWORD "" + +/************************* Adafruit.io Setup *********************************/ + +#define AIO_SERVER "io.adafruit.com" +#define AIO_SERVERPORT 1883 +#define AIO_USERNAME "...your AIO username (see https://accounts.adafruit.com)..." +#define AIO_KEY "...your AIO key..."; + +/************ Global State (you don't need to change this!) ******************/ + +// Store the MQTT server, client ID, username, and password in flash memory. +// This is required for using the Adafruit MQTT library. +const char MQTT_SERVER[] PROGMEM = AIO_SERVER; +const char MQTT_CLIENTID[] PROGMEM = AIO_KEY; +const char MQTT_USERNAME[] PROGMEM = AIO_USERNAME; +const char MQTT_PASSWORD[] PROGMEM = AIO_KEY; + +// Setup the FONA MQTT class by passing in the FONA class and MQTT server and login details. +Adafruit_MQTT_FONA mqtt(&fona, MQTT_SERVER, AIO_SERVERPORT, MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD); + +// You don't need to change anything below this line! +#define halt(s) { Serial.println(F( s )); while(1); } + +// FONAconnect is a helper function that sets up the FONA and connects to +// the GPRS network. See the fonahelper.cpp tab above for the source! +boolean FONAconnect(const __FlashStringHelper *apn, const __FlashStringHelper *username, const __FlashStringHelper *password); + +/****************************** Feeds ***************************************/ + +// Setup a feed called 'photocell' for publishing. +// Notice MQTT paths for AIO follow the form: /feeds/ +const char PHOTOCELL_FEED[] PROGMEM = AIO_USERNAME "/feeds/photocell"; +Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, PHOTOCELL_FEED); + +// Setup a feed called 'onoff' for subscribing to changes. +const char ONOFF_FEED[] PROGMEM = AIO_USERNAME "/feeds/onoff"; +Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, ONOFF_FEED); + +/*************************** Sketch Code ************************************/ + +// How many transmission failures in a row we're willing to be ok with before reset +uint8_t txfailures = 0; +#define MAXTXFAILURES 3 + +void setup() { + while (!Serial); + + Serial.begin(115200); + + Serial.println(F("Adafruit FONA MQTT demo")); + + + mqtt.subscribe(&onoffbutton); + + // Initialise the FONA module + while (! FONAconnect(F(FONA_APN), F(FONA_USERNAME), F(FONA_PASSWORD))) { + halt("Retrying FONA"); + } + + Serial.println(F("Connected to Cellular!")); + + Watchdog.reset(); + delay(3000); // wait a few seconds to stabilize connection + Watchdog.reset(); +} + +uint32_t x=0; + +void loop() { + // Make sure to reset watchdog every loop iteration! + Watchdog.reset(); + + // check if we're still connected + if (!fona.TCPconnected() || (txfailures >= MAXTXFAILURES)) { + ////////////////////////////// + Serial.println(F("Connecting to MQTT...")); + int8_t ret, retries = 5; + while (retries && (ret = mqtt.connect()) != 0) { + switch (ret) { + case 1: Serial.println(F("Wrong protocol")); break; + case 2: Serial.println(F("ID rejected")); break; + case 3: Serial.println(F("Server unavail")); break; + case 4: Serial.println(F("Bad user/pass")); break; + case 5: Serial.println(F("Not authed")); break; + case 6: Serial.println(F("Failed to subscribe")); break; + default: { + Serial.println(F("Connection failed")); + break; + } + } + Serial.println(F("Retrying MQTT connection")); + retries--; + if (retries == 0) halt("Resetting system"); + delay(5000); + } + Serial.println(F("MQTT Connected!")); + txfailures = 0; + } + + + // Try to ping the MQTT server + /* + if (! mqtt.ping(3) ) { + // MQTT pings failed, lets reconnect + Serial.println("Ping fail!"); + } + */ + + + // this is our 'wait for incoming subscription packets' busy subloop + Adafruit_MQTT_Subscribe *subscription; + while (subscription = mqtt.readSubscription(5000)) { + if (subscription == &onoffbutton) { + Serial.print(F("Got: ")); + Serial.println((char *)onoffbutton.lastread); + } + } + + // Now we can publish stuff! + Serial.print(F("\nSending photocell val ")); + Serial.print(x); + Serial.print("..."); + if (! photocell.publish(x++)) { + Serial.println(F("Failed")); + txfailures++; + } else { + Serial.println(F("OK!")); + txfailures = 0; + } +}