Add Library
This commit is contained in:
parent
e365b9dbd9
commit
3c47103b39
46
libraries/Adafruit-MCP23017-Arduino-Library-master/.github/ISSUE_TEMPLATE.md
vendored
Normal file
46
libraries/Adafruit-MCP23017-Arduino-Library-master/.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||||
|
improve the speed of resolution please review the following guidelines and
|
||||||
|
common troubleshooting steps below before creating the issue:
|
||||||
|
|
||||||
|
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||||
|
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||||
|
something isn't working as expected. In many cases the problem is a common issue
|
||||||
|
that you will more quickly receive help from the forum community. GitHub issues
|
||||||
|
are meant for known defects in the code. If you don't know if there is a defect
|
||||||
|
in the code then start with troubleshooting on the forum first.
|
||||||
|
|
||||||
|
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||||
|
check all of the steps and commands to run have been followed. Consult the
|
||||||
|
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||||
|
|
||||||
|
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||||
|
|
||||||
|
- For uploading sketches or communicating with the board make sure you're using
|
||||||
|
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||||
|
very hard to tell the difference between a data and charge cable! Try using the
|
||||||
|
cable with other devices or swapping to another cable to confirm it is not
|
||||||
|
the problem.
|
||||||
|
|
||||||
|
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||||
|
your board and plug in an external power supply. In many cases just
|
||||||
|
plugging a board into your computer is not enough to power it and other
|
||||||
|
peripherals.
|
||||||
|
|
||||||
|
- **Double check all soldering joints and connections.** Flakey connections
|
||||||
|
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||||
|
|
||||||
|
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||||
|
guarantee a clone board will have the same functionality and work as expected
|
||||||
|
with this code and don't support them.
|
||||||
|
|
||||||
|
If you're sure this issue is a defect in the code and checked the steps above
|
||||||
|
please fill in the following fields to provide enough troubleshooting information.
|
||||||
|
You may delete the guideline and text above to just leave the following details:
|
||||||
|
|
||||||
|
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||||
|
|
||||||
|
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||||
|
VERSION HERE**
|
||||||
|
|
||||||
|
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||||
|
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
26
libraries/Adafruit-MCP23017-Arduino-Library-master/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
26
libraries/Adafruit-MCP23017-Arduino-Library-master/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||||
|
Before you open the request please review the following guidelines and tips to
|
||||||
|
help it be more easily integrated:
|
||||||
|
|
||||||
|
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||||
|
of the code were modified.** This will help us understand any risks of integrating
|
||||||
|
the code.
|
||||||
|
|
||||||
|
- **Describe any known limitations with your change.** For example if the change
|
||||||
|
doesn't apply to a supported platform of the library please mention it.
|
||||||
|
|
||||||
|
- **Please run any tests or examples that can exercise your modified code.** We
|
||||||
|
strive to not break users of the code and running tests/examples helps with this
|
||||||
|
process.
|
||||||
|
|
||||||
|
Thank you again for contributing! We will try to test and integrate the change
|
||||||
|
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||||
|
can't immediately respond to every request. There is no need to bump or check in
|
||||||
|
on a pull request (it will clutter the discussion of the request).
|
||||||
|
|
||||||
|
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||||
|
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||||
|
priorities of the pull request. Don't fret, the open source community thrives on
|
||||||
|
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||||
|
|
||||||
|
After reviewing the guidelines above you can delete this text from the pull request.
|
|
@ -0,0 +1,283 @@
|
||||||
|
/***************************************************
|
||||||
|
This is a library for the MCP23017 i2c port expander
|
||||||
|
|
||||||
|
These displays use I2C to communicate, 2 pins are required to
|
||||||
|
interface
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
#ifdef __AVR
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include <pgmspace.h>
|
||||||
|
#endif
|
||||||
|
#include "Adafruit_MCP23017.h"
|
||||||
|
|
||||||
|
#if ARDUINO >= 100
|
||||||
|
#include "Arduino.h"
|
||||||
|
#else
|
||||||
|
#include "WProgram.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// minihelper to keep Arduino backward compatibility
|
||||||
|
static inline void wiresend(uint8_t x) {
|
||||||
|
#if ARDUINO >= 100
|
||||||
|
Wire.write((uint8_t) x);
|
||||||
|
#else
|
||||||
|
Wire.send(x);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t wirerecv(void) {
|
||||||
|
#if ARDUINO >= 100
|
||||||
|
return Wire.read();
|
||||||
|
#else
|
||||||
|
return Wire.receive();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bit number associated to a give Pin
|
||||||
|
*/
|
||||||
|
uint8_t Adafruit_MCP23017::bitForPin(uint8_t pin){
|
||||||
|
return pin%8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register address, port dependent, for a given PIN
|
||||||
|
*/
|
||||||
|
uint8_t Adafruit_MCP23017::regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr){
|
||||||
|
return(pin<8) ?portAaddr:portBaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a given register
|
||||||
|
*/
|
||||||
|
uint8_t Adafruit_MCP23017::readRegister(uint8_t addr){
|
||||||
|
// read the current GPINTEN
|
||||||
|
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
|
||||||
|
wiresend(addr);
|
||||||
|
Wire.endTransmission();
|
||||||
|
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
|
||||||
|
return wirerecv();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a given register
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::writeRegister(uint8_t regAddr, uint8_t regValue){
|
||||||
|
// Write the register
|
||||||
|
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
|
||||||
|
wiresend(regAddr);
|
||||||
|
wiresend(regValue);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to update a single bit of an A/B register.
|
||||||
|
* - Reads the current register value
|
||||||
|
* - Writes the new register value
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::updateRegisterBit(uint8_t pin, uint8_t pValue, uint8_t portAaddr, uint8_t portBaddr) {
|
||||||
|
uint8_t regValue;
|
||||||
|
uint8_t regAddr=regForPin(pin,portAaddr,portBaddr);
|
||||||
|
uint8_t bit=bitForPin(pin);
|
||||||
|
regValue = readRegister(regAddr);
|
||||||
|
|
||||||
|
// set the value for the particular bit
|
||||||
|
bitWrite(regValue,bit,pValue);
|
||||||
|
|
||||||
|
writeRegister(regAddr,regValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the MCP23017 given its HW selected address, see datasheet for Address selection.
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::begin(uint8_t addr) {
|
||||||
|
if (addr > 7) {
|
||||||
|
addr = 7;
|
||||||
|
}
|
||||||
|
i2caddr = addr;
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
// set defaults!
|
||||||
|
// all inputs on port A and B
|
||||||
|
writeRegister(MCP23017_IODIRA,0xff);
|
||||||
|
writeRegister(MCP23017_IODIRB,0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the default MCP23017, with 000 for the configurable part of the address
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::begin(void) {
|
||||||
|
begin(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the pin mode to either INPUT or OUTPUT
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::pinMode(uint8_t p, uint8_t d) {
|
||||||
|
updateRegisterBit(p,(d==INPUT),MCP23017_IODIRA,MCP23017_IODIRB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads all 16 pins (port A and B) into a single 16 bits variable.
|
||||||
|
*/
|
||||||
|
uint16_t Adafruit_MCP23017::readGPIOAB() {
|
||||||
|
uint16_t ba = 0;
|
||||||
|
uint8_t a;
|
||||||
|
|
||||||
|
// read the current GPIO output latches
|
||||||
|
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
|
||||||
|
wiresend(MCP23017_GPIOA);
|
||||||
|
Wire.endTransmission();
|
||||||
|
|
||||||
|
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 2);
|
||||||
|
a = wirerecv();
|
||||||
|
ba = wirerecv();
|
||||||
|
ba <<= 8;
|
||||||
|
ba |= a;
|
||||||
|
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a single port, A or B, and return its current 8 bit value.
|
||||||
|
* Parameter b should be 0 for GPIOA, and 1 for GPIOB.
|
||||||
|
*/
|
||||||
|
uint8_t Adafruit_MCP23017::readGPIO(uint8_t b) {
|
||||||
|
|
||||||
|
// read the current GPIO output latches
|
||||||
|
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
|
||||||
|
if (b == 0)
|
||||||
|
wiresend(MCP23017_GPIOA);
|
||||||
|
else {
|
||||||
|
wiresend(MCP23017_GPIOB);
|
||||||
|
}
|
||||||
|
Wire.endTransmission();
|
||||||
|
|
||||||
|
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
|
||||||
|
return wirerecv();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes all the pins in one go. This method is very useful if you are implementing a multiplexed matrix and want to get a decent refresh rate.
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::writeGPIOAB(uint16_t ba) {
|
||||||
|
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
|
||||||
|
wiresend(MCP23017_GPIOA);
|
||||||
|
wiresend(ba & 0xFF);
|
||||||
|
wiresend(ba >> 8);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MCP23017::digitalWrite(uint8_t pin, uint8_t d) {
|
||||||
|
uint8_t gpio;
|
||||||
|
uint8_t bit=bitForPin(pin);
|
||||||
|
|
||||||
|
|
||||||
|
// read the current GPIO output latches
|
||||||
|
uint8_t regAddr=regForPin(pin,MCP23017_OLATA,MCP23017_OLATB);
|
||||||
|
gpio = readRegister(regAddr);
|
||||||
|
|
||||||
|
// set the pin and direction
|
||||||
|
bitWrite(gpio,bit,d);
|
||||||
|
|
||||||
|
// write the new GPIO
|
||||||
|
regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB);
|
||||||
|
writeRegister(regAddr,gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MCP23017::pullUp(uint8_t p, uint8_t d) {
|
||||||
|
updateRegisterBit(p,d,MCP23017_GPPUA,MCP23017_GPPUB);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Adafruit_MCP23017::digitalRead(uint8_t pin) {
|
||||||
|
uint8_t bit=bitForPin(pin);
|
||||||
|
uint8_t regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB);
|
||||||
|
return (readRegister(regAddr) >> bit) & 0x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the interrupt system. both port A and B are assigned the same configuration.
|
||||||
|
* Mirroring will OR both INTA and INTB pins.
|
||||||
|
* Opendrain will set the INT pin to value or open drain.
|
||||||
|
* polarity will set LOW or HIGH on interrupt.
|
||||||
|
* Default values after Power On Reset are: (false, false, LOW)
|
||||||
|
* If you are connecting the INTA/B pin to arduino 2/3, you should configure the interupt handling as FALLING with
|
||||||
|
* the default configuration.
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::setupInterrupts(uint8_t mirroring, uint8_t openDrain, uint8_t polarity){
|
||||||
|
// configure the port A
|
||||||
|
uint8_t ioconfValue=readRegister(MCP23017_IOCONA);
|
||||||
|
bitWrite(ioconfValue,6,mirroring);
|
||||||
|
bitWrite(ioconfValue,2,openDrain);
|
||||||
|
bitWrite(ioconfValue,1,polarity);
|
||||||
|
writeRegister(MCP23017_IOCONA,ioconfValue);
|
||||||
|
|
||||||
|
// Configure the port B
|
||||||
|
ioconfValue=readRegister(MCP23017_IOCONB);
|
||||||
|
bitWrite(ioconfValue,6,mirroring);
|
||||||
|
bitWrite(ioconfValue,2,openDrain);
|
||||||
|
bitWrite(ioconfValue,1,polarity);
|
||||||
|
writeRegister(MCP23017_IOCONB,ioconfValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set's up a pin for interrupt. uses arduino MODEs: CHANGE, FALLING, RISING.
|
||||||
|
*
|
||||||
|
* Note that the interrupt condition finishes when you read the information about the port / value
|
||||||
|
* that caused the interrupt or you read the port itself. Check the datasheet can be confusing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Adafruit_MCP23017::setupInterruptPin(uint8_t pin, uint8_t mode) {
|
||||||
|
|
||||||
|
// set the pin interrupt control (0 means change, 1 means compare against given value);
|
||||||
|
updateRegisterBit(pin,(mode!=CHANGE),MCP23017_INTCONA,MCP23017_INTCONB);
|
||||||
|
// if the mode is not CHANGE, we need to set up a default value, different value triggers interrupt
|
||||||
|
|
||||||
|
// In a RISING interrupt the default value is 0, interrupt is triggered when the pin goes to 1.
|
||||||
|
// In a FALLING interrupt the default value is 1, interrupt is triggered when pin goes to 0.
|
||||||
|
updateRegisterBit(pin,(mode==FALLING),MCP23017_DEFVALA,MCP23017_DEFVALB);
|
||||||
|
|
||||||
|
// enable the pin for interrupt
|
||||||
|
updateRegisterBit(pin,HIGH,MCP23017_GPINTENA,MCP23017_GPINTENB);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Adafruit_MCP23017::getLastInterruptPin(){
|
||||||
|
uint8_t intf;
|
||||||
|
|
||||||
|
// try port A
|
||||||
|
intf=readRegister(MCP23017_INTFA);
|
||||||
|
for(int i=0;i<8;i++) if (bitRead(intf,i)) return i;
|
||||||
|
|
||||||
|
// try port B
|
||||||
|
intf=readRegister(MCP23017_INTFB);
|
||||||
|
for(int i=0;i<8;i++) if (bitRead(intf,i)) return i+8;
|
||||||
|
|
||||||
|
return MCP23017_INT_ERR;
|
||||||
|
|
||||||
|
}
|
||||||
|
uint8_t Adafruit_MCP23017::getLastInterruptPinValue(){
|
||||||
|
uint8_t intPin=getLastInterruptPin();
|
||||||
|
if(intPin!=MCP23017_INT_ERR){
|
||||||
|
uint8_t intcapreg=regForPin(intPin,MCP23017_INTCAPA,MCP23017_INTCAPB);
|
||||||
|
uint8_t bit=bitForPin(intPin);
|
||||||
|
return (readRegister(intcapreg)>>bit) & (0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCP23017_INT_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/***************************************************
|
||||||
|
This is a library for the MCP23017 i2c port expander
|
||||||
|
|
||||||
|
These displays use I2C to communicate, 2 pins are required to
|
||||||
|
interface
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
#ifndef _Adafruit_MCP23017_H_
|
||||||
|
#define _Adafruit_MCP23017_H_
|
||||||
|
|
||||||
|
// Don't forget the Wire library
|
||||||
|
#ifndef ARDUINO_AVR_GEMMA
|
||||||
|
//TinyWireM is now part of
|
||||||
|
// Adafruit version of Wire Library, so this
|
||||||
|
// will work with Adafruit ATtiny85's
|
||||||
|
//But Arduino Gemma doesn't use that library
|
||||||
|
// We do NOT want to include Wire if it's an arduino Gemma
|
||||||
|
#include <Wire.h>
|
||||||
|
#else
|
||||||
|
#include <TinyWireM.h>
|
||||||
|
#define Wire TinyWireM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class Adafruit_MCP23017 {
|
||||||
|
public:
|
||||||
|
void begin(uint8_t addr);
|
||||||
|
void begin(void);
|
||||||
|
|
||||||
|
void pinMode(uint8_t p, uint8_t d);
|
||||||
|
void digitalWrite(uint8_t p, uint8_t d);
|
||||||
|
void pullUp(uint8_t p, uint8_t d);
|
||||||
|
uint8_t digitalRead(uint8_t p);
|
||||||
|
|
||||||
|
void writeGPIOAB(uint16_t);
|
||||||
|
uint16_t readGPIOAB();
|
||||||
|
uint8_t readGPIO(uint8_t b);
|
||||||
|
|
||||||
|
void setupInterrupts(uint8_t mirroring, uint8_t open, uint8_t polarity);
|
||||||
|
void setupInterruptPin(uint8_t p, uint8_t mode);
|
||||||
|
uint8_t getLastInterruptPin();
|
||||||
|
uint8_t getLastInterruptPinValue();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t i2caddr;
|
||||||
|
|
||||||
|
uint8_t bitForPin(uint8_t pin);
|
||||||
|
uint8_t regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr);
|
||||||
|
|
||||||
|
uint8_t readRegister(uint8_t addr);
|
||||||
|
void writeRegister(uint8_t addr, uint8_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility private method to update a register associated with a pin (whether port A/B)
|
||||||
|
* reads its value, updates the particular bit, and writes its value.
|
||||||
|
*/
|
||||||
|
void updateRegisterBit(uint8_t p, uint8_t pValue, uint8_t portAaddr, uint8_t portBaddr);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MCP23017_ADDRESS 0x20
|
||||||
|
|
||||||
|
// registers
|
||||||
|
#define MCP23017_IODIRA 0x00
|
||||||
|
#define MCP23017_IPOLA 0x02
|
||||||
|
#define MCP23017_GPINTENA 0x04
|
||||||
|
#define MCP23017_DEFVALA 0x06
|
||||||
|
#define MCP23017_INTCONA 0x08
|
||||||
|
#define MCP23017_IOCONA 0x0A
|
||||||
|
#define MCP23017_GPPUA 0x0C
|
||||||
|
#define MCP23017_INTFA 0x0E
|
||||||
|
#define MCP23017_INTCAPA 0x10
|
||||||
|
#define MCP23017_GPIOA 0x12
|
||||||
|
#define MCP23017_OLATA 0x14
|
||||||
|
|
||||||
|
|
||||||
|
#define MCP23017_IODIRB 0x01
|
||||||
|
#define MCP23017_IPOLB 0x03
|
||||||
|
#define MCP23017_GPINTENB 0x05
|
||||||
|
#define MCP23017_DEFVALB 0x07
|
||||||
|
#define MCP23017_INTCONB 0x09
|
||||||
|
#define MCP23017_IOCONB 0x0B
|
||||||
|
#define MCP23017_GPPUB 0x0D
|
||||||
|
#define MCP23017_INTFB 0x0F
|
||||||
|
#define MCP23017_INTCAPB 0x11
|
||||||
|
#define MCP23017_GPIOB 0x13
|
||||||
|
#define MCP23017_OLATB 0x15
|
||||||
|
|
||||||
|
#define MCP23017_INT_ERR 255
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,15 @@
|
||||||
|
This is a library for the MCP23017 I2c Port Expander
|
||||||
|
|
||||||
|
These chips use I2C to communicate, 2 pins required to interface
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, check license.txt for more information
|
||||||
|
All text above must be included in any redistribution
|
||||||
|
|
||||||
|
To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_MCP23017. Check that the Adafruit_MCP23017 folder contains Adafruit_MCP23017.cpp and Adafruit_MCP23017.h
|
||||||
|
|
||||||
|
Place the Adafruit_MCP23017 library folder your <arduinosketchfolder>/libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "Adafruit_MCP23017.h"
|
||||||
|
|
||||||
|
// Basic pin reading and pullup test for the MCP23017 I/O expander
|
||||||
|
// public domain!
|
||||||
|
|
||||||
|
// Connect pin #12 of the expander to Analog 5 (i2c clock)
|
||||||
|
// Connect pin #13 of the expander to Analog 4 (i2c data)
|
||||||
|
// Connect pins #15, 16 and 17 of the expander to ground (address selection)
|
||||||
|
// Connect pin #9 of the expander to 5V (power)
|
||||||
|
// Connect pin #10 of the expander to ground (common ground)
|
||||||
|
// Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)
|
||||||
|
|
||||||
|
// Input #0 is on pin 21 so connect a button or switch from there to ground
|
||||||
|
|
||||||
|
Adafruit_MCP23017 mcp;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
mcp.begin(); // use default address 0
|
||||||
|
|
||||||
|
mcp.pinMode(0, INPUT);
|
||||||
|
mcp.pullUp(0, HIGH); // turn on a 100K pullup internally
|
||||||
|
|
||||||
|
pinMode(13, OUTPUT); // use the p13 LED as debugging
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// The LED will 'echo' the button
|
||||||
|
digitalWrite(13, mcp.digitalRead(0));
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Install the LowPower library for optional sleeping support.
|
||||||
|
// See loop() function comments for details on usage.
|
||||||
|
//#include <LowPower.h>
|
||||||
|
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_MCP23017.h>
|
||||||
|
|
||||||
|
Adafruit_MCP23017 mcp;
|
||||||
|
|
||||||
|
byte ledPin=13;
|
||||||
|
|
||||||
|
// Interrupts from the MCP will be handled by this PIN
|
||||||
|
byte arduinoIntPin=3;
|
||||||
|
|
||||||
|
// ... and this interrupt vector
|
||||||
|
byte arduinoInterrupt=1;
|
||||||
|
|
||||||
|
volatile boolean awakenByInterrupt = false;
|
||||||
|
|
||||||
|
// Two pins at the MCP (Ports A/B where some buttons have been setup.)
|
||||||
|
// Buttons connect the pin to grond, and pins are pulled up.
|
||||||
|
byte mcpPinA=7;
|
||||||
|
byte mcpPinB=15;
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("MCP23007 Interrupt Test");
|
||||||
|
|
||||||
|
pinMode(arduinoIntPin,INPUT);
|
||||||
|
|
||||||
|
mcp.begin(); // use default address 0
|
||||||
|
|
||||||
|
// We mirror INTA and INTB, so that only one line is required between MCP and Arduino for int reporting
|
||||||
|
// The INTA/B will not be Floating
|
||||||
|
// INTs will be signaled with a LOW
|
||||||
|
mcp.setupInterrupts(true,false,LOW);
|
||||||
|
|
||||||
|
// configuration for a button on port A
|
||||||
|
// interrupt will triger when the pin is taken to ground by a pushbutton
|
||||||
|
mcp.pinMode(mcpPinA, INPUT);
|
||||||
|
mcp.pullUp(mcpPinA, HIGH); // turn on a 100K pullup internally
|
||||||
|
mcp.setupInterruptPin(mcpPinA,FALLING);
|
||||||
|
|
||||||
|
// similar, but on port B.
|
||||||
|
mcp.pinMode(mcpPinB, INPUT);
|
||||||
|
mcp.pullUp(mcpPinB, HIGH); // turn on a 100K pullup internall
|
||||||
|
mcp.setupInterruptPin(mcpPinB,FALLING);
|
||||||
|
|
||||||
|
// We will setup a pin for flashing from the int routine
|
||||||
|
pinMode(ledPin, OUTPUT); // use the p13 LED as debugging
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// The int handler will just signal that the int has happen
|
||||||
|
// we will do the work from the main loop.
|
||||||
|
void intCallBack(){
|
||||||
|
awakenByInterrupt=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleInterrupt(){
|
||||||
|
|
||||||
|
// Get more information from the MCP from the INT
|
||||||
|
uint8_t pin=mcp.getLastInterruptPin();
|
||||||
|
uint8_t val=mcp.getLastInterruptPinValue();
|
||||||
|
|
||||||
|
// We will flash the led 1 or 2 times depending on the PIN that triggered the Interrupt
|
||||||
|
// 3 and 4 flases are supposed to be impossible conditions... just for debugging.
|
||||||
|
uint8_t flashes=4;
|
||||||
|
if(pin==mcpPinA) flashes=1;
|
||||||
|
if(pin==mcpPinB) flashes=2;
|
||||||
|
if(val!=LOW) flashes=3;
|
||||||
|
|
||||||
|
// simulate some output associated to this
|
||||||
|
for(int i=0;i<flashes;i++){
|
||||||
|
delay(100);
|
||||||
|
digitalWrite(ledPin,HIGH);
|
||||||
|
delay(100);
|
||||||
|
digitalWrite(ledPin,LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have to wait for the interrupt condition to finish
|
||||||
|
// otherwise we might go to sleep with an ongoing condition and never wake up again.
|
||||||
|
// as, an action is required to clear the INT flag, and allow it to trigger again.
|
||||||
|
// see datasheet for datails.
|
||||||
|
while( ! (mcp.digitalRead(mcpPinB) && mcp.digitalRead(mcpPinA) ));
|
||||||
|
// and clean queued INT signal
|
||||||
|
cleanInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
// handy for interrupts triggered by buttons
|
||||||
|
// normally signal a few due to bouncing issues
|
||||||
|
void cleanInterrupts(){
|
||||||
|
EIFR=0x01;
|
||||||
|
awakenByInterrupt=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* main routine: sleep the arduino, and wake up on Interrups.
|
||||||
|
* the LowPower library, or similar is required for sleeping, but sleep is simulated here.
|
||||||
|
* It is actually posible to get the MCP to draw only 1uA while in standby as the datasheet claims,
|
||||||
|
* however there is no stadndby mode. Its all down to seting up each pin in a way that current does not flow.
|
||||||
|
* and you can wait for interrupts while waiting.
|
||||||
|
*/
|
||||||
|
void loop(){
|
||||||
|
|
||||||
|
// enable interrupts before going to sleep/wait
|
||||||
|
// And we setup a callback for the arduino INT handler.
|
||||||
|
attachInterrupt(arduinoInterrupt,intCallBack,FALLING);
|
||||||
|
|
||||||
|
// Simulate a deep sleep
|
||||||
|
while(!awakenByInterrupt);
|
||||||
|
// Or sleep the arduino, this lib is great, if you have it.
|
||||||
|
//LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
|
||||||
|
|
||||||
|
// disable interrupts while handling them.
|
||||||
|
detachInterrupt(arduinoInterrupt);
|
||||||
|
|
||||||
|
if(awakenByInterrupt) handleInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "Adafruit_MCP23017.h"
|
||||||
|
|
||||||
|
// Basic pin reading and pullup test for the MCP23017 I/O expander
|
||||||
|
// public domain!
|
||||||
|
|
||||||
|
// Connect pin #12 of the expander to Analog 5 (i2c clock)
|
||||||
|
// Connect pin #13 of the expander to Analog 4 (i2c data)
|
||||||
|
// Connect pins #15, 16 and 17 of the expander to ground (address selection)
|
||||||
|
// Connect pin #9 of the expander to 5V (power)
|
||||||
|
// Connect pin #10 of the expander to ground (common ground)
|
||||||
|
// Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)
|
||||||
|
|
||||||
|
// Output #0 is on pin 21 so connect an LED or whatever from that to ground
|
||||||
|
|
||||||
|
Adafruit_MCP23017 mcp;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
mcp.begin(); // use default address 0
|
||||||
|
|
||||||
|
mcp.pinMode(0, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// flip the pin #0 up and down
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
mcp.digitalWrite(0, HIGH);
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
mcp.digitalWrite(0, LOW);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map for MCP23017
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
MCP23017 KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
pullUp KEYWORD2
|
||||||
|
writeGPIOAB KEYWORD2
|
||||||
|
readGPIOAB KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
|
@ -0,0 +1,9 @@
|
||||||
|
name=Adafruit MCP23017 Arduino Library
|
||||||
|
version=1.0.4
|
||||||
|
author=Adafruit
|
||||||
|
maintainer=Adafruit <info@adafruit.com>
|
||||||
|
sentence=Library for the MCP23017 I2C Port Expander
|
||||||
|
paragraph=Library for the MCP23017 I2C Port Expander
|
||||||
|
category=Signal Input/Output
|
||||||
|
url=https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
|
||||||
|
architectures=*
|
|
@ -0,0 +1,26 @@
|
||||||
|
Software License Agreement (BSD License)
|
||||||
|
|
||||||
|
Copyright (c) 2012, Adafruit Industries
|
||||||
|
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.
|
||||||
|
3. Neither the name of the copyright holders nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 COPYRIGHT HOLDER 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 diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,266 @@
|
||||||
|
/***************************************************
|
||||||
|
This is a library for our Adafruit FONA Cellular Module
|
||||||
|
|
||||||
|
Designed specifically to work with the Adafruit FONA
|
||||||
|
----> http://www.adafruit.com/products/1946
|
||||||
|
----> http://www.adafruit.com/products/1963
|
||||||
|
|
||||||
|
These displays use TTL Serial to communicate, 2 pins are required to
|
||||||
|
interface
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#ifndef ADAFRUIT_FONA_H
|
||||||
|
#define ADAFRUIT_FONA_H
|
||||||
|
|
||||||
|
#include "includes/FONAConfig.h"
|
||||||
|
#include "includes/FONAExtIncludes.h"
|
||||||
|
#include "includes/platform/FONAPlatform.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define FONA800L 1
|
||||||
|
#define FONA800H 6
|
||||||
|
|
||||||
|
#define FONA808_V1 2
|
||||||
|
#define FONA808_V2 3
|
||||||
|
|
||||||
|
#define FONA3G_A 4
|
||||||
|
#define FONA3G_E 5
|
||||||
|
|
||||||
|
// Set the preferred SMS storage.
|
||||||
|
// Use "SM" for storage on the SIM.
|
||||||
|
// Use "ME" for internal storage on the FONA chip
|
||||||
|
#define FONA_PREF_SMS_STORAGE "\"SM\""
|
||||||
|
//#define FONA_PREF_SMS_STORAGE "\"ME\""
|
||||||
|
|
||||||
|
#define FONA_HEADSETAUDIO 0
|
||||||
|
#define FONA_EXTAUDIO 1
|
||||||
|
|
||||||
|
#define FONA_STTONE_DIALTONE 1
|
||||||
|
#define FONA_STTONE_BUSY 2
|
||||||
|
#define FONA_STTONE_CONGESTION 3
|
||||||
|
#define FONA_STTONE_PATHACK 4
|
||||||
|
#define FONA_STTONE_DROPPED 5
|
||||||
|
#define FONA_STTONE_ERROR 6
|
||||||
|
#define FONA_STTONE_CALLWAIT 7
|
||||||
|
#define FONA_STTONE_RINGING 8
|
||||||
|
#define FONA_STTONE_BEEP 16
|
||||||
|
#define FONA_STTONE_POSTONE 17
|
||||||
|
#define FONA_STTONE_ERRTONE 18
|
||||||
|
#define FONA_STTONE_INDIANDIALTONE 19
|
||||||
|
#define FONA_STTONE_USADIALTONE 20
|
||||||
|
|
||||||
|
#define FONA_DEFAULT_TIMEOUT_MS 500
|
||||||
|
|
||||||
|
#define FONA_HTTP_GET 0
|
||||||
|
#define FONA_HTTP_POST 1
|
||||||
|
#define FONA_HTTP_HEAD 2
|
||||||
|
|
||||||
|
#define FONA_CALL_READY 0
|
||||||
|
#define FONA_CALL_FAILED 1
|
||||||
|
#define FONA_CALL_UNKNOWN 2
|
||||||
|
#define FONA_CALL_RINGING 3
|
||||||
|
#define FONA_CALL_INPROGRESS 4
|
||||||
|
|
||||||
|
class Adafruit_FONA : public FONAStreamType {
|
||||||
|
public:
|
||||||
|
Adafruit_FONA(int8_t r);
|
||||||
|
boolean begin(FONAStreamType &port);
|
||||||
|
uint8_t type();
|
||||||
|
|
||||||
|
// Stream
|
||||||
|
int available(void);
|
||||||
|
size_t write(uint8_t x);
|
||||||
|
int read(void);
|
||||||
|
int peek(void);
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
// FONA 3G requirements
|
||||||
|
boolean setBaudrate(uint16_t baud);
|
||||||
|
|
||||||
|
// RTC
|
||||||
|
boolean enableRTC(uint8_t i);
|
||||||
|
boolean readRTC(uint8_t *year, uint8_t *month, uint8_t *date, uint8_t *hr, uint8_t *min, uint8_t *sec);
|
||||||
|
|
||||||
|
// Battery and ADC
|
||||||
|
boolean getADCVoltage(uint16_t *v);
|
||||||
|
boolean getBattPercent(uint16_t *p);
|
||||||
|
boolean getBattVoltage(uint16_t *v);
|
||||||
|
|
||||||
|
// SIM query
|
||||||
|
uint8_t unlockSIM(char *pin);
|
||||||
|
uint8_t getSIMCCID(char *ccid);
|
||||||
|
uint8_t getNetworkStatus(void);
|
||||||
|
uint8_t getRSSI(void);
|
||||||
|
|
||||||
|
// IMEI
|
||||||
|
uint8_t getIMEI(char *imei);
|
||||||
|
|
||||||
|
// set Audio output
|
||||||
|
boolean setAudio(uint8_t a);
|
||||||
|
boolean setVolume(uint8_t i);
|
||||||
|
uint8_t getVolume(void);
|
||||||
|
boolean playToolkitTone(uint8_t t, uint16_t len);
|
||||||
|
boolean setMicVolume(uint8_t a, uint8_t level);
|
||||||
|
boolean playDTMF(char tone);
|
||||||
|
|
||||||
|
// FM radio functions.
|
||||||
|
boolean tuneFMradio(uint16_t station);
|
||||||
|
boolean FMradio(boolean onoff, uint8_t a = FONA_HEADSETAUDIO);
|
||||||
|
boolean setFMVolume(uint8_t i);
|
||||||
|
int8_t getFMVolume();
|
||||||
|
int8_t getFMSignalLevel(uint16_t station);
|
||||||
|
|
||||||
|
// SMS handling
|
||||||
|
boolean setSMSInterrupt(uint8_t i);
|
||||||
|
uint8_t getSMSInterrupt(void);
|
||||||
|
int8_t getNumSMS(void);
|
||||||
|
boolean readSMS(uint8_t i, char *smsbuff, uint16_t max, uint16_t *readsize);
|
||||||
|
boolean sendSMS(char *smsaddr, char *smsmsg);
|
||||||
|
boolean deleteSMS(uint8_t i);
|
||||||
|
boolean getSMSSender(uint8_t i, char *sender, int senderlen);
|
||||||
|
boolean sendUSSD(char *ussdmsg, char *ussdbuff, uint16_t maxlen, uint16_t *readlen);
|
||||||
|
|
||||||
|
// Time
|
||||||
|
boolean enableNetworkTimeSync(boolean onoff);
|
||||||
|
boolean enableNTPTimeSync(boolean onoff, FONAFlashStringPtr ntpserver=0);
|
||||||
|
boolean getTime(char *buff, uint16_t maxlen);
|
||||||
|
|
||||||
|
// GPRS handling
|
||||||
|
boolean enableGPRS(boolean onoff);
|
||||||
|
uint8_t GPRSstate(void);
|
||||||
|
boolean getGSMLoc(uint16_t *replycode, char *buff, uint16_t maxlen);
|
||||||
|
boolean getGSMLoc(float *lat, float *lon);
|
||||||
|
void setGPRSNetworkSettings(FONAFlashStringPtr apn, FONAFlashStringPtr username=0, FONAFlashStringPtr password=0);
|
||||||
|
|
||||||
|
// GPS handling
|
||||||
|
boolean enableGPS(boolean onoff);
|
||||||
|
int8_t GPSstatus(void);
|
||||||
|
uint8_t getGPS(uint8_t arg, char *buffer, uint8_t maxbuff);
|
||||||
|
boolean getGPS(float *lat, float *lon, float *speed_kph=0, float *heading=0, float *altitude=0);
|
||||||
|
boolean enableGPSNMEA(uint8_t nmea);
|
||||||
|
|
||||||
|
// TCP raw connections
|
||||||
|
boolean TCPconnect(char *server, uint16_t port);
|
||||||
|
boolean TCPclose(void);
|
||||||
|
boolean TCPconnected(void);
|
||||||
|
boolean TCPsend(char *packet, uint8_t len);
|
||||||
|
uint16_t TCPavailable(void);
|
||||||
|
uint16_t TCPread(uint8_t *buff, uint8_t len);
|
||||||
|
|
||||||
|
// HTTP low level interface (maps directly to SIM800 commands).
|
||||||
|
boolean HTTP_init();
|
||||||
|
boolean HTTP_term();
|
||||||
|
void HTTP_para_start(FONAFlashStringPtr parameter, boolean quoted = true);
|
||||||
|
boolean HTTP_para_end(boolean quoted = true);
|
||||||
|
boolean HTTP_para(FONAFlashStringPtr parameter, const char *value);
|
||||||
|
boolean HTTP_para(FONAFlashStringPtr parameter, FONAFlashStringPtr value);
|
||||||
|
boolean HTTP_para(FONAFlashStringPtr parameter, int32_t value);
|
||||||
|
boolean HTTP_data(uint32_t size, uint32_t maxTime=10000);
|
||||||
|
boolean HTTP_action(uint8_t method, uint16_t *status, uint16_t *datalen, int32_t timeout = 10000);
|
||||||
|
boolean HTTP_readall(uint16_t *datalen);
|
||||||
|
boolean HTTP_ssl(boolean onoff);
|
||||||
|
|
||||||
|
// HTTP high level interface (easier to use, less flexible).
|
||||||
|
boolean HTTP_GET_start(char *url, uint16_t *status, uint16_t *datalen);
|
||||||
|
void HTTP_GET_end(void);
|
||||||
|
boolean HTTP_POST_start(char *url, FONAFlashStringPtr contenttype, const uint8_t *postdata, uint16_t postdatalen, uint16_t *status, uint16_t *datalen);
|
||||||
|
void HTTP_POST_end(void);
|
||||||
|
void setUserAgent(FONAFlashStringPtr useragent);
|
||||||
|
|
||||||
|
// HTTPS
|
||||||
|
void setHTTPSRedirect(boolean onoff);
|
||||||
|
|
||||||
|
// PWM (buzzer)
|
||||||
|
boolean setPWM(uint16_t period, uint8_t duty = 50);
|
||||||
|
|
||||||
|
// Phone calls
|
||||||
|
boolean callPhone(char *phonenum);
|
||||||
|
uint8_t getCallStatus(void);
|
||||||
|
boolean hangUp(void);
|
||||||
|
boolean pickUp(void);
|
||||||
|
boolean callerIdNotification(boolean enable, uint8_t interrupt = 0);
|
||||||
|
boolean incomingCallNumber(char* phonenum);
|
||||||
|
|
||||||
|
// Helper functions to verify responses.
|
||||||
|
boolean expectReply(FONAFlashStringPtr reply, uint16_t timeout = 10000);
|
||||||
|
boolean sendCheckReply(char *send, char *reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
boolean sendCheckReply(FONAFlashStringPtr send, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
boolean sendCheckReply(char* send, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int8_t _rstpin;
|
||||||
|
uint8_t _type;
|
||||||
|
|
||||||
|
char replybuffer[255];
|
||||||
|
FONAFlashStringPtr apn;
|
||||||
|
FONAFlashStringPtr apnusername;
|
||||||
|
FONAFlashStringPtr apnpassword;
|
||||||
|
boolean httpsredirect;
|
||||||
|
FONAFlashStringPtr useragent;
|
||||||
|
FONAFlashStringPtr ok_reply;
|
||||||
|
|
||||||
|
// HTTP helpers
|
||||||
|
boolean HTTP_setup(char *url);
|
||||||
|
|
||||||
|
void flushInput();
|
||||||
|
uint16_t readRaw(uint16_t b);
|
||||||
|
uint8_t readline(uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS, boolean multiline = false);
|
||||||
|
uint8_t getReply(char *send, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
uint8_t getReply(FONAFlashStringPtr send, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
uint8_t getReply(FONAFlashStringPtr prefix, char *suffix, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
uint8_t getReply(FONAFlashStringPtr prefix, int32_t suffix, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
uint8_t getReply(FONAFlashStringPtr prefix, int32_t suffix1, int32_t suffix2, uint16_t timeout); // Don't set default value or else function call is ambiguous.
|
||||||
|
uint8_t getReplyQuoted(FONAFlashStringPtr prefix, FONAFlashStringPtr suffix, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
|
||||||
|
boolean sendCheckReply(FONAFlashStringPtr prefix, char *suffix, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
boolean sendCheckReply(FONAFlashStringPtr prefix, int32_t suffix, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
boolean sendCheckReply(FONAFlashStringPtr prefix, int32_t suffix, int32_t suffix2, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
boolean sendCheckReplyQuoted(FONAFlashStringPtr prefix, FONAFlashStringPtr suffix, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS);
|
||||||
|
|
||||||
|
|
||||||
|
boolean parseReply(FONAFlashStringPtr toreply,
|
||||||
|
uint16_t *v, char divider = ',', uint8_t index=0);
|
||||||
|
boolean parseReply(FONAFlashStringPtr toreply,
|
||||||
|
char *v, char divider = ',', uint8_t index=0);
|
||||||
|
boolean parseReplyQuoted(FONAFlashStringPtr toreply,
|
||||||
|
char *v, int maxlen, char divider, uint8_t index);
|
||||||
|
|
||||||
|
boolean sendParseReply(FONAFlashStringPtr tosend,
|
||||||
|
FONAFlashStringPtr toreply,
|
||||||
|
uint16_t *v, char divider = ',', uint8_t index=0);
|
||||||
|
|
||||||
|
static boolean _incomingCall;
|
||||||
|
static void onIncomingCall();
|
||||||
|
|
||||||
|
FONAStreamType *mySerial;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Adafruit_FONA_3G : public Adafruit_FONA {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Adafruit_FONA_3G (int8_t r) : Adafruit_FONA(r) { _type = FONA3G_A; }
|
||||||
|
|
||||||
|
boolean getBattVoltage(uint16_t *v);
|
||||||
|
boolean playToolkitTone(uint8_t t, uint16_t len);
|
||||||
|
boolean hangUp(void);
|
||||||
|
boolean pickUp(void);
|
||||||
|
boolean enableGPRS(boolean onoff);
|
||||||
|
boolean enableGPS(boolean onoff);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
boolean parseReply(FONAFlashStringPtr toreply,
|
||||||
|
float *f, char divider, uint8_t index);
|
||||||
|
|
||||||
|
boolean sendParseReply(FONAFlashStringPtr tosend,
|
||||||
|
FONAFlashStringPtr toreply,
|
||||||
|
float *f, char divider = ',', uint8_t index=0);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Adafruit FONA Library [![Build Status](https://secure.travis-ci.org/adafruit/Adafruit_FONA_Library.svg?branch=master)](https://travis-ci.org/adafruit/Adafruit_FONA_Library)
|
||||||
|
|
||||||
|
**This library requires Arduino v1.0.6 or higher**
|
||||||
|
|
||||||
|
This is a library for the Adafruit FONA Cellular GSM Breakouts etc
|
||||||
|
|
||||||
|
Designed specifically to work with the Adafruit FONA Breakout
|
||||||
|
* https://www.adafruit.com/products/1946
|
||||||
|
* https://www.adafruit.com/products/1963
|
||||||
|
* http://www.adafruit.com/products/2468
|
||||||
|
* http://www.adafruit.com/products/2542
|
||||||
|
|
||||||
|
These modules use TTL Serial to communicate, 2 pins are required to interface
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Check out the links above for our tutorials and wiring diagrams
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
With updates from Samy Kamkar
|
||||||
|
|
||||||
|
To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_FONA
|
||||||
|
Check that the Adafruit_FONA folder contains Adafruit_FONA.cpp and Adafruit_FONA.h
|
||||||
|
|
||||||
|
Place the Adafruit_FONA library folder your *arduinosketchfolder*/libraries/ folder.
|
||||||
|
You may need to create the libraries subfolder if its your first library. Restart the IDE.
|
|
@ -0,0 +1,77 @@
|
||||||
|
/***************************************************
|
||||||
|
This is an example for our Adafruit FONA Cellular Module
|
||||||
|
since the FONA 3G does not do auto-baud very well, this demo
|
||||||
|
fixes the baud rate to 4800 from the default 115200
|
||||||
|
|
||||||
|
Designed specifically to work with the Adafruit FONA 3G
|
||||||
|
----> http://www.adafruit.com/products/2691
|
||||||
|
----> http://www.adafruit.com/products/2687
|
||||||
|
|
||||||
|
These cellular modules use TTL Serial to communicate, 2 pins are
|
||||||
|
required to interface
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
#include "Adafruit_FONA.h"
|
||||||
|
|
||||||
|
#define FONA_RX 2
|
||||||
|
#define FONA_TX 3
|
||||||
|
#define FONA_RST 4
|
||||||
|
|
||||||
|
// this is a large buffer for replies
|
||||||
|
char replybuffer[255];
|
||||||
|
|
||||||
|
// We default to using software serial. If you want to use hardware serial
|
||||||
|
// (because softserial isnt supported) comment out the following three lines
|
||||||
|
// and uncomment the HardwareSerial line
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
|
||||||
|
SoftwareSerial *fonaSerial = &fonaSS;
|
||||||
|
|
||||||
|
// Hardware serial is also possible!
|
||||||
|
// HardwareSerial *fonaSerial = &Serial1;
|
||||||
|
|
||||||
|
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
|
||||||
|
|
||||||
|
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("FONA set baudrate"));
|
||||||
|
|
||||||
|
Serial.println(F("First trying 115200 baud"));
|
||||||
|
// start at 115200 baud
|
||||||
|
fonaSerial->begin(115200);
|
||||||
|
fona.begin(*fonaSerial);
|
||||||
|
|
||||||
|
// send the command to reset the baud rate to 4800
|
||||||
|
fona.setBaudrate(4800);
|
||||||
|
|
||||||
|
// restart with 4800 baud
|
||||||
|
fonaSerial->begin(4800);
|
||||||
|
Serial.println(F("Initializing @ 4800 baud..."));
|
||||||
|
|
||||||
|
if (! fona.begin(*fonaSerial)) {
|
||||||
|
Serial.println(F("Couldn't find FONA"));
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
Serial.println(F("FONA is OK"));
|
||||||
|
|
||||||
|
// Print module IMEI number.
|
||||||
|
char imei[15] = {0}; // MUST use a 16 character buffer for IMEI!
|
||||||
|
uint8_t imeiLen = fona.getIMEI(imei);
|
||||||
|
if (imeiLen > 0) {
|
||||||
|
Serial.print("Module IMEI: "); Serial.println(imei);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
/***************************************************
|
||||||
|
This is an example for our Adafruit FONA Cellular Module
|
||||||
|
|
||||||
|
Designed specifically to work with the Adafruit FONA
|
||||||
|
----> http://www.adafruit.com/products/1946
|
||||||
|
----> http://www.adafruit.com/products/1963
|
||||||
|
----> http://www.adafruit.com/products/2468
|
||||||
|
----> http://www.adafruit.com/products/2542
|
||||||
|
|
||||||
|
These cellular modules use TTL Serial to communicate, 2 pins are
|
||||||
|
required to interface
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
THIS CODE IS STILL IN PROGRESS!
|
||||||
|
|
||||||
|
Open up the serial console on the Arduino at 115200 baud to interact with FONA
|
||||||
|
|
||||||
|
|
||||||
|
This code will receive an SMS, identify the sender's phone number, and automatically send a response
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Adafruit_FONA.h"
|
||||||
|
|
||||||
|
#define FONA_RX 2
|
||||||
|
#define FONA_TX 3
|
||||||
|
#define FONA_RST 4
|
||||||
|
|
||||||
|
// this is a large buffer for replies
|
||||||
|
char replybuffer[255];
|
||||||
|
|
||||||
|
// We default to using software serial. If you want to use hardware serial
|
||||||
|
// (because softserial isnt supported) comment out the following three lines
|
||||||
|
// and uncomment the HardwareSerial line
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
|
||||||
|
SoftwareSerial *fonaSerial = &fonaSS;
|
||||||
|
|
||||||
|
// Hardware serial is also possible!
|
||||||
|
// HardwareSerial *fonaSerial = &Serial1;
|
||||||
|
|
||||||
|
// Use this for FONA 800 and 808s
|
||||||
|
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
|
||||||
|
// Use this one for FONA 3G
|
||||||
|
//Adafruit_FONA_3G fona = Adafruit_FONA_3G(FONA_RST);
|
||||||
|
|
||||||
|
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("FONA SMS caller ID test"));
|
||||||
|
Serial.println(F("Initializing....(May take 3 seconds)"));
|
||||||
|
|
||||||
|
// make it slow so its easy to read!
|
||||||
|
fonaSerial->begin(4800);
|
||||||
|
if (! fona.begin(*fonaSerial)) {
|
||||||
|
Serial.println(F("Couldn't find FONA"));
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
Serial.println(F("FONA is OK"));
|
||||||
|
|
||||||
|
// Print SIM card IMEI number.
|
||||||
|
char imei[16] = {0}; // MUST use a 16 character buffer for IMEI!
|
||||||
|
uint8_t imeiLen = fona.getIMEI(imei);
|
||||||
|
if (imeiLen > 0) {
|
||||||
|
Serial.print("SIM card IMEI: "); Serial.println(imei);
|
||||||
|
}
|
||||||
|
|
||||||
|
fonaSerial->print("AT+CNMI=2,1\r\n"); //set up the FONA to send a +CMTI notification when an SMS is received
|
||||||
|
|
||||||
|
Serial.println("FONA Ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char fonaNotificationBuffer[64]; //for notifications from the FONA
|
||||||
|
char smsBuffer[250];
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
char* bufPtr = fonaNotificationBuffer; //handy buffer pointer
|
||||||
|
|
||||||
|
if (fona.available()) //any data available from the FONA?
|
||||||
|
{
|
||||||
|
int slot = 0; //this will be the slot number of the SMS
|
||||||
|
int charCount = 0;
|
||||||
|
//Read the notification into fonaInBuffer
|
||||||
|
do {
|
||||||
|
*bufPtr = fona.read();
|
||||||
|
Serial.write(*bufPtr);
|
||||||
|
delay(1);
|
||||||
|
} while ((*bufPtr++ != '\n') && (fona.available()) && (++charCount < (sizeof(fonaNotificationBuffer)-1)));
|
||||||
|
|
||||||
|
//Add a terminal NULL to the notification string
|
||||||
|
*bufPtr = 0;
|
||||||
|
|
||||||
|
//Scan the notification string for an SMS received notification.
|
||||||
|
// If it's an SMS message, we'll get the slot number in 'slot'
|
||||||
|
if (1 == sscanf(fonaNotificationBuffer, "+CMTI: " FONA_PREF_SMS_STORAGE ",%d", &slot)) {
|
||||||
|
Serial.print("slot: "); Serial.println(slot);
|
||||||
|
|
||||||
|
char callerIDbuffer[32]; //we'll store the SMS sender number in here
|
||||||
|
|
||||||
|
// Retrieve SMS sender address/phone number.
|
||||||
|
if (! fona.getSMSSender(slot, callerIDbuffer, 31)) {
|
||||||
|
Serial.println("Didn't find SMS message in slot!");
|
||||||
|
}
|
||||||
|
Serial.print(F("FROM: ")); Serial.println(callerIDbuffer);
|
||||||
|
|
||||||
|
// Retrieve SMS value.
|
||||||
|
uint16_t smslen;
|
||||||
|
if (fona.readSMS(slot, smsBuffer, 250, &smslen)) { // pass in buffer and max len!
|
||||||
|
Serial.println(smsBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Send back an automatic response
|
||||||
|
Serial.println("Sending reponse...");
|
||||||
|
if (!fona.sendSMS(callerIDbuffer, "Hey, I got your text!")) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Sent!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the original msg after it is processed
|
||||||
|
// otherwise, we will fill up all the slots
|
||||||
|
// and then we won't be able to receive SMS anymore
|
||||||
|
if (fona.deleteSMS(slot)) {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("Couldn't delete SMS in slot ")); Serial.println(slot);
|
||||||
|
fona.print(F("AT+CMGD=?\r\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,886 @@
|
||||||
|
/***************************************************
|
||||||
|
This is an example for our Adafruit FONA Cellular Module
|
||||||
|
|
||||||
|
Designed specifically to work with the Adafruit FONA
|
||||||
|
----> http://www.adafruit.com/products/1946
|
||||||
|
----> http://www.adafruit.com/products/1963
|
||||||
|
----> http://www.adafruit.com/products/2468
|
||||||
|
----> http://www.adafruit.com/products/2542
|
||||||
|
|
||||||
|
These cellular modules use TTL Serial to communicate, 2 pins are
|
||||||
|
required to interface
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
THIS CODE IS STILL IN PROGRESS!
|
||||||
|
|
||||||
|
Open up the serial console on the Arduino at 115200 baud to interact with FONA
|
||||||
|
|
||||||
|
Note that if you need to set a GPRS APN, username, and password scroll down to
|
||||||
|
the commented section below at the end of the setup() function.
|
||||||
|
*/
|
||||||
|
#include "Adafruit_FONA.h"
|
||||||
|
|
||||||
|
#define FONA_RX 2
|
||||||
|
#define FONA_TX 3
|
||||||
|
#define FONA_RST 4
|
||||||
|
|
||||||
|
// this is a large buffer for replies
|
||||||
|
char replybuffer[255];
|
||||||
|
|
||||||
|
// We default to using software serial. If you want to use hardware serial
|
||||||
|
// (because softserial isnt supported) comment out the following three lines
|
||||||
|
// and uncomment the HardwareSerial line
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
|
||||||
|
SoftwareSerial *fonaSerial = &fonaSS;
|
||||||
|
|
||||||
|
// Hardware serial is also possible!
|
||||||
|
// HardwareSerial *fonaSerial = &Serial1;
|
||||||
|
|
||||||
|
// Use this for FONA 800 and 808s
|
||||||
|
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
|
||||||
|
// Use this one for FONA 3G
|
||||||
|
//Adafruit_FONA_3G fona = Adafruit_FONA_3G(FONA_RST);
|
||||||
|
|
||||||
|
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
|
||||||
|
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("FONA basic test"));
|
||||||
|
Serial.println(F("Initializing....(May take 3 seconds)"));
|
||||||
|
|
||||||
|
fonaSerial->begin(4800);
|
||||||
|
if (! fona.begin(*fonaSerial)) {
|
||||||
|
Serial.println(F("Couldn't find FONA"));
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
type = fona.type();
|
||||||
|
Serial.println(F("FONA is OK"));
|
||||||
|
Serial.print(F("Found "));
|
||||||
|
switch (type) {
|
||||||
|
case FONA800L:
|
||||||
|
Serial.println(F("FONA 800L")); break;
|
||||||
|
case FONA800H:
|
||||||
|
Serial.println(F("FONA 800H")); break;
|
||||||
|
case FONA808_V1:
|
||||||
|
Serial.println(F("FONA 808 (v1)")); break;
|
||||||
|
case FONA808_V2:
|
||||||
|
Serial.println(F("FONA 808 (v2)")); break;
|
||||||
|
case FONA3G_A:
|
||||||
|
Serial.println(F("FONA 3G (American)")); break;
|
||||||
|
case FONA3G_E:
|
||||||
|
Serial.println(F("FONA 3G (European)")); break;
|
||||||
|
default:
|
||||||
|
Serial.println(F("???")); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print module IMEI number.
|
||||||
|
char imei[16] = {0}; // MUST use a 16 character buffer for IMEI!
|
||||||
|
uint8_t imeiLen = fona.getIMEI(imei);
|
||||||
|
if (imeiLen > 0) {
|
||||||
|
Serial.print("Module IMEI: "); Serial.println(imei);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//fona.setGPRSNetworkSettings(F("your APN"), F("your username"), F("your password"));
|
||||||
|
|
||||||
|
// Optionally configure HTTP gets to follow redirects over SSL.
|
||||||
|
// Default is not to follow SSL redirects, however if you uncomment
|
||||||
|
// the following line then redirects over SSL will be followed.
|
||||||
|
//fona.setHTTPSRedirect(true);
|
||||||
|
|
||||||
|
printMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printMenu(void) {
|
||||||
|
Serial.println(F("-------------------------------------"));
|
||||||
|
Serial.println(F("[?] Print this menu"));
|
||||||
|
Serial.println(F("[a] read the ADC 2.8V max (FONA800 & 808)"));
|
||||||
|
Serial.println(F("[b] read the Battery V and % charged"));
|
||||||
|
Serial.println(F("[C] read the SIM CCID"));
|
||||||
|
Serial.println(F("[U] Unlock SIM with PIN code"));
|
||||||
|
Serial.println(F("[i] read RSSI"));
|
||||||
|
Serial.println(F("[n] get Network status"));
|
||||||
|
Serial.println(F("[v] set audio Volume"));
|
||||||
|
Serial.println(F("[V] get Volume"));
|
||||||
|
Serial.println(F("[H] set Headphone audio (FONA800 & 808)"));
|
||||||
|
Serial.println(F("[e] set External audio (FONA800 & 808)"));
|
||||||
|
Serial.println(F("[T] play audio Tone"));
|
||||||
|
Serial.println(F("[P] PWM/Buzzer out (FONA800 & 808)"));
|
||||||
|
|
||||||
|
// FM (SIM800 only!)
|
||||||
|
Serial.println(F("[f] tune FM radio (FONA800)"));
|
||||||
|
Serial.println(F("[F] turn off FM (FONA800)"));
|
||||||
|
Serial.println(F("[m] set FM volume (FONA800)"));
|
||||||
|
Serial.println(F("[M] get FM volume (FONA800)"));
|
||||||
|
Serial.println(F("[q] get FM station signal level (FONA800)"));
|
||||||
|
|
||||||
|
// Phone
|
||||||
|
Serial.println(F("[c] make phone Call"));
|
||||||
|
Serial.println(F("[A] get call status"));
|
||||||
|
Serial.println(F("[h] Hang up phone"));
|
||||||
|
Serial.println(F("[p] Pick up phone"));
|
||||||
|
|
||||||
|
// SMS
|
||||||
|
Serial.println(F("[N] Number of SMSs"));
|
||||||
|
Serial.println(F("[r] Read SMS #"));
|
||||||
|
Serial.println(F("[R] Read All SMS"));
|
||||||
|
Serial.println(F("[d] Delete SMS #"));
|
||||||
|
Serial.println(F("[s] Send SMS"));
|
||||||
|
Serial.println(F("[u] Send USSD"));
|
||||||
|
|
||||||
|
// Time
|
||||||
|
Serial.println(F("[y] Enable network time sync (FONA 800 & 808)"));
|
||||||
|
Serial.println(F("[Y] Enable NTP time sync (GPRS FONA 800 & 808)"));
|
||||||
|
Serial.println(F("[t] Get network time"));
|
||||||
|
|
||||||
|
// GPRS
|
||||||
|
Serial.println(F("[G] Enable GPRS"));
|
||||||
|
Serial.println(F("[g] Disable GPRS"));
|
||||||
|
Serial.println(F("[l] Query GSMLOC (GPRS)"));
|
||||||
|
Serial.println(F("[w] Read webpage (GPRS)"));
|
||||||
|
Serial.println(F("[W] Post to website (GPRS)"));
|
||||||
|
|
||||||
|
// GPS
|
||||||
|
if ((type == FONA3G_A) || (type == FONA3G_E) || (type == FONA808_V1) || (type == FONA808_V2)) {
|
||||||
|
Serial.println(F("[O] Turn GPS on (FONA 808 & 3G)"));
|
||||||
|
Serial.println(F("[o] Turn GPS off (FONA 808 & 3G)"));
|
||||||
|
Serial.println(F("[L] Query GPS location (FONA 808 & 3G)"));
|
||||||
|
if (type == FONA808_V1) {
|
||||||
|
Serial.println(F("[x] GPS fix status (FONA808 v1 only)"));
|
||||||
|
}
|
||||||
|
Serial.println(F("[E] Raw NMEA out (FONA808)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(F("[S] create Serial passthru tunnel"));
|
||||||
|
Serial.println(F("-------------------------------------"));
|
||||||
|
Serial.println(F(""));
|
||||||
|
|
||||||
|
}
|
||||||
|
void loop() {
|
||||||
|
Serial.print(F("FONA> "));
|
||||||
|
while (! Serial.available() ) {
|
||||||
|
if (fona.available()) {
|
||||||
|
Serial.write(fona.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char command = Serial.read();
|
||||||
|
Serial.println(command);
|
||||||
|
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case '?': {
|
||||||
|
printMenu();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'a': {
|
||||||
|
// read the ADC
|
||||||
|
uint16_t adc;
|
||||||
|
if (! fona.getADCVoltage(&adc)) {
|
||||||
|
Serial.println(F("Failed to read ADC"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("ADC = ")); Serial.print(adc); Serial.println(F(" mV"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'b': {
|
||||||
|
// read the battery voltage and percentage
|
||||||
|
uint16_t vbat;
|
||||||
|
if (! fona.getBattVoltage(&vbat)) {
|
||||||
|
Serial.println(F("Failed to read Batt"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("VBat = ")); Serial.print(vbat); Serial.println(F(" mV"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (! fona.getBattPercent(&vbat)) {
|
||||||
|
Serial.println(F("Failed to read Batt"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("VPct = ")); Serial.print(vbat); Serial.println(F("%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'U': {
|
||||||
|
// Unlock the SIM with a PIN code
|
||||||
|
char PIN[5];
|
||||||
|
flushSerial();
|
||||||
|
Serial.println(F("Enter 4-digit PIN"));
|
||||||
|
readline(PIN, 3);
|
||||||
|
Serial.println(PIN);
|
||||||
|
Serial.print(F("Unlocking SIM card: "));
|
||||||
|
if (! fona.unlockSIM(PIN)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'C': {
|
||||||
|
// read the CCID
|
||||||
|
fona.getSIMCCID(replybuffer); // make sure replybuffer is at least 21 bytes!
|
||||||
|
Serial.print(F("SIM CCID = ")); Serial.println(replybuffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'i': {
|
||||||
|
// read the RSSI
|
||||||
|
uint8_t n = fona.getRSSI();
|
||||||
|
int8_t r;
|
||||||
|
|
||||||
|
Serial.print(F("RSSI = ")); Serial.print(n); Serial.print(": ");
|
||||||
|
if (n == 0) r = -115;
|
||||||
|
if (n == 1) r = -111;
|
||||||
|
if (n == 31) r = -52;
|
||||||
|
if ((n >= 2) && (n <= 30)) {
|
||||||
|
r = map(n, 2, 30, -110, -54);
|
||||||
|
}
|
||||||
|
Serial.print(r); Serial.println(F(" dBm"));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'n': {
|
||||||
|
// read the network/cellular status
|
||||||
|
uint8_t n = fona.getNetworkStatus();
|
||||||
|
Serial.print(F("Network status "));
|
||||||
|
Serial.print(n);
|
||||||
|
Serial.print(F(": "));
|
||||||
|
if (n == 0) Serial.println(F("Not registered"));
|
||||||
|
if (n == 1) Serial.println(F("Registered (home)"));
|
||||||
|
if (n == 2) Serial.println(F("Not registered (searching)"));
|
||||||
|
if (n == 3) Serial.println(F("Denied"));
|
||||||
|
if (n == 4) Serial.println(F("Unknown"));
|
||||||
|
if (n == 5) Serial.println(F("Registered roaming"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Audio ***/
|
||||||
|
case 'v': {
|
||||||
|
// set volume
|
||||||
|
flushSerial();
|
||||||
|
if ( (type == FONA3G_A) || (type == FONA3G_E) ) {
|
||||||
|
Serial.print(F("Set Vol [0-8] "));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("Set Vol % [0-100] "));
|
||||||
|
}
|
||||||
|
uint8_t vol = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
if (! fona.setVolume(vol)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'V': {
|
||||||
|
uint8_t v = fona.getVolume();
|
||||||
|
Serial.print(v);
|
||||||
|
if ( (type == FONA3G_A) || (type == FONA3G_E) ) {
|
||||||
|
Serial.println(" / 8");
|
||||||
|
} else {
|
||||||
|
Serial.println("%");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'H': {
|
||||||
|
// Set Headphone output
|
||||||
|
if (! fona.setAudio(FONA_HEADSETAUDIO)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
fona.setMicVolume(FONA_HEADSETAUDIO, 15);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'e': {
|
||||||
|
// Set External output
|
||||||
|
if (! fona.setAudio(FONA_EXTAUDIO)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fona.setMicVolume(FONA_EXTAUDIO, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'T': {
|
||||||
|
// play tone
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Play tone #"));
|
||||||
|
uint8_t kittone = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
// play for 1 second (1000 ms)
|
||||||
|
if (! fona.playToolkitTone(kittone, 1000)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** FM Radio ***/
|
||||||
|
|
||||||
|
case 'f': {
|
||||||
|
// get freq
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("FM Freq (eg 1011 == 101.1 MHz): "));
|
||||||
|
uint16_t station = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
// FM radio ON using headset
|
||||||
|
if (fona.FMradio(true, FONA_HEADSETAUDIO)) {
|
||||||
|
Serial.println(F("Opened"));
|
||||||
|
}
|
||||||
|
if (! fona.tuneFMradio(station)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Tuned"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'F': {
|
||||||
|
// FM radio off
|
||||||
|
if (! fona.FMradio(false)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
// Set FM volume.
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Set FM Vol [0-6]:"));
|
||||||
|
uint8_t vol = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
if (!fona.setFMVolume(vol)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'M': {
|
||||||
|
// Get FM volume.
|
||||||
|
uint8_t fmvol = fona.getFMVolume();
|
||||||
|
if (fmvol < 0) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("FM volume: "));
|
||||||
|
Serial.println(fmvol, DEC);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'q': {
|
||||||
|
// Get FM station signal level (in decibels).
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("FM Freq (eg 1011 == 101.1 MHz): "));
|
||||||
|
uint16_t station = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
int8_t level = fona.getFMSignalLevel(station);
|
||||||
|
if (level < 0) {
|
||||||
|
Serial.println(F("Failed! Make sure FM radio is on (tuned to station)."));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("Signal level (dB): "));
|
||||||
|
Serial.println(level, DEC);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** PWM ***/
|
||||||
|
|
||||||
|
case 'P': {
|
||||||
|
// PWM Buzzer output @ 2KHz max
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("PWM Freq, 0 = Off, (1-2000): "));
|
||||||
|
uint16_t freq = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
if (! fona.setPWM(freq)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Call ***/
|
||||||
|
case 'c': {
|
||||||
|
// call a phone!
|
||||||
|
char number[30];
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Call #"));
|
||||||
|
readline(number, 30);
|
||||||
|
Serial.println();
|
||||||
|
Serial.print(F("Calling ")); Serial.println(number);
|
||||||
|
if (!fona.callPhone(number)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Sent!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'A': {
|
||||||
|
// get call status
|
||||||
|
int8_t callstat = fona.getCallStatus();
|
||||||
|
switch (callstat) {
|
||||||
|
case 0: Serial.println(F("Ready")); break;
|
||||||
|
case 1: Serial.println(F("Could not get status")); break;
|
||||||
|
case 3: Serial.println(F("Ringing (incoming)")); break;
|
||||||
|
case 4: Serial.println(F("Ringing/in progress (outgoing)")); break;
|
||||||
|
default: Serial.println(F("Unknown")); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'h': {
|
||||||
|
// hang up!
|
||||||
|
if (! fona.hangUp()) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'p': {
|
||||||
|
// pick up!
|
||||||
|
if (! fona.pickUp()) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** SMS ***/
|
||||||
|
|
||||||
|
case 'N': {
|
||||||
|
// read the number of SMS's!
|
||||||
|
int8_t smsnum = fona.getNumSMS();
|
||||||
|
if (smsnum < 0) {
|
||||||
|
Serial.println(F("Could not read # SMS"));
|
||||||
|
} else {
|
||||||
|
Serial.print(smsnum);
|
||||||
|
Serial.println(F(" SMS's on SIM card!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'r': {
|
||||||
|
// read an SMS
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Read #"));
|
||||||
|
uint8_t smsn = readnumber();
|
||||||
|
Serial.print(F("\n\rReading SMS #")); Serial.println(smsn);
|
||||||
|
|
||||||
|
// Retrieve SMS sender address/phone number.
|
||||||
|
if (! fona.getSMSSender(smsn, replybuffer, 250)) {
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Serial.print(F("FROM: ")); Serial.println(replybuffer);
|
||||||
|
|
||||||
|
// Retrieve SMS value.
|
||||||
|
uint16_t smslen;
|
||||||
|
if (! fona.readSMS(smsn, replybuffer, 250, &smslen)) { // pass in buffer and max len!
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Serial.print(F("***** SMS #")); Serial.print(smsn);
|
||||||
|
Serial.print(" ("); Serial.print(smslen); Serial.println(F(") bytes *****"));
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
Serial.println(F("*****"));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'R': {
|
||||||
|
// read all SMS
|
||||||
|
int8_t smsnum = fona.getNumSMS();
|
||||||
|
uint16_t smslen;
|
||||||
|
int8_t smsn;
|
||||||
|
|
||||||
|
if ( (type == FONA3G_A) || (type == FONA3G_E) ) {
|
||||||
|
smsn = 0; // zero indexed
|
||||||
|
smsnum--;
|
||||||
|
} else {
|
||||||
|
smsn = 1; // 1 indexed
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ; smsn <= smsnum; smsn++) {
|
||||||
|
Serial.print(F("\n\rReading SMS #")); Serial.println(smsn);
|
||||||
|
if (!fona.readSMS(smsn, replybuffer, 250, &smslen)) { // pass in buffer and max len!
|
||||||
|
Serial.println(F("Failed!"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// if the length is zero, its a special case where the index number is higher
|
||||||
|
// so increase the max we'll look at!
|
||||||
|
if (smslen == 0) {
|
||||||
|
Serial.println(F("[empty slot]"));
|
||||||
|
smsnum++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(F("***** SMS #")); Serial.print(smsn);
|
||||||
|
Serial.print(" ("); Serial.print(smslen); Serial.println(F(") bytes *****"));
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
Serial.println(F("*****"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'd': {
|
||||||
|
// delete an SMS
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Delete #"));
|
||||||
|
uint8_t smsn = readnumber();
|
||||||
|
|
||||||
|
Serial.print(F("\n\rDeleting SMS #")); Serial.println(smsn);
|
||||||
|
if (fona.deleteSMS(smsn)) {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Couldn't delete"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 's': {
|
||||||
|
// send an SMS!
|
||||||
|
char sendto[21], message[141];
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Send to #"));
|
||||||
|
readline(sendto, 20);
|
||||||
|
Serial.println(sendto);
|
||||||
|
Serial.print(F("Type out one-line message (140 char): "));
|
||||||
|
readline(message, 140);
|
||||||
|
Serial.println(message);
|
||||||
|
if (!fona.sendSMS(sendto, message)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Sent!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'u': {
|
||||||
|
// send a USSD!
|
||||||
|
char message[141];
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Type out one-line message (140 char): "));
|
||||||
|
readline(message, 140);
|
||||||
|
Serial.println(message);
|
||||||
|
|
||||||
|
uint16_t ussdlen;
|
||||||
|
if (!fona.sendUSSD(message, replybuffer, 250, &ussdlen)) { // pass in buffer and max len!
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Sent!"));
|
||||||
|
Serial.print(F("***** USSD Reply"));
|
||||||
|
Serial.print(" ("); Serial.print(ussdlen); Serial.println(F(") bytes *****"));
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
Serial.println(F("*****"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Time ***/
|
||||||
|
|
||||||
|
case 'y': {
|
||||||
|
// enable network time sync
|
||||||
|
if (!fona.enableNetworkTimeSync(true))
|
||||||
|
Serial.println(F("Failed to enable"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'Y': {
|
||||||
|
// enable NTP time sync
|
||||||
|
if (!fona.enableNTPTimeSync(true, F("pool.ntp.org")))
|
||||||
|
Serial.println(F("Failed to enable"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 't': {
|
||||||
|
// read the time
|
||||||
|
char buffer[23];
|
||||||
|
|
||||||
|
fona.getTime(buffer, 23); // make sure replybuffer is at least 23 bytes!
|
||||||
|
Serial.print(F("Time = ")); Serial.println(buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** GPS (SIM808 only) */
|
||||||
|
|
||||||
|
case 'o': {
|
||||||
|
// turn GPS off
|
||||||
|
if (!fona.enableGPS(false))
|
||||||
|
Serial.println(F("Failed to turn off"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'O': {
|
||||||
|
// turn GPS on
|
||||||
|
if (!fona.enableGPS(true))
|
||||||
|
Serial.println(F("Failed to turn on"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': {
|
||||||
|
int8_t stat;
|
||||||
|
// check GPS fix
|
||||||
|
stat = fona.GPSstatus();
|
||||||
|
if (stat < 0)
|
||||||
|
Serial.println(F("Failed to query"));
|
||||||
|
if (stat == 0) Serial.println(F("GPS off"));
|
||||||
|
if (stat == 1) Serial.println(F("No fix"));
|
||||||
|
if (stat == 2) Serial.println(F("2D fix"));
|
||||||
|
if (stat == 3) Serial.println(F("3D fix"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'L': {
|
||||||
|
// check for GPS location
|
||||||
|
char gpsdata[120];
|
||||||
|
fona.getGPS(0, gpsdata, 120);
|
||||||
|
if (type == FONA808_V1)
|
||||||
|
Serial.println(F("Reply in format: mode,longitude,latitude,altitude,utctime(yyyymmddHHMMSS),ttff,satellites,speed,course"));
|
||||||
|
else
|
||||||
|
Serial.println(F("Reply in format: mode,fixstatus,utctime(yyyymmddHHMMSS),latitude,longitude,altitude,speed,course,fixmode,reserved1,HDOP,PDOP,VDOP,reserved2,view_satellites,used_satellites,reserved3,C/N0max,HPA,VPA"));
|
||||||
|
Serial.println(gpsdata);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'E': {
|
||||||
|
flushSerial();
|
||||||
|
if (type == FONA808_V1) {
|
||||||
|
Serial.print(F("GPS NMEA output sentences (0 = off, 34 = RMC+GGA, 255 = all)"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("On (1) or Off (0)? "));
|
||||||
|
}
|
||||||
|
uint8_t nmeaout = readnumber();
|
||||||
|
|
||||||
|
// turn on NMEA output
|
||||||
|
fona.enableGPSNMEA(nmeaout);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************** GPRS */
|
||||||
|
|
||||||
|
case 'g': {
|
||||||
|
// turn GPRS off
|
||||||
|
if (!fona.enableGPRS(false))
|
||||||
|
Serial.println(F("Failed to turn off"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'G': {
|
||||||
|
// turn GPRS on
|
||||||
|
if (!fona.enableGPRS(true))
|
||||||
|
Serial.println(F("Failed to turn on"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'l': {
|
||||||
|
// check for GSMLOC (requires GPRS)
|
||||||
|
uint16_t returncode;
|
||||||
|
|
||||||
|
if (!fona.getGSMLoc(&returncode, replybuffer, 250))
|
||||||
|
Serial.println(F("Failed!"));
|
||||||
|
if (returncode == 0) {
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
} else {
|
||||||
|
Serial.print(F("Fail code #")); Serial.println(returncode);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'w': {
|
||||||
|
// read website URL
|
||||||
|
uint16_t statuscode;
|
||||||
|
int16_t length;
|
||||||
|
char url[80];
|
||||||
|
|
||||||
|
flushSerial();
|
||||||
|
Serial.println(F("NOTE: in beta! Use small webpages to read!"));
|
||||||
|
Serial.println(F("URL to read (e.g. wifitest.adafruit.com/testwifi/index.html):"));
|
||||||
|
Serial.print(F("http://")); readline(url, 79);
|
||||||
|
Serial.println(url);
|
||||||
|
|
||||||
|
Serial.println(F("****"));
|
||||||
|
if (!fona.HTTP_GET_start(url, &statuscode, (uint16_t *)&length)) {
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (length > 0) {
|
||||||
|
while (fona.available()) {
|
||||||
|
char c = fona.read();
|
||||||
|
|
||||||
|
// Serial.write is too slow, we'll write directly to Serial register!
|
||||||
|
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
|
||||||
|
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
|
||||||
|
UDR0 = c;
|
||||||
|
#else
|
||||||
|
Serial.write(c);
|
||||||
|
#endif
|
||||||
|
length--;
|
||||||
|
if (! length) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println(F("\n****"));
|
||||||
|
fona.HTTP_GET_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'W': {
|
||||||
|
// Post data to website
|
||||||
|
uint16_t statuscode;
|
||||||
|
int16_t length;
|
||||||
|
char url[80];
|
||||||
|
char data[80];
|
||||||
|
|
||||||
|
flushSerial();
|
||||||
|
Serial.println(F("NOTE: in beta! Use simple websites to post!"));
|
||||||
|
Serial.println(F("URL to post (e.g. httpbin.org/post):"));
|
||||||
|
Serial.print(F("http://")); readline(url, 79);
|
||||||
|
Serial.println(url);
|
||||||
|
Serial.println(F("Data to post (e.g. \"foo\" or \"{\"simple\":\"json\"}\"):"));
|
||||||
|
readline(data, 79);
|
||||||
|
Serial.println(data);
|
||||||
|
|
||||||
|
Serial.println(F("****"));
|
||||||
|
if (!fona.HTTP_POST_start(url, F("text/plain"), (uint8_t *) data, strlen(data), &statuscode, (uint16_t *)&length)) {
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (length > 0) {
|
||||||
|
while (fona.available()) {
|
||||||
|
char c = fona.read();
|
||||||
|
|
||||||
|
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
|
||||||
|
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
|
||||||
|
UDR0 = c;
|
||||||
|
#else
|
||||||
|
Serial.write(c);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
length--;
|
||||||
|
if (! length) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println(F("\n****"));
|
||||||
|
fona.HTTP_POST_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*****************************************/
|
||||||
|
|
||||||
|
case 'S': {
|
||||||
|
Serial.println(F("Creating SERIAL TUBE"));
|
||||||
|
while (1) {
|
||||||
|
while (Serial.available()) {
|
||||||
|
delay(1);
|
||||||
|
fona.write(Serial.read());
|
||||||
|
}
|
||||||
|
if (fona.available()) {
|
||||||
|
Serial.write(fona.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
Serial.println(F("Unknown command"));
|
||||||
|
printMenu();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// flush input
|
||||||
|
flushSerial();
|
||||||
|
while (fona.available()) {
|
||||||
|
Serial.write(fona.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void flushSerial() {
|
||||||
|
while (Serial.available())
|
||||||
|
Serial.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
char readBlocking() {
|
||||||
|
while (!Serial.available());
|
||||||
|
return Serial.read();
|
||||||
|
}
|
||||||
|
uint16_t readnumber() {
|
||||||
|
uint16_t x = 0;
|
||||||
|
char c;
|
||||||
|
while (! isdigit(c = readBlocking())) {
|
||||||
|
//Serial.print(c);
|
||||||
|
}
|
||||||
|
Serial.print(c);
|
||||||
|
x = c - '0';
|
||||||
|
while (isdigit(c = readBlocking())) {
|
||||||
|
Serial.print(c);
|
||||||
|
x *= 10;
|
||||||
|
x += c - '0';
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout) {
|
||||||
|
uint16_t buffidx = 0;
|
||||||
|
boolean timeoutvalid = true;
|
||||||
|
if (timeout == 0) timeoutvalid = false;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (buffidx > maxbuff) {
|
||||||
|
//Serial.println(F("SPACE"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (Serial.available()) {
|
||||||
|
char c = Serial.read();
|
||||||
|
|
||||||
|
//Serial.print(c, HEX); Serial.print("#"); Serial.println(c);
|
||||||
|
|
||||||
|
if (c == '\r') continue;
|
||||||
|
if (c == 0xA) {
|
||||||
|
if (buffidx == 0) // the first 0x0A is ignored
|
||||||
|
continue;
|
||||||
|
|
||||||
|
timeout = 0; // the second 0x0A is the end of the line
|
||||||
|
timeoutvalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buff[buffidx] = c;
|
||||||
|
buffidx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeoutvalid && timeout == 0) {
|
||||||
|
//Serial.println(F("TIMEOUT"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
buff[buffidx] = 0; // null term
|
||||||
|
return buffidx;
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,909 @@
|
||||||
|
/***************************************************
|
||||||
|
This is an example for our Adafruit FONA Cellular Module
|
||||||
|
|
||||||
|
Designed specifically to work with the Adafruit FONA
|
||||||
|
----> http://www.adafruit.com/products/1946
|
||||||
|
----> http://www.adafruit.com/products/1963
|
||||||
|
----> http://www.adafruit.com/products/2468
|
||||||
|
----> http://www.adafruit.com/products/2542
|
||||||
|
|
||||||
|
These cellular modules use TTL Serial to communicate, 2 pins are
|
||||||
|
required to interface
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
BSD license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
THIS CODE IS STILL IN PROGRESS!
|
||||||
|
|
||||||
|
Open up the serial console on the Arduino at 115200 baud to interact with FONA
|
||||||
|
|
||||||
|
Note that if you need to set a GPRS APN, username, and password scroll down to
|
||||||
|
the commented section below at the end of the setup() function.
|
||||||
|
*/
|
||||||
|
#include "Adafruit_FONA.h"
|
||||||
|
|
||||||
|
#define FONA_RX 2
|
||||||
|
#define FONA_TX 3
|
||||||
|
#define FONA_RST 4
|
||||||
|
#define FONA_KEY 8
|
||||||
|
|
||||||
|
// this is a large buffer for replies
|
||||||
|
char replybuffer[255];
|
||||||
|
|
||||||
|
// We default to using software serial. If you want to use hardware serial
|
||||||
|
// (because softserial isnt supported) comment out the following three lines
|
||||||
|
// and uncomment the HardwareSerial line
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
|
||||||
|
SoftwareSerial *fonaSerial = &fonaSS;
|
||||||
|
|
||||||
|
// Hardware serial is also possible!
|
||||||
|
// HardwareSerial *fonaSerial = &Serial1;
|
||||||
|
|
||||||
|
// Use this for FONA 800 and 808s
|
||||||
|
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
|
||||||
|
// Use this one for FONA 3G
|
||||||
|
//Adafruit_FONA_3G fona = Adafruit_FONA_3G(FONA_RST);
|
||||||
|
|
||||||
|
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
|
||||||
|
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
pinMode(FONA_KEY, OUTPUT);
|
||||||
|
digitalWrite(FONA_KEY, HIGH);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("FONA basic test"));
|
||||||
|
Serial.println(F("Initializing....(May take 3 seconds)"));
|
||||||
|
|
||||||
|
fonaSerial->begin(4800);
|
||||||
|
if (! fona.begin(*fonaSerial)) {
|
||||||
|
Serial.println(F("Couldn't find FONA"));
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
type = fona.type();
|
||||||
|
Serial.println(F("FONA is OK"));
|
||||||
|
Serial.print(F("Found "));
|
||||||
|
switch (type) {
|
||||||
|
case FONA800L:
|
||||||
|
Serial.println(F("FONA 800L")); break;
|
||||||
|
case FONA800H:
|
||||||
|
Serial.println(F("FONA 800H")); break;
|
||||||
|
case FONA808_V1:
|
||||||
|
Serial.println(F("FONA 808 (v1)")); break;
|
||||||
|
case FONA808_V2:
|
||||||
|
Serial.println(F("FONA 808 (v2)")); break;
|
||||||
|
case FONA3G_A:
|
||||||
|
Serial.println(F("FONA 3G (American)")); break;
|
||||||
|
case FONA3G_E:
|
||||||
|
Serial.println(F("FONA 3G (European)")); break;
|
||||||
|
default:
|
||||||
|
Serial.println(F("???")); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print module IMEI number.
|
||||||
|
char imei[15] = {0}; // MUST use a 16 character buffer for IMEI!
|
||||||
|
uint8_t imeiLen = fona.getIMEI(imei);
|
||||||
|
if (imeiLen > 0) {
|
||||||
|
Serial.print("Module IMEI: "); Serial.println(imei);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//fona.setGPRSNetworkSettings(F("your APN"), F("your username"), F("your password"));
|
||||||
|
|
||||||
|
// Optionally configure HTTP gets to follow redirects over SSL.
|
||||||
|
// Default is not to follow SSL redirects, however if you uncomment
|
||||||
|
// the following line then redirects over SSL will be followed.
|
||||||
|
//fona.setHTTPSRedirect(true);
|
||||||
|
|
||||||
|
printMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printMenu(void) {
|
||||||
|
Serial.println(F("-------------------------------------"));
|
||||||
|
Serial.println(F("[?] Print this menu"));
|
||||||
|
Serial.println(F("[a] read the ADC 2.8V max (FONA800 & 808)"));
|
||||||
|
Serial.println(F("[b] read the Battery V and % charged"));
|
||||||
|
Serial.println(F("[C] read the SIM CCID"));
|
||||||
|
Serial.println(F("[U] Unlock SIM with PIN code"));
|
||||||
|
Serial.println(F("[i] read RSSI"));
|
||||||
|
Serial.println(F("[n] get Network status"));
|
||||||
|
Serial.println(F("[v] set audio Volume"));
|
||||||
|
Serial.println(F("[V] get Volume"));
|
||||||
|
Serial.println(F("[H] set Headphone audio (FONA800 & 808)"));
|
||||||
|
Serial.println(F("[e] set External audio (FONA800 & 808)"));
|
||||||
|
Serial.println(F("[T] play audio Tone"));
|
||||||
|
Serial.println(F("[P] PWM/Buzzer out (FONA800 & 808)"));
|
||||||
|
Serial.println(F("[Z] power off with Key"));
|
||||||
|
Serial.println(F("[z] power on with Key"));
|
||||||
|
|
||||||
|
// FM (SIM800 only!)
|
||||||
|
Serial.println(F("[f] tune FM radio (FONA800)"));
|
||||||
|
Serial.println(F("[F] turn off FM (FONA800)"));
|
||||||
|
Serial.println(F("[m] set FM volume (FONA800)"));
|
||||||
|
Serial.println(F("[M] get FM volume (FONA800)"));
|
||||||
|
Serial.println(F("[q] get FM station signal level (FONA800)"));
|
||||||
|
|
||||||
|
// Phone
|
||||||
|
Serial.println(F("[c] make phone Call"));
|
||||||
|
Serial.println(F("[A] get call status"));
|
||||||
|
Serial.println(F("[h] Hang up phone"));
|
||||||
|
Serial.println(F("[p] Pick up phone"));
|
||||||
|
|
||||||
|
// SMS
|
||||||
|
Serial.println(F("[N] Number of SMSs"));
|
||||||
|
Serial.println(F("[r] Read SMS #"));
|
||||||
|
Serial.println(F("[R] Read All SMS"));
|
||||||
|
Serial.println(F("[d] Delete SMS #"));
|
||||||
|
Serial.println(F("[s] Send SMS"));
|
||||||
|
Serial.println(F("[u] Send USSD"));
|
||||||
|
|
||||||
|
// Time
|
||||||
|
Serial.println(F("[y] Enable network time sync (FONA 800 & 808)"));
|
||||||
|
Serial.println(F("[Y] Enable NTP time sync (GPRS FONA 800 & 808)"));
|
||||||
|
Serial.println(F("[t] Get network time"));
|
||||||
|
|
||||||
|
// GPRS
|
||||||
|
Serial.println(F("[G] Enable GPRS"));
|
||||||
|
Serial.println(F("[g] Disable GPRS"));
|
||||||
|
Serial.println(F("[l] Query GSMLOC (GPRS)"));
|
||||||
|
Serial.println(F("[w] Read webpage (GPRS)"));
|
||||||
|
Serial.println(F("[W] Post to website (GPRS)"));
|
||||||
|
|
||||||
|
// GPS
|
||||||
|
if ((type == FONA3G_A) || (type == FONA3G_E) || (type == FONA808_V1) || (type == FONA808_V2)) {
|
||||||
|
Serial.println(F("[O] Turn GPS on (FONA 808 & 3G)"));
|
||||||
|
Serial.println(F("[o] Turn GPS off (FONA 808 & 3G)"));
|
||||||
|
Serial.println(F("[L] Query GPS location (FONA 808 & 3G)"));
|
||||||
|
if (type == FONA808_V1) {
|
||||||
|
Serial.println(F("[x] GPS fix status (FONA808 v1 only)"));
|
||||||
|
}
|
||||||
|
Serial.println(F("[E] Raw NMEA out (FONA808)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(F("[S] create Serial passthru tunnel"));
|
||||||
|
Serial.println(F("-------------------------------------"));
|
||||||
|
Serial.println(F(""));
|
||||||
|
|
||||||
|
}
|
||||||
|
void loop() {
|
||||||
|
Serial.print(F("FONA> "));
|
||||||
|
while (! Serial.available() ) {
|
||||||
|
if (fona.available()) {
|
||||||
|
Serial.write(fona.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char command = Serial.read();
|
||||||
|
Serial.println(command);
|
||||||
|
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case '?': {
|
||||||
|
printMenu();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'Z': {
|
||||||
|
digitalWrite(FONA_KEY, LOW);
|
||||||
|
delay(2000);
|
||||||
|
digitalWrite(FONA_KEY, HIGH);
|
||||||
|
delay(3000);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'z': {
|
||||||
|
digitalWrite(FONA_KEY, LOW);
|
||||||
|
delay(2000);
|
||||||
|
digitalWrite(FONA_KEY, HIGH);
|
||||||
|
delay(3000);
|
||||||
|
if (! fona.begin(*fonaSerial)) {
|
||||||
|
Serial.println(F("Couldn't find FONA"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'a': {
|
||||||
|
// read the ADC
|
||||||
|
uint16_t adc;
|
||||||
|
if (! fona.getADCVoltage(&adc)) {
|
||||||
|
Serial.println(F("Failed to read ADC"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("ADC = ")); Serial.print(adc); Serial.println(F(" mV"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'b': {
|
||||||
|
// read the battery voltage and percentage
|
||||||
|
uint16_t vbat;
|
||||||
|
if (! fona.getBattVoltage(&vbat)) {
|
||||||
|
Serial.println(F("Failed to read Batt"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("VBat = ")); Serial.print(vbat); Serial.println(F(" mV"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (! fona.getBattPercent(&vbat)) {
|
||||||
|
Serial.println(F("Failed to read Batt"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("VPct = ")); Serial.print(vbat); Serial.println(F("%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'U': {
|
||||||
|
// Unlock the SIM with a PIN code
|
||||||
|
char PIN[5];
|
||||||
|
flushSerial();
|
||||||
|
Serial.println(F("Enter 4-digit PIN"));
|
||||||
|
readline(PIN, 3);
|
||||||
|
Serial.println(PIN);
|
||||||
|
Serial.print(F("Unlocking SIM card: "));
|
||||||
|
if (! fona.unlockSIM(PIN)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'C': {
|
||||||
|
// read the CCID
|
||||||
|
fona.getSIMCCID(replybuffer); // make sure replybuffer is at least 21 bytes!
|
||||||
|
Serial.print(F("SIM CCID = ")); Serial.println(replybuffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'i': {
|
||||||
|
// read the RSSI
|
||||||
|
uint8_t n = fona.getRSSI();
|
||||||
|
int8_t r;
|
||||||
|
|
||||||
|
Serial.print(F("RSSI = ")); Serial.print(n); Serial.print(": ");
|
||||||
|
if (n == 0) r = -115;
|
||||||
|
if (n == 1) r = -111;
|
||||||
|
if (n == 31) r = -52;
|
||||||
|
if ((n >= 2) && (n <= 30)) {
|
||||||
|
r = map(n, 2, 30, -110, -54);
|
||||||
|
}
|
||||||
|
Serial.print(r); Serial.println(F(" dBm"));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'n': {
|
||||||
|
// read the network/cellular status
|
||||||
|
uint8_t n = fona.getNetworkStatus();
|
||||||
|
Serial.print(F("Network status "));
|
||||||
|
Serial.print(n);
|
||||||
|
Serial.print(F(": "));
|
||||||
|
if (n == 0) Serial.println(F("Not registered"));
|
||||||
|
if (n == 1) Serial.println(F("Registered (home)"));
|
||||||
|
if (n == 2) Serial.println(F("Not registered (searching)"));
|
||||||
|
if (n == 3) Serial.println(F("Denied"));
|
||||||
|
if (n == 4) Serial.println(F("Unknown"));
|
||||||
|
if (n == 5) Serial.println(F("Registered roaming"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Audio ***/
|
||||||
|
case 'v': {
|
||||||
|
// set volume
|
||||||
|
flushSerial();
|
||||||
|
if ( (type == FONA3G_A) || (type == FONA3G_E) ) {
|
||||||
|
Serial.print(F("Set Vol [0-8] "));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("Set Vol % [0-100] "));
|
||||||
|
}
|
||||||
|
uint8_t vol = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
if (! fona.setVolume(vol)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'V': {
|
||||||
|
uint8_t v = fona.getVolume();
|
||||||
|
Serial.print(v);
|
||||||
|
if ( (type == FONA3G_A) || (type == FONA3G_E) ) {
|
||||||
|
Serial.println(" / 8");
|
||||||
|
} else {
|
||||||
|
Serial.println("%");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'H': {
|
||||||
|
// Set Headphone output
|
||||||
|
if (! fona.setAudio(FONA_HEADSETAUDIO)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
fona.setMicVolume(FONA_HEADSETAUDIO, 15);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'e': {
|
||||||
|
// Set External output
|
||||||
|
if (! fona.setAudio(FONA_EXTAUDIO)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fona.setMicVolume(FONA_EXTAUDIO, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'T': {
|
||||||
|
// play tone
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Play tone #"));
|
||||||
|
uint8_t kittone = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
// play for 1 second (1000 ms)
|
||||||
|
if (! fona.playToolkitTone(kittone, 1000)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** FM Radio ***/
|
||||||
|
|
||||||
|
case 'f': {
|
||||||
|
// get freq
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("FM Freq (eg 1011 == 101.1 MHz): "));
|
||||||
|
uint16_t station = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
// FM radio ON using headset
|
||||||
|
if (fona.FMradio(true, FONA_HEADSETAUDIO)) {
|
||||||
|
Serial.println(F("Opened"));
|
||||||
|
}
|
||||||
|
if (! fona.tuneFMradio(station)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Tuned"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'F': {
|
||||||
|
// FM radio off
|
||||||
|
if (! fona.FMradio(false)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
// Set FM volume.
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Set FM Vol [0-6]:"));
|
||||||
|
uint8_t vol = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
if (!fona.setFMVolume(vol)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'M': {
|
||||||
|
// Get FM volume.
|
||||||
|
uint8_t fmvol = fona.getFMVolume();
|
||||||
|
if (fmvol < 0) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("FM volume: "));
|
||||||
|
Serial.println(fmvol, DEC);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'q': {
|
||||||
|
// Get FM station signal level (in decibels).
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("FM Freq (eg 1011 == 101.1 MHz): "));
|
||||||
|
uint16_t station = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
int8_t level = fona.getFMSignalLevel(station);
|
||||||
|
if (level < 0) {
|
||||||
|
Serial.println(F("Failed! Make sure FM radio is on (tuned to station)."));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("Signal level (dB): "));
|
||||||
|
Serial.println(level, DEC);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** PWM ***/
|
||||||
|
|
||||||
|
case 'P': {
|
||||||
|
// PWM Buzzer output @ 2KHz max
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("PWM Freq, 0 = Off, (1-2000): "));
|
||||||
|
uint16_t freq = readnumber();
|
||||||
|
Serial.println();
|
||||||
|
if (! fona.setPWM(freq)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Call ***/
|
||||||
|
case 'c': {
|
||||||
|
// call a phone!
|
||||||
|
char number[30];
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Call #"));
|
||||||
|
readline(number, 30);
|
||||||
|
Serial.println();
|
||||||
|
Serial.print(F("Calling ")); Serial.println(number);
|
||||||
|
if (!fona.callPhone(number)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Sent!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'A': {
|
||||||
|
// get call status
|
||||||
|
int8_t callstat = fona.getCallStatus();
|
||||||
|
switch (callstat) {
|
||||||
|
case 0: Serial.println(F("Ready")); break;
|
||||||
|
case 1: Serial.println(F("Could not get status")); break;
|
||||||
|
case 3: Serial.println(F("Ringing (incoming)")); break;
|
||||||
|
case 4: Serial.println(F("Ringing/in progress (outgoing)")); break;
|
||||||
|
default: Serial.println(F("Unknown")); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'h': {
|
||||||
|
// hang up!
|
||||||
|
if (! fona.hangUp()) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'p': {
|
||||||
|
// pick up!
|
||||||
|
if (! fona.pickUp()) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** SMS ***/
|
||||||
|
|
||||||
|
case 'N': {
|
||||||
|
// read the number of SMS's!
|
||||||
|
int8_t smsnum = fona.getNumSMS();
|
||||||
|
if (smsnum < 0) {
|
||||||
|
Serial.println(F("Could not read # SMS"));
|
||||||
|
} else {
|
||||||
|
Serial.print(smsnum);
|
||||||
|
Serial.println(F(" SMS's on SIM card!"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'r': {
|
||||||
|
// read an SMS
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Read #"));
|
||||||
|
uint8_t smsn = readnumber();
|
||||||
|
Serial.print(F("\n\rReading SMS #")); Serial.println(smsn);
|
||||||
|
|
||||||
|
// Retrieve SMS sender address/phone number.
|
||||||
|
if (! fona.getSMSSender(smsn, replybuffer, 250)) {
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Serial.print(F("FROM: ")); Serial.println(replybuffer);
|
||||||
|
|
||||||
|
// Retrieve SMS value.
|
||||||
|
uint16_t smslen;
|
||||||
|
if (! fona.readSMS(smsn, replybuffer, 250, &smslen)) { // pass in buffer and max len!
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Serial.print(F("***** SMS #")); Serial.print(smsn);
|
||||||
|
Serial.print(" ("); Serial.print(smslen); Serial.println(F(") bytes *****"));
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
Serial.println(F("*****"));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'R': {
|
||||||
|
// read all SMS
|
||||||
|
int8_t smsnum = fona.getNumSMS();
|
||||||
|
uint16_t smslen;
|
||||||
|
int8_t smsn;
|
||||||
|
|
||||||
|
if ( (type == FONA3G_A) || (type == FONA3G_E) ) {
|
||||||
|
smsn = 0; // zero indexed
|
||||||
|
smsnum--;
|
||||||
|
} else {
|
||||||
|
smsn = 1; // 1 indexed
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ; smsn <= smsnum; smsn++) {
|
||||||
|
Serial.print(F("\n\rReading SMS #")); Serial.println(smsn);
|
||||||
|
if (!fona.readSMS(smsn, replybuffer, 250, &smslen)) { // pass in buffer and max len!
|
||||||
|
Serial.println(F("Failed!"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// if the length is zero, its a special case where the index number is higher
|
||||||
|
// so increase the max we'll look at!
|
||||||
|
if (smslen == 0) {
|
||||||
|
Serial.println(F("[empty slot]"));
|
||||||
|
smsnum++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(F("***** SMS #")); Serial.print(smsn);
|
||||||
|
Serial.print(" ("); Serial.print(smslen); Serial.println(F(") bytes *****"));
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
Serial.println(F("*****"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'd': {
|
||||||
|
// delete an SMS
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Delete #"));
|
||||||
|
uint8_t smsn = readnumber();
|
||||||
|
|
||||||
|
Serial.print(F("\n\rDeleting SMS #")); Serial.println(smsn);
|
||||||
|
if (fona.deleteSMS(smsn)) {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Couldn't delete"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 's': {
|
||||||
|
// send an SMS!
|
||||||
|
char sendto[21], message[141];
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Send to #"));
|
||||||
|
readline(sendto, 20);
|
||||||
|
Serial.println(sendto);
|
||||||
|
Serial.print(F("Type out one-line message (140 char): "));
|
||||||
|
readline(message, 140);
|
||||||
|
Serial.println(message);
|
||||||
|
if (!fona.sendSMS(sendto, message)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Sent!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'u': {
|
||||||
|
// send a USSD!
|
||||||
|
char message[141];
|
||||||
|
flushSerial();
|
||||||
|
Serial.print(F("Type out one-line message (140 char): "));
|
||||||
|
readline(message, 140);
|
||||||
|
Serial.println(message);
|
||||||
|
|
||||||
|
uint16_t ussdlen;
|
||||||
|
if (!fona.sendUSSD(message, replybuffer, 250, &ussdlen)) { // pass in buffer and max len!
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("Sent!"));
|
||||||
|
Serial.print(F("***** USSD Reply"));
|
||||||
|
Serial.print(" ("); Serial.print(ussdlen); Serial.println(F(") bytes *****"));
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
Serial.println(F("*****"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Time ***/
|
||||||
|
|
||||||
|
case 'y': {
|
||||||
|
// enable network time sync
|
||||||
|
if (!fona.enableNetworkTimeSync(true))
|
||||||
|
Serial.println(F("Failed to enable"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'Y': {
|
||||||
|
// enable NTP time sync
|
||||||
|
if (!fona.enableNTPTimeSync(true, F("pool.ntp.org")))
|
||||||
|
Serial.println(F("Failed to enable"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 't': {
|
||||||
|
// read the time
|
||||||
|
char buffer[23];
|
||||||
|
|
||||||
|
fona.getTime(buffer, 23); // make sure replybuffer is at least 23 bytes!
|
||||||
|
Serial.print(F("Time = ")); Serial.println(buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** GPS (SIM808 only) */
|
||||||
|
|
||||||
|
case 'o': {
|
||||||
|
// turn GPS off
|
||||||
|
if (!fona.enableGPS(false))
|
||||||
|
Serial.println(F("Failed to turn off"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'O': {
|
||||||
|
// turn GPS on
|
||||||
|
if (!fona.enableGPS(true))
|
||||||
|
Serial.println(F("Failed to turn on"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': {
|
||||||
|
int8_t stat;
|
||||||
|
// check GPS fix
|
||||||
|
stat = fona.GPSstatus();
|
||||||
|
if (stat < 0)
|
||||||
|
Serial.println(F("Failed to query"));
|
||||||
|
if (stat == 0) Serial.println(F("GPS off"));
|
||||||
|
if (stat == 1) Serial.println(F("No fix"));
|
||||||
|
if (stat == 2) Serial.println(F("2D fix"));
|
||||||
|
if (stat == 3) Serial.println(F("3D fix"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'L': {
|
||||||
|
// check for GPS location
|
||||||
|
char gpsdata[120];
|
||||||
|
fona.getGPS(0, gpsdata, 120);
|
||||||
|
if (type == FONA808_V1)
|
||||||
|
Serial.println(F("Reply in format: mode,longitude,latitude,altitude,utctime(yyyymmddHHMMSS),ttff,satellites,speed,course"));
|
||||||
|
else
|
||||||
|
Serial.println(F("Reply in format: mode,fixstatus,utctime(yyyymmddHHMMSS),latitude,longitude,altitude,speed,course,fixmode,reserved1,HDOP,PDOP,VDOP,reserved2,view_satellites,used_satellites,reserved3,C/N0max,HPA,VPA"));
|
||||||
|
Serial.println(gpsdata);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'E': {
|
||||||
|
flushSerial();
|
||||||
|
if (type == FONA808_V1) {
|
||||||
|
Serial.print(F("GPS NMEA output sentences (0 = off, 34 = RMC+GGA, 255 = all)"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("On (1) or Off (0)? "));
|
||||||
|
}
|
||||||
|
uint8_t nmeaout = readnumber();
|
||||||
|
|
||||||
|
// turn on NMEA output
|
||||||
|
fona.enableGPSNMEA(nmeaout);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************** GPRS */
|
||||||
|
|
||||||
|
case 'g': {
|
||||||
|
// turn GPRS off
|
||||||
|
if (!fona.enableGPRS(false))
|
||||||
|
Serial.println(F("Failed to turn off"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'G': {
|
||||||
|
// turn GPRS on
|
||||||
|
if (!fona.enableGPRS(true))
|
||||||
|
Serial.println(F("Failed to turn on"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'l': {
|
||||||
|
// check for GSMLOC (requires GPRS)
|
||||||
|
uint16_t returncode;
|
||||||
|
|
||||||
|
if (!fona.getGSMLoc(&returncode, replybuffer, 250))
|
||||||
|
Serial.println(F("Failed!"));
|
||||||
|
if (returncode == 0) {
|
||||||
|
Serial.println(replybuffer);
|
||||||
|
} else {
|
||||||
|
Serial.print(F("Fail code #")); Serial.println(returncode);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'w': {
|
||||||
|
// read website URL
|
||||||
|
uint16_t statuscode;
|
||||||
|
int16_t length;
|
||||||
|
char url[80];
|
||||||
|
|
||||||
|
flushSerial();
|
||||||
|
Serial.println(F("NOTE: in beta! Use small webpages to read!"));
|
||||||
|
Serial.println(F("URL to read (e.g. www.adafruit.com/testwifi/index.html):"));
|
||||||
|
Serial.print(F("http://")); readline(url, 79);
|
||||||
|
Serial.println(url);
|
||||||
|
|
||||||
|
Serial.println(F("****"));
|
||||||
|
if (!fona.HTTP_GET_start(url, &statuscode, (uint16_t *)&length)) {
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (length > 0) {
|
||||||
|
while (fona.available()) {
|
||||||
|
char c = fona.read();
|
||||||
|
|
||||||
|
// Serial.write is too slow, we'll write directly to Serial register!
|
||||||
|
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
|
||||||
|
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
|
||||||
|
UDR0 = c;
|
||||||
|
#else
|
||||||
|
Serial.write(c);
|
||||||
|
#endif
|
||||||
|
length--;
|
||||||
|
if (! length) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println(F("\n****"));
|
||||||
|
fona.HTTP_GET_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'W': {
|
||||||
|
// Post data to website
|
||||||
|
uint16_t statuscode;
|
||||||
|
int16_t length;
|
||||||
|
char url[80];
|
||||||
|
char data[80];
|
||||||
|
|
||||||
|
flushSerial();
|
||||||
|
Serial.println(F("NOTE: in beta! Use simple websites to post!"));
|
||||||
|
Serial.println(F("URL to post (e.g. httpbin.org/post):"));
|
||||||
|
Serial.print(F("http://")); readline(url, 79);
|
||||||
|
Serial.println(url);
|
||||||
|
Serial.println(F("Data to post (e.g. \"foo\" or \"{\"simple\":\"json\"}\"):"));
|
||||||
|
readline(data, 79);
|
||||||
|
Serial.println(data);
|
||||||
|
|
||||||
|
Serial.println(F("****"));
|
||||||
|
if (!fona.HTTP_POST_start(url, F("text/plain"), (uint8_t *) data, strlen(data), &statuscode, (uint16_t *)&length)) {
|
||||||
|
Serial.println("Failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (length > 0) {
|
||||||
|
while (fona.available()) {
|
||||||
|
char c = fona.read();
|
||||||
|
|
||||||
|
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
|
||||||
|
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
|
||||||
|
UDR0 = c;
|
||||||
|
#else
|
||||||
|
Serial.write(c);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
length--;
|
||||||
|
if (! length) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println(F("\n****"));
|
||||||
|
fona.HTTP_POST_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*****************************************/
|
||||||
|
|
||||||
|
case 'S': {
|
||||||
|
Serial.println(F("Creating SERIAL TUBE"));
|
||||||
|
while (1) {
|
||||||
|
while (Serial.available()) {
|
||||||
|
delay(1);
|
||||||
|
fona.write(Serial.read());
|
||||||
|
}
|
||||||
|
if (fona.available()) {
|
||||||
|
Serial.write(fona.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
Serial.println(F("Unknown command"));
|
||||||
|
printMenu();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// flush input
|
||||||
|
flushSerial();
|
||||||
|
while (fona.available()) {
|
||||||
|
Serial.write(fona.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void flushSerial() {
|
||||||
|
while (Serial.available())
|
||||||
|
Serial.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
char readBlocking() {
|
||||||
|
while (!Serial.available());
|
||||||
|
return Serial.read();
|
||||||
|
}
|
||||||
|
uint16_t readnumber() {
|
||||||
|
uint16_t x = 0;
|
||||||
|
char c;
|
||||||
|
while (! isdigit(c = readBlocking())) {
|
||||||
|
//Serial.print(c);
|
||||||
|
}
|
||||||
|
Serial.print(c);
|
||||||
|
x = c - '0';
|
||||||
|
while (isdigit(c = readBlocking())) {
|
||||||
|
Serial.print(c);
|
||||||
|
x *= 10;
|
||||||
|
x += c - '0';
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout) {
|
||||||
|
uint16_t buffidx = 0;
|
||||||
|
boolean timeoutvalid = true;
|
||||||
|
if (timeout == 0) timeoutvalid = false;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (buffidx > maxbuff) {
|
||||||
|
//Serial.println(F("SPACE"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (Serial.available()) {
|
||||||
|
char c = Serial.read();
|
||||||
|
|
||||||
|
//Serial.print(c, HEX); Serial.print("#"); Serial.println(c);
|
||||||
|
|
||||||
|
if (c == '\r') continue;
|
||||||
|
if (c == 0xA) {
|
||||||
|
if (buffidx == 0) // the first 0x0A is ignored
|
||||||
|
continue;
|
||||||
|
|
||||||
|
timeout = 0; // the second 0x0A is the end of the line
|
||||||
|
timeoutvalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buff[buffidx] = c;
|
||||||
|
buffidx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeoutvalid && timeout == 0) {
|
||||||
|
//Serial.println(F("TIMEOUT"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
buff[buffidx] = 0; // null term
|
||||||
|
return buffidx;
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/**
|
||||||
|
* ___ ___ _ _ _ ___ __ ___ ___ ___ ___
|
||||||
|
* | __/ _ \| \| | /_\ ( _ )/ \( _ ) / __| _ \/ __|
|
||||||
|
* | _| (_) | .` |/ _ \ / _ \ () / _ \ | (_ | _/\__ \
|
||||||
|
* |_| \___/|_|\_/_/ \_\ \___/\__/\___/ \___|_| |___/
|
||||||
|
*
|
||||||
|
* This example is meant to work with the Adafruit
|
||||||
|
* FONA 808 or 3G Shield or Breakout
|
||||||
|
*
|
||||||
|
* Copyright: 2015 Adafruit
|
||||||
|
* Author: Todd Treece
|
||||||
|
* Licence: MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "Adafruit_FONA.h"
|
||||||
|
|
||||||
|
// standard pins for the shield, adjust as necessary
|
||||||
|
#define FONA_RX 2
|
||||||
|
#define FONA_TX 3
|
||||||
|
#define FONA_RST 4
|
||||||
|
|
||||||
|
// We default to using software serial. If you want to use hardware serial
|
||||||
|
// (because softserial isnt supported) comment out the following three lines
|
||||||
|
// and uncomment the HardwareSerial line
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
|
||||||
|
SoftwareSerial *fonaSerial = &fonaSS;
|
||||||
|
|
||||||
|
// Hardware serial is also possible!
|
||||||
|
// HardwareSerial *fonaSerial = &Serial1;
|
||||||
|
|
||||||
|
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
|
||||||
|
|
||||||
|
// Have a FONA 3G? use this object type instead
|
||||||
|
//Adafruit_FONA_3G fona = Adafruit_FONA_3G(FONA_RST);
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
while (! Serial);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("Adafruit FONA 808 & 3G GPS demo"));
|
||||||
|
Serial.println(F("Initializing FONA... (May take a few seconds)"));
|
||||||
|
|
||||||
|
fonaSerial->begin(4800);
|
||||||
|
if (! fona.begin(*fonaSerial)) {
|
||||||
|
Serial.println(F("Couldn't find FONA"));
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
Serial.println(F("FONA is OK"));
|
||||||
|
// Try to enable GPRS
|
||||||
|
|
||||||
|
|
||||||
|
Serial.println(F("Enabling GPS..."));
|
||||||
|
fona.enableGPS(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
float latitude, longitude, speed_kph, heading, speed_mph, altitude;
|
||||||
|
|
||||||
|
// if you ask for an altitude reading, getGPS will return false if there isn't a 3D fix
|
||||||
|
boolean gps_success = fona.getGPS(&latitude, &longitude, &speed_kph, &heading, &altitude);
|
||||||
|
|
||||||
|
if (gps_success) {
|
||||||
|
|
||||||
|
Serial.print("GPS lat:");
|
||||||
|
Serial.println(latitude, 6);
|
||||||
|
Serial.print("GPS long:");
|
||||||
|
Serial.println(longitude, 6);
|
||||||
|
Serial.print("GPS speed KPH:");
|
||||||
|
Serial.println(speed_kph);
|
||||||
|
Serial.print("GPS speed MPH:");
|
||||||
|
speed_mph = speed_kph * 0.621371192;
|
||||||
|
Serial.println(speed_mph);
|
||||||
|
Serial.print("GPS heading:");
|
||||||
|
Serial.println(heading);
|
||||||
|
Serial.print("GPS altitude:");
|
||||||
|
Serial.println(altitude);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Serial.println("Waiting for FONA GPS 3D fix...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fona 3G doesnt have GPRSlocation :/
|
||||||
|
if ((fona.type() == FONA3G_A) || (fona.type() == FONA3G_E))
|
||||||
|
return;
|
||||||
|
// Check for network, then GPRS
|
||||||
|
Serial.println(F("Checking for Cell network..."));
|
||||||
|
if (fona.getNetworkStatus() == 1) {
|
||||||
|
// network & GPRS? Great! Print out the GSM location to compare
|
||||||
|
boolean gsmloc_success = fona.getGSMLoc(&latitude, &longitude);
|
||||||
|
|
||||||
|
if (gsmloc_success) {
|
||||||
|
Serial.print("GSMLoc lat:");
|
||||||
|
Serial.println(latitude, 6);
|
||||||
|
Serial.print("GSMLoc long:");
|
||||||
|
Serial.println(longitude, 6);
|
||||||
|
} else {
|
||||||
|
Serial.println("GSM location failed...");
|
||||||
|
Serial.println(F("Disabling GPRS"));
|
||||||
|
fona.enableGPRS(false);
|
||||||
|
Serial.println(F("Enabling GPRS"));
|
||||||
|
if (!fona.enableGPRS(true)) {
|
||||||
|
Serial.println(F("Failed to turn GPRS on"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
// FONA Incoming Call Number Example
|
||||||
|
// Listens for a call and displays the phone number of the caller (if available).
|
||||||
|
// Use this example to add phone call detection to your own FONA sketch.
|
||||||
|
#include "Adafruit_FONA.h"
|
||||||
|
|
||||||
|
// Pins which are connected to the FONA.
|
||||||
|
// Note that this is different from FONAtest!
|
||||||
|
#define FONA_RX 3
|
||||||
|
#define FONA_TX 4
|
||||||
|
#define FONA_RST 5
|
||||||
|
|
||||||
|
// Note you need to map interrupt number to pin number
|
||||||
|
// for your board. On an Uno & Mega interrupt 0 is
|
||||||
|
// digital pin 2, and on a Leonardo interrupt 0 is
|
||||||
|
// digital pin 3. See this page for a complete table:
|
||||||
|
// http://arduino.cc/en/Reference/attachInterrupt
|
||||||
|
// Make sure this interrupt pin is connected to FONA RI!
|
||||||
|
#define FONA_RI_INTERRUPT 0
|
||||||
|
|
||||||
|
// We default to using software serial. If you want to use hardware serial
|
||||||
|
// (because softserial isnt supported) comment out the following three lines
|
||||||
|
// and uncomment the HardwareSerial line
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
|
||||||
|
SoftwareSerial *fonaSerial = &fonaSS;
|
||||||
|
|
||||||
|
// Hardware serial is also possible!
|
||||||
|
// HardwareSerial *fonaSerial = &Serial1;
|
||||||
|
|
||||||
|
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("FONA incoming call example"));
|
||||||
|
Serial.println(F("Initializing....(May take 3 seconds)"));
|
||||||
|
|
||||||
|
fonaSerial->begin(4800);
|
||||||
|
if (! fona.begin(*fonaSerial)) {
|
||||||
|
Serial.println(F("Couldn't find FONA"));
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
Serial.println(F("FONA is OK"));
|
||||||
|
|
||||||
|
// Enable incoming call notification.
|
||||||
|
if(fona.callerIdNotification(true, FONA_RI_INTERRUPT)) {
|
||||||
|
Serial.println(F("Caller id notification enabled."));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.println(F("Caller id notification disabled"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// Create a small string buffer to hold incoming call number.
|
||||||
|
char phone[32] = {0};
|
||||||
|
// Check for an incoming call. Will return true if a call is incoming.
|
||||||
|
if(fona.incomingCallNumber(phone)){
|
||||||
|
Serial.println(F("RING!"));
|
||||||
|
Serial.print(F("Phone Number: "));
|
||||||
|
Serial.println(phone);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* FONAConfig.h -- compile-time configuration
|
||||||
|
* This is part of the library for the Adafruit FONA Cellular Module
|
||||||
|
*
|
||||||
|
* Designed specifically to work with the Adafruit FONA
|
||||||
|
* ----> https://www.adafruit.com/products/1946
|
||||||
|
* ----> https://www.adafruit.com/products/1963
|
||||||
|
* ----> http://www.adafruit.com/products/2468
|
||||||
|
* ----> http://www.adafruit.com/products/2542
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
|
* products from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Pat Deegan, http://flyingcarsandstuff.com, for inclusion in
|
||||||
|
* the Adafruit_FONA_Library and released under the
|
||||||
|
* BSD license, all text above must be included in any redistribution.
|
||||||
|
*
|
||||||
|
* Created on: Jan 16, 2016
|
||||||
|
* Author: Pat Deegan
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_FONACONFIG_H_
|
||||||
|
#define ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_FONACONFIG_H_
|
||||||
|
|
||||||
|
/* ADAFRUIT_FONA_DEBUG
|
||||||
|
* When defined, will cause extensive debug output on the
|
||||||
|
* DebugStream set in the appropriate platform/ header.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ADAFRUIT_FONA_DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_FONACONFIG_H_ */
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* FONAExtIncludes.h -- system-wide includes
|
||||||
|
* This is part of the library for the Adafruit FONA Cellular Module
|
||||||
|
*
|
||||||
|
* Designed specifically to work with the Adafruit FONA
|
||||||
|
* ----> https://www.adafruit.com/products/1946
|
||||||
|
* ----> https://www.adafruit.com/products/1963
|
||||||
|
* ----> http://www.adafruit.com/products/2468
|
||||||
|
* ----> http://www.adafruit.com/products/2542
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
|
* products from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Pat Deegan, http://flyingcarsandstuff.com, for inclusion in
|
||||||
|
* the Adafruit_FONA_Library and released under the
|
||||||
|
* BSD license, all text above must be included in any redistribution.
|
||||||
|
*
|
||||||
|
* Created on: Jan 16, 2016
|
||||||
|
* Author: Pat Deegan
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_FONAEXTINCLUDES_H_
|
||||||
|
#define ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_FONAEXTINCLUDES_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "FONAConfig.h"
|
||||||
|
// include any system-wide includes required here
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_FONAEXTINCLUDES_H_ */
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* FONAPlatStd.h -- standard AVR/Arduino platform.
|
||||||
|
*
|
||||||
|
* This is part of the library for the Adafruit FONA Cellular Module
|
||||||
|
*
|
||||||
|
* Designed specifically to work with the Adafruit FONA
|
||||||
|
* ----> https://www.adafruit.com/products/1946
|
||||||
|
* ----> https://www.adafruit.com/products/1963
|
||||||
|
* ----> http://www.adafruit.com/products/2468
|
||||||
|
* ----> http://www.adafruit.com/products/2542
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
|
* products from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Pat Deegan, http://flyingcarsandstuff.com, for inclusion in
|
||||||
|
* the Adafruit_FONA_Library and released under the
|
||||||
|
* BSD license, all text above must be included in any redistribution.
|
||||||
|
*
|
||||||
|
* Created on: Jan 16, 2016
|
||||||
|
* Author: Pat Deegan
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_PLATFORM_FONAPLATSTD_H_
|
||||||
|
#define ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_PLATFORM_FONAPLATSTD_H_
|
||||||
|
|
||||||
|
#include "../FONAConfig.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if (ARDUINO >= 100)
|
||||||
|
#include "Arduino.h"
|
||||||
|
#if !defined(__SAM3X8E__) && !defined(ARDUINO_ARCH_SAMD) // Arduino Due doesn't support #include <SoftwareSerial.h>
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include "WProgram.h"
|
||||||
|
#include <NewSoftSerial.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__AVR__))
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#elif (defined(ESP8266))
|
||||||
|
#include <pgmspace.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DebugStream sets the Stream output to use
|
||||||
|
// for debug (only applies when ADAFRUIT_FONA_DEBUG
|
||||||
|
// is defined in config)
|
||||||
|
#define DebugStream Serial
|
||||||
|
|
||||||
|
#ifdef ADAFRUIT_FONA_DEBUG
|
||||||
|
// need to do some debugging...
|
||||||
|
#define DEBUG_PRINT(...) DebugStream.print(__VA_ARGS__)
|
||||||
|
#define DEBUG_PRINTLN(...) DebugStream.println(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// a few typedefs to keep things portable
|
||||||
|
typedef Stream FONAStreamType;
|
||||||
|
typedef const __FlashStringHelper * FONAFlashStringPtr;
|
||||||
|
|
||||||
|
#define prog_char char PROGMEM
|
||||||
|
|
||||||
|
#define prog_char_strcmp(a, b) strcmp_P((a), (b))
|
||||||
|
// define prog_char_strncmp(a, b, c) strncmp_P((a), (b), (c))
|
||||||
|
#define prog_char_strstr(a, b) strstr_P((a), (b))
|
||||||
|
#define prog_char_strlen(a) strlen_P((a))
|
||||||
|
#define prog_char_strcpy(to, fromprogmem) strcpy_P((to), (fromprogmem))
|
||||||
|
//define prog_char_strncpy(to, from, len) strncpy_P((to), (fromprogmem), (len))
|
||||||
|
|
||||||
|
#endif /* ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_PLATFORM_FONAPLATSTD_H_ */
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* FONAPlatform.h -- platform definitions includes.
|
||||||
|
*
|
||||||
|
* This is part of the library for the Adafruit FONA Cellular Module
|
||||||
|
*
|
||||||
|
* Designed specifically to work with the Adafruit FONA
|
||||||
|
* ----> https://www.adafruit.com/products/1946
|
||||||
|
* ----> https://www.adafruit.com/products/1963
|
||||||
|
* ----> http://www.adafruit.com/products/2468
|
||||||
|
* ----> http://www.adafruit.com/products/2542
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
|
* products from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Pat Deegan, http://flyingcarsandstuff.com, for inclusion in
|
||||||
|
* the Adafruit_FONA_Library and released under the
|
||||||
|
* BSD license, all text above must be included in any redistribution.
|
||||||
|
*
|
||||||
|
* Created on: Jan 16, 2016
|
||||||
|
* Author: Pat Deegan
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_PLATFORM_FONAPLATFORM_H_
|
||||||
|
#define ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_PLATFORM_FONAPLATFORM_H_
|
||||||
|
|
||||||
|
#include "../FONAConfig.h"
|
||||||
|
|
||||||
|
// only "standard" config supported in this release -- namely AVR-based arduino type affairs
|
||||||
|
#include "FONAPlatStd.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DEBUG_PRINT
|
||||||
|
// debug is disabled
|
||||||
|
|
||||||
|
#define DEBUG_PRINT(...)
|
||||||
|
#define DEBUG_PRINTLN(...)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef prog_char_strcmp
|
||||||
|
#define prog_char_strcmp(a, b) strcmp((a), (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef prog_char_strstr
|
||||||
|
#define prog_char_strstr(a, b) strstr((a), (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef prog_char_strlen
|
||||||
|
#define prog_char_strlen(a) strlen((a))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef prog_char_strcpy
|
||||||
|
#define prog_char_strcpy(to, fromprogmem) strcpy((to), (fromprogmem))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ADAFRUIT_FONA_LIBRARY_SRC_INCLUDES_PLATFORM_FONAPLATFORM_H_ */
|
|
@ -0,0 +1,9 @@
|
||||||
|
name=Adafruit FONA Library
|
||||||
|
version=1.3.5
|
||||||
|
author=Adafruit
|
||||||
|
maintainer=Adafruit <info@adafruit.com>
|
||||||
|
sentence=Arduino library for the Adafruit FONA
|
||||||
|
paragraph=Arduino library for the Adafruit FONA
|
||||||
|
category=Communication
|
||||||
|
url=https://github.com/adafruit/Adafruit_FONA
|
||||||
|
architectures=*
|
|
@ -0,0 +1,851 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Adafruit Industries
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_ARCH_SAMD)
|
||||||
|
static char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
|
||||||
|
char fmt[20];
|
||||||
|
sprintf(fmt, "%%%d.%df", width, prec);
|
||||||
|
sprintf(sout, fmt, val);
|
||||||
|
return sout;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
int strncasecmp(const char * str1, const char * str2, int len) {
|
||||||
|
int d = 0;
|
||||||
|
while(len--) {
|
||||||
|
int c1 = tolower(*str1++);
|
||||||
|
int c2 = tolower(*str2++);
|
||||||
|
if(((d = c1 - c2) != 0) || (c2 == '\0')) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void printBuffer(uint8_t *buffer, uint16_t len) {
|
||||||
|
DEBUG_PRINTER.print('\t');
|
||||||
|
for (uint16_t i=0; i<len; i++) {
|
||||||
|
if (isprint(buffer[i]))
|
||||||
|
DEBUG_PRINTER.write(buffer[i]);
|
||||||
|
else
|
||||||
|
DEBUG_PRINTER.print(" ");
|
||||||
|
DEBUG_PRINTER.print(F(" [0x"));
|
||||||
|
if (buffer[i] < 0x10)
|
||||||
|
DEBUG_PRINTER.print("0");
|
||||||
|
DEBUG_PRINTER.print(buffer[i],HEX);
|
||||||
|
DEBUG_PRINTER.print("], ");
|
||||||
|
if (i % 8 == 7) {
|
||||||
|
DEBUG_PRINTER.print("\n\t");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTER.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not used now, but might be useful in the future
|
||||||
|
static uint8_t *stringprint(uint8_t *p, char *s) {
|
||||||
|
uint16_t len = strlen(s);
|
||||||
|
p[0] = len >> 8; p++;
|
||||||
|
p[0] = len & 0xFF; p++;
|
||||||
|
memmove(p, s, len);
|
||||||
|
return p+len;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint8_t *stringprint(uint8_t *p, const char *s, uint16_t maxlen=0) {
|
||||||
|
// If maxlen is specified (has a non-zero value) then use it as the maximum
|
||||||
|
// length of the source string to write to the buffer. Otherwise write
|
||||||
|
// the entire source string.
|
||||||
|
uint16_t len = strlen(s);
|
||||||
|
if (maxlen > 0 && len > maxlen) {
|
||||||
|
len = maxlen;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
for (uint8_t i=0; i<len; i++) {
|
||||||
|
Serial.write(pgm_read_byte(s+i));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
p[0] = len >> 8; p++;
|
||||||
|
p[0] = len & 0xFF; p++;
|
||||||
|
strncpy((char *)p, s, len);
|
||||||
|
return p+len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Adafruit_MQTT Definition ////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Adafruit_MQTT::Adafruit_MQTT(const char *server,
|
||||||
|
uint16_t port,
|
||||||
|
const char *cid,
|
||||||
|
const char *user,
|
||||||
|
const char *pass) {
|
||||||
|
servername = server;
|
||||||
|
portnum = port;
|
||||||
|
clientid = cid;
|
||||||
|
username = user;
|
||||||
|
password = pass;
|
||||||
|
|
||||||
|
// reset subscriptions
|
||||||
|
for (uint8_t i=0; i<MAXSUBSCRIPTIONS; i++) {
|
||||||
|
subscriptions[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
will_topic = 0;
|
||||||
|
will_payload = 0;
|
||||||
|
will_qos = 0;
|
||||||
|
will_retain = 0;
|
||||||
|
|
||||||
|
packet_id_counter = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Adafruit_MQTT::Adafruit_MQTT(const char *server,
|
||||||
|
uint16_t port,
|
||||||
|
const char *user,
|
||||||
|
const char *pass) {
|
||||||
|
servername = server;
|
||||||
|
portnum = port;
|
||||||
|
clientid = "";
|
||||||
|
username = user;
|
||||||
|
password = pass;
|
||||||
|
|
||||||
|
// reset subscriptions
|
||||||
|
for (uint8_t i=0; i<MAXSUBSCRIPTIONS; i++) {
|
||||||
|
subscriptions[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
will_topic = 0;
|
||||||
|
will_payload = 0;
|
||||||
|
will_qos = 0;
|
||||||
|
will_retain = 0;
|
||||||
|
|
||||||
|
packet_id_counter = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t Adafruit_MQTT::connect() {
|
||||||
|
// Connect to the server.
|
||||||
|
if (!connectServer())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Construct and send connect packet.
|
||||||
|
uint8_t len = connectPacket(buffer);
|
||||||
|
if (!sendPacket(buffer, len))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Read connect response packet and verify it
|
||||||
|
len = readFullPacket(buffer, MAXBUFFERSIZE, CONNECT_TIMEOUT_MS);
|
||||||
|
if (len != 4)
|
||||||
|
return -1;
|
||||||
|
if ((buffer[0] != (MQTT_CTRL_CONNECTACK << 4)) || (buffer[1] != 2))
|
||||||
|
return -1;
|
||||||
|
if (buffer[3] != 0)
|
||||||
|
return buffer[3];
|
||||||
|
|
||||||
|
// Setup subscriptions once connected.
|
||||||
|
for (uint8_t i=0; i<MAXSUBSCRIPTIONS; i++) {
|
||||||
|
// Ignore subscriptions that aren't defined.
|
||||||
|
if (subscriptions[i] == 0) continue;
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
for (uint8_t retry=0; (retry<3) && !success; retry++) { // retry until we get a suback
|
||||||
|
// Construct and send subscription packet.
|
||||||
|
uint8_t len = subscribePacket(buffer, subscriptions[i]->topic, subscriptions[i]->qos);
|
||||||
|
if (!sendPacket(buffer, len))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(MQTT_PROTOCOL_LEVEL < 3) // older versions didn't suback
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Check for SUBACK if using MQTT 3.1.1 or higher
|
||||||
|
// TODO: The Server is permitted to start sending PUBLISH packets matching the
|
||||||
|
// Subscription before the Server sends the SUBACK Packet. (will really need to use callbacks - ada)
|
||||||
|
|
||||||
|
//Serial.println("\t**looking for suback");
|
||||||
|
if (processPacketsUntil(buffer, MQTT_CTRL_SUBACK, SUBACK_TIMEOUT_MS)) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! success) return -2; // failed to sub for some reason
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t Adafruit_MQTT::connect(const char *user, const char *pass)
|
||||||
|
{
|
||||||
|
username = user;
|
||||||
|
password = pass;
|
||||||
|
return connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Adafruit_MQTT::processPacketsUntil(uint8_t *buffer, uint8_t waitforpackettype, uint16_t timeout) {
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
len = readFullPacket(buffer, MAXBUFFERSIZE, timeout);
|
||||||
|
|
||||||
|
if(len == 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((buffer[0] >> 4) == waitforpackettype)
|
||||||
|
{
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_PRINTLN(F("Dropped a packet"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Adafruit_MQTT::readFullPacket(uint8_t *buffer, uint16_t maxsize, uint16_t timeout) {
|
||||||
|
// will read a packet and Do The Right Thing with length
|
||||||
|
uint8_t *pbuff = buffer;
|
||||||
|
|
||||||
|
uint8_t rlen;
|
||||||
|
|
||||||
|
// read the packet type:
|
||||||
|
rlen = readPacket(pbuff, 1, timeout);
|
||||||
|
if (rlen != 1) return 0;
|
||||||
|
|
||||||
|
DEBUG_PRINT(F("Packet Type:\t")); DEBUG_PRINTBUFFER(pbuff, rlen);
|
||||||
|
pbuff++;
|
||||||
|
|
||||||
|
uint32_t value = 0;
|
||||||
|
uint32_t multiplier = 1;
|
||||||
|
uint8_t encodedByte;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rlen = readPacket(pbuff, 1, timeout);
|
||||||
|
if (rlen != 1) return 0;
|
||||||
|
encodedByte = pbuff[0]; // save the last read val
|
||||||
|
pbuff++; // get ready for reading the next byte
|
||||||
|
uint32_t intermediate = encodedByte & 0x7F;
|
||||||
|
intermediate *= multiplier;
|
||||||
|
value += intermediate;
|
||||||
|
multiplier *= 128;
|
||||||
|
if (multiplier > (128UL*128UL*128UL)) {
|
||||||
|
DEBUG_PRINT(F("Malformed packet len\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} while (encodedByte & 0x80);
|
||||||
|
|
||||||
|
DEBUG_PRINT(F("Packet Length:\t")); DEBUG_PRINTLN(value);
|
||||||
|
|
||||||
|
if (value > (maxsize - (pbuff-buffer) - 1)) {
|
||||||
|
DEBUG_PRINTLN(F("Packet too big for buffer"));
|
||||||
|
rlen = readPacket(pbuff, (maxsize - (pbuff-buffer) - 1), timeout);
|
||||||
|
} else {
|
||||||
|
rlen = readPacket(pbuff, value, timeout);
|
||||||
|
}
|
||||||
|
//DEBUG_PRINT(F("Remaining packet:\t")); DEBUG_PRINTBUFFER(pbuff, rlen);
|
||||||
|
|
||||||
|
return ((pbuff - buffer)+rlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
const __FlashStringHelper* Adafruit_MQTT::connectErrorString(int8_t code) {
|
||||||
|
switch (code) {
|
||||||
|
case 1: return F("The Server does not support the level of the MQTT protocol requested");
|
||||||
|
case 2: return F("The Client identifier is correct UTF-8 but not allowed by the Server");
|
||||||
|
case 3: return F("The MQTT service is unavailable");
|
||||||
|
case 4: return F("The data in the user name or password is malformed");
|
||||||
|
case 5: return F("Not authorized to connect");
|
||||||
|
case 6: return F("Exceeded reconnect rate limit. Please try again later.");
|
||||||
|
case 7: return F("You have been banned from connecting. Please contact the MQTT server administrator for more details.");
|
||||||
|
case -1: return F("Connection failed");
|
||||||
|
case -2: return F("Failed to subscribe");
|
||||||
|
default: return F("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT::disconnect() {
|
||||||
|
|
||||||
|
// Construct and send disconnect packet.
|
||||||
|
uint8_t len = disconnectPacket(buffer);
|
||||||
|
if (! sendPacket(buffer, len))
|
||||||
|
DEBUG_PRINTLN(F("Unable to send disconnect packet"));
|
||||||
|
|
||||||
|
return disconnectServer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Adafruit_MQTT::publish(const char *topic, const char *data, uint8_t qos) {
|
||||||
|
return publish(topic, (uint8_t*)(data), strlen(data), qos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT::publish(const char *topic, uint8_t *data, uint16_t bLen, uint8_t qos) {
|
||||||
|
// Construct and send publish packet.
|
||||||
|
uint16_t len = publishPacket(buffer, topic, data, bLen, qos);
|
||||||
|
if (!sendPacket(buffer, len))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If QOS level is high enough verify the response packet.
|
||||||
|
if (qos > 0) {
|
||||||
|
len = readFullPacket(buffer, MAXBUFFERSIZE, PUBLISH_TIMEOUT_MS);
|
||||||
|
DEBUG_PRINT(F("Publish QOS1+ reply:\t"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
if (len != 4)
|
||||||
|
return false;
|
||||||
|
if ((buffer[0] >> 4) != MQTT_CTRL_PUBACK)
|
||||||
|
return false;
|
||||||
|
uint16_t packnum = buffer[2];
|
||||||
|
packnum <<= 8;
|
||||||
|
packnum |= buffer[3];
|
||||||
|
|
||||||
|
// we increment the packet_id_counter right after publishing so inc here too to match
|
||||||
|
packnum++;
|
||||||
|
if (packnum != packet_id_counter)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT::will(const char *topic, const char *payload, uint8_t qos, uint8_t retain) {
|
||||||
|
|
||||||
|
if (connected()) {
|
||||||
|
DEBUG_PRINT(F("Will defined after connect"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
will_topic = topic;
|
||||||
|
will_payload = payload;
|
||||||
|
will_qos = qos;
|
||||||
|
will_retain = retain;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT::subscribe(Adafruit_MQTT_Subscribe *sub) {
|
||||||
|
uint8_t i;
|
||||||
|
// see if we are already subscribed
|
||||||
|
for (i=0; i<MAXSUBSCRIPTIONS; i++) {
|
||||||
|
if (subscriptions[i] == sub) {
|
||||||
|
DEBUG_PRINTLN(F("Already subscribed"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i==MAXSUBSCRIPTIONS) { // add to subscriptionlist
|
||||||
|
for (i=0; i<MAXSUBSCRIPTIONS; i++) {
|
||||||
|
if (subscriptions[i] == 0) {
|
||||||
|
DEBUG_PRINT(F("Added sub ")); DEBUG_PRINTLN(i);
|
||||||
|
subscriptions[i] = sub;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINTLN(F("no more subscription space :("));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT::unsubscribe(Adafruit_MQTT_Subscribe *sub) {
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
// see if we are already subscribed
|
||||||
|
for (i=0; i<MAXSUBSCRIPTIONS; i++) {
|
||||||
|
|
||||||
|
if (subscriptions[i] == sub) {
|
||||||
|
|
||||||
|
DEBUG_PRINTLN(F("Found matching subscription and attempting to unsubscribe."));
|
||||||
|
|
||||||
|
// Construct and send unsubscribe packet.
|
||||||
|
uint8_t len = unsubscribePacket(buffer, subscriptions[i]->topic);
|
||||||
|
|
||||||
|
// sending unsubscribe failed
|
||||||
|
if (! sendPacket(buffer, len))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// if QoS for this subscription is 1 or 2, we need
|
||||||
|
// to wait for the unsuback to confirm unsubscription
|
||||||
|
if(subscriptions[i]->qos > 0 && MQTT_PROTOCOL_LEVEL > 3) {
|
||||||
|
|
||||||
|
// wait for UNSUBACK
|
||||||
|
len = readFullPacket(buffer, MAXBUFFERSIZE, CONNECT_TIMEOUT_MS);
|
||||||
|
DEBUG_PRINT(F("UNSUBACK:\t"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
|
||||||
|
if ((len != 5) || (buffer[0] != (MQTT_CTRL_UNSUBACK << 4))) {
|
||||||
|
return false; // failure to unsubscribe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions[i] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscription not found, so we are unsubscribed
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MQTT::processPackets(int16_t timeout) {
|
||||||
|
|
||||||
|
uint32_t elapsed = 0, endtime, starttime = millis();
|
||||||
|
|
||||||
|
while (elapsed < (uint32_t)timeout) {
|
||||||
|
Adafruit_MQTT_Subscribe *sub = readSubscription(timeout - elapsed);
|
||||||
|
if (sub) {
|
||||||
|
//Serial.println("**** sub packet received");
|
||||||
|
if (sub->callback_uint32t != NULL) {
|
||||||
|
// huh lets do the callback in integer mode
|
||||||
|
uint32_t data = 0;
|
||||||
|
data = atoi((char *)sub->lastread);
|
||||||
|
//Serial.print("*** calling int callback with : "); Serial.println(data);
|
||||||
|
sub->callback_uint32t(data);
|
||||||
|
}
|
||||||
|
else if (sub->callback_double != NULL) {
|
||||||
|
// huh lets do the callback in doublefloat mode
|
||||||
|
double data = 0;
|
||||||
|
data = atof((char *)sub->lastread);
|
||||||
|
//Serial.print("*** calling double callback with : "); Serial.println(data);
|
||||||
|
sub->callback_double(data);
|
||||||
|
}
|
||||||
|
else if (sub->callback_buffer != NULL) {
|
||||||
|
// huh lets do the callback in buffer mode
|
||||||
|
//Serial.print("*** calling buffer callback with : "); Serial.println((char *)sub->lastread);
|
||||||
|
sub->callback_buffer((char *)sub->lastread, sub->datalen);
|
||||||
|
}
|
||||||
|
else if (sub->callback_io != NULL) {
|
||||||
|
// huh lets do the callback in io mode
|
||||||
|
//Serial.print("*** calling io instance callback with : "); Serial.println((char *)sub->lastread);
|
||||||
|
((sub->io_mqtt)->*(sub->callback_io))((char *)sub->lastread, sub->datalen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track over elapsed time
|
||||||
|
endtime = millis();
|
||||||
|
if (endtime < starttime) {
|
||||||
|
starttime = endtime; // looped around!")
|
||||||
|
}
|
||||||
|
elapsed += (endtime - starttime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adafruit_MQTT_Subscribe *Adafruit_MQTT::readSubscription(int16_t timeout) {
|
||||||
|
uint16_t i, topiclen, datalen;
|
||||||
|
|
||||||
|
// Check if data is available to read.
|
||||||
|
uint16_t len = readFullPacket(buffer, MAXBUFFERSIZE, timeout); // return one full packet
|
||||||
|
if (!len)
|
||||||
|
return NULL; // No data available, just quit.
|
||||||
|
DEBUG_PRINT("Packet len: "); DEBUG_PRINTLN(len);
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
|
||||||
|
if (len<3) return NULL;
|
||||||
|
if ((buffer[0] & 0xF0) != (MQTT_CTRL_PUBLISH) << 4) return NULL;
|
||||||
|
|
||||||
|
// Parse out length of packet.
|
||||||
|
topiclen = buffer[3];
|
||||||
|
DEBUG_PRINT(F("Looking for subscription len ")); DEBUG_PRINTLN(topiclen);
|
||||||
|
|
||||||
|
// Find subscription associated with this packet.
|
||||||
|
for (i=0; i<MAXSUBSCRIPTIONS; i++) {
|
||||||
|
if (subscriptions[i]) {
|
||||||
|
// Skip this subscription if its name length isn't the same as the
|
||||||
|
// received topic name.
|
||||||
|
if (strlen(subscriptions[i]->topic) != topiclen)
|
||||||
|
continue;
|
||||||
|
// Stop if the subscription topic matches the received topic. Be careful
|
||||||
|
// to make comparison case insensitive.
|
||||||
|
if (strncasecmp((char*)buffer+4, subscriptions[i]->topic, topiclen) == 0) {
|
||||||
|
DEBUG_PRINT(F("Found sub #")); DEBUG_PRINTLN(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i==MAXSUBSCRIPTIONS) return NULL; // matching sub not found ???
|
||||||
|
|
||||||
|
uint8_t packet_id_len = 0;
|
||||||
|
uint16_t packetid = 0;
|
||||||
|
// Check if it is QoS 1, TODO: we dont support QoS 2
|
||||||
|
if ((buffer[0] & 0x6) == 0x2) {
|
||||||
|
packet_id_len = 2;
|
||||||
|
packetid = buffer[topiclen+4];
|
||||||
|
packetid <<= 8;
|
||||||
|
packetid |= buffer[topiclen+5];
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero out the old data
|
||||||
|
memset(subscriptions[i]->lastread, 0, SUBSCRIPTIONDATALEN);
|
||||||
|
|
||||||
|
datalen = len - topiclen - packet_id_len - 4;
|
||||||
|
if (datalen > SUBSCRIPTIONDATALEN) {
|
||||||
|
datalen = SUBSCRIPTIONDATALEN-1; // cut it off
|
||||||
|
}
|
||||||
|
// extract out just the data, into the subscription object itself
|
||||||
|
memmove(subscriptions[i]->lastread, buffer+4+topiclen+packet_id_len, datalen);
|
||||||
|
subscriptions[i]->datalen = datalen;
|
||||||
|
DEBUG_PRINT(F("Data len: ")); DEBUG_PRINTLN(datalen);
|
||||||
|
DEBUG_PRINT(F("Data: ")); DEBUG_PRINTLN((char *)subscriptions[i]->lastread);
|
||||||
|
|
||||||
|
if ((MQTT_PROTOCOL_LEVEL > 3) &&(buffer[0] & 0x6) == 0x2) {
|
||||||
|
uint8_t ackpacket[4];
|
||||||
|
|
||||||
|
// Construct and send puback packet.
|
||||||
|
uint8_t len = pubackPacket(ackpacket, packetid);
|
||||||
|
if (!sendPacket(ackpacket, len))
|
||||||
|
DEBUG_PRINT(F("Failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the valid matching subscription
|
||||||
|
return subscriptions[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MQTT::flushIncoming(uint16_t timeout) {
|
||||||
|
// flush input!
|
||||||
|
DEBUG_PRINTLN(F("Flushing input buffer"));
|
||||||
|
while (readPacket(buffer, MAXBUFFERSIZE, timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT::ping(uint8_t num) {
|
||||||
|
//flushIncoming(100);
|
||||||
|
|
||||||
|
while (num--) {
|
||||||
|
// Construct and send ping packet.
|
||||||
|
uint8_t len = pingPacket(buffer);
|
||||||
|
if (!sendPacket(buffer, len))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Process ping reply.
|
||||||
|
len = processPacketsUntil(buffer, MQTT_CTRL_PINGRESP, PING_TIMEOUT_MS);
|
||||||
|
if (buffer[0] == (MQTT_CTRL_PINGRESP << 4))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packet Generation Functions /////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// The current MQTT spec is 3.1.1 and available here:
|
||||||
|
// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
|
||||||
|
// However this connect packet and code follows the MQTT 3.1 spec here (some
|
||||||
|
// small differences in the protocol):
|
||||||
|
// http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect
|
||||||
|
uint8_t Adafruit_MQTT::connectPacket(uint8_t *packet) {
|
||||||
|
uint8_t *p = packet;
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
// fixed header, connection messsage no flags
|
||||||
|
p[0] = (MQTT_CTRL_CONNECT << 4) | 0x0;
|
||||||
|
p+=2;
|
||||||
|
// fill in packet[1] last
|
||||||
|
|
||||||
|
#if MQTT_PROTOCOL_LEVEL == 3
|
||||||
|
p = stringprint(p, "MQIsdp");
|
||||||
|
#elif MQTT_PROTOCOL_LEVEL == 4
|
||||||
|
p = stringprint(p, "MQTT");
|
||||||
|
#else
|
||||||
|
#error "MQTT level not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
p[0] = MQTT_PROTOCOL_LEVEL;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
// always clean the session
|
||||||
|
p[0] = MQTT_CONN_CLEANSESSION;
|
||||||
|
|
||||||
|
// set the will flags if needed
|
||||||
|
if (will_topic && pgm_read_byte(will_topic) != 0) {
|
||||||
|
|
||||||
|
p[0] |= MQTT_CONN_WILLFLAG;
|
||||||
|
|
||||||
|
if(will_qos == 1)
|
||||||
|
p[0] |= MQTT_CONN_WILLQOS_1;
|
||||||
|
else if(will_qos == 2)
|
||||||
|
p[0] |= MQTT_CONN_WILLQOS_2;
|
||||||
|
|
||||||
|
if(will_retain == 1)
|
||||||
|
p[0] |= MQTT_CONN_WILLRETAIN;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgm_read_byte(username) != 0)
|
||||||
|
p[0] |= MQTT_CONN_USERNAMEFLAG;
|
||||||
|
if (pgm_read_byte(password) != 0)
|
||||||
|
p[0] |= MQTT_CONN_PASSWORDFLAG;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
p[0] = MQTT_CONN_KEEPALIVE >> 8;
|
||||||
|
p++;
|
||||||
|
p[0] = MQTT_CONN_KEEPALIVE & 0xFF;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if(MQTT_PROTOCOL_LEVEL == 3) {
|
||||||
|
p = stringprint(p, clientid, 23); // Limit client ID to first 23 characters.
|
||||||
|
} else {
|
||||||
|
if (pgm_read_byte(clientid) != 0) {
|
||||||
|
p = stringprint(p, clientid);
|
||||||
|
} else {
|
||||||
|
p[0] = 0x0;
|
||||||
|
p++;
|
||||||
|
p[0] = 0x0;
|
||||||
|
p++;
|
||||||
|
DEBUG_PRINTLN(F("SERVER GENERATING CLIENT ID"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (will_topic && pgm_read_byte(will_topic) != 0) {
|
||||||
|
p = stringprint(p, will_topic);
|
||||||
|
p = stringprint(p, will_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgm_read_byte(username) != 0) {
|
||||||
|
p = stringprint(p, username);
|
||||||
|
}
|
||||||
|
if (pgm_read_byte(password) != 0) {
|
||||||
|
p = stringprint(p, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
len = p - packet;
|
||||||
|
|
||||||
|
packet[1] = len-2; // don't include the 2 bytes of fixed header data
|
||||||
|
DEBUG_PRINTLN(F("MQTT connect packet:"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// as per http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718040
|
||||||
|
uint16_t Adafruit_MQTT::publishPacket(uint8_t *packet, const char *topic,
|
||||||
|
uint8_t *data, uint16_t bLen, uint8_t qos) {
|
||||||
|
uint8_t *p = packet;
|
||||||
|
uint16_t len=0;
|
||||||
|
|
||||||
|
// calc length of non-header data
|
||||||
|
len += 2; // two bytes to set the topic size
|
||||||
|
len += strlen(topic); // topic length
|
||||||
|
if(qos > 0) {
|
||||||
|
len += 2; // qos packet id
|
||||||
|
}
|
||||||
|
len += bLen; // payload length
|
||||||
|
|
||||||
|
// Now you can start generating the packet!
|
||||||
|
p[0] = MQTT_CTRL_PUBLISH << 4 | qos << 1;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
// fill in packet[1] last
|
||||||
|
do {
|
||||||
|
uint8_t encodedByte = len % 128;
|
||||||
|
len /= 128;
|
||||||
|
// if there are more data to encode, set the top bit of this byte
|
||||||
|
if ( len > 0 ) {
|
||||||
|
encodedByte |= 0x80;
|
||||||
|
}
|
||||||
|
p[0] = encodedByte;
|
||||||
|
p++;
|
||||||
|
} while ( len > 0 );
|
||||||
|
|
||||||
|
// topic comes before packet identifier
|
||||||
|
p = stringprint(p, topic);
|
||||||
|
|
||||||
|
// add packet identifier. used for checking PUBACK in QOS > 0
|
||||||
|
if(qos > 0) {
|
||||||
|
p[0] = (packet_id_counter >> 8) & 0xFF;
|
||||||
|
p[1] = packet_id_counter & 0xFF;
|
||||||
|
p+=2;
|
||||||
|
|
||||||
|
// increment the packet id
|
||||||
|
packet_id_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(p, data, bLen);
|
||||||
|
p+= bLen;
|
||||||
|
len = p - packet;
|
||||||
|
DEBUG_PRINTLN(F("MQTT publish packet:"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Adafruit_MQTT::subscribePacket(uint8_t *packet, const char *topic,
|
||||||
|
uint8_t qos) {
|
||||||
|
uint8_t *p = packet;
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
p[0] = MQTT_CTRL_SUBSCRIBE << 4 | MQTT_QOS_1 << 1;
|
||||||
|
// fill in packet[1] last
|
||||||
|
p+=2;
|
||||||
|
|
||||||
|
// packet identifier. used for checking SUBACK
|
||||||
|
p[0] = (packet_id_counter >> 8) & 0xFF;
|
||||||
|
p[1] = packet_id_counter & 0xFF;
|
||||||
|
p+=2;
|
||||||
|
|
||||||
|
// increment the packet id
|
||||||
|
packet_id_counter++;
|
||||||
|
|
||||||
|
p = stringprint(p, topic);
|
||||||
|
|
||||||
|
p[0] = qos;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
len = p - packet;
|
||||||
|
packet[1] = len-2; // don't include the 2 bytes of fixed header data
|
||||||
|
DEBUG_PRINTLN(F("MQTT subscription packet:"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t Adafruit_MQTT::unsubscribePacket(uint8_t *packet, const char *topic) {
|
||||||
|
|
||||||
|
uint8_t *p = packet;
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
p[0] = MQTT_CTRL_UNSUBSCRIBE << 4 | 0x1;
|
||||||
|
// fill in packet[1] last
|
||||||
|
p+=2;
|
||||||
|
|
||||||
|
// packet identifier. used for checking UNSUBACK
|
||||||
|
p[0] = (packet_id_counter >> 8) & 0xFF;
|
||||||
|
p[1] = packet_id_counter & 0xFF;
|
||||||
|
p+=2;
|
||||||
|
|
||||||
|
// increment the packet id
|
||||||
|
packet_id_counter++;
|
||||||
|
|
||||||
|
p = stringprint(p, topic);
|
||||||
|
|
||||||
|
len = p - packet;
|
||||||
|
packet[1] = len-2; // don't include the 2 bytes of fixed header data
|
||||||
|
DEBUG_PRINTLN(F("MQTT unsubscription packet:"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
return len;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Adafruit_MQTT::pingPacket(uint8_t *packet) {
|
||||||
|
packet[0] = MQTT_CTRL_PINGREQ << 4;
|
||||||
|
packet[1] = 0;
|
||||||
|
DEBUG_PRINTLN(F("MQTT ping packet:"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, 2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Adafruit_MQTT::pubackPacket(uint8_t *packet, uint16_t packetid) {
|
||||||
|
packet[0] = MQTT_CTRL_PUBACK << 4;
|
||||||
|
packet[1] = 2;
|
||||||
|
packet[2] = packetid >> 8;
|
||||||
|
packet[3] = packetid;
|
||||||
|
DEBUG_PRINTLN(F("MQTT puback packet:"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, 4);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Adafruit_MQTT::disconnectPacket(uint8_t *packet) {
|
||||||
|
packet[0] = MQTT_CTRL_DISCONNECT << 4;
|
||||||
|
packet[1] = 0;
|
||||||
|
DEBUG_PRINTLN(F("MQTT disconnect packet:"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, 2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adafruit_MQTT_Publish Definition ////////////////////////////////////////////
|
||||||
|
|
||||||
|
Adafruit_MQTT_Publish::Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver,
|
||||||
|
const char *feed, uint8_t q) {
|
||||||
|
mqtt = mqttserver;
|
||||||
|
topic = feed;
|
||||||
|
qos = q;
|
||||||
|
}
|
||||||
|
bool Adafruit_MQTT_Publish::publish(int32_t i) {
|
||||||
|
char payload[12];
|
||||||
|
ltoa(i, payload, 10);
|
||||||
|
return mqtt->publish(topic, payload, qos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT_Publish::publish(uint32_t i) {
|
||||||
|
char payload[11];
|
||||||
|
ultoa(i, payload, 10);
|
||||||
|
return mqtt->publish(topic, payload, qos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT_Publish::publish(double f, uint8_t precision) {
|
||||||
|
char payload[41]; // Need to technically hold float max, 39 digits and minus sign.
|
||||||
|
dtostrf(f, 0, precision, payload);
|
||||||
|
return mqtt->publish(topic, payload, qos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT_Publish::publish(const char *payload) {
|
||||||
|
return mqtt->publish(topic, payload, qos);
|
||||||
|
}
|
||||||
|
|
||||||
|
//publish buffer of arbitrary length
|
||||||
|
bool Adafruit_MQTT_Publish::publish(uint8_t *payload, uint16_t bLen) {
|
||||||
|
|
||||||
|
return mqtt->publish(topic, payload, bLen, qos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Adafruit_MQTT_Subscribe Definition //////////////////////////////////////////
|
||||||
|
|
||||||
|
Adafruit_MQTT_Subscribe::Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver,
|
||||||
|
const char *feed, uint8_t q) {
|
||||||
|
mqtt = mqttserver;
|
||||||
|
topic = feed;
|
||||||
|
qos = q;
|
||||||
|
datalen = 0;
|
||||||
|
callback_uint32t = 0;
|
||||||
|
callback_buffer = 0;
|
||||||
|
callback_double = 0;
|
||||||
|
callback_io = 0;
|
||||||
|
io_mqtt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MQTT_Subscribe::setCallback(SubscribeCallbackUInt32Type cb) {
|
||||||
|
callback_uint32t = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MQTT_Subscribe::setCallback(SubscribeCallbackDoubleType cb) {
|
||||||
|
callback_double = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MQTT_Subscribe::setCallback(SubscribeCallbackBufferType cb) {
|
||||||
|
callback_buffer = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MQTT_Subscribe::setCallback(AdafruitIO_MQTT *io, SubscribeCallbackIOType cb) {
|
||||||
|
callback_io = cb;
|
||||||
|
io_mqtt= io;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_MQTT_Subscribe::removeCallback(void) {
|
||||||
|
callback_uint32t = 0;
|
||||||
|
callback_buffer = 0;
|
||||||
|
callback_double = 0;
|
||||||
|
callback_io = 0;
|
||||||
|
io_mqtt = 0;
|
||||||
|
}
|
|
@ -0,0 +1,302 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Adafruit Industries
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
#ifndef _ADAFRUIT_MQTT_H_
|
||||||
|
#define _ADAFRUIT_MQTT_H_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_STM32_FEATHER)
|
||||||
|
#define strncpy_P(dest, src, len) strncpy((dest), (src), (len))
|
||||||
|
#define strncasecmp_P(f1, f2, len) strncasecmp((f1), (f2), (len))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ADAFRUIT_MQTT_VERSION_MAJOR 0
|
||||||
|
#define ADAFRUIT_MQTT_VERSION_MINOR 17
|
||||||
|
#define ADAFRUIT_MQTT_VERSION_PATCH 0
|
||||||
|
|
||||||
|
// Uncomment/comment to turn on/off debug output messages.
|
||||||
|
//#define MQTT_DEBUG
|
||||||
|
// Uncomment/comment to turn on/off error output messages.
|
||||||
|
#define MQTT_ERROR
|
||||||
|
|
||||||
|
// Set where debug messages will be printed.
|
||||||
|
#define DEBUG_PRINTER Serial
|
||||||
|
// If using something like Zero or Due, change the above to SerialUSB
|
||||||
|
|
||||||
|
// Define actual debug output functions when necessary.
|
||||||
|
#ifdef MQTT_DEBUG
|
||||||
|
#define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); }
|
||||||
|
#define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); }
|
||||||
|
#define DEBUG_PRINTBUFFER(buffer, len) { printBuffer(buffer, len); }
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINT(...) {}
|
||||||
|
#define DEBUG_PRINTLN(...) {}
|
||||||
|
#define DEBUG_PRINTBUFFER(buffer, len) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MQTT_ERROR
|
||||||
|
#define ERROR_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); }
|
||||||
|
#define ERROR_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); }
|
||||||
|
#define ERROR_PRINTBUFFER(buffer, len) { printBuffer(buffer, len); }
|
||||||
|
#else
|
||||||
|
#define ERROR_PRINT(...) {}
|
||||||
|
#define ERROR_PRINTLN(...) {}
|
||||||
|
#define ERROR_PRINTBUFFER(buffer, len) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Use 3 (MQTT 3.0) or 4 (MQTT 3.1.1)
|
||||||
|
#define MQTT_PROTOCOL_LEVEL 4
|
||||||
|
|
||||||
|
#define MQTT_CTRL_CONNECT 0x1
|
||||||
|
#define MQTT_CTRL_CONNECTACK 0x2
|
||||||
|
#define MQTT_CTRL_PUBLISH 0x3
|
||||||
|
#define MQTT_CTRL_PUBACK 0x4
|
||||||
|
#define MQTT_CTRL_PUBREC 0x5
|
||||||
|
#define MQTT_CTRL_PUBREL 0x6
|
||||||
|
#define MQTT_CTRL_PUBCOMP 0x7
|
||||||
|
#define MQTT_CTRL_SUBSCRIBE 0x8
|
||||||
|
#define MQTT_CTRL_SUBACK 0x9
|
||||||
|
#define MQTT_CTRL_UNSUBSCRIBE 0xA
|
||||||
|
#define MQTT_CTRL_UNSUBACK 0xB
|
||||||
|
#define MQTT_CTRL_PINGREQ 0xC
|
||||||
|
#define MQTT_CTRL_PINGRESP 0xD
|
||||||
|
#define MQTT_CTRL_DISCONNECT 0xE
|
||||||
|
|
||||||
|
#define MQTT_QOS_1 0x1
|
||||||
|
#define MQTT_QOS_0 0x0
|
||||||
|
|
||||||
|
#define CONNECT_TIMEOUT_MS 6000
|
||||||
|
#define PUBLISH_TIMEOUT_MS 500
|
||||||
|
#define PING_TIMEOUT_MS 500
|
||||||
|
#define SUBACK_TIMEOUT_MS 500
|
||||||
|
|
||||||
|
// Adjust as necessary, in seconds. Default to 5 minutes.
|
||||||
|
#define MQTT_CONN_KEEPALIVE 300
|
||||||
|
|
||||||
|
// Largest full packet we're able to send.
|
||||||
|
// Need to be able to store at least ~90 chars for a connect packet with full
|
||||||
|
// 23 char client ID.
|
||||||
|
#define MAXBUFFERSIZE (150)
|
||||||
|
|
||||||
|
#define MQTT_CONN_USERNAMEFLAG 0x80
|
||||||
|
#define MQTT_CONN_PASSWORDFLAG 0x40
|
||||||
|
#define MQTT_CONN_WILLRETAIN 0x20
|
||||||
|
#define MQTT_CONN_WILLQOS_1 0x08
|
||||||
|
#define MQTT_CONN_WILLQOS_2 0x18
|
||||||
|
#define MQTT_CONN_WILLFLAG 0x04
|
||||||
|
#define MQTT_CONN_CLEANSESSION 0x02
|
||||||
|
|
||||||
|
// how much data we save in a subscription object
|
||||||
|
// and how many subscriptions we want to be able to track.
|
||||||
|
#if defined (__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__)
|
||||||
|
#define MAXSUBSCRIPTIONS 5
|
||||||
|
#define SUBSCRIPTIONDATALEN 20
|
||||||
|
#else
|
||||||
|
#define MAXSUBSCRIPTIONS 15
|
||||||
|
#define SUBSCRIPTIONDATALEN 100
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class AdafruitIO_MQTT; // forward decl
|
||||||
|
|
||||||
|
//Function pointer that returns an int
|
||||||
|
typedef void (*SubscribeCallbackUInt32Type)(uint32_t);
|
||||||
|
// returns a double
|
||||||
|
typedef void (*SubscribeCallbackDoubleType)(double);
|
||||||
|
// returns a chunk of raw data
|
||||||
|
typedef void (*SubscribeCallbackBufferType)(char *str, uint16_t len);
|
||||||
|
// returns an io data wrapper instance
|
||||||
|
typedef void (AdafruitIO_MQTT::*SubscribeCallbackIOType)(char *str, uint16_t len);
|
||||||
|
|
||||||
|
extern void printBuffer(uint8_t *buffer, uint16_t len);
|
||||||
|
|
||||||
|
class Adafruit_MQTT_Subscribe; // forward decl
|
||||||
|
|
||||||
|
class Adafruit_MQTT {
|
||||||
|
public:
|
||||||
|
Adafruit_MQTT(const char *server,
|
||||||
|
uint16_t port,
|
||||||
|
const char *cid,
|
||||||
|
const char *user,
|
||||||
|
const char *pass);
|
||||||
|
|
||||||
|
Adafruit_MQTT(const char *server,
|
||||||
|
uint16_t port,
|
||||||
|
const char *user = "",
|
||||||
|
const char *pass = "");
|
||||||
|
virtual ~Adafruit_MQTT() {}
|
||||||
|
|
||||||
|
// Connect to the MQTT server. Returns 0 on success, otherwise an error code
|
||||||
|
// that indicates something went wrong:
|
||||||
|
// -1 = Error connecting to server
|
||||||
|
// 1 = Wrong protocol
|
||||||
|
// 2 = ID rejected
|
||||||
|
// 3 = Server unavailable
|
||||||
|
// 4 = Bad username or password
|
||||||
|
// 5 = Not authenticated
|
||||||
|
// 6 = Failed to subscribe
|
||||||
|
// Use connectErrorString() to get a printable string version of the
|
||||||
|
// error.
|
||||||
|
int8_t connect();
|
||||||
|
int8_t connect(const char *user, const char *pass);
|
||||||
|
|
||||||
|
// Return a printable string version of the error code returned by
|
||||||
|
// connect(). This returns a __FlashStringHelper*, which points to a
|
||||||
|
// string stored in flash, but can be directly passed to e.g.
|
||||||
|
// Serial.println without any further processing.
|
||||||
|
const __FlashStringHelper* connectErrorString(int8_t code);
|
||||||
|
|
||||||
|
// Sends MQTT disconnect packet and calls disconnectServer()
|
||||||
|
bool disconnect();
|
||||||
|
|
||||||
|
// Return true if connected to the MQTT server, otherwise false.
|
||||||
|
virtual bool connected() = 0; // Subclasses need to fill this in!
|
||||||
|
|
||||||
|
// Set MQTT last will topic, payload, QOS, and retain. This needs
|
||||||
|
// to be called before connect() because it is sent as part of the
|
||||||
|
// connect control packet.
|
||||||
|
bool will(const char *topic, const char *payload, uint8_t qos = 0, uint8_t retain = 0);
|
||||||
|
|
||||||
|
// Publish a message to a topic using the specified QoS level. Returns true
|
||||||
|
// if the message was published, false otherwise.
|
||||||
|
bool publish(const char *topic, const char *payload, uint8_t qos = 0);
|
||||||
|
bool publish(const char *topic, uint8_t *payload, uint16_t bLen, uint8_t qos = 0);
|
||||||
|
|
||||||
|
// Add a subscription to receive messages for a topic. Returns true if the
|
||||||
|
// subscription could be added or was already present, false otherwise.
|
||||||
|
// Must be called before connect(), subscribing after the connection
|
||||||
|
// is made is not currently supported.
|
||||||
|
bool subscribe(Adafruit_MQTT_Subscribe *sub);
|
||||||
|
|
||||||
|
// Unsubscribe from a previously subscribed MQTT topic.
|
||||||
|
bool unsubscribe(Adafruit_MQTT_Subscribe *sub);
|
||||||
|
|
||||||
|
// Check if any subscriptions have new messages. Will return a reference to
|
||||||
|
// an Adafruit_MQTT_Subscribe object which has a new message. Should be called
|
||||||
|
// in the sketch's loop function to ensure new messages are recevied. Note
|
||||||
|
// that subscribe should be called first for each topic that receives messages!
|
||||||
|
Adafruit_MQTT_Subscribe *readSubscription(int16_t timeout=0);
|
||||||
|
|
||||||
|
void processPackets(int16_t timeout);
|
||||||
|
|
||||||
|
// Ping the server to ensure the connection is still alive.
|
||||||
|
bool ping(uint8_t n = 1);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Interface that subclasses need to implement:
|
||||||
|
|
||||||
|
// Connect to the server and return true if successful, false otherwise.
|
||||||
|
virtual bool connectServer() = 0;
|
||||||
|
|
||||||
|
// Disconnect from the MQTT server. Returns true if disconnected, false otherwise.
|
||||||
|
virtual bool disconnectServer() = 0; // Subclasses need to fill this in!
|
||||||
|
|
||||||
|
// Send data to the server specified by the buffer and length of data.
|
||||||
|
virtual bool sendPacket(uint8_t *buffer, uint16_t len) = 0;
|
||||||
|
|
||||||
|
// Read MQTT packet from the server. Will read up to maxlen bytes and store
|
||||||
|
// the data in the provided buffer. Waits up to the specified timeout (in
|
||||||
|
// milliseconds) for data to be available.
|
||||||
|
virtual uint16_t readPacket(uint8_t *buffer, uint16_t maxlen, int16_t timeout) = 0;
|
||||||
|
|
||||||
|
// Read a full packet, keeping note of the correct length
|
||||||
|
uint16_t readFullPacket(uint8_t *buffer, uint16_t maxsize, uint16_t timeout);
|
||||||
|
// Properly process packets until you get to one you want
|
||||||
|
uint16_t processPacketsUntil(uint8_t *buffer, uint8_t waitforpackettype, uint16_t timeout);
|
||||||
|
|
||||||
|
// Shared state that subclasses can use:
|
||||||
|
const char *servername;
|
||||||
|
int16_t portnum;
|
||||||
|
const char *clientid;
|
||||||
|
const char *username;
|
||||||
|
const char *password;
|
||||||
|
const char *will_topic;
|
||||||
|
const char *will_payload;
|
||||||
|
uint8_t will_qos;
|
||||||
|
uint8_t will_retain;
|
||||||
|
uint8_t buffer[MAXBUFFERSIZE]; // one buffer, used for all incoming/outgoing
|
||||||
|
uint16_t packet_id_counter;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Adafruit_MQTT_Subscribe *subscriptions[MAXSUBSCRIPTIONS];
|
||||||
|
|
||||||
|
void flushIncoming(uint16_t timeout);
|
||||||
|
|
||||||
|
// Functions to generate MQTT packets.
|
||||||
|
uint8_t connectPacket(uint8_t *packet);
|
||||||
|
uint8_t disconnectPacket(uint8_t *packet);
|
||||||
|
uint16_t publishPacket(uint8_t *packet, const char *topic, uint8_t *payload, uint16_t bLen, uint8_t qos);
|
||||||
|
uint8_t subscribePacket(uint8_t *packet, const char *topic, uint8_t qos);
|
||||||
|
uint8_t unsubscribePacket(uint8_t *packet, const char *topic);
|
||||||
|
uint8_t pingPacket(uint8_t *packet);
|
||||||
|
uint8_t pubackPacket(uint8_t *packet, uint16_t packetid);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Adafruit_MQTT_Publish {
|
||||||
|
public:
|
||||||
|
Adafruit_MQTT_Publish(Adafruit_MQTT *mqttserver, const char *feed, uint8_t qos = 0);
|
||||||
|
|
||||||
|
bool publish(const char *s);
|
||||||
|
bool publish(double f, uint8_t precision=2); // Precision controls the minimum number of digits after decimal.
|
||||||
|
// This might be ignored and a higher precision value sent.
|
||||||
|
bool publish(int32_t i);
|
||||||
|
bool publish(uint32_t i);
|
||||||
|
bool publish(uint8_t *b, uint16_t bLen);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
Adafruit_MQTT *mqtt;
|
||||||
|
const char *topic;
|
||||||
|
uint8_t qos;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Adafruit_MQTT_Subscribe {
|
||||||
|
public:
|
||||||
|
Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, const char *feedname, uint8_t q=0);
|
||||||
|
|
||||||
|
void setCallback(SubscribeCallbackUInt32Type callb);
|
||||||
|
void setCallback(SubscribeCallbackDoubleType callb);
|
||||||
|
void setCallback(SubscribeCallbackBufferType callb);
|
||||||
|
void setCallback(AdafruitIO_MQTT *io, SubscribeCallbackIOType callb);
|
||||||
|
void removeCallback(void);
|
||||||
|
|
||||||
|
const char *topic;
|
||||||
|
uint8_t qos;
|
||||||
|
|
||||||
|
uint8_t lastread[SUBSCRIPTIONDATALEN];
|
||||||
|
// Number valid bytes in lastread. Limited to SUBSCRIPTIONDATALEN-1 to
|
||||||
|
// ensure nul terminating lastread.
|
||||||
|
uint16_t datalen;
|
||||||
|
|
||||||
|
SubscribeCallbackUInt32Type callback_uint32t;
|
||||||
|
SubscribeCallbackDoubleType callback_double;
|
||||||
|
SubscribeCallbackBufferType callback_buffer;
|
||||||
|
SubscribeCallbackIOType callback_io;
|
||||||
|
|
||||||
|
AdafruitIO_MQTT *io_mqtt;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Adafruit_MQTT *mqtt;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,106 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Adafruit Industries
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool Adafruit_MQTT_Client::connectServer() {
|
||||||
|
// Grab server name from flash and copy to buffer for name resolution.
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
strcpy((char *)buffer, servername);
|
||||||
|
DEBUG_PRINT(F("Connecting to: ")); DEBUG_PRINTLN((char *)buffer);
|
||||||
|
// Connect and check for success (0 result).
|
||||||
|
int r = client->connect((char *)buffer, portnum);
|
||||||
|
DEBUG_PRINT(F("Connect result: ")); DEBUG_PRINTLN(r);
|
||||||
|
return r != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT_Client::disconnectServer() {
|
||||||
|
// Stop connection if connected and return success (stop has no indication of
|
||||||
|
// failure).
|
||||||
|
if (client->connected()) {
|
||||||
|
client->stop();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT_Client::connected() {
|
||||||
|
// Return true if connected, false if not connected.
|
||||||
|
return client->connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Adafruit_MQTT_Client::readPacket(uint8_t *buffer, uint16_t maxlen,
|
||||||
|
int16_t timeout) {
|
||||||
|
/* Read data until either the connection is closed, or the idle timeout is reached. */
|
||||||
|
uint16_t len = 0;
|
||||||
|
int16_t t = timeout;
|
||||||
|
|
||||||
|
|
||||||
|
while (client->connected() && (timeout >= 0)) {
|
||||||
|
//DEBUG_PRINT('.');
|
||||||
|
while (client->available()) {
|
||||||
|
//DEBUG_PRINT('!');
|
||||||
|
char c = client->read();
|
||||||
|
timeout = t; // reset the timeout
|
||||||
|
buffer[len] = c;
|
||||||
|
//DEBUG_PRINTLN((uint8_t)c, HEX);
|
||||||
|
len++;
|
||||||
|
|
||||||
|
if (maxlen == 0) { // handle zero-length packets
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == maxlen) { // we read all we want, bail
|
||||||
|
DEBUG_PRINT(F("Read data:\t"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeout -= MQTT_CLIENT_READINTERVAL_MS;
|
||||||
|
delay(MQTT_CLIENT_READINTERVAL_MS);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_MQTT_Client::sendPacket(uint8_t *buffer, uint16_t len) {
|
||||||
|
uint16_t ret = 0;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
if (client->connected()) {
|
||||||
|
// send 250 bytes at most at a time, can adjust this later based on Client
|
||||||
|
|
||||||
|
uint16_t sendlen = len > 250 ? 250 : len;
|
||||||
|
//Serial.print("Sending: "); Serial.println(sendlen);
|
||||||
|
ret = client->write(buffer, sendlen);
|
||||||
|
DEBUG_PRINT(F("Client sendPacket returned: ")); DEBUG_PRINTLN(ret);
|
||||||
|
len -= ret;
|
||||||
|
|
||||||
|
if (ret != sendlen) {
|
||||||
|
DEBUG_PRINTLN("Failed to send packet.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DEBUG_PRINTLN(F("Connection failed!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Adafruit Industries
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
#ifndef _ADAFRUIT_MQTT_CLIENT_H_
|
||||||
|
#define _ADAFRUIT_MQTT_CLIENT_H_
|
||||||
|
|
||||||
|
#include "Client.h"
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
|
||||||
|
|
||||||
|
// How long to delay waiting for new data to be available in readPacket.
|
||||||
|
#define MQTT_CLIENT_READINTERVAL_MS 10
|
||||||
|
|
||||||
|
|
||||||
|
// MQTT client implementation for a generic Arduino Client interface. Can work
|
||||||
|
// with almost all Arduino network hardware like ethernet shield, wifi shield,
|
||||||
|
// and even other platforms like ESP8266.
|
||||||
|
class Adafruit_MQTT_Client : public Adafruit_MQTT {
|
||||||
|
public:
|
||||||
|
Adafruit_MQTT_Client(Client *client, const char *server, uint16_t port,
|
||||||
|
const char *cid, const char *user, const char *pass):
|
||||||
|
Adafruit_MQTT(server, port, cid, user, pass),
|
||||||
|
client(client)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Adafruit_MQTT_Client(Client *client, const char *server, uint16_t port,
|
||||||
|
const char *user="", const char *pass=""):
|
||||||
|
Adafruit_MQTT(server, port, user, pass),
|
||||||
|
client(client)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool connectServer();
|
||||||
|
bool disconnectServer();
|
||||||
|
bool connected();
|
||||||
|
uint16_t readPacket(uint8_t *buffer, uint16_t maxlen, int16_t timeout);
|
||||||
|
bool sendPacket(uint8_t *buffer, uint16_t len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Client* client;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,142 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Adafruit Industries
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
#ifndef _ADAFRUIT_MQTT_FONA_H_
|
||||||
|
#define _ADAFRUIT_MQTT_FONA_H_
|
||||||
|
|
||||||
|
#include <Adafruit_FONA.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
|
||||||
|
#define MQTT_FONA_INTERAVAILDELAY 100
|
||||||
|
#define MQTT_FONA_QUERYDELAY 500
|
||||||
|
|
||||||
|
|
||||||
|
// FONA-specific version of the Adafruit_MQTT class.
|
||||||
|
// Note that this is defined as a header-only class to prevent issues with using
|
||||||
|
// the library on non-FONA platforms (since Arduino will include all .cpp files
|
||||||
|
// in the compilation of the library).
|
||||||
|
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)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Adafruit_MQTT_FONA(Adafruit_FONA *f, const char *server, uint16_t port,
|
||||||
|
const char *user="", const char *pass=""):
|
||||||
|
Adafruit_MQTT(server, port, user, pass),
|
||||||
|
fona(f)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool connectServer() {
|
||||||
|
char server[40];
|
||||||
|
strncpy(server, servername, 40);
|
||||||
|
#ifdef ADAFRUIT_SLEEPYDOG_H
|
||||||
|
Watchdog.reset();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// connect to server
|
||||||
|
DEBUG_PRINTLN(F("Connecting to TCP"));
|
||||||
|
return fona->TCPconnect(server, portnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool disconnectServer() {
|
||||||
|
return fona->TCPclose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connected() {
|
||||||
|
// Return true if connected, false if not connected.
|
||||||
|
return fona->TCPconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t readPacket(uint8_t *buffer, uint16_t maxlen, int16_t timeout) {
|
||||||
|
uint8_t *buffp = buffer;
|
||||||
|
DEBUG_PRINTLN(F("Reading data.."));
|
||||||
|
|
||||||
|
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) {
|
||||||
|
avail = maxlen - len;
|
||||||
|
if (avail == 0) 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:\t"));
|
||||||
|
DEBUG_PRINTBUFFER(buffer, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef ADAFRUIT_SLEEPYDOG_H
|
||||||
|
Watchdog.reset();
|
||||||
|
#endif
|
||||||
|
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, uint16_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
|
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Adafruit Industries
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Adafruit MQTT Library [![Build Status](https://travis-ci.com/adafruit/Adafruit_MQTT_Library.svg?branch=master)](https://travis-ci.com/adafruit/Adafruit_MQTT_Library)
|
||||||
|
|
||||||
|
Arduino library for MQTT support, including access to Adafruit IO. Works with
|
||||||
|
the Adafruit FONA, Arduino Yun, ESP8266 Arduino platforms, and anything that supports
|
||||||
|
Arduino's Client interface (like Ethernet shield).
|
||||||
|
|
||||||
|
See included examples for how to use the library to access an MQTT service to
|
||||||
|
publish and subscribe to feeds. Note that this does not support the full MQTT
|
||||||
|
spec but is intended to support enough for QoS 0 and 1 publishing.
|
||||||
|
|
||||||
|
Depends on the following other libraries depending on the target platform:
|
||||||
|
|
||||||
|
- [Adafruit SleepyDog](https://github.com/adafruit/Adafruit_SleepyDog), watchdog
|
||||||
|
library used by FONA code for reliability.
|
||||||
|
|
||||||
|
- [Adafruit FONA](https://github.com/adafruit/Adafruit_FONA_Library), required for
|
||||||
|
the FONA hardware.
|
||||||
|
|
||||||
|
Future todos:
|
||||||
|
|
||||||
|
- Subscription callbacks
|
||||||
|
|
||||||
|
- remove watchdog
|
||||||
|
|
||||||
|
<!-- START COMPATIBILITY TABLE -->
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
MCU | Tested Works | Doesn't Work | Not Tested | Notes
|
||||||
|
------------------ | :----------: | :----------: | :---------: | -----
|
||||||
|
Atmega328 @ 16MHz | | | X |
|
||||||
|
Atmega328 @ 12MHz | | | X |
|
||||||
|
Atmega32u4 @ 16MHz | | | X |
|
||||||
|
Atmega32u4 @ 8MHz | | | X |
|
||||||
|
ESP8266 | | | X |
|
||||||
|
Atmega2560 @ 16MHz | | | X |
|
||||||
|
ATSAM3X8E | | | X |
|
||||||
|
ATSAM21D | | | X |
|
||||||
|
ATSAMD51J20 | | | X |
|
||||||
|
ATtiny85 @ 16MHz | | | X |
|
||||||
|
ATtiny85 @ 8MHz | | | X |
|
||||||
|
Intel Curie @ 32MHz | | | X |
|
||||||
|
STM32F2 | | | X |
|
||||||
|
|
||||||
|
* ATmega328 @ 16MHz : Arduino UNO, Adafruit Pro Trinket 5V, Adafruit Metro 328, Adafruit Metro Mini
|
||||||
|
* ATmega328 @ 12MHz : Adafruit Pro Trinket 3V
|
||||||
|
* ATmega32u4 @ 16MHz : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0
|
||||||
|
* ATmega32u4 @ 8MHz : Adafruit Flora, Bluefruit Micro
|
||||||
|
* ESP8266 : Adafruit Huzzah
|
||||||
|
* ATmega2560 @ 16MHz : Arduino Mega
|
||||||
|
* ATSAM3X8E : Arduino Due
|
||||||
|
* ATSAM21D : Arduino Zero, M0 Pro
|
||||||
|
* ATSAMD51J20: Adafruit PyPortal
|
||||||
|
* ATtiny85 @ 16MHz : Adafruit Trinket 5V
|
||||||
|
* ATtiny85 @ 8MHz : Adafruit Gemma, Arduino Gemma, Adafruit Trinket 3V
|
||||||
|
|
||||||
|
<!-- END COMPATIBILITY TABLE -->
|
|
@ -0,0 +1,122 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library ESP8266 Adafruit IO Anonymous Time Query
|
||||||
|
|
||||||
|
Must use the latest version of ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board & Feather
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
----> https://www.adafruit.com/products/2821
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
#define WLAN_SSID "network"
|
||||||
|
#define WLAN_PASS "password"
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
#define AIO_SERVERPORT 8883
|
||||||
|
|
||||||
|
WiFiClientSecure client;
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT);
|
||||||
|
|
||||||
|
Adafruit_MQTT_Subscribe timefeed = Adafruit_MQTT_Subscribe(&mqtt, "time/seconds");
|
||||||
|
|
||||||
|
// set timezone offset from UTC
|
||||||
|
int timeZone = -4; // UTC - 4 eastern daylight time (nyc)
|
||||||
|
int interval = 4; // trigger every X hours
|
||||||
|
|
||||||
|
int last_min = -1;
|
||||||
|
|
||||||
|
void timecallback(uint32_t current) {
|
||||||
|
// adjust to local time zone
|
||||||
|
current += (timeZone * 60 * 60);
|
||||||
|
int curr_hour = (current / 60 / 60) % 24;
|
||||||
|
int curr_min = (current / 60 ) % 60;
|
||||||
|
int curr_sec = (current) % 60;
|
||||||
|
|
||||||
|
Serial.print("Time: ");
|
||||||
|
Serial.print(curr_hour); Serial.print(':');
|
||||||
|
Serial.print(curr_min); Serial.print(':');
|
||||||
|
Serial.println(curr_sec);
|
||||||
|
|
||||||
|
// only trigger on minute change
|
||||||
|
if(curr_min != last_min) {
|
||||||
|
last_min = curr_min;
|
||||||
|
|
||||||
|
Serial.println("This will print out every minute!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.print(F("\nAdafruit IO anonymous Time Demo"));
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(F("."));
|
||||||
|
}
|
||||||
|
Serial.println(F(" WiFi connected."));
|
||||||
|
|
||||||
|
timefeed.setCallback(timecallback);
|
||||||
|
mqtt.subscribe(&timefeed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// wait 10 seconds for subscription messages
|
||||||
|
// since we have no other tasks in this example.
|
||||||
|
mqtt.processPackets(10000);
|
||||||
|
|
||||||
|
// keep the connection alive
|
||||||
|
mqtt.ping();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library ESP8266 Example
|
||||||
|
|
||||||
|
Must use ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board & Feather
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
----> https://www.adafruit.com/products/2821
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Tony DiCola for Adafruit Industries.
|
||||||
|
Error examples by Todd Treece for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
|
||||||
|
#define WLAN_SSID "...your SSID..."
|
||||||
|
#define WLAN_PASS "...your password..."
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
#define AIO_SERVERPORT 1883 // 8883 for MQTTS
|
||||||
|
#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!) ******************/
|
||||||
|
|
||||||
|
// Create an ESP8266 WiFiClient class to connect to the MQTT server.
|
||||||
|
WiFiClient client;
|
||||||
|
// or... use WiFiFlientSecure for SSL
|
||||||
|
//WiFiClientSecure client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'photocell' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");
|
||||||
|
|
||||||
|
// Setup a feed called 'onoff' for subscribing to changes.
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
|
||||||
|
|
||||||
|
/*************************** Error Reporting *********************************/
|
||||||
|
|
||||||
|
Adafruit_MQTT_Subscribe errors = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/errors");
|
||||||
|
Adafruit_MQTT_Subscribe throttle = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/throttle");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
|
||||||
|
// for some reason (only affects ESP8266, likely an arduino-builder bug).
|
||||||
|
void MQTT_connect();
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit MQTT demo"));
|
||||||
|
|
||||||
|
// Connect to WiFi access point.
|
||||||
|
Serial.println(); Serial.println();
|
||||||
|
Serial.print("Connecting to ");
|
||||||
|
Serial.println(WLAN_SSID);
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
Serial.println("WiFi connected");
|
||||||
|
Serial.println("IP address: "); Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
// Setup MQTT subscription for onoff feed
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
|
||||||
|
// Setup MQTT subscriptions for throttle & error messages
|
||||||
|
mqtt.subscribe(&throttle);
|
||||||
|
mqtt.subscribe(&errors);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// this is our 'wait for incoming subscription packets' busy subloop
|
||||||
|
// try to spend your time here
|
||||||
|
Adafruit_MQTT_Subscribe *subscription;
|
||||||
|
while ((subscription = mqtt.readSubscription(5000))) {
|
||||||
|
if (subscription == &onoffbutton) {
|
||||||
|
Serial.print(F("Got onoff: "));
|
||||||
|
Serial.println((char *)onoffbutton.lastread);
|
||||||
|
} else if(subscription == &errors) {
|
||||||
|
Serial.print(F("ERROR: "));
|
||||||
|
Serial.println((char *)errors.lastread);
|
||||||
|
} else if(subscription == &throttle) {
|
||||||
|
Serial.println((char *)throttle.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"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/****************************************************************
|
||||||
|
Adafruit MQTT Library, Adafruit IO SSL/TLS Example for AirLift
|
||||||
|
|
||||||
|
Must use the latest version of nina-fw from:
|
||||||
|
https://github.com/adafruit/nina-fw
|
||||||
|
|
||||||
|
Works great with Adafruit AirLift ESP32 Co-Processors!
|
||||||
|
--> https://www.adafruit.com/product/4201
|
||||||
|
--> https://www.adafruit.com/product/4116
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Brent Rubell for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
#include <WiFiNINA.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
// For AirLift Breakout/Wing/Shield: Configure the following to match the ESP32 Pins!
|
||||||
|
#if !defined(SPIWIFI_SS)
|
||||||
|
// Don't change the names of these #define's! they match the variant ones
|
||||||
|
#define SPIWIFI SPI
|
||||||
|
#define SPIWIFI_SS 11 // Chip select pin
|
||||||
|
#define SPIWIFI_ACK 10 // a.k.a BUSY or READY pin
|
||||||
|
#define ESP32_RESETN 9 // Reset pin
|
||||||
|
#define ESP32_GPIO0 -1 // Not connected
|
||||||
|
#define SET_PINS 1 // Pins were set using this IFNDEF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
|
||||||
|
#define WLAN_SSID "WLAN_SSID"
|
||||||
|
#define WLAN_PASS "WIFI_PASSWORD"
|
||||||
|
int keyIndex = 0; // your network key Index number (needed only for WEP)
|
||||||
|
|
||||||
|
int status = WL_IDLE_STATUS;
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
// Using port 8883 for MQTTS
|
||||||
|
#define AIO_SERVERPORT 8883
|
||||||
|
// Adafruit IO Account Configuration
|
||||||
|
// (to obtain these values, visit https://io.adafruit.com and click on Active Key)
|
||||||
|
#define AIO_USERNAME "YOUR_ADAFRUIT_IO_USERNAME"
|
||||||
|
#define AIO_KEY "YOUR_ADAFRUIT_IO_KEY"
|
||||||
|
|
||||||
|
/************ Global State (you don't need to change this!) ******************/
|
||||||
|
|
||||||
|
// WiFiSSLClient for SSL/TLS support
|
||||||
|
WiFiSSLClient client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by WLAN_PASSing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'test' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish test = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/test");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
//Initialize serial and wait for port to open:
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial)
|
||||||
|
{
|
||||||
|
; // wait for serial port to connect. Needed for native USB port only
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the AirLift's pins were defined above...
|
||||||
|
#ifdef SET_PINS
|
||||||
|
WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// check for the wifi module
|
||||||
|
while (WiFi.status() == WL_NO_MODULE)
|
||||||
|
{
|
||||||
|
Serial.println("Communication with WiFi module failed!");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
String fv = WiFi.firmwareVersion();
|
||||||
|
if (fv < "1.0.0")
|
||||||
|
{
|
||||||
|
Serial.println("Please upgrade the firmware");
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to connect to Wifi network:
|
||||||
|
Serial.print("Attempting to connect to SSID: ");
|
||||||
|
Serial.println(WLAN_SSID);
|
||||||
|
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
|
||||||
|
do
|
||||||
|
{
|
||||||
|
status = WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
delay(100); // wait until connected
|
||||||
|
} while (status != WL_CONNECTED);
|
||||||
|
Serial.println("Connected to wifi");
|
||||||
|
printWiFiStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x = 0;
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// Now we can publish stuff!
|
||||||
|
Serial.print(F("\nSending val "));
|
||||||
|
Serial.print(x);
|
||||||
|
Serial.print(F(" to test feed..."));
|
||||||
|
if (!test.publish(x++))
|
||||||
|
{
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait a couple seconds to avoid rate limit
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect()
|
||||||
|
{
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0)
|
||||||
|
{ // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0)
|
||||||
|
{
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void printWiFiStatus()
|
||||||
|
{
|
||||||
|
// print the SSID of the network you're attached to:
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(WiFi.SSID());
|
||||||
|
|
||||||
|
// print your board's IP address:
|
||||||
|
IPAddress ip = WiFi.localIP();
|
||||||
|
Serial.print("IP Address: ");
|
||||||
|
Serial.println(ip);
|
||||||
|
|
||||||
|
// print the received signal strength:
|
||||||
|
long rssi = WiFi.RSSI();
|
||||||
|
Serial.print("signal strength (RSSI):");
|
||||||
|
Serial.print(rssi);
|
||||||
|
Serial.println(" dBm");
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library ESP8266 Adafruit IO SSL/TLS example
|
||||||
|
|
||||||
|
Must use the latest version of ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board & Feather
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
----> https://www.adafruit.com/products/2821
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Tony DiCola for Adafruit Industries.
|
||||||
|
SSL/TLS additions by Todd Treece for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
|
||||||
|
#define WLAN_SSID "WLAN_SSID"
|
||||||
|
#define WLAN_PASS "WIFI_PASSWORD"
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
// Using port 8883 for MQTTS
|
||||||
|
#define AIO_SERVERPORT 8883
|
||||||
|
// Adafruit IO Account Configuration
|
||||||
|
// (to obtain these values, visit https://io.adafruit.com and click on Active Key)
|
||||||
|
#define AIO_USERNAME "YOUR_ADAFRUIT_IO_USERNAME"
|
||||||
|
#define AIO_KEY "YOUR_ADAFRUIT_IO_KEY"
|
||||||
|
|
||||||
|
/************ Global State (you don't need to change this!) ******************/
|
||||||
|
|
||||||
|
// WiFiFlientSecure for SSL/TLS support
|
||||||
|
WiFiClientSecure client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
// io.adafruit.com SHA1 fingerprint
|
||||||
|
static const char *fingerprint PROGMEM = "77 00 54 2D DA E7 D8 03 27 31 23 99 EB 27 DB CB A5 4C 57 18";
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'test' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish test = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/test");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit IO MQTTS (SSL/TLS) Example"));
|
||||||
|
|
||||||
|
// Connect to WiFi access point.
|
||||||
|
Serial.println(); Serial.println();
|
||||||
|
Serial.print("Connecting to ");
|
||||||
|
Serial.println(WLAN_SSID);
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
Serial.println("WiFi connected");
|
||||||
|
Serial.println("IP address: "); Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
// check the fingerprint of io.adafruit.com's SSL cert
|
||||||
|
client.setFingerprint(fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// Now we can publish stuff!
|
||||||
|
Serial.print(F("\nSending val "));
|
||||||
|
Serial.print(x);
|
||||||
|
Serial.print(F(" to test feed..."));
|
||||||
|
if (! test.publish(x++)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait a couple seconds to avoid rate limit
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library ESP8266 Adafruit IO SSL/TLS example
|
||||||
|
|
||||||
|
Must use the latest version of ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board & Feather
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
----> https://www.adafruit.com/products/2821
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Tony DiCola for Adafruit Industries.
|
||||||
|
Time additions by Todd Treece for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
#define WLAN_SSID "network"
|
||||||
|
#define WLAN_PASS "password"
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
#define AIO_SERVERPORT 8883
|
||||||
|
#define AIO_USERNAME "user"
|
||||||
|
#define AIO_KEY "key"
|
||||||
|
|
||||||
|
WiFiClientSecure client;
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
Adafruit_MQTT_Subscribe timefeed = Adafruit_MQTT_Subscribe(&mqtt, "time/seconds");
|
||||||
|
|
||||||
|
// set timezone offset from UTC
|
||||||
|
int timeZone = -4; // UTC - 4 eastern daylight time (nyc)
|
||||||
|
int interval = 4; // trigger every X hours
|
||||||
|
int hour = 0; // current hour
|
||||||
|
|
||||||
|
void timecallback(uint32_t current) {
|
||||||
|
|
||||||
|
// stash previous hour
|
||||||
|
int previous = hour;
|
||||||
|
|
||||||
|
// adjust to local time zone
|
||||||
|
current += (timeZone * 60 * 60);
|
||||||
|
|
||||||
|
// calculate current hour
|
||||||
|
hour = (current / 60 / 60) % 24;
|
||||||
|
|
||||||
|
// only trigger on interval
|
||||||
|
if((hour != previous) && (hour % interval) == 0) {
|
||||||
|
Serial.println("Run your code here");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.print(F("Adafruit IO Time Demo"));
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(F("."));
|
||||||
|
}
|
||||||
|
Serial.println(F(" WiFi connected."));
|
||||||
|
|
||||||
|
timefeed.setCallback(timecallback);
|
||||||
|
mqtt.subscribe(&timefeed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// wait 10 seconds for subscription messages
|
||||||
|
// since we have no other tasks in this example.
|
||||||
|
mqtt.processPackets(10000);
|
||||||
|
|
||||||
|
// keep the connection alive
|
||||||
|
mqtt.ping();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library ESP8266 Example
|
||||||
|
|
||||||
|
Must use ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board & Feather
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
----> https://www.adafruit.com/products/2821
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Tony DiCola for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
// the on off button feed turns this LED on/off
|
||||||
|
#define LED 2
|
||||||
|
// the slider feed sets the PWM output of this pin
|
||||||
|
#define PWMOUT 12
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
|
||||||
|
#define WLAN_SSID "...your SSID..."
|
||||||
|
#define WLAN_PASS "...your password..."
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
#define AIO_SERVERPORT 1883 // use 8883 for SSL
|
||||||
|
#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!) ******************/
|
||||||
|
|
||||||
|
// Create an ESP8266 WiFiClient class to connect to the MQTT server.
|
||||||
|
WiFiClient client;
|
||||||
|
// or... use WiFiFlientSecure for SSL
|
||||||
|
//WiFiClientSecure client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
|
||||||
|
Adafruit_MQTT_Subscribe slider = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/slider");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
|
||||||
|
// for some reason (only affects ESP8266, likely an arduino-builder bug).
|
||||||
|
void MQTT_connect();
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(LED, OUTPUT);
|
||||||
|
pinMode(PWMOUT, OUTPUT);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit MQTT demo"));
|
||||||
|
|
||||||
|
// Connect to WiFi access point.
|
||||||
|
Serial.println(); Serial.println();
|
||||||
|
Serial.print("Connecting to ");
|
||||||
|
Serial.println(WLAN_SSID);
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
Serial.println("WiFi connected");
|
||||||
|
Serial.println("IP address: "); Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
// Setup MQTT subscription for onoff & slider feed.
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
mqtt.subscribe(&slider);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// this is our 'wait for incoming subscription packets' busy subloop
|
||||||
|
// try to spend your time here
|
||||||
|
|
||||||
|
Adafruit_MQTT_Subscribe *subscription;
|
||||||
|
while ((subscription = mqtt.readSubscription(5000))) {
|
||||||
|
// Check if its the onoff button feed
|
||||||
|
if (subscription == &onoffbutton) {
|
||||||
|
Serial.print(F("On-Off button: "));
|
||||||
|
Serial.println((char *)onoffbutton.lastread);
|
||||||
|
|
||||||
|
if (strcmp((char *)onoffbutton.lastread, "ON") == 0) {
|
||||||
|
digitalWrite(LED, LOW);
|
||||||
|
}
|
||||||
|
if (strcmp((char *)onoffbutton.lastread, "OFF") == 0) {
|
||||||
|
digitalWrite(LED, HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if its the slider feed
|
||||||
|
if (subscription == &slider) {
|
||||||
|
Serial.print(F("Slider: "));
|
||||||
|
Serial.println((char *)slider.lastread);
|
||||||
|
uint16_t sliderval = atoi((char *)slider.lastread); // convert to a number
|
||||||
|
analogWrite(PWMOUT, sliderval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ping the server to keep the mqtt connection alive
|
||||||
|
if(! mqtt.ping()) {
|
||||||
|
mqtt.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Adafruit MQTT Library Arbitrary Data Publish Example
|
||||||
|
|
||||||
|
This example illustrates publishing an arbitrary data packet using the Adafruit MQTT library to an MQTT feed which can then be parsed by the included python subscriber client. Possible usage cases include adding metadata (collection time, sensor info etc) to a datapoint.
|
||||||
|
|
||||||
|
![alt-text](https://raw.githubusercontent.com/stuthedew/Adafruit_MQTT_Library/Arbitrary_data_publish/examples/mqtt_arbitrary_data/python_subscriber/mqtt_figure.png "Arbitrary data flow diagram")
|
||||||
|
|
||||||
|
My motivation for this was wanting to be able to include metadata to a post.
|
||||||
|
Specifically, I was playing around with a [Teviso RD3024 radiation sensor](http://www.teviso.com/en/products/radiation-sensor-rd3024.htm), and a salvaged Americium radiation source from a smoke detector, at varying distances from the sensor. I wanted a way to associate the collection time, and distance between the source and sensor with the actual radiation reading itself.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installing and configuring Mosquitto broker (minimal working setup):
|
||||||
|
|
||||||
|
####_Installing on Raspberry Pi/Linux:_
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get install mosquitto
|
||||||
|
cd /etc/mosquitto/
|
||||||
|
#See "Configuring Mosquitto Broker below"
|
||||||
|
```
|
||||||
|
|
||||||
|
####_Installing On a Mac:_
|
||||||
|
```bash
|
||||||
|
brew install mosquitto
|
||||||
|
cd /usr/local/etc/mosquitto
|
||||||
|
#See "Configuring Mosquitto Broker below"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
####Configuring Mosquitto broker
|
||||||
|
```bash
|
||||||
|
sudo nano mosquitto.conf
|
||||||
|
```
|
||||||
|
Now we have to enable a password file to correctly interface with the Adafruit MQTT library. Scroll about two thirds of the way down until you see:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# Default authentication and topic access control
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see `#password_file` about a paragraph after that.
|
||||||
|
Change
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#password_file
|
||||||
|
```
|
||||||
|
|
||||||
|
To
|
||||||
|
|
||||||
|
```bash
|
||||||
|
password_file pwfile
|
||||||
|
```
|
||||||
|
|
||||||
|
Now `ctrl-x` to save and exit.
|
||||||
|
|
||||||
|
You're almost done! We just have to create and populate the password file we just configured. The default user info is:
|
||||||
|
* **Arduino Subscriber:**
|
||||||
|
* Username: TestUser
|
||||||
|
* Password: TestUser
|
||||||
|
|
||||||
|
* **Python Subscriber:**
|
||||||
|
* Username: TestPy
|
||||||
|
* Password: TestPy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
touch pwfile #create the password file
|
||||||
|
mosquitto_passwd pwfile TestUser #Enter and confirm password when prompted
|
||||||
|
mosquitto_passwd pwfile TestPy #Enter and confirm password when prompted
|
||||||
|
```
|
||||||
|
|
||||||
|
####Running Mosquitto broker
|
||||||
|
Now run Mosquitto broker to allow Arduino publisher and Python subscriber to communicate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mosquitto
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using Example Python Subscriber:
|
||||||
|
|
||||||
|
####Installing Python subscriber
|
||||||
|
Install dependencies if you haven't already
|
||||||
|
```bash
|
||||||
|
cd ../Adafruit_MQTT_Library/examples/mqtt_arbitrary_buffer/python_subscriber
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
####Installing Python subscriber
|
||||||
|
Run python script with default values and watch your parsed data print out.
|
||||||
|
```bash
|
||||||
|
python subscriber.py #Add -h flag to see modifiable options
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming that the Mosquitto broker is running in the background and the Adafruit_MQTT client (Arduino) is publishing, you should see the example data print out every 10 seconds.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MQTT: Connection successful
|
||||||
|
Connection successful
|
||||||
|
Subscribed to /feeds/arb_packet
|
||||||
|
Received char Array: "Hello!", val1: -4533, val2: 73102, val3: 3354...
|
||||||
|
Received char Array: "Hello!", val1: -4533, val2: 83611, val3: 3354...
|
||||||
|
Received char Array: "Hello!", val1: -4533, val2: 94115, val3: 3354...
|
||||||
|
```
|
|
@ -0,0 +1,184 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library Arbitrary Data Example
|
||||||
|
|
||||||
|
Must use ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board & Feather
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
----> https://www.adafruit.com/products/2821
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Stuart Feichtinger
|
||||||
|
Modifed from the mqtt_esp8266 example written by Tony DiCola for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
|
||||||
|
#define WLAN_SSID "...your SSID..."
|
||||||
|
#define WLAN_PASS "...your password..."
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define ARB_SERVER "...host computer ip address..."
|
||||||
|
#define ARB_SERVERPORT 1883 // use 8883 for SSL
|
||||||
|
#define ARB_USERNAME "TestUser"
|
||||||
|
#define ARB_PW "TestUser"
|
||||||
|
|
||||||
|
|
||||||
|
/************ Global State (you don't need to change this!) ******************/
|
||||||
|
|
||||||
|
// Create an ESP8266 WiFiClient class to connect to the MQTT server.
|
||||||
|
WiFiClient client;
|
||||||
|
// or... use WiFiFlientSecure for SSL
|
||||||
|
//WiFiClientSecure client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, ARB_SERVER, ARB_SERVERPORT, ARB_USERNAME, ARB_PW);
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'arb_packet' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
#define ARB_FEED "/feeds/arb_packet"
|
||||||
|
Adafruit_MQTT_Publish ap = Adafruit_MQTT_Publish(&mqtt, ARB_FEED);
|
||||||
|
|
||||||
|
|
||||||
|
// Arbitrary Payload
|
||||||
|
// Union allows for easier interaction of members in struct form with easy publishing
|
||||||
|
// of "raw" bytes
|
||||||
|
typedef union{
|
||||||
|
//Customize struct with whatever variables/types you like.
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)){ // packed to eliminate padding for easier parsing.
|
||||||
|
char charAry[10];
|
||||||
|
int16_t val1;
|
||||||
|
unsigned long val2;
|
||||||
|
uint16_t val3;
|
||||||
|
}s;
|
||||||
|
|
||||||
|
uint8_t raw[sizeof(s)]; // For publishing
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Alternate Option with anonymous struct, but manual byte count:
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)){ // packed to eliminate padding for easier parsing.
|
||||||
|
char charAry[10]; // 10 x 1 byte = 10 bytes
|
||||||
|
int16_t val1; // 1 x 2 bytes = 2 bytes
|
||||||
|
unsigned long val2; // 1 x 4 bytes = 4 bytes
|
||||||
|
uint16_t val3; // 1 x 2 bytes = 2 bytes
|
||||||
|
-------------------
|
||||||
|
TOTAL = 18 bytes
|
||||||
|
};
|
||||||
|
uint8_t raw[18]; // For publishing
|
||||||
|
*/
|
||||||
|
|
||||||
|
} packet_t;
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
|
||||||
|
// for some reason (only affects ESP8266, likely an arduino-builder bug).
|
||||||
|
void MQTT_connect();
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit MQTT demo"));
|
||||||
|
|
||||||
|
// Connect to WiFi access point.
|
||||||
|
Serial.println(); Serial.println();
|
||||||
|
Serial.print(F("Connecting to "));
|
||||||
|
Serial.println(WLAN_SSID);
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(F("."));
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
Serial.println(F("WiFi connected"));
|
||||||
|
Serial.println(F("IP address: ")); Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_t arbPac;
|
||||||
|
|
||||||
|
const char strVal[] PROGMEM = "Hello!";
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
//Update arbitrary packet values
|
||||||
|
strcpy_P(arbPac.s.charAry, strVal);
|
||||||
|
arbPac.s.val1 = -4533;
|
||||||
|
arbPac.s.val2 = millis();
|
||||||
|
arbPac.s.val3 = 3354;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Alternate Union with anonymous struct
|
||||||
|
// (see union declaration above)
|
||||||
|
|
||||||
|
strcpy_P(arbPac.charAry, strVal);
|
||||||
|
arbPac.val1 = -4533;
|
||||||
|
arbPac.val2 = millis();
|
||||||
|
arbPac.val3 = 3354;
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (! ap.publish(arbPac.raw, sizeof(packet_t)))
|
||||||
|
Serial.println(F("Publish Failed."));
|
||||||
|
else {
|
||||||
|
Serial.println(F("Publish Success!"));
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(10000);
|
||||||
|
|
||||||
|
|
||||||
|
// ping the server to keep the mqtt connection alive
|
||||||
|
// NOT required if you are publishing once every KEEPALIVE seconds
|
||||||
|
/*
|
||||||
|
if(! mqtt.ping()) {
|
||||||
|
mqtt.disconnect();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print(F("Connecting to MQTT... "));
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println(F("Retrying MQTT connection in 5 seconds..."));
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println(F("MQTT Connected!"));
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 865 KiB |
|
@ -0,0 +1 @@
|
||||||
|
paho-mqtt>=1.1
|
|
@ -0,0 +1,114 @@
|
||||||
|
'''MQTT subscriber for Adafruit MQTT library mqtt_arbitrary_buffer example'''
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import argparse
|
||||||
|
import struct
|
||||||
|
import array
|
||||||
|
import sys
|
||||||
|
|
||||||
|
return_str =[
|
||||||
|
"Connection successful",
|
||||||
|
"incorrect protocol version",
|
||||||
|
"invalid client identifier",
|
||||||
|
"server unavailable",
|
||||||
|
"bad username or password",
|
||||||
|
"not authorised"
|
||||||
|
]
|
||||||
|
|
||||||
|
args = None
|
||||||
|
|
||||||
|
# The callback for when the client receives a CONNACK response from the server.
|
||||||
|
def on_connect(client, userdata, rc):
|
||||||
|
"""callback function on connect. Subscribes or exits depending on outcome"""
|
||||||
|
print("MQTT: "),
|
||||||
|
print(return_str[rc])
|
||||||
|
if(rc > 1):
|
||||||
|
print("Connection refused - " + return_str[rc])
|
||||||
|
sys.exit(rc)
|
||||||
|
# Subscribing in on_connect() means that if we lose the connection and
|
||||||
|
# reconnect then subscriptions will be renewed.
|
||||||
|
else:
|
||||||
|
print(return_str[rc])
|
||||||
|
client.subscribe(args.topic)
|
||||||
|
print("Subscribed to {}".format(args.topic))
|
||||||
|
|
||||||
|
def on_disconnect(client, userdata, rc):
|
||||||
|
"""Callback for disconnect"""
|
||||||
|
if rc != 0:
|
||||||
|
print("Unexpected disconnection.")
|
||||||
|
client.reconnect()
|
||||||
|
|
||||||
|
# The callback for when a PUBLISH message is received from the server.
|
||||||
|
def on_message(client, userdata, msg):
|
||||||
|
try:
|
||||||
|
pMsg = parseMsg(msg.payload)
|
||||||
|
print("Received char Array: \"{}\", val1: {}, val2: {}, val3: {}...".format(pMsg[0], pMsg[1], pMsg[2], pMsg[3]))
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
print err
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def argBegin():
|
||||||
|
parser = argparse.ArgumentParser(description='MQTT subscriber for Adafruit MQTT library mqtt_arbitrary_buffer example')
|
||||||
|
parser.add_argument("--host", default="localhost", help='mqtt host to connect to. Defaults to localhost.')
|
||||||
|
parser.add_argument("-p", "--port", default=1883, help='network port to connect to. Defaults to 1883.')
|
||||||
|
parser.add_argument("-t", "--topic", nargs='*', default="/feeds/arb_packet", help="mqtt topic to subscribe to. May be repeated multiple times.")
|
||||||
|
parser.add_argument("-u", "--username", default="testPy", help="provide a username (requires MQTT 3.1 broker)")
|
||||||
|
parser.add_argument("-P", "--password", default="testPy", help="provide a password (requires MQTT 3.1 broker)")
|
||||||
|
parser.add_argument("-k", "--keepalive", default=60, help="keep alive in seconds for this client. Defaults to 60.")
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def parseMsg(payload):
|
||||||
|
"""Parses C struct from MQTT publisher Adafruit MQTT client to Python list"""
|
||||||
|
|
||||||
|
arr = array.array('B', payload) #convert python list to C-like array of unsigned char (B)
|
||||||
|
|
||||||
|
parsedStruct = struct.Struct('< 10s h L H') #define struct template (see below)
|
||||||
|
'''
|
||||||
|
Format of Struct from Adafruit MQTT client, Arduino, etc:
|
||||||
|
|
||||||
|
Adafruit MQTT client == Little endian (<)
|
||||||
|
|
||||||
|
Var NAME | C TYPE (python symbol) | size of member x bytes
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
"charAry" | uchar (s) | 10s x 1 = 10 bytes
|
||||||
|
"val1" | int16 / short (h) | 1h x 2 = 2 bytes
|
||||||
|
"val2" | unsigned long (L) | 1L x 4 = 4 bytes
|
||||||
|
"val3" | uint16/unsigned short(H)| 1H x 2 = 2 bytes
|
||||||
|
------------------------------------------------------------------
|
||||||
|
Total packet size = | 18 bytes |
|
||||||
|
|
||||||
|
See Section 7.3.2 of Python struct module documentation for complete format list
|
||||||
|
https://docs.python.org/2/library/struct.html
|
||||||
|
'''
|
||||||
|
|
||||||
|
charAry, val1, val2, val3 = parsedStruct.unpack_from(arr) #convert byte array to formatted struct
|
||||||
|
charAry = charAry.rstrip(' \t\r\n\0') #remove trailing white space from buffer
|
||||||
|
return charAry, val1, val2, val3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Wait for incoming message published by Adafruit MQTT client"""
|
||||||
|
global args
|
||||||
|
args = argBegin()
|
||||||
|
client = mqtt.Client()
|
||||||
|
client.on_connect = on_connect
|
||||||
|
client.on_message = on_message
|
||||||
|
client.username_pw_set(args.username, args.password)
|
||||||
|
client.connect(args.host, args.port, args.keepalive)
|
||||||
|
|
||||||
|
# Blocking call that processes network traffic, dispatches callbacks and
|
||||||
|
# handles reconnecting.
|
||||||
|
# Other loop*() functions are available that give a threaded interface and a
|
||||||
|
# manual interface.
|
||||||
|
client.loop_forever()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
|
@ -0,0 +1,147 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library ESP8266 Example
|
||||||
|
|
||||||
|
Must use ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board & Feather
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
----> https://www.adafruit.com/products/2821
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Tony DiCola for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
|
||||||
|
#define WLAN_SSID "...your SSID..."
|
||||||
|
#define WLAN_PASS "...your password..."
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
#define AIO_SERVERPORT 1883 // use 8883 for SSL
|
||||||
|
#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!) ******************/
|
||||||
|
|
||||||
|
// Create an ESP8266 WiFiClient class to connect to the MQTT server.
|
||||||
|
WiFiClient client;
|
||||||
|
// or... use WiFiFlientSecure for SSL
|
||||||
|
//WiFiClientSecure client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'photocell' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");
|
||||||
|
|
||||||
|
// Setup a feed called 'onoff' for subscribing to changes.
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
|
||||||
|
// for some reason (only affects ESP8266, likely an arduino-builder bug).
|
||||||
|
void MQTT_connect();
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit MQTT demo"));
|
||||||
|
|
||||||
|
// Connect to WiFi access point.
|
||||||
|
Serial.println(); Serial.println();
|
||||||
|
Serial.print("Connecting to ");
|
||||||
|
Serial.println(WLAN_SSID);
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
Serial.println("WiFi connected");
|
||||||
|
Serial.println("IP address: "); Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
// Setup MQTT subscription for onoff feed.
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// this is our 'wait for incoming subscription packets' busy subloop
|
||||||
|
// try to spend your time here
|
||||||
|
|
||||||
|
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"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ping the server to keep the mqtt connection alive
|
||||||
|
// NOT required if you are publishing once every KEEPALIVE seconds
|
||||||
|
/*
|
||||||
|
if(! mqtt.ping()) {
|
||||||
|
mqtt.disconnect();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library ESP8266 Example
|
||||||
|
|
||||||
|
Must use ESP8266 Arduino from:
|
||||||
|
https://github.com/esp8266/Arduino
|
||||||
|
|
||||||
|
Works great with Adafruit's Huzzah ESP board:
|
||||||
|
----> https://www.adafruit.com/product/2471
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Tony DiCola for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* WiFi Access Point *********************************/
|
||||||
|
|
||||||
|
#define WLAN_SSID "network"
|
||||||
|
#define WLAN_PASS "password"
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
#define AIO_SERVERPORT 1883
|
||||||
|
#define AIO_USERNAME "user"
|
||||||
|
#define AIO_KEY "key"
|
||||||
|
|
||||||
|
/************ Global State (you don't need to change this!) ******************/
|
||||||
|
|
||||||
|
// Create an ESP8266 WiFiClient class to connect to the MQTT server.
|
||||||
|
WiFiClient client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'time' for subscribing to current time
|
||||||
|
Adafruit_MQTT_Subscribe timefeed = Adafruit_MQTT_Subscribe(&mqtt, "time/seconds");
|
||||||
|
|
||||||
|
// Setup a feed called 'slider' for subscribing to changes on the slider
|
||||||
|
Adafruit_MQTT_Subscribe slider = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/slider", MQTT_QOS_1);
|
||||||
|
|
||||||
|
// Setup a feed called 'onoff' for subscribing to changes to the button
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff", MQTT_QOS_1);
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
int sec;
|
||||||
|
int min;
|
||||||
|
int hour;
|
||||||
|
|
||||||
|
int timeZone = -4; // utc-4 eastern daylight time (nyc)
|
||||||
|
|
||||||
|
void timecallback(uint32_t current) {
|
||||||
|
|
||||||
|
// adjust to local time zone
|
||||||
|
current += (timeZone * 60 * 60);
|
||||||
|
|
||||||
|
// calculate current time
|
||||||
|
sec = current % 60;
|
||||||
|
current /= 60;
|
||||||
|
min = current % 60;
|
||||||
|
current /= 60;
|
||||||
|
hour = current % 24;
|
||||||
|
|
||||||
|
// print hour
|
||||||
|
if(hour == 0 || hour == 12)
|
||||||
|
Serial.print("12");
|
||||||
|
if(hour < 12)
|
||||||
|
Serial.print(hour);
|
||||||
|
else
|
||||||
|
Serial.print(hour - 12);
|
||||||
|
|
||||||
|
// print mins
|
||||||
|
Serial.print(":");
|
||||||
|
if(min < 10) Serial.print("0");
|
||||||
|
Serial.print(min);
|
||||||
|
|
||||||
|
// print seconds
|
||||||
|
Serial.print(":");
|
||||||
|
if(sec < 10) Serial.print("0");
|
||||||
|
Serial.print(sec);
|
||||||
|
|
||||||
|
if(hour < 12)
|
||||||
|
Serial.println(" am");
|
||||||
|
else
|
||||||
|
Serial.println(" pm");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void slidercallback(double x) {
|
||||||
|
Serial.print("Hey we're in a slider callback, the slider value is: ");
|
||||||
|
Serial.println(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onoffcallback(char *data, uint16_t len) {
|
||||||
|
Serial.print("Hey we're in a onoff callback, the button value is: ");
|
||||||
|
Serial.println(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit MQTT demo"));
|
||||||
|
|
||||||
|
// Connect to WiFi access point.
|
||||||
|
Serial.println(); Serial.println();
|
||||||
|
Serial.print("Connecting to ");
|
||||||
|
Serial.println(WLAN_SSID);
|
||||||
|
|
||||||
|
WiFi.begin(WLAN_SSID, WLAN_PASS);
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
Serial.println("WiFi connected");
|
||||||
|
Serial.println("IP address: "); Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
timefeed.setCallback(timecallback);
|
||||||
|
slider.setCallback(slidercallback);
|
||||||
|
onoffbutton.setCallback(onoffcallback);
|
||||||
|
|
||||||
|
// Setup MQTT subscription for time feed.
|
||||||
|
mqtt.subscribe(&timefeed);
|
||||||
|
mqtt.subscribe(&slider);
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// this is our 'wait for incoming subscription packets and callback em' busy subloop
|
||||||
|
// try to spend your time here:
|
||||||
|
mqtt.processPackets(10000);
|
||||||
|
|
||||||
|
// ping the server to keep the mqtt connection alive
|
||||||
|
// NOT required if you are publishing once every KEEPALIVE seconds
|
||||||
|
|
||||||
|
if(! mqtt.ping()) {
|
||||||
|
mqtt.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
uint8_t retries = 3;
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 10 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(10000); // wait 10 seconds
|
||||||
|
retries--;
|
||||||
|
if (retries == 0) {
|
||||||
|
// basically die and wait for WDT to reset me
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library Ethernet Example
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Alec Moore
|
||||||
|
Derived from the code written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <SPI.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <EthernetClient.h>
|
||||||
|
#include <Dns.h>
|
||||||
|
#include <Dhcp.h>
|
||||||
|
|
||||||
|
/************************* Ethernet Client Setup *****************************/
|
||||||
|
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
|
||||||
|
|
||||||
|
//Uncomment the following, and set to a valid ip if you don't have dhcp available.
|
||||||
|
//IPAddress iotIP (192, 168, 0, 42);
|
||||||
|
//Uncomment the following, and set to your preference if you don't have automatic dns.
|
||||||
|
//IPAddress dnsIP (8, 8, 8, 8);
|
||||||
|
//If you uncommented either of the above lines, make sure to change "Ethernet.begin(mac)" to "Ethernet.begin(mac, iotIP)" or "Ethernet.begin(mac, iotIP, dnsIP)"
|
||||||
|
|
||||||
|
|
||||||
|
/************************* 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!) ******************/
|
||||||
|
|
||||||
|
//Set up the ethernet client
|
||||||
|
EthernetClient client;
|
||||||
|
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
// You don't need to change anything below this line!
|
||||||
|
#define halt(s) { Serial.println(F( s )); while(1); }
|
||||||
|
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'photocell' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");
|
||||||
|
|
||||||
|
// Setup a feed called 'onoff' for subscribing to changes.
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit MQTT demo"));
|
||||||
|
|
||||||
|
// Initialise the Client
|
||||||
|
Serial.print(F("\nInit the Client..."));
|
||||||
|
Ethernet.begin(mac);
|
||||||
|
delay(1000); //give the ethernet a second to initialize
|
||||||
|
|
||||||
|
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// this is our 'wait for incoming subscription packets' busy subloop
|
||||||
|
Adafruit_MQTT_Subscribe *subscription;
|
||||||
|
while ((subscription = mqtt.readSubscription(1000))) {
|
||||||
|
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"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ping the server to keep the mqtt connection alive
|
||||||
|
if(! mqtt.ping()) {
|
||||||
|
mqtt.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
}
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#include <Adafruit_SleepyDog.h>
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#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.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();
|
||||||
|
delay(5000); // wait a few seconds to stabilize connection
|
||||||
|
Watchdog.reset();
|
||||||
|
|
||||||
|
fona.setGPRSNetworkSettings(apn, username, password);
|
||||||
|
|
||||||
|
Serial.println(F("Disabling GPRS"));
|
||||||
|
fona.enableGPRS(false);
|
||||||
|
|
||||||
|
Watchdog.reset();
|
||||||
|
delay(5000); // wait a few seconds to stabilize connection
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library FONA Example
|
||||||
|
|
||||||
|
Designed specifically to work with the Adafruit FONA
|
||||||
|
----> http://www.adafruit.com/products/1946
|
||||||
|
----> http://www.adafruit.com/products/1963
|
||||||
|
----> http://www.adafruit.com/products/2468
|
||||||
|
----> http://www.adafruit.com/products/2542
|
||||||
|
|
||||||
|
These cellular modules use TTL Serial to communicate, 2 pins are
|
||||||
|
required to interface.
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <Adafruit_SleepyDog.h>
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#include "Adafruit_FONA.h"
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_FONA.h"
|
||||||
|
|
||||||
|
/*************************** FONA Pins ***********************************/
|
||||||
|
|
||||||
|
// Default pins for Feather 32u4 FONA
|
||||||
|
#define FONA_RX 9
|
||||||
|
#define FONA_TX 8
|
||||||
|
#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!) ******************/
|
||||||
|
|
||||||
|
// Setup the FONA MQTT class by passing in the FONA class and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_FONA mqtt(&fona, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
// 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: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");
|
||||||
|
|
||||||
|
// Setup a feed called 'onoff' for subscribing to changes.
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
|
||||||
|
|
||||||
|
/*************************** 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);
|
||||||
|
|
||||||
|
// Watchdog is optional!
|
||||||
|
//Watchdog.enable(8000);
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit FONA MQTT demo"));
|
||||||
|
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
|
||||||
|
Watchdog.reset();
|
||||||
|
delay(5000); // wait a few seconds to stabilize connection
|
||||||
|
Watchdog.reset();
|
||||||
|
|
||||||
|
// Initialise the FONA module
|
||||||
|
while (! FONAconnect(F(FONA_APN), F(FONA_USERNAME), F(FONA_PASSWORD))) {
|
||||||
|
Serial.println("Retrying FONA");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(F("Connected to Cellular!"));
|
||||||
|
|
||||||
|
Watchdog.reset();
|
||||||
|
delay(5000); // 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();
|
||||||
|
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
Watchdog.reset();
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Watchdog.reset();
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ping the server to keep the mqtt connection alive, only needed if we're not publishing
|
||||||
|
//if(! mqtt.ping()) {
|
||||||
|
// Serial.println(F("MQTT Ping failed."));
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
}
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library WINC1500 Example
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <SPI.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
#include <WiFi101.h>
|
||||||
|
|
||||||
|
/************************* WiFI Setup *****************************/
|
||||||
|
#define WINC_CS 8
|
||||||
|
#define WINC_IRQ 7
|
||||||
|
#define WINC_RST 4
|
||||||
|
#define WINC_EN 2 // or, tie EN to VCC
|
||||||
|
|
||||||
|
char ssid[] = "yournetwork"; // your network SSID (name)
|
||||||
|
char pass[] = "yourpassword"; // your network password (use for WPA, or use as key for WEP)
|
||||||
|
int keyIndex = 0; // your network key Index number (needed only for WEP)
|
||||||
|
|
||||||
|
int status = WL_IDLE_STATUS;
|
||||||
|
|
||||||
|
/************************* Adafruit.io Setup *********************************/
|
||||||
|
|
||||||
|
#define AIO_SERVER "io.adafruit.com"
|
||||||
|
#define AIO_SERVERPORT 1883
|
||||||
|
#define AIO_USERNAME "adafruitiousername"
|
||||||
|
#define AIO_KEY "adafruitiokey"
|
||||||
|
|
||||||
|
/************ Global State (you don't need to change this!) ******************/
|
||||||
|
|
||||||
|
//Set up the wifi client
|
||||||
|
WiFiClient client;
|
||||||
|
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
// You don't need to change anything below this line!
|
||||||
|
#define halt(s) { Serial.println(F( s )); while(1); }
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'photocell' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");
|
||||||
|
|
||||||
|
// Setup a feed called 'onoff' for subscribing to changes.
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
#define LEDPIN 13
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
WiFi.setPins(WINC_CS, WINC_IRQ, WINC_RST, WINC_EN);
|
||||||
|
|
||||||
|
while (!Serial);
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
Serial.println(F("Adafruit MQTT demo for WINC1500"));
|
||||||
|
|
||||||
|
// Initialise the Client
|
||||||
|
Serial.print(F("\nInit the WiFi module..."));
|
||||||
|
// check for the presence of the breakout
|
||||||
|
if (WiFi.status() == WL_NO_SHIELD) {
|
||||||
|
Serial.println("WINC1500 not present");
|
||||||
|
// don't continue:
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
Serial.println("ATWINC OK!");
|
||||||
|
|
||||||
|
pinMode(LEDPIN, OUTPUT);
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (0 == strcmp((char *)onoffbutton.lastread, "OFF")) {
|
||||||
|
digitalWrite(LEDPIN, LOW);
|
||||||
|
}
|
||||||
|
if (0 == strcmp((char *)onoffbutton.lastread, "ON")) {
|
||||||
|
digitalWrite(LEDPIN, HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can publish stuff!
|
||||||
|
Serial.print(F("\nSending photocell val "));
|
||||||
|
Serial.print(x);
|
||||||
|
Serial.print("...");
|
||||||
|
if (! photocell.publish(x++)) {
|
||||||
|
Serial.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Serial.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// attempt to connect to Wifi network:
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
Serial.print("Attempting to connect to SSID: ");
|
||||||
|
Serial.println(ssid);
|
||||||
|
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
|
||||||
|
status = WiFi.begin(ssid, pass);
|
||||||
|
|
||||||
|
// wait 10 seconds for connection:
|
||||||
|
uint8_t timeout = 10;
|
||||||
|
while (timeout && (WiFi.status() != WL_CONNECTED)) {
|
||||||
|
timeout--;
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Serial.println(mqtt.connectErrorString(ret));
|
||||||
|
Serial.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
}
|
||||||
|
Serial.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
/***************************************************
|
||||||
|
Adafruit MQTT Library Arduino Yun Example
|
||||||
|
|
||||||
|
Make sure your Arduino Yun is connected to a WiFi access point which
|
||||||
|
has internet access. Also note this sketch uses the Console class
|
||||||
|
for debug output so make sure to connect to the Yun over WiFi and
|
||||||
|
open the serial monitor to see the console output.
|
||||||
|
|
||||||
|
Works great with the Arduino Yun:
|
||||||
|
----> https://www.adafruit.com/products/1498
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Tony DiCola for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
****************************************************/
|
||||||
|
#include <Bridge.h>
|
||||||
|
#include <Console.h>
|
||||||
|
#include <BridgeClient.h>
|
||||||
|
#include "Adafruit_MQTT.h"
|
||||||
|
#include "Adafruit_MQTT_Client.h"
|
||||||
|
|
||||||
|
/************************* 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!) ******************/
|
||||||
|
|
||||||
|
// Create a BridgeClient instance to communicate using the Yun's bridge & Linux OS.
|
||||||
|
BridgeClient client;
|
||||||
|
|
||||||
|
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
|
||||||
|
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
|
||||||
|
|
||||||
|
/****************************** Feeds ***************************************/
|
||||||
|
|
||||||
|
// Setup a feed called 'photocell' for publishing.
|
||||||
|
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
|
||||||
|
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell");
|
||||||
|
|
||||||
|
// Setup a feed called 'onoff' for subscribing to changes.
|
||||||
|
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
|
||||||
|
|
||||||
|
/*************************** Sketch Code ************************************/
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Bridge.begin();
|
||||||
|
Console.begin();
|
||||||
|
Console.println(F("Adafruit MQTT demo"));
|
||||||
|
|
||||||
|
// Setup MQTT subscription for onoff feed.
|
||||||
|
mqtt.subscribe(&onoffbutton);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t x=0;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Ensure the connection to the MQTT server is alive (this will make the first
|
||||||
|
// connection and automatically reconnect when disconnected). See the MQTT_connect
|
||||||
|
// function definition further below.
|
||||||
|
MQTT_connect();
|
||||||
|
|
||||||
|
// this is our 'wait for incoming subscription packets' busy subloop
|
||||||
|
Adafruit_MQTT_Subscribe *subscription;
|
||||||
|
while ((subscription = mqtt.readSubscription(1000))) {
|
||||||
|
if (subscription == &onoffbutton) {
|
||||||
|
Console.print(F("Got: "));
|
||||||
|
Console.println((char *)onoffbutton.lastread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can publish stuff!
|
||||||
|
Console.print(F("\nSending photocell val "));
|
||||||
|
Console.print(x);
|
||||||
|
Console.print("...");
|
||||||
|
if (! photocell.publish(x++)) {
|
||||||
|
Console.println(F("Failed"));
|
||||||
|
} else {
|
||||||
|
Console.println(F("OK!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ping the server to keep the mqtt connection alive
|
||||||
|
if(! mqtt.ping()) {
|
||||||
|
Console.println(F("MQTT Ping failed."));
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to connect and reconnect as necessary to the MQTT server.
|
||||||
|
// Should be called in the loop function and it will take care if connecting.
|
||||||
|
void MQTT_connect() {
|
||||||
|
int8_t ret;
|
||||||
|
|
||||||
|
// Stop if already connected.
|
||||||
|
if (mqtt.connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.print("Connecting to MQTT... ");
|
||||||
|
|
||||||
|
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
|
||||||
|
Console.println(mqtt.connectErrorString(ret));
|
||||||
|
Console.println("Retrying MQTT connection in 5 seconds...");
|
||||||
|
mqtt.disconnect();
|
||||||
|
delay(5000); // wait 5 seconds
|
||||||
|
}
|
||||||
|
Console.println("MQTT Connected!");
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
Adafruit_MQTT KEYWORD1
|
||||||
|
Adafruit_MQTT_FONA KEYWORD1
|
||||||
|
Adafruit_MQTT_Client KEYWORD1
|
||||||
|
Adafruit_MQTT_Publish KEYWORD1
|
||||||
|
Adafruit_MQTT_Subscribe KEYWORD1
|
||||||
|
connect KEYWORD2
|
||||||
|
connectErrorString KEYWORD2
|
||||||
|
disconnect KEYWORD2
|
||||||
|
connected KEYWORD2
|
||||||
|
will KEYWORD2
|
||||||
|
publish KEYWORD2
|
||||||
|
subscribe KEYWORD2
|
||||||
|
unsubscribe KEYWORD2
|
||||||
|
readSubscription KEYWORD2
|
||||||
|
ping KEYWORD2
|
||||||
|
setCallback KEYWORD2
|
||||||
|
connectServer KEYWORD2
|
||||||
|
disconnectServer KEYWORD2
|
||||||
|
readPacket KEYWORD2
|
||||||
|
sendPacket KEYWORD2
|
|
@ -0,0 +1,9 @@
|
||||||
|
name=Adafruit MQTT Library
|
||||||
|
version=1.0.3
|
||||||
|
author=Adafruit
|
||||||
|
maintainer=Adafruit <info@adafruit.com>
|
||||||
|
sentence=MQTT library that supports the FONA, ESP8266, Yun, and generic Arduino Client hardware.
|
||||||
|
paragraph=Simple MQTT library that supports the bare minimum to publish and subscribe to topics.
|
||||||
|
category=Communication
|
||||||
|
url=https://github.com/adafruit/Adafruit_MQTT_Library
|
||||||
|
architectures=*
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,504 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
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 Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random
|
||||||
|
Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
|
@ -0,0 +1,108 @@
|
||||||
|
|
||||||
|
# Arduino library to upload sketch over network to supported Arduino board
|
||||||
|
|
||||||
|
This library allows you to update sketches on your board over WiFi or Ethernet.
|
||||||
|
|
||||||
|
The library is a modification of the Arduino WiFi101OTA library.
|
||||||
|
|
||||||
|
![network port in IDE](ArduinoOTA.png)
|
||||||
|
|
||||||
|
## Supported micro-controllers
|
||||||
|
|
||||||
|
* ATmega AVR with at least 64 kB of flash (Arduino Mega, [MegaCore](https://github.com/MCUdude/MegaCore) MCUs, MightyCore 1284p and 644)
|
||||||
|
* Arduino SAMD boards like Zero, M0 or MKR
|
||||||
|
* nRF5 board supported by [nRF5 core](https://github.com/sandeepmistry/arduino-nRF5).
|
||||||
|
* boards supported by ESP8266 and ESP32 Arduino boards package
|
||||||
|
|
||||||
|
## Supported networking libraries
|
||||||
|
|
||||||
|
* Ethernet library - shields and modules with Wiznet 5100, 5200 and 5500 chips
|
||||||
|
* WiFi101 - WiFi101 shield and MKR 1000
|
||||||
|
* WiFiNINA - MKR 1010, MKR 4000, ESP32 as SPI network adapter with WiFiNINA firmware
|
||||||
|
* WiFiLink - esp8266 as network adapter with WiFiLink firmware (SPI or Serial)
|
||||||
|
* UIPEthernet - shields and modules with ENC28j60 chip
|
||||||
|
* WiFiSpi - esp8266 as SPI network adapter with WiFiSpi library updated with [this PR](https://github.com/JiriBilek/WiFiSpi/pull/12)
|
||||||
|
* WiFi - Arduino WiFi Shield (not tested)
|
||||||
|
* WiFi library of ESP8266 and ESP32 Arduino boards package
|
||||||
|
|
||||||
|
UIPEthernet, WiFiSpi and WiFi library don't support UDP multicast for MDNS, so Arduino IDE will not show the network upload port. WiFiLink doesn't support UDP multicast, but the firmware propagates the MDNS record.
|
||||||
|
|
||||||
|
WiFiEsp library for esp8266 with AT firmware failed tests and there is no easy fix.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The library is in Library Manager of the Arduino IDE.
|
||||||
|
|
||||||
|
Arduino SAMD boards (Zero, M0, MKR) are supported 'out of the box'.
|
||||||
|
|
||||||
|
For nRF5 boards two lines need to be added to platform.txt file of the nRF5 Arduino package (until the PR that adds them is accepted and included in a release). Only nRF51 was tested until now. For details scroll down.
|
||||||
|
|
||||||
|
For ESP8266 and ESP32 boards, platform.local.txt from extras folder need to be copied into boards package installation folder and the bundled ArduinoOTA library must be deleted. For details scroll down.
|
||||||
|
|
||||||
|
ATmega boards require to flash a modified Optiboot bootloader for flash write operations. Details are below.
|
||||||
|
|
||||||
|
## ATmega support
|
||||||
|
|
||||||
|
The size of networking library and SD library limit the use of ArduinoOTA library to ATmega MCUs with at least 64 kB flash memory.
|
||||||
|
|
||||||
|
*There are other network upload options for here excluded ATmega328p: ([Ariadne bootloader](https://github.com/loathingKernel/ariadne-bootloader) for Wiznet chips, [WiFiLink firmware](https://github.com/jandrassy/arduino-firmware-wifilink) for the esp8266).*
|
||||||
|
|
||||||
|
For upload over InternalStorage, Optiboot bootloader with [`copy_flash_pages` function](https://github.com/Optiboot/optiboot/pull/269) is required. Arduino AVR package doesn't use Optiboot for Arduino Mega. For Arduino Mega you can download [my boards definitions](https://github.com/jandrassy/my_boards) and use it [to burn](https://arduino.stackexchange.com/questions/473/how-do-i-burn-the-bootloader) the modified Optiboot and to upload sketches to your Mega over USB and over network.
|
||||||
|
|
||||||
|
For SDStorage a 'SD bootloader' is required to load the uploaded file from the SD card. There is no good SD bootloader. 2boots works only with not available old types of SD cards and zevero/avr_boot doesn't yet support USB upload of sketch. The SDStorage was tested with zevero/avr_boot. The ATmega_SD example shows how to use this ArduinoOTA library with SD bootloader.
|
||||||
|
|
||||||
|
To use remote upload from IDE with SDStorage or InternalStorage, copy platform.local.txt from extras/avr folder, next to platform.txt in the boards package used (Arduino-avr or MCUdude packages). Packages are located in ~/.arduino15/packages/ on Linux and %userprofile%\AppData\Local\Arduino15\packages\ on Windows (AppData is a hidden folder). The platform.local.txt contains a line to create a .bin file and a line to override tools.avrdude.upload.network_pattern, because in platform.txt it is defined for Yun.
|
||||||
|
|
||||||
|
The IDE upload tool is installed with Arduino AVR core package. At least version 1.2 of the arduinoOTA tool is required. For upload from command line without IDE see the command template in extras/avr/platform.local.txt.
|
||||||
|
|
||||||
|
## ESP8266 and ESP32 support
|
||||||
|
|
||||||
|
The ArduinoOTA library bundled with ESP8266 and ESP32 Arduino packages works only with native WiFi libraries. This library allows to upload a sketch to esp8266 or esp32 over Ethernet with Ethernet or UIPEthernet library. Upload over the native WiFi library works too.
|
||||||
|
|
||||||
|
To use this library instead of the bundled library, the bundled library must be removed from the boards package library folder. To override the configuration of OTA upload in platform.txt, copy the platform.local.txt file from extras folder of this library next to platform.txt file in boards package installation folder. Packages are located in ~/.arduino15/packages/ on Linux and %userprofile%\AppData\Local\Arduino15\packages\ on Windows (AppData is a hidden folder).
|
||||||
|
|
||||||
|
The esp8266 boards package has bundled Ethernet library. It is an old version of the Arduino Ethernet library which works only with W5100 chips. It is better to remove it from boards package and install the Ethernet library from Library Manager in IDE.
|
||||||
|
|
||||||
|
This library supports SPIFFS upload to esp8266 and esp32, but the IDE plugins have the network upload tool hardcoded to espota. It can't be changed in configuration. To upload SPIFFS, call the plugin in Tools menu and after it fails to upload over network, go to location of the created bin file and upload the file with arduinoOTA tool from command line. The location of the file is printed in the IDE console window. Upload command example (linux):
|
||||||
|
```
|
||||||
|
~/arduino-1.8.8/hardware/tools/avr/bin/arduinoOTA -address 192.168.1.107 -port 65280 -username arduino -password password -sketch OTEthernet.spiffs.bin -upload /data -b
|
||||||
|
```
|
||||||
|
(the same command can be used to upload the sketch binary, only use `-upload /sketch`)
|
||||||
|
|
||||||
|
## nRF5 support
|
||||||
|
|
||||||
|
Note: Only nRF51 was tested for now
|
||||||
|
|
||||||
|
If SoftDevice is not used, the sketch is written from address 0. For write to address 0 the sketch must be compiled with -fno-delete-null-pointer-checks.
|
||||||
|
|
||||||
|
For SD card update use [SDUnRF5 library](https://github.com/jandrassy/SDUnRF5).
|
||||||
|
|
||||||
|
To use remote upload from IDE, add lines from [this PR](https://github.com/sandeepmistry/arduino-nRF5/pull/327/commits/4b70ae7207124bd92afa14e562e4f0c4d931220d) to sandeepmistry/hardware/nRF5/0.6.0/platform.txt at the end of section "OpenOCD sketch upload":
|
||||||
|
|
||||||
|
If you use SoftDevice, stop BLE before applying update. Use `ArduinoOTA.beforeApply` to register a callback function. For example in setup `ArduinoOTA.beforeApply(shutdown);` and add the function to to sketch:
|
||||||
|
|
||||||
|
```
|
||||||
|
void shutdown() {
|
||||||
|
blePeripheral.end();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Boards tested
|
||||||
|
|
||||||
|
* SAMD
|
||||||
|
- Arduino MKR Zero
|
||||||
|
- Crowduino M0 SD
|
||||||
|
* nRF5
|
||||||
|
- Seeed Arch Link
|
||||||
|
* ATmega
|
||||||
|
- Arduino Mega
|
||||||
|
- Badio 1284p
|
||||||
|
* esp8266
|
||||||
|
- Wemos D1 mini
|
||||||
|
* esp32
|
||||||
|
- ESP32 Dev Module
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
Please report tested boards.
|
||||||
|
|
||||||
|
Other ARM based MCUs could be added with code similar to SAMD and nRF5.
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
This example polls for sketch updates over Ethernet, sketches
|
||||||
|
can be updated by selecting a network port from within
|
||||||
|
the Arduino IDE: Tools -> Port -> Network Ports ...
|
||||||
|
|
||||||
|
Circuit:
|
||||||
|
* W5100, W5200 or W5500 Ethernet shield with SD card attached
|
||||||
|
|
||||||
|
created 13 July 2010
|
||||||
|
by dlf (Metodo2 srl)
|
||||||
|
modified 31 May 2012
|
||||||
|
by Tom Igoe
|
||||||
|
modified 16 January 2017
|
||||||
|
by Sandeep Mistry
|
||||||
|
ArduinoOTA version Dec 2018
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <SD.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
|
||||||
|
// Enter a MAC address for your controller below.
|
||||||
|
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
|
||||||
|
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//Initialize serial:
|
||||||
|
Serial.begin(9600);
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
// setup SD card
|
||||||
|
Serial.print("Initializing SD card...");
|
||||||
|
if (!SD.begin(SDCARD_SS_PIN)) {
|
||||||
|
Serial.println("initialization failed!");
|
||||||
|
// don't continue:
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
Serial.println("initialization done.");
|
||||||
|
|
||||||
|
// start the Ethernet connection:
|
||||||
|
Serial.println("Initialize Ethernet with DHCP:");
|
||||||
|
if (Ethernet.begin(mac) == 0) {
|
||||||
|
Serial.println("Failed to configure Ethernet using DHCP");
|
||||||
|
} else {
|
||||||
|
Serial.print(" DHCP assigned IP ");
|
||||||
|
Serial.println(Ethernet.localIP());
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the OTEthernet library with SD based storage
|
||||||
|
SDStorage.setUpdateFileName("FIRMWARE.BIN"); // for https://github.com/zevero/avr_boot/
|
||||||
|
SDStorage.clear(); // AVR SD bootloaders don't delete the update file
|
||||||
|
ArduinoOTA.begin(Ethernet.localIP(), "Arduino", "password", SDStorage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// check for updates
|
||||||
|
ArduinoOTA.poll();
|
||||||
|
|
||||||
|
// add your normal loop code below ...
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
This example polls for sketch updates over Ethernet, sketches
|
||||||
|
can be updated by selecting a network port from within
|
||||||
|
the Arduino IDE: Tools -> Port -> Network Ports ...
|
||||||
|
|
||||||
|
Circuit:
|
||||||
|
* W5100, W5200 or W5500 Ethernet shield attached
|
||||||
|
|
||||||
|
created 13 July 2010
|
||||||
|
by dlf (Metodo2 srl)
|
||||||
|
modified 31 May 2012
|
||||||
|
by Tom Igoe
|
||||||
|
modified 16 January 2017
|
||||||
|
by Sandeep Mistry
|
||||||
|
Ethernet version August 2018
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
|
||||||
|
//#define Serial SerialUSB
|
||||||
|
|
||||||
|
// Enter a MAC address for your controller below.
|
||||||
|
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
|
||||||
|
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//Initialize serial:
|
||||||
|
Serial.begin(9600);
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
// start the Ethernet connection:
|
||||||
|
Serial.println("Initialize Ethernet with DHCP:");
|
||||||
|
if (Ethernet.begin(mac) == 0) {
|
||||||
|
Serial.println("Failed to configure Ethernet using DHCP");
|
||||||
|
} else {
|
||||||
|
Serial.print(" DHCP assigned IP ");
|
||||||
|
Serial.println(Ethernet.localIP());
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the OTEthernet library with internal (flash) based storage
|
||||||
|
ArduinoOTA.begin(Ethernet.localIP(), "Arduino", "password", InternalStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// check for updates
|
||||||
|
ArduinoOTA.poll();
|
||||||
|
|
||||||
|
// add your normal loop code below ...
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
This example polls for sketch updates over Ethernet, sketches
|
||||||
|
can be updated by selecting a network port from within
|
||||||
|
the Arduino IDE: Tools -> Port -> Network Ports ...
|
||||||
|
|
||||||
|
Circuit:
|
||||||
|
* W5100, W5200 or W5500 Ethernet shield with SD card attached
|
||||||
|
|
||||||
|
created 13 July 2010
|
||||||
|
by dlf (Metodo2 srl)
|
||||||
|
modified 31 May 2012
|
||||||
|
by Tom Igoe
|
||||||
|
modified 16 January 2017
|
||||||
|
by Sandeep Mistry
|
||||||
|
Ethernet version August 2018
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <SD.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
#include <SDU.h>
|
||||||
|
|
||||||
|
//#define Serial SerialUSB
|
||||||
|
|
||||||
|
// Enter a MAC address for your controller below.
|
||||||
|
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
|
||||||
|
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//Initialize serial:
|
||||||
|
Serial.begin(9600);
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
// setup SD card
|
||||||
|
Serial.print("Initializing SD card...");
|
||||||
|
if (!SD.begin(SDCARD_SS_PIN)) {
|
||||||
|
Serial.println("initialization failed!");
|
||||||
|
// don't continue:
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
Serial.println("initialization done.");
|
||||||
|
|
||||||
|
// start the Ethernet connection:
|
||||||
|
Serial.println("Initialize Ethernet with DHCP:");
|
||||||
|
if (Ethernet.begin(mac) == 0) {
|
||||||
|
Serial.println("Failed to configure Ethernet using DHCP");
|
||||||
|
} else {
|
||||||
|
Serial.print(" DHCP assigned IP ");
|
||||||
|
Serial.println(Ethernet.localIP());
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the OTEthernet library with SD based storage
|
||||||
|
ArduinoOTA.begin(Ethernet.localIP(), "Arduino", "password", SDStorage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// check for updates
|
||||||
|
ArduinoOTA.poll();
|
||||||
|
|
||||||
|
// add your normal loop code below ...
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
This example connects to an WPA encrypted WiFi network.
|
||||||
|
Then it prints the MAC address of the Wifi shield,
|
||||||
|
the IP address obtained, and other network details.
|
||||||
|
It then polls for sketch updates over WiFi, sketches
|
||||||
|
can be updated by selecting a network port from within
|
||||||
|
the Arduino IDE: Tools -> Port -> Network Ports ...
|
||||||
|
|
||||||
|
Circuit:
|
||||||
|
* WiFi shield attached
|
||||||
|
|
||||||
|
created 13 July 2010
|
||||||
|
by dlf (Metodo2 srl)
|
||||||
|
modified 31 May 2012
|
||||||
|
by Tom Igoe
|
||||||
|
modified 16 January 2017
|
||||||
|
by Sandeep Mistry
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <WiFi101.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
|
||||||
|
#include "arduino_secrets.h"
|
||||||
|
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
|
||||||
|
/////// Wifi Settings ///////
|
||||||
|
char ssid[] = SECRET_SSID; // your network SSID (name)
|
||||||
|
char pass[] = SECRET_PASS; // your network password
|
||||||
|
|
||||||
|
int status = WL_IDLE_STATUS;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//Initialize serial:
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// check for the presence of the shield:
|
||||||
|
if (WiFi.status() == WL_NO_SHIELD) {
|
||||||
|
Serial.println("WiFi shield not present");
|
||||||
|
// don't continue:
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to connect to Wifi network:
|
||||||
|
while ( status != WL_CONNECTED) {
|
||||||
|
Serial.print("Attempting to connect to SSID: ");
|
||||||
|
Serial.println(ssid);
|
||||||
|
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
|
||||||
|
status = WiFi.begin(ssid, pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the WiFi OTA library with internal (flash) based storage
|
||||||
|
ArduinoOTA.begin(WiFi.localIP(), "Arduino", "password", InternalStorage);
|
||||||
|
|
||||||
|
// you're connected now, so print out the status:
|
||||||
|
printWifiStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// check for WiFi OTA updates
|
||||||
|
ArduinoOTA.poll();
|
||||||
|
|
||||||
|
// add your normal loop code below ...
|
||||||
|
}
|
||||||
|
|
||||||
|
void printWifiStatus() {
|
||||||
|
// print the SSID of the network you're attached to:
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(WiFi.SSID());
|
||||||
|
|
||||||
|
// print your WiFi shield's IP address:
|
||||||
|
IPAddress ip = WiFi.localIP();
|
||||||
|
Serial.print("IP Address: ");
|
||||||
|
Serial.println(ip);
|
||||||
|
|
||||||
|
// print the received signal strength:
|
||||||
|
long rssi = WiFi.RSSI();
|
||||||
|
Serial.print("signal strength (RSSI):");
|
||||||
|
Serial.print(rssi);
|
||||||
|
Serial.println(" dBm");
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#define SECRET_SSID ""
|
||||||
|
#define SECRET_PASS ""
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
This example connects to an WPA encrypted WiFi network.
|
||||||
|
Then it prints the MAC address of the Wifi shield,
|
||||||
|
the IP address obtained, and other network details.
|
||||||
|
It then polls for sketch updates over WiFi, sketches
|
||||||
|
can be updated by selecting a network port from within
|
||||||
|
the Arduino IDE: Tools -> Port -> Network Ports ...
|
||||||
|
|
||||||
|
Circuit:
|
||||||
|
* WiFi shield attached
|
||||||
|
* SD shield attached
|
||||||
|
|
||||||
|
created 13 July 2010
|
||||||
|
by dlf (Metodo2 srl)
|
||||||
|
modified 31 May 2012
|
||||||
|
by Tom Igoe
|
||||||
|
modified 16 January 2017
|
||||||
|
by Sandeep Mistry
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <SD.h>
|
||||||
|
#include <WiFi101.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
#include <SDU.h>
|
||||||
|
|
||||||
|
#include "arduino_secrets.h"
|
||||||
|
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
|
||||||
|
/////// Wifi Settings ///////
|
||||||
|
char ssid[] = SECRET_SSID; // your network SSID (name)
|
||||||
|
char pass[] = SECRET_PASS; // your network password
|
||||||
|
|
||||||
|
int status = WL_IDLE_STATUS;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//Initialize serial:
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// setup SD card
|
||||||
|
Serial.print("Initializing SD card...");
|
||||||
|
if (!SD.begin(SDCARD_SS_PIN)) {
|
||||||
|
Serial.println("initialization failed!");
|
||||||
|
// don't continue:
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
Serial.println("initialization done.");
|
||||||
|
|
||||||
|
// check for the presence of the shield:
|
||||||
|
if (WiFi.status() == WL_NO_SHIELD) {
|
||||||
|
Serial.println("WiFi shield not present");
|
||||||
|
// don't continue:
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to connect to Wifi network:
|
||||||
|
while ( status != WL_CONNECTED) {
|
||||||
|
Serial.print("Attempting to connect to SSID: ");
|
||||||
|
Serial.println(ssid);
|
||||||
|
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
|
||||||
|
status = WiFi.begin(ssid, pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the WiFi OTA library with SD based storage
|
||||||
|
ArduinoOTA.begin(WiFi.localIP(), "Arduino", "password", SDStorage);
|
||||||
|
|
||||||
|
// you're connected now, so print out the status:
|
||||||
|
printWifiStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// check for WiFi OTA updates
|
||||||
|
ArduinoOTA.poll();
|
||||||
|
|
||||||
|
// add your normal loop code below ...
|
||||||
|
}
|
||||||
|
|
||||||
|
void printWifiStatus() {
|
||||||
|
// print the SSID of the network you're attached to:
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(WiFi.SSID());
|
||||||
|
|
||||||
|
// print your WiFi shield's IP address:
|
||||||
|
IPAddress ip = WiFi.localIP();
|
||||||
|
Serial.print("IP Address: ");
|
||||||
|
Serial.println(ip);
|
||||||
|
|
||||||
|
// print the received signal strength:
|
||||||
|
long rssi = WiFi.RSSI();
|
||||||
|
Serial.print("signal strength (RSSI):");
|
||||||
|
Serial.print(rssi);
|
||||||
|
Serial.println(" dBm");
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#define SECRET_SSID ""
|
||||||
|
#define SECRET_PASS ""
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
# This configuration file supports the general ArduinoOTA library https://github.com/jandrassy/ArduinoOTA
|
||||||
|
|
||||||
|
## Create output (bin file)
|
||||||
|
recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2hex.cmd}" -O binary {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin"
|
||||||
|
|
||||||
|
tools.avrdude.upload.network_pattern="{network_cmd}" -address {serial.port} -port 65280 -username arduino -password "{network.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -b
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
# This configuration file supports the general ArduinoOTA library https://github.com/jandrassy/ArduinoOTA
|
||||||
|
|
||||||
|
tools.esptool_py.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA
|
||||||
|
tools.esptool_py.upload.network_pattern="{network_cmd}" -address {serial.port} -port 65280 -username arduino -password "{network.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -b
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
# This configuration file supports the general ArduinoOTA library https://github.com/jandrassy/ArduinoOTA
|
||||||
|
|
||||||
|
tools.esptool.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA
|
||||||
|
tools.esptool.upload.network_pattern="{network_cmd}" -address {serial.port} -port 65280 -username arduino -password "{network.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -b
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map For WiFi101OTA
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
ArduinoOTA KEYWORD1
|
||||||
|
|
||||||
|
InternalStorage KEYWORD1
|
||||||
|
SDStorage KEYWORD1
|
||||||
|
SerialFlashStorage KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
begin KEYWORD2
|
||||||
|
poll KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
|
@ -0,0 +1,10 @@
|
||||||
|
name=ArduinoOTA
|
||||||
|
version=1.0.1
|
||||||
|
author=Arduino,Juraj Andrassy
|
||||||
|
maintainer=Juraj Andrassy <juraj.andrassy@gmail.com>
|
||||||
|
sentence=Upload sketch over network to Arduino board with WiFi or Ethernet libraries
|
||||||
|
paragraph=Based on WiFi101OTA library. Uploads over Ethernet, UIPEthernet, WiFi101, WiFiNina, WiFiLink, WiFi to SAMD, nRF5, esp8266, esp32 and to ATmega with more then 64 kB flash memory.
|
||||||
|
category=Other
|
||||||
|
url=https://github.com/jandrassy/ArduinoOTA
|
||||||
|
architectures=*
|
||||||
|
includes=ArduinoOTA.h
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2018 Juraj Andrassy
|
||||||
|
|
||||||
|
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 _ARDUINOOTA_H_
|
||||||
|
#define _ARDUINOOTA_H_
|
||||||
|
|
||||||
|
#include "WiFiOTA.h"
|
||||||
|
#ifdef __AVR__
|
||||||
|
#if FLASHEND >= 0xFFFF
|
||||||
|
#include "InternalStorageAVR.h"
|
||||||
|
#endif
|
||||||
|
#elif defined(ESP8266) || defined(ESP32)
|
||||||
|
#include "InternalStorageESP.h"
|
||||||
|
#else
|
||||||
|
#include "InternalStorage.h"
|
||||||
|
#endif
|
||||||
|
#ifdef __SD_H__
|
||||||
|
#include "SDStorage.h"
|
||||||
|
SDStorageClass SDStorage;
|
||||||
|
#endif
|
||||||
|
#ifdef SerialFlash_h_
|
||||||
|
#include "SerialFlashStorage.h"
|
||||||
|
SerialFlashStorageClass SerialFlashStorage;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
template <class NetServer, class NetClient>
|
||||||
|
class ArduinoOTAClass : public WiFiOTAClass {
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetServer server;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ArduinoOTAClass() : server(65280) {};
|
||||||
|
|
||||||
|
void begin(IPAddress localIP, const char* name, const char* password, OTAStorage& storage) {
|
||||||
|
WiFiOTAClass::begin(localIP, name, password, storage);
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll() {
|
||||||
|
NetClient client = server.available();
|
||||||
|
pollServer(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle() { // alias
|
||||||
|
poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class NetServer, class NetClient, class NetUDP>
|
||||||
|
class ArduinoOTAMdnsClass : public ArduinoOTAClass<NetServer, NetClient> {
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetUDP mdnsSocket;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ArduinoOTAMdnsClass() {};
|
||||||
|
|
||||||
|
void begin(IPAddress localIP, const char* name, const char* password, OTAStorage& storage) {
|
||||||
|
ArduinoOTAClass<NetServer, NetClient>::begin(localIP, name, password, storage);
|
||||||
|
#if defined(ESP8266) && !(defined(ethernet_h_) || defined(ethernet_h) || defined(UIPETHERNET_H))
|
||||||
|
mdnsSocket.beginMulticast(localIP, IPAddress(224, 0, 0, 251), 5353);
|
||||||
|
#else
|
||||||
|
mdnsSocket.beginMulticast(IPAddress(224, 0, 0, 251), 5353);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll() {
|
||||||
|
ArduinoOTAClass<NetServer, NetClient>::poll();
|
||||||
|
WiFiOTAClass::pollMdns(mdnsSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(ethernet_h_) || defined(ethernet_h) // Ethernet library
|
||||||
|
ArduinoOTAMdnsClass <EthernetServer, EthernetClient, EthernetUDP> ArduinoOTA;
|
||||||
|
|
||||||
|
#elif defined(UIPETHERNET_H) // no UDP multicast implementation yet
|
||||||
|
ArduinoOTAClass <EthernetServer, EthernetClient> ArduinoOTA;
|
||||||
|
|
||||||
|
#elif defined(WiFiNINA_h) || defined(WIFI_H) || defined(ESP8266) || defined(ESP32) // NINA, WiFi101 and Espressif WiFi
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
ArduinoOTAMdnsClass <WiFiServer, WiFiClient, WiFiUDP> ArduinoOTA;
|
||||||
|
|
||||||
|
#elif defined(WiFi_h) // WiFi and WiFiLink lib (no UDP multicast), for WiFiLink firmware handles mdns
|
||||||
|
ArduinoOTAClass <WiFiServer, WiFiClient> ArduinoOTA;
|
||||||
|
|
||||||
|
#elif defined(_WIFISPI_H_INCLUDED) // no UDP multicast implementation
|
||||||
|
ArduinoOTAClass <WiFiSpiServer, WiFiSpiClient> ArduinoOTA;
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Network library not included or not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
WiFi101OTA version Feb 2017
|
||||||
|
by Sandeep Mistry (Arduino)
|
||||||
|
modified for ArduinoOTA Dec 2018
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(__AVR__) && !defined(ESP8266) && !defined(ESP32)
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "InternalStorage.h"
|
||||||
|
|
||||||
|
InternalStorageClass::InternalStorageClass() :
|
||||||
|
MAX_PARTIONED_SKETCH_SIZE((MAX_FLASH - SKETCH_START_ADDRESS) / 2),
|
||||||
|
STORAGE_START_ADDRESS(SKETCH_START_ADDRESS + MAX_PARTIONED_SKETCH_SIZE)
|
||||||
|
{
|
||||||
|
_writeIndex = 0;
|
||||||
|
_writeAddress = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// these functions must be in RAM (.data) and NOT inlined
|
||||||
|
// as they erase and copy the sketch data in flash
|
||||||
|
|
||||||
|
__attribute__ ((long_call, noinline, section (".data#"))) //
|
||||||
|
void waitForReady() {
|
||||||
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
|
while (!NVMCTRL->INTFLAG.bit.READY);
|
||||||
|
#elif defined(ARDUINO_ARCH_NRF5)
|
||||||
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((long_call, noinline, section (".data#")))
|
||||||
|
static void eraseFlash(int address, int length, int pageSize)
|
||||||
|
{
|
||||||
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
|
int rowSize = pageSize * 4;
|
||||||
|
for (int i = 0; i < length; i += rowSize) {
|
||||||
|
NVMCTRL->ADDR.reg = ((uint32_t)(address + i)) / 2;
|
||||||
|
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
|
||||||
|
|
||||||
|
waitForReady();
|
||||||
|
}
|
||||||
|
#elif defined(ARDUINO_ARCH_NRF5)
|
||||||
|
// Enable erasing flash
|
||||||
|
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
|
||||||
|
|
||||||
|
// Erase page(s)
|
||||||
|
int end_address = address + length;
|
||||||
|
while (address < end_address) {
|
||||||
|
waitForReady();
|
||||||
|
// Erase one 1k/4k page
|
||||||
|
NRF_NVMC->ERASEPAGE = address;
|
||||||
|
address = address + pageSize;
|
||||||
|
}
|
||||||
|
// Disable erasing, enable write
|
||||||
|
waitForReady();
|
||||||
|
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
|
||||||
|
waitForReady();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((long_call, noinline, section (".data#")))
|
||||||
|
static void copyFlashAndReset(int dest, int src, int length, int pageSize)
|
||||||
|
{
|
||||||
|
uint32_t* d = (uint32_t*)dest;
|
||||||
|
uint32_t* s = (uint32_t*)src;
|
||||||
|
|
||||||
|
eraseFlash(dest, length, pageSize);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i += 4) {
|
||||||
|
*d++ = *s++;
|
||||||
|
|
||||||
|
waitForReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int InternalStorageClass::open(int length)
|
||||||
|
{
|
||||||
|
(void)length;
|
||||||
|
_writeIndex = 0;
|
||||||
|
_writeAddress = (uint32_t*)STORAGE_START_ADDRESS;
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_SAMD
|
||||||
|
// enable auto page writes
|
||||||
|
NVMCTRL->CTRLB.bit.MANW = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
eraseFlash(STORAGE_START_ADDRESS, MAX_PARTIONED_SKETCH_SIZE, PAGE_SIZE);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t InternalStorageClass::write(uint8_t b)
|
||||||
|
{
|
||||||
|
_addressData.u8[_writeIndex] = b;
|
||||||
|
_writeIndex++;
|
||||||
|
|
||||||
|
if (_writeIndex == 4) {
|
||||||
|
_writeIndex = 0;
|
||||||
|
|
||||||
|
*_writeAddress = _addressData.u32;
|
||||||
|
|
||||||
|
_writeAddress++;
|
||||||
|
|
||||||
|
waitForReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageClass::close()
|
||||||
|
{
|
||||||
|
while ((int)_writeAddress % PAGE_SIZE) {
|
||||||
|
write(0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageClass::clear()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageClass::apply()
|
||||||
|
{
|
||||||
|
// disable interrupts, as vector table will be erase during flash sequence
|
||||||
|
noInterrupts();
|
||||||
|
|
||||||
|
copyFlashAndReset(SKETCH_START_ADDRESS, STORAGE_START_ADDRESS, MAX_PARTIONED_SKETCH_SIZE, PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
long InternalStorageClass::maxSize()
|
||||||
|
{
|
||||||
|
return MAX_PARTIONED_SKETCH_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalStorageClass InternalStorage;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
WiFi101OTA version Feb 2017
|
||||||
|
by Sandeep Mistry (Arduino)
|
||||||
|
modified for ArduinoOTA Dec 2018
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INTERNAL_STORAGE_H_INCLUDED
|
||||||
|
#define _INTERNAL_STORAGE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "OTAStorage.h"
|
||||||
|
|
||||||
|
class InternalStorageClass : public OTAStorage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
InternalStorageClass();
|
||||||
|
|
||||||
|
virtual int open(int length);
|
||||||
|
virtual size_t write(uint8_t);
|
||||||
|
virtual void close();
|
||||||
|
virtual void clear();
|
||||||
|
virtual void apply();
|
||||||
|
virtual long maxSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint32_t MAX_PARTIONED_SKETCH_SIZE, STORAGE_START_ADDRESS;
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint32_t u32;
|
||||||
|
uint8_t u8[4];
|
||||||
|
} _addressData;
|
||||||
|
|
||||||
|
int _writeIndex;
|
||||||
|
uint32_t* _writeAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern InternalStorageClass InternalStorage;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2018 Juraj Andrassy
|
||||||
|
|
||||||
|
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 <Arduino.h>
|
||||||
|
|
||||||
|
#if defined(__AVR__) && FLASHEND >= 0xFFFF
|
||||||
|
|
||||||
|
#include "InternalStorageAVR.h"
|
||||||
|
#include "utility/optiboot.h"
|
||||||
|
|
||||||
|
InternalStorageAVRClass::InternalStorageAVRClass() {
|
||||||
|
maxSketchSize = (MAX_FLASH - bootloaderSize) / 2;
|
||||||
|
maxSketchSize = (maxSketchSize / SPM_PAGESIZE) * SPM_PAGESIZE; // align to page
|
||||||
|
pageAddress = maxSketchSize;
|
||||||
|
pageIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int InternalStorageAVRClass::open(int length) {
|
||||||
|
(void)length;
|
||||||
|
pageAddress = maxSketchSize;
|
||||||
|
pageIndex = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t InternalStorageAVRClass::write(uint8_t b) {
|
||||||
|
if (pageIndex == 0) {
|
||||||
|
optiboot_page_erase(pageAddress);
|
||||||
|
}
|
||||||
|
dataWord.u8[pageIndex % 2] = b;
|
||||||
|
if (pageIndex % 2) {
|
||||||
|
optiboot_page_fill(pageAddress + pageIndex, dataWord.u16);
|
||||||
|
}
|
||||||
|
pageIndex++;
|
||||||
|
if (pageIndex == SPM_PAGESIZE) {
|
||||||
|
optiboot_page_write(pageAddress);
|
||||||
|
pageIndex = 0;
|
||||||
|
pageAddress += SPM_PAGESIZE;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageAVRClass::close() {
|
||||||
|
if (pageIndex) {
|
||||||
|
optiboot_page_write(pageAddress);
|
||||||
|
}
|
||||||
|
pageIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageAVRClass::clear() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageAVRClass::apply() {
|
||||||
|
copy_flash_pages_cli(SKETCH_START_ADDRESS, maxSketchSize, (pageAddress - maxSketchSize) / SPM_PAGESIZE + 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
long InternalStorageAVRClass::maxSize() {
|
||||||
|
return maxSketchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalStorageAVRClass InternalStorage;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2018 Juraj Andrassy
|
||||||
|
|
||||||
|
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 _INTERNAL_STORAGE_AVR_H_INCLUDED
|
||||||
|
#define _INTERNAL_STORAGE_AVR_H_INCLUDED
|
||||||
|
|
||||||
|
#include "OTAStorage.h"
|
||||||
|
|
||||||
|
class InternalStorageAVRClass : public OTAStorage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
InternalStorageAVRClass();
|
||||||
|
|
||||||
|
virtual int open(int length);
|
||||||
|
virtual size_t write(uint8_t);
|
||||||
|
virtual void close();
|
||||||
|
virtual void clear();
|
||||||
|
virtual void apply();
|
||||||
|
virtual long maxSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t maxSketchSize;
|
||||||
|
uint32_t pageAddress;
|
||||||
|
uint16_t pageIndex;
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint16_t u16;
|
||||||
|
uint8_t u8[2];
|
||||||
|
} dataWord;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern InternalStorageAVRClass InternalStorage;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2019 Juraj Andrassy. All right reserved.
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(ESP8266) || defined(ESP32)
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
#include <Update.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "InternalStorageESP.h"
|
||||||
|
|
||||||
|
InternalStorageESPClass::InternalStorageESPClass()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int InternalStorageESPClass::open(int length, uint8_t command)
|
||||||
|
{
|
||||||
|
return Update.begin(length, command == 0 ? U_FLASH : U_SPIFFS);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t InternalStorageESPClass::write(uint8_t b)
|
||||||
|
{
|
||||||
|
return Update.write(&b, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageESPClass::close()
|
||||||
|
{
|
||||||
|
Update.end(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageESPClass::clear()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalStorageESPClass::apply()
|
||||||
|
{
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
long InternalStorageESPClass::maxSize()
|
||||||
|
{
|
||||||
|
return ESP.getFlashChipSize() / 2; // Update.begin() in open() does the exact check
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalStorageESPClass InternalStorage;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2019 Juraj Andrassy. All right reserved.
|
||||||
|
|
||||||
|
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 _INTERNAL_STORAGE_ESP_H_INCLUDED
|
||||||
|
#define _INTERNAL_STORAGE_ESP_H_INCLUDED
|
||||||
|
|
||||||
|
#include "OTAStorage.h"
|
||||||
|
|
||||||
|
class InternalStorageESPClass : public OTAStorage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
InternalStorageESPClass();
|
||||||
|
|
||||||
|
virtual int open(int length) {
|
||||||
|
return open(length, 0);
|
||||||
|
}
|
||||||
|
virtual int open(int length, uint8_t command);
|
||||||
|
virtual size_t write(uint8_t);
|
||||||
|
virtual void close();
|
||||||
|
virtual void clear();
|
||||||
|
virtual void apply();
|
||||||
|
virtual long maxSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
extern InternalStorageESPClass InternalStorage;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include "OTAStorage.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
|
static const uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 };
|
||||||
|
extern "C" {
|
||||||
|
char * __text_start__(); // 0x2000, 0x0 without bootloader and 0x4000 for M0 original bootloader
|
||||||
|
}
|
||||||
|
#elif defined(ARDUINO_ARCH_NRF5)
|
||||||
|
extern "C" {
|
||||||
|
char * __isr_vector();
|
||||||
|
}
|
||||||
|
#elif defined(__AVR__)
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
#include <avr/boot.h>
|
||||||
|
#define MIN_BOOTSZ (4 * SPM_PAGESIZE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OTAStorage::OTAStorage() :
|
||||||
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
|
SKETCH_START_ADDRESS((uint32_t) __text_start__),
|
||||||
|
PAGE_SIZE(pageSizes[NVMCTRL->PARAM.bit.PSZ]),
|
||||||
|
MAX_FLASH(PAGE_SIZE * NVMCTRL->PARAM.bit.NVMP)
|
||||||
|
#elif defined(ARDUINO_ARCH_NRF5)
|
||||||
|
SKETCH_START_ADDRESS((uint32_t) __isr_vector),
|
||||||
|
PAGE_SIZE((size_t) NRF_FICR->CODEPAGESIZE),
|
||||||
|
MAX_FLASH(PAGE_SIZE * (uint32_t) NRF_FICR->CODESIZE)
|
||||||
|
#elif defined(__AVR__)
|
||||||
|
SKETCH_START_ADDRESS(0),
|
||||||
|
PAGE_SIZE(SPM_PAGESIZE),
|
||||||
|
MAX_FLASH((uint32_t) FLASHEND + 1)
|
||||||
|
#elif defined(ESP8266) || defined(ESP32)
|
||||||
|
SKETCH_START_ADDRESS(0), // not used
|
||||||
|
PAGE_SIZE(0), // not used
|
||||||
|
MAX_FLASH(0) // not used
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
bootloaderSize = 0;
|
||||||
|
#ifdef __AVR__
|
||||||
|
cli();
|
||||||
|
uint8_t highBits = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
|
||||||
|
sei();
|
||||||
|
if (!(highBits & bit(FUSE_BOOTRST))) {
|
||||||
|
byte v = (highBits & ((~FUSE_BOOTSZ1 ) + (~FUSE_BOOTSZ0 )));
|
||||||
|
bootloaderSize = MIN_BOOTSZ << ((v >> 1) ^ 3);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExternalOTAStorage::apply() {
|
||||||
|
#ifdef __AVR__
|
||||||
|
wdt_enable(WDTO_15MS);
|
||||||
|
while (true);
|
||||||
|
#elif defined(ESP8266) || defined(ESP32)
|
||||||
|
ESP.restart();
|
||||||
|
#else
|
||||||
|
NVIC_SystemReset();
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
WiFi101OTA version Feb 2017
|
||||||
|
by Sandeep Mistry (Arduino)
|
||||||
|
modified for ArduinoOTA Dec 2018, Apr 2019
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _OTA_STORAGE_H_INCLUDED
|
||||||
|
#define _OTA_STORAGE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class OTAStorage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
OTAStorage();
|
||||||
|
|
||||||
|
virtual int open(int length) = 0;
|
||||||
|
virtual int open(int length, uint8_t command) {
|
||||||
|
(void) command;
|
||||||
|
return open(length);
|
||||||
|
}
|
||||||
|
virtual size_t write(uint8_t) = 0;
|
||||||
|
virtual void close() = 0;
|
||||||
|
virtual void clear() = 0;
|
||||||
|
virtual void apply() = 0;
|
||||||
|
|
||||||
|
virtual long maxSize() {
|
||||||
|
return (MAX_FLASH - SKETCH_START_ADDRESS - bootloaderSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const uint32_t SKETCH_START_ADDRESS;
|
||||||
|
const uint32_t PAGE_SIZE;
|
||||||
|
const uint32_t MAX_FLASH;
|
||||||
|
uint32_t bootloaderSize;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExternalOTAStorage : public OTAStorage {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const char* updateFileName = "UPDATE.BIN";
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setUpdateFileName(const char* _updateFileName) {
|
||||||
|
updateFileName = _updateFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void apply();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
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 _SD_STORAGE_H_INCLUDED
|
||||||
|
#define _SD_STORAGE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <SD.h>
|
||||||
|
|
||||||
|
#include "OTAStorage.h"
|
||||||
|
|
||||||
|
#ifndef SDCARD_SS_PIN
|
||||||
|
#define SDCARD_SS_PIN 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SDStorageClass : public ExternalOTAStorage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual int open(int length) {
|
||||||
|
_file = SD.open(updateFileName, O_CREAT | O_WRITE);
|
||||||
|
if (!_file)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t write(uint8_t b) {
|
||||||
|
return _file.write(b);
|
||||||
|
}
|
||||||
|
virtual void close() {
|
||||||
|
_file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void clear() {
|
||||||
|
SD.remove(updateFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
File _file;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
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 _SERIALFLASH_STORAGE_H_INCLUDED
|
||||||
|
#define _SERIALFLASH_STORAGE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <SerialFlash.h>
|
||||||
|
|
||||||
|
#include "OTAStorage.h"
|
||||||
|
|
||||||
|
#define SERIAL_FLASH_BUFFER_SIZE 64
|
||||||
|
#define SERIAL_FLASH_CS 5
|
||||||
|
|
||||||
|
class SerialFlashStorageClass : public ExternalOTAStorage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual int open(int length) {
|
||||||
|
if (!SerialFlash.begin(SERIAL_FLASH_CS)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!SerialFlash.ready()) {}
|
||||||
|
|
||||||
|
if (SerialFlash.exists(updateFileName)) {
|
||||||
|
SerialFlash.remove(updateFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SerialFlash.create(updateFileName, length)) {
|
||||||
|
_file = SerialFlash.open(updateFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_file) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t write(uint8_t b) {
|
||||||
|
while (!SerialFlash.ready()) {}
|
||||||
|
int ret = _file.write(&b, 1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void close() {
|
||||||
|
_file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void clear() {
|
||||||
|
SerialFlash.remove(updateFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SerialFlashFile _file;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,358 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
WiFi101OTA version Feb 2017
|
||||||
|
by Sandeep Mistry (Arduino)
|
||||||
|
modified for ArduinoOTA Dec 2018, Apr 2019
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "WiFiOTA.h"
|
||||||
|
|
||||||
|
#define BOARD "arduino"
|
||||||
|
#define BOARD_LENGTH (sizeof(BOARD) - 1)
|
||||||
|
|
||||||
|
static String base64Encode(const String& in)
|
||||||
|
{
|
||||||
|
static const char* CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
|
int b;
|
||||||
|
String out;
|
||||||
|
out.reserve((in.length()) * 4 / 3);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < in.length(); i += 3) {
|
||||||
|
b = (in.charAt(i) & 0xFC) >> 2;
|
||||||
|
out += CODES[b];
|
||||||
|
|
||||||
|
b = (in.charAt(i) & 0x03) << 4;
|
||||||
|
if (i + 1 < in.length()) {
|
||||||
|
b |= (in.charAt(i + 1) & 0xF0) >> 4;
|
||||||
|
out += CODES[b];
|
||||||
|
b = (in.charAt(i + 1) & 0x0F) << 2;
|
||||||
|
if (i + 2 < in.length()) {
|
||||||
|
b |= (in.charAt(i + 2) & 0xC0) >> 6;
|
||||||
|
out += CODES[b];
|
||||||
|
b = in.charAt(i + 2) & 0x3F;
|
||||||
|
out += CODES[b];
|
||||||
|
} else {
|
||||||
|
out += CODES[b];
|
||||||
|
out += '=';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out += CODES[b];
|
||||||
|
out += "==";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFiOTAClass::WiFiOTAClass() :
|
||||||
|
_storage(NULL),
|
||||||
|
localIp(0),
|
||||||
|
_lastMdnsResponseTime(0),
|
||||||
|
beforeApplyCallback(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiOTAClass::begin(IPAddress& localIP, const char* name, const char* password, OTAStorage& storage)
|
||||||
|
{
|
||||||
|
localIp = localIP;
|
||||||
|
_name = name;
|
||||||
|
_expectedAuthorization = "Basic " + base64Encode("arduino:" + String(password));
|
||||||
|
_storage = &storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiOTAClass::pollMdns(UDP &_mdnsSocket)
|
||||||
|
{
|
||||||
|
int packetLength = _mdnsSocket.parsePacket();
|
||||||
|
|
||||||
|
if (packetLength <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte ARDUINO_SERVICE_REQUEST[37] = {
|
||||||
|
0x00, 0x00, // transaction id
|
||||||
|
0x00, 0x00, // flags
|
||||||
|
0x00, 0x01, // questions
|
||||||
|
0x00, 0x00, // answer RRs
|
||||||
|
0x00, 0x00, // authority RRs
|
||||||
|
0x00, 0x00, // additional RRs
|
||||||
|
0x08,
|
||||||
|
0x5f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, // _arduino
|
||||||
|
0x04,
|
||||||
|
0x5f, 0x74, 0x63, 0x70, // _tcp
|
||||||
|
0x05,
|
||||||
|
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, // local
|
||||||
|
0x00, 0x0c, // PTR
|
||||||
|
0x00, 0x01 // Class IN
|
||||||
|
};
|
||||||
|
|
||||||
|
if (packetLength != sizeof(ARDUINO_SERVICE_REQUEST)) {
|
||||||
|
while (packetLength) {
|
||||||
|
if (_mdnsSocket.available()) {
|
||||||
|
packetLength--;
|
||||||
|
_mdnsSocket.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte request[packetLength];
|
||||||
|
|
||||||
|
_mdnsSocket.read(request, sizeof(request));
|
||||||
|
|
||||||
|
if (memcmp(&request[2], &ARDUINO_SERVICE_REQUEST[2], packetLength - 2) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((millis() - _lastMdnsResponseTime) < 1000) {
|
||||||
|
// ignore request
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_lastMdnsResponseTime = millis();
|
||||||
|
|
||||||
|
_mdnsSocket.beginPacket(IPAddress(224, 0, 0, 251), 5353);
|
||||||
|
|
||||||
|
const byte responseHeader[] = {
|
||||||
|
0x00, 0x00, // transaction id
|
||||||
|
0x84, 0x00, // flags
|
||||||
|
0x00, 0x00, // questions
|
||||||
|
0x00, 0x04, // answers RRs
|
||||||
|
0x00, 0x00, // authority RRs
|
||||||
|
0x00, 0x00 // additional RRS
|
||||||
|
};
|
||||||
|
_mdnsSocket.write(responseHeader, sizeof(responseHeader));
|
||||||
|
|
||||||
|
const byte ptrRecordStart[] = {
|
||||||
|
0x08,
|
||||||
|
'_', 'a', 'r', 'd', 'u', 'i', 'n', 'o',
|
||||||
|
|
||||||
|
0x04,
|
||||||
|
'_', 't', 'c', 'p',
|
||||||
|
|
||||||
|
0x05,
|
||||||
|
'l', 'o', 'c', 'a', 'l',
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
0x00, 0x0c, // PTR
|
||||||
|
0x00, 0x01, // class IN
|
||||||
|
0x00, 0x00, 0x11, 0x94, // TTL
|
||||||
|
|
||||||
|
0x00, (byte)(_name.length() + 3), // length
|
||||||
|
(byte)_name.length()
|
||||||
|
};
|
||||||
|
|
||||||
|
const byte ptrRecordEnd[] = {
|
||||||
|
0xc0, 0x0c
|
||||||
|
};
|
||||||
|
|
||||||
|
_mdnsSocket.write(ptrRecordStart, sizeof(ptrRecordStart));
|
||||||
|
_mdnsSocket.write((const byte*) _name.c_str(), _name.length());
|
||||||
|
_mdnsSocket.write(ptrRecordEnd, sizeof(ptrRecordEnd));
|
||||||
|
|
||||||
|
const byte txtRecord[] = {
|
||||||
|
0xc0, 0x2b,
|
||||||
|
0x00, 0x10, // TXT strings
|
||||||
|
0x80, 0x01, // class
|
||||||
|
0x00, 0x00, 0x11, 0x94, // TTL
|
||||||
|
0x00, (50 + BOARD_LENGTH),
|
||||||
|
13,
|
||||||
|
's', 's', 'h', '_', 'u', 'p', 'l', 'o', 'a', 'd', '=', 'n', 'o',
|
||||||
|
12,
|
||||||
|
't', 'c', 'p', '_', 'c', 'h', 'e', 'c', 'k', '=', 'n', 'o',
|
||||||
|
15,
|
||||||
|
'a', 'u', 't', 'h', '_', 'u', 'p', 'l', 'o', 'a', 'd', '=', 'y', 'e', 's',
|
||||||
|
(6 + BOARD_LENGTH),
|
||||||
|
'b', 'o', 'a', 'r', 'd', '=',
|
||||||
|
};
|
||||||
|
_mdnsSocket.write(txtRecord, sizeof(txtRecord));
|
||||||
|
_mdnsSocket.write((byte*)BOARD, BOARD_LENGTH);
|
||||||
|
|
||||||
|
const byte srvRecordStart[] = {
|
||||||
|
0xc0, 0x2b,
|
||||||
|
0x00, 0x21, // SRV
|
||||||
|
0x80, 0x01, // class
|
||||||
|
0x00, 0x00, 0x00, 0x78, // TTL
|
||||||
|
0x00, (byte)(_name.length() + 9), // length
|
||||||
|
0x00, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
0xff, 0x00, // port
|
||||||
|
(byte)_name.length()
|
||||||
|
};
|
||||||
|
|
||||||
|
const byte srvRecordEnd[] = {
|
||||||
|
0xc0, 0x1a
|
||||||
|
};
|
||||||
|
|
||||||
|
_mdnsSocket.write(srvRecordStart, sizeof(srvRecordStart));
|
||||||
|
_mdnsSocket.write((const byte*) _name.c_str(), _name.length());
|
||||||
|
_mdnsSocket.write(srvRecordEnd, sizeof(srvRecordEnd));
|
||||||
|
|
||||||
|
byte aRecordNameOffset = sizeof(responseHeader) +
|
||||||
|
sizeof(ptrRecordStart) + _name.length() + sizeof(ptrRecordEnd) +
|
||||||
|
sizeof(txtRecord) + BOARD_LENGTH +
|
||||||
|
sizeof(srvRecordStart) - 1;
|
||||||
|
|
||||||
|
byte aRecord[] = {
|
||||||
|
0xc0, aRecordNameOffset,
|
||||||
|
|
||||||
|
0x00, 0x01, // A record
|
||||||
|
0x80, 0x01, // class
|
||||||
|
0x00, 0x00, 0x00, 0x78, // TTL
|
||||||
|
0x00, 0x04,
|
||||||
|
0xff, 0xff, 0xff, 0xff // IP
|
||||||
|
};
|
||||||
|
memcpy(&aRecord[sizeof(aRecord) - 4], &localIp, sizeof(localIp));
|
||||||
|
_mdnsSocket.write(aRecord, sizeof(aRecord));
|
||||||
|
|
||||||
|
_mdnsSocket.endPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiOTAClass::pollServer(Client& client)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
String request = client.readStringUntil('\n');
|
||||||
|
request.trim();
|
||||||
|
|
||||||
|
String header;
|
||||||
|
long contentLength = -1;
|
||||||
|
String authorization;
|
||||||
|
|
||||||
|
do {
|
||||||
|
header = client.readStringUntil('\n');
|
||||||
|
header.trim();
|
||||||
|
|
||||||
|
if (header.startsWith("Content-Length: ")) {
|
||||||
|
header.remove(0, 16);
|
||||||
|
|
||||||
|
contentLength = header.toInt();
|
||||||
|
} else if (header.startsWith("Authorization: ")) {
|
||||||
|
header.remove(0, 15);
|
||||||
|
|
||||||
|
authorization = header;
|
||||||
|
}
|
||||||
|
} while (header != "");
|
||||||
|
|
||||||
|
bool dataUpload = false;
|
||||||
|
#if defined(ESP8266) || defined(ESP32)
|
||||||
|
if (request == "POST /data HTTP/1.1") {
|
||||||
|
dataUpload = true;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
if (request != "POST /sketch HTTP/1.1") {
|
||||||
|
flushRequestBody(client, contentLength);
|
||||||
|
sendHttpResponse(client, 404, "Not Found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_expectedAuthorization != authorization) {
|
||||||
|
flushRequestBody(client, contentLength);
|
||||||
|
sendHttpResponse(client, 401, "Unauthorized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentLength <= 0) {
|
||||||
|
sendHttpResponse(client, 400, "Bad Request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_storage == NULL || !_storage->open(contentLength, dataUpload)) {
|
||||||
|
flushRequestBody(client, contentLength);
|
||||||
|
sendHttpResponse(client, 500, "Internal Server Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentLength > _storage->maxSize()) {
|
||||||
|
_storage->close();
|
||||||
|
flushRequestBody(client, contentLength);
|
||||||
|
sendHttpResponse(client, 413, "Payload Too Large");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long read = 0;
|
||||||
|
byte buff[64];
|
||||||
|
|
||||||
|
while (client.connected() && read < contentLength) {
|
||||||
|
while (client.available()) {
|
||||||
|
int l = client.read(buff, sizeof(buff));
|
||||||
|
for (int i = 0; i < l; i++) {
|
||||||
|
_storage->write(buff[i]);
|
||||||
|
}
|
||||||
|
read += l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_storage->close();
|
||||||
|
|
||||||
|
if (read == contentLength) {
|
||||||
|
sendHttpResponse(client, 200, "OK");
|
||||||
|
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
if (beforeApplyCallback) {
|
||||||
|
beforeApplyCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the update
|
||||||
|
_storage->apply();
|
||||||
|
|
||||||
|
while (true);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
sendHttpResponse(client, 414, "Payload size wrong");
|
||||||
|
_storage->clear();
|
||||||
|
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
client.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiOTAClass::sendHttpResponse(Client& client, int code, const char* status)
|
||||||
|
{
|
||||||
|
while (client.available()) {
|
||||||
|
client.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
client.print("HTTP/1.1 ");
|
||||||
|
client.print(code);
|
||||||
|
client.print(" ");
|
||||||
|
client.println(status);
|
||||||
|
client.println("Connection: close");
|
||||||
|
client.println();
|
||||||
|
delay(500);
|
||||||
|
client.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiOTAClass::flushRequestBody(Client& client, long contentLength)
|
||||||
|
{
|
||||||
|
long read = 0;
|
||||||
|
|
||||||
|
while (client.connected() && read < contentLength) {
|
||||||
|
if (client.available()) {
|
||||||
|
read++;
|
||||||
|
|
||||||
|
client.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2017 Arduino LLC. All right reserved.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
WiFi101OTA version Feb 2017
|
||||||
|
by Sandeep Mistry (Arduino)
|
||||||
|
modified for ArduinoOTA Dec 2018
|
||||||
|
by Juraj Andrassy
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WIFI_OTA_H_INCLUDED
|
||||||
|
#define _WIFI_OTA_H_INCLUDED
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Server.h>
|
||||||
|
#include <Client.h>
|
||||||
|
#include <IPAddress.h>
|
||||||
|
#include <Udp.h>
|
||||||
|
|
||||||
|
#include "OTAStorage.h"
|
||||||
|
|
||||||
|
class WiFiOTAClass {
|
||||||
|
protected:
|
||||||
|
WiFiOTAClass();
|
||||||
|
|
||||||
|
void begin(IPAddress& localIP, const char* name, const char* password, OTAStorage& storage);
|
||||||
|
|
||||||
|
void pollMdns(UDP &mdnsSocket);
|
||||||
|
void pollServer(Client& client);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void beforeApply(void (*fn)(void)) {
|
||||||
|
beforeApplyCallback = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sendHttpResponse(Client& client, int code, const char* status);
|
||||||
|
void flushRequestBody(Client& client, long contentLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
String _name;
|
||||||
|
String _expectedAuthorization;
|
||||||
|
OTAStorage* _storage;
|
||||||
|
|
||||||
|
uint32_t localIp;
|
||||||
|
uint32_t _lastMdnsResponseTime;
|
||||||
|
|
||||||
|
void (*beforeApplyCallback)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*------------------------ Optiboot header file ----------------------------|
|
||||||
|
| |
|
||||||
|
| June 2015 by Marek Wodzinski, https://github.com/majekw |
|
||||||
|
| Modified June 2016 by MCUdude, https://github.com/MCUdude |
|
||||||
|
| Modified Dec 2018 by Juraj Andrassy, https://github.com/jandrassy |
|
||||||
|
| Released to public domain |
|
||||||
|
| |
|
||||||
|
| This header file gives possibility to use SPM instruction |
|
||||||
|
| from Optiboot bootloader memory. |
|
||||||
|
| |
|
||||||
|
| There are 5 convenient functions available here: |
|
||||||
|
| * optiboot_page_erase - to erase a FLASH page |
|
||||||
|
| * optiboot_page_fill - to put words into temporary buffer |
|
||||||
|
| * optiboot_page_write - to write contents of temporary buffer into FLASH | |
|
||||||
|
| * optiboot_readPage - higher level function to read a flash page and |
|
||||||
|
| store it in an array |
|
||||||
|
| * optiboot_writePage - higher level function to write content to |
|
||||||
|
| a flash page |
|
||||||
|
| |
|
||||||
|
| For some hardcore users, you could use 'do_spm' as raw entry to |
|
||||||
|
| bootloader spm function. |
|
||||||
|
|-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef _OPTIBOOT_H_
|
||||||
|
#define _OPTIBOOT_H_ 1
|
||||||
|
|
||||||
|
#include <avr/boot.h>
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main 'magic' function - enter to bootloader do_spm function
|
||||||
|
*
|
||||||
|
* address - address to write (in bytes) but must be even number
|
||||||
|
* command - one of __BOOT_PAGE_WRITE, __BOOT_PAGE_ERASE or __BOOT_PAGE_FILL
|
||||||
|
* data - data to write in __BOOT_PAGE_FILL. In __BOOT_PAGE_ERASE or
|
||||||
|
* __BOOT_PAGE_WRITE it control if boot_rww_enable is run
|
||||||
|
* (0 = run, !0 = skip running boot_rww_enable)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 'typedef' (in following line) and 'const' (few lines below) are a way to define external function at some arbitrary address
|
||||||
|
typedef void (*do_spm_t)(uint16_t address, uint8_t command, uint16_t data);
|
||||||
|
typedef void (*copy_flash_pages_t)(uint32_t dest, uint32_t src, uint16_t page_count, uint8_t reset);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Devices with more than 64KB of flash:
|
||||||
|
* - have larger bootloader area (1KB) (they are BIGBOOT targets)
|
||||||
|
* - have RAMPZ register :-)
|
||||||
|
* - need larger variable to hold address (pgmspace.h uses uint32_t)
|
||||||
|
*/
|
||||||
|
#ifdef RAMPZ
|
||||||
|
typedef uint32_t optiboot_addr_t;
|
||||||
|
#else
|
||||||
|
typedef uint16_t optiboot_addr_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FLASHEND > 65534
|
||||||
|
const do_spm_t do_spm = (do_spm_t)((FLASHEND-1023+2)>>1);
|
||||||
|
const copy_flash_pages_t copy_flash_pages = (copy_flash_pages_t)((FLASHEND-1023+4)>>1);
|
||||||
|
#else
|
||||||
|
const do_spm_t do_spm = (do_spm_t)((FLASHEND-511+2)>>1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The same as do_spm but with disable/restore interrupts state
|
||||||
|
* required to succesfull SPM execution
|
||||||
|
*
|
||||||
|
* On devices with more than 64kB flash, 16 bit address is not enough,
|
||||||
|
* so there is also RAMPZ used in that case.
|
||||||
|
*
|
||||||
|
* On devices with more than 128kB flash, 16 bit word address is not enough
|
||||||
|
* for a function call above 0x20000, so there is also EIND used in that case.
|
||||||
|
*/
|
||||||
|
void do_spm_cli(optiboot_addr_t address, uint8_t command, uint16_t data) {
|
||||||
|
uint8_t sreg_save;
|
||||||
|
|
||||||
|
sreg_save = SREG; // save old SREG value
|
||||||
|
asm volatile("cli"); // disable interrupts
|
||||||
|
#ifdef RAMPZ
|
||||||
|
RAMPZ = (address >> 16) & 0xff; // address bits 23-16 goes to RAMPZ
|
||||||
|
#ifdef EIND
|
||||||
|
uint8_t eind = EIND;
|
||||||
|
EIND = FLASHEND / 0x20000;
|
||||||
|
#endif
|
||||||
|
do_spm((address & 0xffff), command, data); // do_spm accepts only lower 16 bits of address
|
||||||
|
#ifdef EIND
|
||||||
|
EIND = eind;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
do_spm(address, command, data); // 16 bit address - no problems to pass directly
|
||||||
|
#endif
|
||||||
|
SREG = sreg_save; // restore last interrupts state
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy contents of the flash pages. Addresses must be aligned to page boundary.
|
||||||
|
*
|
||||||
|
* On devices with more than 128kB flash, 16 bit word address is not enough
|
||||||
|
* for a function call above 0x20000, so there is also EIND used in that case.
|
||||||
|
*
|
||||||
|
* If reset_mcu is true, watchdog is used to reset the MCU after pages are copied.
|
||||||
|
* That enables to copy a new version of application from upper half of the flash.
|
||||||
|
*/
|
||||||
|
#if FLASHEND > 65534
|
||||||
|
void copy_flash_pages_cli(uint32_t dest, uint32_t src, uint16_t page_count, uint8_t reset_mcu) {
|
||||||
|
uint8_t sreg_save = SREG; // save old SREG value
|
||||||
|
asm volatile("cli"); // disable interrupts
|
||||||
|
#ifdef EIND
|
||||||
|
uint8_t eind = EIND;
|
||||||
|
EIND = FLASHEND / 0x20000;
|
||||||
|
#endif
|
||||||
|
copy_flash_pages(dest, src, page_count, reset_mcu);
|
||||||
|
#ifdef EIND
|
||||||
|
EIND = eind;
|
||||||
|
#endif
|
||||||
|
SREG = sreg_save; // restore last interrupts state
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Erase page in FLASH
|
||||||
|
void optiboot_page_erase(optiboot_addr_t address) {
|
||||||
|
do_spm_cli(address, __BOOT_PAGE_ERASE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Write word into temporary buffer
|
||||||
|
void optiboot_page_fill(optiboot_addr_t address, uint16_t data) {
|
||||||
|
do_spm_cli(address, __BOOT_PAGE_FILL, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Write temporary buffer into FLASH
|
||||||
|
void optiboot_page_write(optiboot_addr_t address) {
|
||||||
|
do_spm_cli(address, __BOOT_PAGE_WRITE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Higher level functions for reading and writing from flash
|
||||||
|
* See the examples for more info on how to use these functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Function to read a flash page and store it in an array (storage_array[])
|
||||||
|
void optiboot_readPage(const uint8_t allocated_flash_space[], uint8_t storage_array[], uint16_t page, char blank_character)
|
||||||
|
{
|
||||||
|
uint8_t read_character;
|
||||||
|
for(uint16_t j = 0; j < SPM_PAGESIZE; j++)
|
||||||
|
{
|
||||||
|
read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]);
|
||||||
|
if(read_character != 0 && read_character != 255)
|
||||||
|
storage_array[j] = read_character;
|
||||||
|
else
|
||||||
|
storage_array[j] = blank_character;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function to read a flash page and store it in an array (storage_array[]), but without blank_character
|
||||||
|
void optiboot_readPage(const uint8_t allocated_flash_space[], uint8_t storage_array[], uint16_t page)
|
||||||
|
{
|
||||||
|
uint8_t read_character;
|
||||||
|
for(uint16_t j = 0; j < SPM_PAGESIZE; j++)
|
||||||
|
{
|
||||||
|
read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]);
|
||||||
|
if(read_character != 0 && read_character != 255)
|
||||||
|
storage_array[j] = read_character;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function to write data to a flash page
|
||||||
|
void optiboot_writePage(const uint8_t allocated_flash_space[], uint8_t data_to_store[], uint16_t page)
|
||||||
|
{
|
||||||
|
uint16_t word_buffer = 0;
|
||||||
|
|
||||||
|
// Erase the flash page
|
||||||
|
optiboot_page_erase((optiboot_addr_t)(void*) &allocated_flash_space[SPM_PAGESIZE*(page-1)]);
|
||||||
|
|
||||||
|
// Copy ram buffer to temporary flash buffer
|
||||||
|
for(uint16_t i = 0; i < SPM_PAGESIZE; i++)
|
||||||
|
{
|
||||||
|
if(i % 2 == 0) // We must write words
|
||||||
|
word_buffer = data_to_store[i];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
word_buffer += (data_to_store[i] << 8);
|
||||||
|
optiboot_page_fill((optiboot_addr_t)(void*) &allocated_flash_space[i + SPM_PAGESIZE*(page-1)], word_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writing temporary buffer to flash
|
||||||
|
optiboot_page_write((optiboot_addr_t)(void*) &allocated_flash_space[SPM_PAGESIZE*(page-1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _OPTIBOOT_H_ */
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Ivan Seidel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,208 @@
|
||||||
|
![ArduinoThread Logo](https://raw.githubusercontent.com/ivanseidel/ArduinoThread/master/extras/ArduinoThread.png)
|
||||||
|
|
||||||
|
## ArduinoThreads Motivation
|
||||||
|
Arduino does not support isolated parallel tasks ([Threads](https://en.wikipedia.org/wiki/Thread_(computing))),
|
||||||
|
but we can make the main `loop` switch function execution conditionally and
|
||||||
|
thus simulate threading with [Protothread](https://en.wikipedia.org/wiki/Protothread) mechanism.
|
||||||
|
This library implements it and helps you to:
|
||||||
|
|
||||||
|
- schedule, manage and simplify parallel, periodic tasks
|
||||||
|
- define fixed or variable time between runs
|
||||||
|
- organize the code in any type of project
|
||||||
|
- put all sensor readings in a thread
|
||||||
|
- keep the main loop clean
|
||||||
|
- hide the complexity of thread management
|
||||||
|
- run "pseudo-background" tasks using Timer interrupts
|
||||||
|
|
||||||
|
Blinking an LED is often the very first thing an Arduino user learns.
|
||||||
|
And this demonstrates that periodically performing one single task, like toggling the LED state, is really easy.
|
||||||
|
However, one may quickly discover that managing multiple periodic tasks is not so simple
|
||||||
|
if the tasks have different schedule.
|
||||||
|
|
||||||
|
The user defines a Thread object for each of those tasks, then lets the library manage their scheduled execution.
|
||||||
|
|
||||||
|
It should be noted that these are not “threads” in the real computer-science meaning of the term:
|
||||||
|
tasks are implemented as functions that are run periodically.
|
||||||
|
On the one hand, this means that the only way a task can *yield* the CPU is by returning to the caller,
|
||||||
|
and it is thus inadvisable to `delay()` or do long waits inside any task.
|
||||||
|
On the other hand, this makes ArduinoThreads memory friendly, as no stack need to be allocated per task.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Download [the Master branch](https://github.com/ivanseidel/ArduinoThread/archive/master.zip) from gitHub.
|
||||||
|
2. Unzip and modify the Folder name to "ArduinoThread" (Remove the '-master' suffix)
|
||||||
|
3. Paste the modified folder on your Library folder (On your `Libraries` folder inside Sketchbooks or Arduino software).
|
||||||
|
4. Restart the Arduino IDE
|
||||||
|
|
||||||
|
**If you are here just because another library requires a class from ArduinoThread, then you are done now
|
||||||
|
.**
|
||||||
|
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
There are many examples showing many ways to use it. We will explain Class itself,
|
||||||
|
what it does and how it does.
|
||||||
|
|
||||||
|
There are three main classes included in the library:
|
||||||
|
`Thread`, `ThreadController` and `StaticThreadController` (both controllers inherit from `Thread`).
|
||||||
|
|
||||||
|
- `Thread`: Basic class, witch contains methods to set and run callbacks,
|
||||||
|
check if the Thread should be run, and also creates a unique ThreadID on the instantiation.
|
||||||
|
|
||||||
|
- `ThreadController`: Responsible for managing multiple Threads. Can also be thought of
|
||||||
|
as "a group of Threads", and is used to perform `run` in every Thread ONLY when needed.
|
||||||
|
|
||||||
|
- `StaticThreadController`: Slightly faster and smaller version of the `ThreadController`.
|
||||||
|
It works similar to `ThreadController`, but once constructed it can't add or remove threads to run.
|
||||||
|
|
||||||
|
#### Create Thread instance:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
Thread myThread = Thread();
|
||||||
|
// or, if initializing a pointer
|
||||||
|
Thread* myThread = new Thread();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Setup thread behaviour
|
||||||
|
You can configure many things:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
myThread.enabled = true; // Default enabled value is true
|
||||||
|
myThread.setInterval(10); // Setts the wanted interval to be 10ms
|
||||||
|
/*
|
||||||
|
This is useful for debugging
|
||||||
|
(Thread Name is disabled by default, to use less memory)
|
||||||
|
(Enable it by definint USE_THREAD_NAMES on 'Thread.h')
|
||||||
|
*/
|
||||||
|
myThread.ThreadName = "myThread tag";
|
||||||
|
// This will set the callback of the Thread: "What should I run"?
|
||||||
|
myThread.onRun(callback_function); // callback_function is the name of the function
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Running threads manually
|
||||||
|
Ok, creating threads isn't too hard, but what do we do with them?
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// First check if our Thread should be run
|
||||||
|
if(myThread.shouldRun()){
|
||||||
|
// Yes, the Thread should run, let's run it
|
||||||
|
myThread.run();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Running threads via a controller
|
||||||
|
If you had 3, 5 or 100 threads, managing them manually could become tedious.
|
||||||
|
That's when `ThreadController` or `StaticThreadController` comes into play and saves you the repetitive thread management parts of code.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// Instantiate new ThreadController
|
||||||
|
ThreadController controller = ThreadController();
|
||||||
|
// Now, put bunch of Threads inside it, FEED it!
|
||||||
|
controller.add(&myThread); // Notice the '&' sign before the thread, IF it's not instantied as a pointer.
|
||||||
|
controller.add(&hisThread);
|
||||||
|
controller.add(&sensorReadings);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```c++
|
||||||
|
// Instantiate a new StaticThreadController with the number of threads to be supplied as template parameter
|
||||||
|
StaticThreadController<3> controller (&myThread, &hisThread, &sensorReadings);
|
||||||
|
// You don't need to do anything else, controller now contains all the threads.
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
You have created, configured, grouped it. What is missing? Yes, whe should RUN it!
|
||||||
|
The following will run all the threads that NEED to run.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// call run on a Thread, a ThreadController or a StaticThreadController to run it
|
||||||
|
controller.run();
|
||||||
|
```
|
||||||
|
|
||||||
|
Congratulations, you have learned the basics of the `ArduinoThread` library. If you want to learn more, see bellow.
|
||||||
|
|
||||||
|
### Tips and Warnings
|
||||||
|
|
||||||
|
* `ThreadController` is not of a dynamic size (like a `LinkedList`). The maximum number of threads that it can manage
|
||||||
|
is defined in `ThreadController.h` (default is 15)
|
||||||
|
|
||||||
|
* ☢ When extending the `Thread` class and overriding the `run()` function,
|
||||||
|
remember to always call `runned();` at the end, otherwise the thread will hang forever.
|
||||||
|
|
||||||
|
* It's a good idea, to create a `Timer` interrupt and call a `ThreadController.run()` there.
|
||||||
|
That way, you don't need to worry about reading sensors and doing time-sensitive stuff
|
||||||
|
in your main code (`loop`). Check `ControllerWithTimer` example.
|
||||||
|
|
||||||
|
* Inheriting from `Thread` or even `ThreadController` is always a good idea.
|
||||||
|
For example, I always create base classes of sensors that extends `Thread`,
|
||||||
|
so that I can "register" the sensors inside a `ThreadController`, and forget
|
||||||
|
about reading sensors, just having the values available in my main code.
|
||||||
|
Check the `SensorThread` example.
|
||||||
|
|
||||||
|
* Remember that `ThreadController` is in fact, a `Thread` itself. If you want to group threads and
|
||||||
|
manage them together (enable or disable), think about putting all of them inside a `ThreadController`,
|
||||||
|
and adding this `ThreadController` to another `ThreadController` (YES! One inside another).
|
||||||
|
Check `ControllerInController` example.
|
||||||
|
|
||||||
|
* `StaticThreadController` is optimal when you know the exact number of
|
||||||
|
threads to run. You cannot add or remove threads at runtime, but it
|
||||||
|
doesn't require additional memory to keep all the treads together, doesn't limit the number of thread
|
||||||
|
(except for available memory) and the code may be slightly
|
||||||
|
better optimized because all the threads always exist and no need to do any runtime checks.
|
||||||
|
|
||||||
|
* Check the full example `CustomTimedThread` for a cool application of threads that run
|
||||||
|
for a period, after a button is pressed.
|
||||||
|
|
||||||
|
* Running tasks on the `Timer` interrupts must be thought though REALLY carefully
|
||||||
|
|
||||||
|
- You mustn't use `sleep()` inside an interrupt, because it would cause an infinite loop.
|
||||||
|
|
||||||
|
- Things execute quickly. Waiting too loooong on a interrupt, means waiting too
|
||||||
|
loooong on the main code (`loop`)
|
||||||
|
|
||||||
|
- Things might get "scrambled". Since Timers interrupts actually "BREAK" your code in half
|
||||||
|
and start running the interrupt, you might want to call `noInterrupts` and `interrupts`
|
||||||
|
on places where cannot be interrupted:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
noInterrupts();
|
||||||
|
// Put the code that CANNOT be interrupted...
|
||||||
|
interrupts(); // This will enable the interrupts egain. DO NOT FORGET!
|
||||||
|
```
|
||||||
|
## Library Reference
|
||||||
|
|
||||||
|
### Configuration options
|
||||||
|
#### Thread
|
||||||
|
- `bool Thread::enabled` - Enables or disables the thread. (doesn't prevent it from running, but will
|
||||||
|
return `false` when `shouldRun()` is called)
|
||||||
|
- `void Thread::setInterval()` - Schedules the thread run interval in milliseconds
|
||||||
|
- `bool Thread::shouldRun()` - Returns true, if the thread should be run.
|
||||||
|
(Basically,the logic is: (reached time AND is enabled?).
|
||||||
|
- `void Thread::onRun(<function>)` - The target callback function to be called.
|
||||||
|
- `void Thread::run()` - Runs the thread (executes the callback function).
|
||||||
|
- `int Thread::ThreadID` - Theoretically, it's the memory address. It's unique, and can
|
||||||
|
be used to compare if two threads are identical.
|
||||||
|
- `int Thread::ThreadName` - A human-readable thread name.
|
||||||
|
Default is "Thread ThreadID", eg.: "Thread 141515".
|
||||||
|
Note that to enable this attribute, you must uncomment the line that disables it on `Thread.h`
|
||||||
|
- protected: `void Thread::runned()` - Used to reset internal timer of the thread.
|
||||||
|
This is automatically called AFTER a call to `run()`.
|
||||||
|
|
||||||
|
#### ThreadController
|
||||||
|
- `void ThreadController::run()` - Runs the all threads grouped by the controller,
|
||||||
|
but only if needed (if `shouldRun()` returns true);
|
||||||
|
- `bool ThreadController::add(Thread* _thread)` - Adds a the thread to the controller,
|
||||||
|
and returns `true` if succeeded (returns false if the array is full).
|
||||||
|
- `void ThreadController::remove(Thread* _thread)` - Removes the thread from the controller
|
||||||
|
- `void ThreadController::remove(int index)` - Removes the thread at the `index` position
|
||||||
|
- `void ThreadController::clear()` - Removes ALL threads from the controller
|
||||||
|
- `int ThreadController::size(bool cached = true)` - Returns number of threads allocated
|
||||||
|
in the ThreadController. Re-calculates thread count if `cached` is `false`
|
||||||
|
- `Thread* ThreadController::get(int index)` - Returns the thread at the `index` position
|
||||||
|
|
||||||
|
#### StaticThreadController
|
||||||
|
- `void StaticThreadController::run()` - Runs all the threads within the controller,
|
||||||
|
but only if needed (if `shouldRun()` returns true);
|
||||||
|
- `int StaticThreadController::size()` - Returns how many Threads are allocated inside the controller.
|
||||||
|
- `Thread* ThreadController::get(int index)` - Returns the thread at the `index` position - or `nullptr` if `index`
|
||||||
|
is out of bounds.
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
StaticThreadController.h - Controlls a list of Threads with different timings
|
||||||
|
|
||||||
|
Basicaly, what it does is to keep track of current Threads and run when
|
||||||
|
necessary.
|
||||||
|
|
||||||
|
StaticThreadController is an extended class of Thread, because of that,
|
||||||
|
it allows you to add a StaticThreadController inside another kind of ThreadController...
|
||||||
|
|
||||||
|
It works exact as ThreadController except you can't add or remove treads dynamically.
|
||||||
|
|
||||||
|
Created by Alex Eremin, September, 2016.
|
||||||
|
Released into the public domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef StaticThreadController_h
|
||||||
|
#define StaticThreadController_h
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
class StaticThreadController: public Thread{
|
||||||
|
protected:
|
||||||
|
//since this is a static controller, the pointers themselves can be const
|
||||||
|
//it should be distinguished from 'const Thread* thread[N]'
|
||||||
|
Thread * const thread[N];
|
||||||
|
public:
|
||||||
|
template <typename... T>
|
||||||
|
StaticThreadController(T... params) :
|
||||||
|
Thread(),
|
||||||
|
thread{params...}
|
||||||
|
{
|
||||||
|
#ifdef USE_THREAD_NAMES
|
||||||
|
// Overrides name
|
||||||
|
ThreadName = "StaticThreadController ";
|
||||||
|
ThreadName = ThreadName + ThreadID;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// run() Method is overrided
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
// Run this thread before
|
||||||
|
if(_onRun != nullptr && shouldRun())
|
||||||
|
_onRun();
|
||||||
|
|
||||||
|
for(int i = 0; i < N; i++){
|
||||||
|
// Is enabled? Timeout exceeded?
|
||||||
|
if(thread[i]->shouldRun()){
|
||||||
|
thread[i]->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticThreadController extends Thread, so we should flag as runned thread
|
||||||
|
runned();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the quantity of Threads
|
||||||
|
static constexpr int size() { return N; };
|
||||||
|
|
||||||
|
// Return the I Thread on the array
|
||||||
|
// Returns nullptr if index is out of bounds
|
||||||
|
Thread* get(int index) {
|
||||||
|
return (index >= 0 && index < N) ? thread[index] : nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the I Thread on the array
|
||||||
|
// Doesn't perform any bounds checks and behaviour is
|
||||||
|
// unpredictable in case of index > N
|
||||||
|
Thread& operator[](int index) {
|
||||||
|
return *thread[index];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
Thread::Thread(void (*callback)(void), unsigned long _interval){
|
||||||
|
enabled = true;
|
||||||
|
onRun(callback);
|
||||||
|
_cached_next_run = 0;
|
||||||
|
last_run = millis();
|
||||||
|
|
||||||
|
ThreadID = (int)this;
|
||||||
|
#ifdef USE_THREAD_NAMES
|
||||||
|
ThreadName = "Thread ";
|
||||||
|
ThreadName = ThreadName + ThreadID;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setInterval(_interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
void Thread::runned(unsigned long time){
|
||||||
|
// Saves last_run
|
||||||
|
last_run = time;
|
||||||
|
|
||||||
|
// Cache next run
|
||||||
|
_cached_next_run = last_run + interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::setInterval(unsigned long _interval){
|
||||||
|
// Save interval
|
||||||
|
interval = _interval;
|
||||||
|
|
||||||
|
// Cache the next run based on the last_run
|
||||||
|
_cached_next_run = last_run + interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::shouldRun(unsigned long time){
|
||||||
|
// If the "sign" bit is set the signed difference would be negative
|
||||||
|
bool time_remaining = (time - _cached_next_run) & 0x80000000;
|
||||||
|
|
||||||
|
// Exceeded the time limit, AND is enabled? Then should run...
|
||||||
|
return !time_remaining && enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::onRun(void (*callback)(void)){
|
||||||
|
_onRun = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::run(){
|
||||||
|
if(_onRun != NULL)
|
||||||
|
_onRun();
|
||||||
|
|
||||||
|
// Update last_run and _cached_next_run
|
||||||
|
runned();
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
Thread.h - An runnable object
|
||||||
|
|
||||||
|
Thread is responsable for holding the "action" for something,
|
||||||
|
also, it responds if it "should" or "should not" run, based on
|
||||||
|
the current time;
|
||||||
|
|
||||||
|
For instructions, go to https://github.com/ivanseidel/ArduinoThread
|
||||||
|
|
||||||
|
Created by Ivan Seidel Gomes, March, 2013.
|
||||||
|
Released into the public domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Thread_h
|
||||||
|
#define Thread_h
|
||||||
|
|
||||||
|
#if defined(ARDUINO) && ARDUINO >= 100
|
||||||
|
#include <Arduino.h>
|
||||||
|
#else
|
||||||
|
#include <WProgram.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Uncomment this line to enable ThreadName Strings.
|
||||||
|
|
||||||
|
It might be usefull if you are loging thread with Serial,
|
||||||
|
or displaying a list of threads...
|
||||||
|
*/
|
||||||
|
// #define USE_THREAD_NAMES 1
|
||||||
|
|
||||||
|
class Thread{
|
||||||
|
protected:
|
||||||
|
// Desired interval between runs
|
||||||
|
unsigned long interval;
|
||||||
|
|
||||||
|
// Last runned time in Ms
|
||||||
|
unsigned long last_run;
|
||||||
|
|
||||||
|
// Scheduled run in Ms (MUST BE CACHED)
|
||||||
|
unsigned long _cached_next_run;
|
||||||
|
|
||||||
|
/*
|
||||||
|
IMPORTANT! Run after all calls to run()
|
||||||
|
Updates last_run and cache next run.
|
||||||
|
NOTE: This MUST be called if extending
|
||||||
|
this class and implementing run() method
|
||||||
|
*/
|
||||||
|
void runned(unsigned long time);
|
||||||
|
|
||||||
|
// Default is to mark it runned "now"
|
||||||
|
void runned() { runned(millis()); }
|
||||||
|
|
||||||
|
// Callback for run() if not implemented
|
||||||
|
void (*_onRun)(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// If the current Thread is enabled or not
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
// ID of the Thread (initialized from memory adr.)
|
||||||
|
int ThreadID;
|
||||||
|
|
||||||
|
#ifdef USE_THREAD_NAMES
|
||||||
|
// Thread Name (used for better UI).
|
||||||
|
String ThreadName;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Thread(void (*callback)(void) = NULL, unsigned long _interval = 0);
|
||||||
|
|
||||||
|
// Set the desired interval for calls, and update _cached_next_run
|
||||||
|
virtual void setInterval(unsigned long _interval);
|
||||||
|
|
||||||
|
// Return if the Thread should be runned or not
|
||||||
|
virtual bool shouldRun(unsigned long time);
|
||||||
|
|
||||||
|
// Default is to check whether it should run "now"
|
||||||
|
bool shouldRun() { return shouldRun(millis()); }
|
||||||
|
|
||||||
|
// Callback set
|
||||||
|
void onRun(void (*callback)(void));
|
||||||
|
|
||||||
|
// Runs Thread
|
||||||
|
virtual void run();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "ThreadController.h"
|
||||||
|
|
||||||
|
ThreadController::ThreadController(unsigned long _interval): Thread(){
|
||||||
|
cached_size = 0;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
setInterval(_interval);
|
||||||
|
|
||||||
|
#ifdef USE_THREAD_NAMES
|
||||||
|
// Overrides name
|
||||||
|
ThreadName = "ThreadController ";
|
||||||
|
ThreadName = ThreadName + ThreadID;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ThreadController run() (cool stuf)
|
||||||
|
*/
|
||||||
|
void ThreadController::run(){
|
||||||
|
// Run this thread before
|
||||||
|
if(_onRun != NULL)
|
||||||
|
_onRun();
|
||||||
|
|
||||||
|
unsigned long time = millis();
|
||||||
|
int checks = 0;
|
||||||
|
for(int i = 0; i < MAX_THREADS && checks < cached_size; i++){
|
||||||
|
// Object exists? Is enabled? Timeout exceeded?
|
||||||
|
if(thread[i]){
|
||||||
|
checks++;
|
||||||
|
if(thread[i]->shouldRun(time)){
|
||||||
|
thread[i]->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThreadController extends Thread, so we should flag as runned thread
|
||||||
|
runned();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
List controller (boring part)
|
||||||
|
*/
|
||||||
|
bool ThreadController::add(Thread* _thread){
|
||||||
|
// Check if the Thread already exists on the array
|
||||||
|
for(int i = 0; i < MAX_THREADS; i++){
|
||||||
|
if(thread[i] != NULL && thread[i]->ThreadID == _thread->ThreadID)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find an empty slot
|
||||||
|
for(int i = 0; i < MAX_THREADS; i++){
|
||||||
|
if(!thread[i]){
|
||||||
|
// Found a empty slot, now add Thread
|
||||||
|
thread[i] = _thread;
|
||||||
|
cached_size++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array is full
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadController::remove(int id){
|
||||||
|
// Find Threads with the id, and removes
|
||||||
|
for(int i = 0; i < MAX_THREADS; i++){
|
||||||
|
if(thread[i]->ThreadID == id){
|
||||||
|
thread[i] = NULL;
|
||||||
|
cached_size--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadController::remove(Thread* _thread){
|
||||||
|
remove(_thread->ThreadID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadController::clear(){
|
||||||
|
for(int i = 0; i < MAX_THREADS; i++){
|
||||||
|
thread[i] = NULL;
|
||||||
|
}
|
||||||
|
cached_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ThreadController::size(bool cached){
|
||||||
|
if(cached)
|
||||||
|
return cached_size;
|
||||||
|
|
||||||
|
int size = 0;
|
||||||
|
for(int i = 0; i < MAX_THREADS; i++){
|
||||||
|
if(thread[i])
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
cached_size = size;
|
||||||
|
|
||||||
|
return cached_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* ThreadController::get(int index){
|
||||||
|
int pos = -1;
|
||||||
|
for(int i = 0; i < MAX_THREADS; i++){
|
||||||
|
if(thread[i] != NULL){
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if(pos == index)
|
||||||
|
return thread[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
ThreadController.h - Controlls a list of Threads with different timings
|
||||||
|
|
||||||
|
Basicaly, what it does is to keep track of current Threads and run when
|
||||||
|
necessary.
|
||||||
|
|
||||||
|
ThreadController is an extended class of Thread, because of that,
|
||||||
|
it allows you to add a ThreadController inside another ThreadController...
|
||||||
|
|
||||||
|
For instructions, go to https://github.com/ivanseidel/ArduinoThread
|
||||||
|
|
||||||
|
Created by Ivan Seidel Gomes, March, 2013.
|
||||||
|
Released into the public domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ThreadController_h
|
||||||
|
#define ThreadController_h
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "inttypes.h"
|
||||||
|
|
||||||
|
#define MAX_THREADS 15
|
||||||
|
|
||||||
|
class ThreadController: public Thread{
|
||||||
|
protected:
|
||||||
|
Thread* thread[MAX_THREADS];
|
||||||
|
int cached_size;
|
||||||
|
public:
|
||||||
|
ThreadController(unsigned long _interval = 0);
|
||||||
|
|
||||||
|
// run() Method is overrided
|
||||||
|
void run();
|
||||||
|
|
||||||
|
// Adds a thread in the first available slot (remove first)
|
||||||
|
// Returns if the Thread could be added or not
|
||||||
|
bool add(Thread* _thread);
|
||||||
|
|
||||||
|
// remove the thread (given the Thread* or ThreadID)
|
||||||
|
void remove(int _id);
|
||||||
|
void remove(Thread* _thread);
|
||||||
|
|
||||||
|
// Removes all threads
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
// Return the quantity of Threads
|
||||||
|
int size(bool cached = true);
|
||||||
|
|
||||||
|
// Return the I Thread on the array
|
||||||
|
// Returns NULL if none found
|
||||||
|
Thread* get(int index);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include <Thread.h>
|
||||||
|
#include <ThreadController.h>
|
||||||
|
|
||||||
|
int ledPin = 13;
|
||||||
|
|
||||||
|
// ThreadController that will controll all threads
|
||||||
|
ThreadController controll = ThreadController();
|
||||||
|
|
||||||
|
//My Thread
|
||||||
|
Thread myThread = Thread();
|
||||||
|
//His Thread
|
||||||
|
Thread hisThread = Thread();
|
||||||
|
//Blink Led Thread
|
||||||
|
Thread blinkLedThread = Thread();
|
||||||
|
//ThreadController, that will be added to controll
|
||||||
|
ThreadController groupOfThreads = ThreadController();
|
||||||
|
|
||||||
|
// callback for myThread
|
||||||
|
void niceCallback(){
|
||||||
|
Serial.print("COOL! I'm running on: ");
|
||||||
|
Serial.println(millis());
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for hisThread
|
||||||
|
void boringCallback(){
|
||||||
|
Serial.println("BORING...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for blinkLedThread
|
||||||
|
void blinkLed(){
|
||||||
|
static bool ledStatus = false;
|
||||||
|
ledStatus = !ledStatus;
|
||||||
|
|
||||||
|
digitalWrite(ledPin, ledStatus);
|
||||||
|
|
||||||
|
Serial.print("blinking: ");
|
||||||
|
Serial.println(ledStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
pinMode(ledPin, OUTPUT);
|
||||||
|
|
||||||
|
// Configure myThread
|
||||||
|
myThread.onRun(niceCallback);
|
||||||
|
myThread.setInterval(500);
|
||||||
|
|
||||||
|
// Configure hisThread
|
||||||
|
hisThread.onRun(boringCallback);
|
||||||
|
hisThread.setInterval(250);
|
||||||
|
|
||||||
|
// Configure blinkLedThread
|
||||||
|
blinkLedThread.onRun(blinkLed);
|
||||||
|
blinkLedThread.setInterval(100);
|
||||||
|
|
||||||
|
// Adds myThread to the controll
|
||||||
|
controll.add(&myThread);
|
||||||
|
|
||||||
|
// Adds hisThread and blinkLedThread to groupOfThreads
|
||||||
|
groupOfThreads.add(&hisThread);
|
||||||
|
groupOfThreads.add(&blinkLedThread);
|
||||||
|
|
||||||
|
// Add groupOfThreads to controll
|
||||||
|
controll.add(&groupOfThreads);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// run ThreadController
|
||||||
|
// this will check every thread inside ThreadController,
|
||||||
|
// if it should run. If yes, he will run it;
|
||||||
|
controll.run();
|
||||||
|
|
||||||
|
// Rest of code
|
||||||
|
float h = 3.1415;
|
||||||
|
h/=2;
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
#include <Thread.h>
|
||||||
|
#include <ThreadController.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example, requires a Timer Interrupt Library.
|
||||||
|
If you are using Arduino NANO, UNO... (with ATmega168/328)
|
||||||
|
Please go to: http://playground.arduino.cc/code/timer1
|
||||||
|
If you are using Arduino DUE,
|
||||||
|
Please go to: https://github.com/ivanseidel/DueTimer
|
||||||
|
|
||||||
|
Include the library corresponding to your Arduino.
|
||||||
|
*/
|
||||||
|
// #include <DueTimer.h>
|
||||||
|
// #include <TimerOne.h>
|
||||||
|
|
||||||
|
// ThreadController that will controll all threads
|
||||||
|
ThreadController controll = ThreadController();
|
||||||
|
|
||||||
|
//My Thread
|
||||||
|
Thread myThread = Thread();
|
||||||
|
//His Thread
|
||||||
|
Thread hisThread = Thread();
|
||||||
|
|
||||||
|
// callback for myThread
|
||||||
|
void myThreadCallback(){
|
||||||
|
Serial.println("myThread\t\tcallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for hisThread
|
||||||
|
void hisThreadCallback(){
|
||||||
|
Serial.println("\thisThread\tcallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the callback for the Timer
|
||||||
|
void timerCallback(){
|
||||||
|
controll.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// Configure myThread
|
||||||
|
myThread.onRun(myThreadCallback);
|
||||||
|
myThread.setInterval(500);
|
||||||
|
|
||||||
|
// Configure myThread
|
||||||
|
hisThread.onRun(hisThreadCallback);
|
||||||
|
hisThread.setInterval(200);
|
||||||
|
|
||||||
|
// Adds both threads to the controller
|
||||||
|
controll.add(&myThread); // & to pass the pointer to it
|
||||||
|
controll.add(&hisThread);
|
||||||
|
|
||||||
|
/*
|
||||||
|
If using DueTimer...
|
||||||
|
*/
|
||||||
|
// Timer1.attachInterrupt(timerCallback).start(20000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
If using TimerOne...
|
||||||
|
*/
|
||||||
|
// Timer1.initialize(20000);
|
||||||
|
// Timer1.attachInterrupt(timerCallback);
|
||||||
|
// Timer1.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitSerial(){
|
||||||
|
while (!Serial.available());
|
||||||
|
delay(10);
|
||||||
|
while (Serial.available() && Serial.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
while(1){
|
||||||
|
noInterrupts(); // Call to disable interrupts
|
||||||
|
Serial.println("Type anyting to stop myThread!");
|
||||||
|
interrupts(); // Call to enable interrupts
|
||||||
|
waitSerial();
|
||||||
|
myThread.enabled = false;
|
||||||
|
|
||||||
|
noInterrupts();
|
||||||
|
Serial.println("Type anyting to stop hisThread!");
|
||||||
|
interrupts();
|
||||||
|
waitSerial();
|
||||||
|
hisThread.enabled = false;
|
||||||
|
|
||||||
|
noInterrupts();
|
||||||
|
Serial.println("Type anyting to enable myThread!");
|
||||||
|
interrupts();
|
||||||
|
waitSerial();
|
||||||
|
myThread.enabled = true;
|
||||||
|
|
||||||
|
noInterrupts();
|
||||||
|
Serial.println("Type anyting to enable hisThread!");
|
||||||
|
interrupts();
|
||||||
|
waitSerial();
|
||||||
|
hisThread.enabled = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
/*
|
||||||
|
This is an example from ArduinoThread. You can find more information
|
||||||
|
in https://github.com/ivanseidel/ArduinoThread.
|
||||||
|
|
||||||
|
Coded by Ivan Seidel, Jun/2014 - ivanseidel@gmail.com
|
||||||
|
|
||||||
|
Dont be afraid. 90% is commented lines. READ them, they will teach you.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Thread.h>
|
||||||
|
#include <ThreadController.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example provides an object-oriented approach to
|
||||||
|
develop a custom Thread that overrides the 'shouldRun'
|
||||||
|
method, to only run the thread after a button was pushed.
|
||||||
|
|
||||||
|
After the push, it should 'keep' running for a desired time.
|
||||||
|
|
||||||
|
It should also provide us, a way to easily implement this
|
||||||
|
controll multiple times, without trouble.
|
||||||
|
|
||||||
|
We are giving this Custom Thread the name 'ButtonThread'.
|
||||||
|
|
||||||
|
Exemplifying what it does:
|
||||||
|
+ ButtonThread added to our mainController ThreadList
|
||||||
|
=> Instantiated with a custom Pin #,
|
||||||
|
=> and a time duration (in miliseconds)
|
||||||
|
|
||||||
|
+ ButtonThread is not running.
|
||||||
|
|
||||||
|
+ When the button is pressed:
|
||||||
|
+ Thread will start and keep running.
|
||||||
|
+ If the thread runned for our defined period,
|
||||||
|
we stop it.
|
||||||
|
|
||||||
|
================ HOW TO SETUP HARDWARE ==================
|
||||||
|
In order to make this example work with any arduino, hook up
|
||||||
|
the pins on the board to 3 buttons. You can change the inputs
|
||||||
|
if you need below here.
|
||||||
|
|
||||||
|
The Buttons are being SOFTWARE pulled UP (to VCC), and when
|
||||||
|
pushed, should go LOW. Connect like this:
|
||||||
|
(Arduino Input) <----> (Btn) <----> GND (-)
|
||||||
|
|
||||||
|
We are using digital pins 9, 10 and 11 as input.
|
||||||
|
It also uses a LED, but we are using the default one in the board.
|
||||||
|
|
||||||
|
=============== WHAT YO LEARN WITH THIS =================
|
||||||
|
1) Threads are actually running in 'parallel'.
|
||||||
|
|
||||||
|
Since each thread process time is very tiny, they appear
|
||||||
|
as being runned in parallel.
|
||||||
|
|
||||||
|
Because of that, clicking multiple buttons at any time,
|
||||||
|
will looks like there is a program for each one of them.
|
||||||
|
|
||||||
|
2) If you keep the button 'pressed', it will continue to run.
|
||||||
|
|
||||||
|
Since we are 'enabling' the thread, and reseting the timer
|
||||||
|
flag (_lastButtonPushed) every time the button is pressed,
|
||||||
|
we should notice that in btn1Callback, where we print this
|
||||||
|
flag, it will never go beyond 0 if we keep pressing it.
|
||||||
|
|
||||||
|
3) The LED turns off, only because the Thread runs a last time
|
||||||
|
with the flag 'enabled' as false. This way, we can turn the
|
||||||
|
LED off and remain OFF until we press it egain.
|
||||||
|
|
||||||
|
I hope you enjoy, and learn some advanced-cool stuf with this tutorial.
|
||||||
|
Any feedback is apreciated!
|
||||||
|
*/
|
||||||
|
#define BTN1 9
|
||||||
|
#define BTN2 10
|
||||||
|
#define BTN3 11
|
||||||
|
|
||||||
|
#define LED 13
|
||||||
|
|
||||||
|
// ThreadController that will controll all button threads
|
||||||
|
ThreadController controll = ThreadController();
|
||||||
|
|
||||||
|
// Here we implement our custom ButtonThread, that Inherits from Thread
|
||||||
|
class ButtonThread: public Thread{
|
||||||
|
public:
|
||||||
|
// Our custom thread attributes
|
||||||
|
int pin;
|
||||||
|
long duration;
|
||||||
|
long _lastButtonPushed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Our Constructor. This will initialize the thread
|
||||||
|
with it's corresponding pin and duration after clicked.
|
||||||
|
*/
|
||||||
|
ButtonThread(int _pin, long _duration): Thread(){
|
||||||
|
// Set our attributes on construct
|
||||||
|
pin = _pin;
|
||||||
|
duration = _duration;
|
||||||
|
_lastButtonPushed = 0;
|
||||||
|
|
||||||
|
// Thread will start disabled
|
||||||
|
enabled = false;
|
||||||
|
|
||||||
|
// Configure the pin as INPUT and enable pull-up
|
||||||
|
pinMode(pin, INPUT);
|
||||||
|
digitalWrite(pin, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Override the method responsible for
|
||||||
|
checking if the thread should run.
|
||||||
|
|
||||||
|
It will first check if the button is pressed.
|
||||||
|
If so, we enable the thread, and then let the
|
||||||
|
"Old" default Thread method 'shouldRun' return if
|
||||||
|
it should run.
|
||||||
|
*/
|
||||||
|
bool shouldRun(unsigned long time){
|
||||||
|
// Override enabled on thread when pin goes LOW.
|
||||||
|
if(digitalRead(pin) == LOW){
|
||||||
|
enabled = true;
|
||||||
|
/*
|
||||||
|
Here, we save the current time in this object,
|
||||||
|
to compare it later.
|
||||||
|
|
||||||
|
the 'time' parameter in this method, is an override for the
|
||||||
|
'millis()' method. It allows who is checking the thread, to
|
||||||
|
pass a custom time.
|
||||||
|
|
||||||
|
This is sintax for writing an 'inline' if is very usefull,
|
||||||
|
it's the same as:
|
||||||
|
if(time > 0){
|
||||||
|
_lastButtonPushed = time;
|
||||||
|
}else{
|
||||||
|
_lastButtonPushed = millis();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
_lastButtonPushed = (time ? time : millis());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let default method check for it.
|
||||||
|
return Thread::shouldRun(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
We 'disable' the thread after the duration on the
|
||||||
|
'run' method.
|
||||||
|
|
||||||
|
What we should do here, is check if the time saved
|
||||||
|
in the _lastButtonPushed variable plus the duration,
|
||||||
|
is greater than our current time. If that's true, it
|
||||||
|
means we exceeded the thread time, and that we must
|
||||||
|
disable it and prevent from running.
|
||||||
|
*/
|
||||||
|
void run(){
|
||||||
|
// Check if time elapsed since last button push
|
||||||
|
if(millis() > _lastButtonPushed + duration){
|
||||||
|
// It exceeded time. We should disable it.
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Run the thread.
|
||||||
|
|
||||||
|
Note that this method will only get called
|
||||||
|
from the ThreadList, IF the 'shouldRun' returns true.
|
||||||
|
|
||||||
|
If the thread is not enabled anymore, it will run a 'last'
|
||||||
|
time with the flag 'enabled' as false, meaning it's the last
|
||||||
|
run in the period. You can use it for doing something only
|
||||||
|
before it stops running.
|
||||||
|
*/
|
||||||
|
Thread::run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
ButtonThreads objects instantiation
|
||||||
|
(we are instantiating 2 as a member, and one
|
||||||
|
as pointer in the setup, just to show you
|
||||||
|
different ways of doing it)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Thread 1 will be reading BTN1 pin, and will run for 3 secs
|
||||||
|
ButtonThread btn1Thread(BTN1, 3000);
|
||||||
|
|
||||||
|
// Thread 2 will be reading BTN1 pin, and will run for 5 secs
|
||||||
|
ButtonThread btn2Thread = ButtonThread(BTN2, 5000);
|
||||||
|
|
||||||
|
// Thread 3 will be instantiated in the setup()
|
||||||
|
ButtonThread *btn3Thread;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Callback for ButtonThreads
|
||||||
|
*/
|
||||||
|
void btn1Callback(){
|
||||||
|
// When it's running, this thread will write to the serial.
|
||||||
|
/*
|
||||||
|
This math will print 'how long' the thread has been running,
|
||||||
|
since the button was/is pressed.
|
||||||
|
|
||||||
|
After pressing it, it should print as 0, and goes up untill
|
||||||
|
the thread duration (in this case, +-5000ms).
|
||||||
|
*/
|
||||||
|
Serial.print("BTN 1 Thread: ");
|
||||||
|
Serial.println(millis() - btn1Thread._lastButtonPushed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void btn2Callback(){
|
||||||
|
/*
|
||||||
|
This thread will remain with the LED on pin 13 turned on
|
||||||
|
while it is running.
|
||||||
|
|
||||||
|
We detect that this method is called for the LAST time, if
|
||||||
|
the flag 'enabled' is FALSE on the btn2Thread object.
|
||||||
|
|
||||||
|
So, basically: If it's TRUE, we should turn ON the led, if not
|
||||||
|
we should turn OFF. We can simplify that into one line.
|
||||||
|
(Same 'inline' sintax as above)
|
||||||
|
*/
|
||||||
|
digitalWrite(LED, btn2Thread.enabled ? HIGH : LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void btn3Callback(){
|
||||||
|
// When it's running, this thread will also write to the serial
|
||||||
|
Serial.println("BTN 3 Thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
// Configure serial and output pins
|
||||||
|
Serial.begin(9600);
|
||||||
|
pinMode(LED, OUTPUT);
|
||||||
|
|
||||||
|
// Configure btn1Thread callback
|
||||||
|
// (During the 'enabled' time, it will run every 100ms, aka Interval)
|
||||||
|
btn1Thread.onRun(btn1Callback);
|
||||||
|
btn1Thread.setInterval(100);
|
||||||
|
|
||||||
|
// Configure btn2Thread callback and interval
|
||||||
|
btn2Thread.onRun(btn2Callback);
|
||||||
|
btn2Thread.setInterval(200);
|
||||||
|
|
||||||
|
// Instantiate btn3Thread
|
||||||
|
btn3Thread = new ButtonThread(BTN3, 4000);
|
||||||
|
// Configure btn3Thread callback and interval
|
||||||
|
btn3Thread->onRun(btn3Callback);
|
||||||
|
btn3Thread->setInterval(100);
|
||||||
|
|
||||||
|
// Adds all threads to the controller
|
||||||
|
controll.add(&btn1Thread); // & to pass the pointer to it
|
||||||
|
controll.add(&btn2Thread);
|
||||||
|
controll.add(btn3Thread); // Its already a pointer, no need for &
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// Here we just run the main thread controller
|
||||||
|
controll.run();
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "ThreadController.h"
|
||||||
|
/*
|
||||||
|
This is a more "complex" for of using Threads.
|
||||||
|
You can also inherit from Thread, and do your entire code on the class.
|
||||||
|
|
||||||
|
This allows you, to create for example:
|
||||||
|
Sensor Readings (aquire, filter, and save localy values)
|
||||||
|
Custom Blinks, Beeps...
|
||||||
|
Anything you can imagine.
|
||||||
|
|
||||||
|
Threads are more "usefull" when used within Timer interrupts
|
||||||
|
|
||||||
|
This way of coding is more "reusable", and "correct" (Object Oriented)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example, requires a Timer Interrupt Library.
|
||||||
|
If you are using Arduino NANO, UNO... (with ATmega168/328)
|
||||||
|
Please go to: http://playground.arduino.cc/code/timer1
|
||||||
|
If you are using Arduino DUE,
|
||||||
|
Please go to: https://github.com/ivanseidel/DueTimer
|
||||||
|
|
||||||
|
Include the library corresponding to your Arduino.
|
||||||
|
*/
|
||||||
|
#include <DueTimer.h>
|
||||||
|
// #include <TimerOne.h>
|
||||||
|
|
||||||
|
// Create a new Class, called SensorThread, that inherits from Thread
|
||||||
|
class SensorThread: public Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int value;
|
||||||
|
int pin;
|
||||||
|
|
||||||
|
// No, "run" cannot be anything...
|
||||||
|
// Because Thread uses the method "run" to run threads,
|
||||||
|
// we MUST overload this method here. using anything other
|
||||||
|
// than "run" will not work properly...
|
||||||
|
void run(){
|
||||||
|
// Reads the analog pin, and saves it localy
|
||||||
|
value = map(analogRead(pin), 0,1023,0,255);
|
||||||
|
runned();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now, let's use our new class of Thread
|
||||||
|
SensorThread analog1 = SensorThread();
|
||||||
|
SensorThread analog2 = SensorThread();
|
||||||
|
|
||||||
|
// Instantiate a new ThreadController
|
||||||
|
ThreadController controller = ThreadController();
|
||||||
|
|
||||||
|
// This is the callback for the Timer
|
||||||
|
void timerCallback(){
|
||||||
|
controller.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// Configures Thread analog1
|
||||||
|
analog1.pin = A1;
|
||||||
|
analog1.setInterval(100);
|
||||||
|
|
||||||
|
// Configures Thread analog2
|
||||||
|
analog2.pin = A2;
|
||||||
|
analog2.setInterval(100);
|
||||||
|
|
||||||
|
// Add the Threads to our ThreadController
|
||||||
|
controller.add(&analog1);
|
||||||
|
controller.add(&analog2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
If using DueTimer...
|
||||||
|
*/
|
||||||
|
Timer1.attachInterrupt(timerCallback).start(10000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
If using TimerOne...
|
||||||
|
*/
|
||||||
|
// Timer1.initialize(20000);
|
||||||
|
// Timer1.attachInterrupt(timerCallback);
|
||||||
|
// Timer1.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// Do complex-crazy-timeconsuming-tasks here
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Get the fresh readings
|
||||||
|
Serial.print("Analog1 Thread: ");
|
||||||
|
Serial.println(analog1.value);
|
||||||
|
|
||||||
|
Serial.print("Analog2 Thread: ");
|
||||||
|
Serial.println(analog2.value);
|
||||||
|
|
||||||
|
// Do more complex-crazy-timeconsuming-tasks here
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include <Thread.h>
|
||||||
|
int ledPin = 13;
|
||||||
|
|
||||||
|
//My simple Thread
|
||||||
|
Thread myThread = Thread();
|
||||||
|
|
||||||
|
// callback for myThread
|
||||||
|
void niceCallback(){
|
||||||
|
static bool ledStatus = false;
|
||||||
|
ledStatus = !ledStatus;
|
||||||
|
|
||||||
|
digitalWrite(ledPin, ledStatus);
|
||||||
|
|
||||||
|
Serial.print("COOL! I'm running on: ");
|
||||||
|
Serial.println(millis());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
pinMode(ledPin, OUTPUT);
|
||||||
|
|
||||||
|
myThread.onRun(niceCallback);
|
||||||
|
myThread.setInterval(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// checks if thread should run
|
||||||
|
if(myThread.shouldRun())
|
||||||
|
myThread.run();
|
||||||
|
|
||||||
|
// Other code...
|
||||||
|
int x = 0;
|
||||||
|
x = 1 + 2;
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include <Thread.h>
|
||||||
|
#include <ThreadController.h>
|
||||||
|
|
||||||
|
// ThreadController that will controll all threads
|
||||||
|
ThreadController controll = ThreadController();
|
||||||
|
|
||||||
|
//My Thread (as a pointer)
|
||||||
|
Thread* myThread = new Thread();
|
||||||
|
//His Thread (not pointer)
|
||||||
|
Thread hisThread = Thread();
|
||||||
|
|
||||||
|
// callback for myThread
|
||||||
|
void niceCallback(){
|
||||||
|
Serial.print("COOL! I'm running on: ");
|
||||||
|
Serial.println(millis());
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for hisThread
|
||||||
|
void boringCallback(){
|
||||||
|
Serial.println("BORING...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// Configure myThread
|
||||||
|
myThread->onRun(niceCallback);
|
||||||
|
myThread->setInterval(500);
|
||||||
|
|
||||||
|
// Configure myThread
|
||||||
|
hisThread.onRun(boringCallback);
|
||||||
|
hisThread.setInterval(250);
|
||||||
|
|
||||||
|
// Adds both threads to the controller
|
||||||
|
controll.add(myThread);
|
||||||
|
controll.add(&hisThread); // & to pass the pointer to it
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// run ThreadController
|
||||||
|
// this will check every thread inside ThreadController,
|
||||||
|
// if it should run. If yes, he will run it;
|
||||||
|
controll.run();
|
||||||
|
|
||||||
|
// Rest of code
|
||||||
|
float h = 3.1415;
|
||||||
|
h/=2;
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <Thread.h>
|
||||||
|
#include <StaticThreadController.h>
|
||||||
|
|
||||||
|
//My Thread (as a pointer)
|
||||||
|
Thread* myThread = new Thread();
|
||||||
|
//His Thread (not pointer)
|
||||||
|
Thread hisThread = Thread();
|
||||||
|
|
||||||
|
// callback for myThread
|
||||||
|
void niceCallback(){
|
||||||
|
Serial.print("COOL! I'm running on: ");
|
||||||
|
Serial.println(millis());
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for hisThread
|
||||||
|
void boringCallback(){
|
||||||
|
Serial.println("BORING...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for theThread
|
||||||
|
void justCallback(){
|
||||||
|
Serial.println("executing...");
|
||||||
|
}
|
||||||
|
|
||||||
|
//The Thread (as a pointer) with justCallback initialized
|
||||||
|
Thread* theThread = new Thread(justCallback);
|
||||||
|
|
||||||
|
// StaticThreadController that will controll all threads
|
||||||
|
// All non-pointers go with '&', but pointers go without '&',
|
||||||
|
StaticThreadController<3> controll (myThread, &hisThread, theThread);
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// Configure myThread
|
||||||
|
myThread->onRun(niceCallback);
|
||||||
|
myThread->setInterval(500);
|
||||||
|
|
||||||
|
// Configure hisThread
|
||||||
|
hisThread.onRun(boringCallback);
|
||||||
|
hisThread.setInterval(250);
|
||||||
|
|
||||||
|
// Set interval for theThread using StaticThreadController interface
|
||||||
|
controll[3].setInterval(375);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// run StaticThreadController
|
||||||
|
// this will check every thread inside ThreadController,
|
||||||
|
// if it should run. If yes, he will run it;
|
||||||
|
controll.run();
|
||||||
|
|
||||||
|
// Rest of code
|
||||||
|
float h = 3.1415;
|
||||||
|
h/=2;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
|
@ -0,0 +1,32 @@
|
||||||
|
#######################################
|
||||||
|
# Syntax Coloring
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
Thread KEYWORD1
|
||||||
|
ThreadController KEYWORD1
|
||||||
|
StaticThreadController KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
runned KEYWORD2
|
||||||
|
setInterval KEYWORD2
|
||||||
|
shouldRun KEYWORD2
|
||||||
|
onRun KEYWORD2
|
||||||
|
run KEYWORD2
|
||||||
|
|
||||||
|
# Specific of ThreadController or StaticThreadController
|
||||||
|
add KEYWORD2
|
||||||
|
remove KEYWORD2
|
||||||
|
clear KEYWORD2
|
||||||
|
size KEYWORD2
|
||||||
|
get KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "Thread",
|
||||||
|
"keywords": "thread, task",
|
||||||
|
"description": "A library for managing the periodic execution of multiple tasks",
|
||||||
|
"repository":
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ivanseidel/ArduinoThread.git"
|
||||||
|
},
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": "atmelavr"
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue