added arduino, modified build
This commit is contained in:
433
arduino/libraries/Temboo/src/Temboo.cpp
Normal file
433
arduino/libraries/Temboo/src/Temboo.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// ARDUINO YUN AND TRE SUPPORT IN HEADER FILE
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#else //ARDUINO_AVR_YUN
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <string.h>
|
||||
#include <Client.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <Temboo.h>
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooSession.h"
|
||||
|
||||
static const char HTTP_CODE[] PROGMEM = "HTTP_CODE\x0A\x1F";
|
||||
static char HTTP_EOL[] = "\r\n";
|
||||
static char HTTP_EOH[] = "\r\n\r\n";
|
||||
|
||||
TembooChoreo::TembooChoreo(Client& client) : m_client(client) {
|
||||
m_accountName = NULL;
|
||||
m_appKeyName = NULL;
|
||||
m_appKeyValue = NULL;
|
||||
m_path = NULL;
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
}
|
||||
|
||||
void TembooChoreo::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKey(const String& appKeyValue) {
|
||||
m_appKeyValue = appKeyValue.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setAppKey(const char* appKeyValue) {
|
||||
m_appKeyValue = appKeyValue;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setChoreo(const String& path) {
|
||||
m_path = path.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setChoreo(const char* path) {
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setSavedInputs(const String& savedInputsName) {
|
||||
m_preset.put(savedInputsName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setSavedInputs(const char* savedInputsName) {
|
||||
m_preset.put(savedInputsName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setCredential(const String& credentialName) {
|
||||
m_preset.put(credentialName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setCredential(const char* credentialName) {
|
||||
m_preset.put(credentialName);
|
||||
}
|
||||
|
||||
void TembooChoreo::setProfile(const String& profileName) {
|
||||
m_preset.put(profileName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::setProfile(const char* profileName) {
|
||||
m_preset.put(profileName);
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceType(const String& deviceType) {
|
||||
m_deviceType.put(deviceType.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceType(const char* deviceType) {
|
||||
m_deviceType.put(deviceType);
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceName(const String& deviceName) {
|
||||
m_deviceName.put(deviceName.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::setDeviceName(const char* deviceName) {
|
||||
m_deviceName.put(deviceName);
|
||||
}
|
||||
|
||||
void TembooChoreo::addInput(const String& inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addInput(const char* inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addInput(const char* inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addInput(const String& inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputWithSensor(const String& inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputWithSensor(const char* inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputWithSensor(const char* inputName, const char* inputValue) {
|
||||
m_expressions.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputExpression(const String& inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputExpression(const char* inputName, const String& inputValue) {
|
||||
m_expressions.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
void TembooChoreo::addInputExpression(const char* inputName, const char* inputValue) {
|
||||
m_expressions.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue, const char* conversion) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, calibrationValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorValue(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, rawLow, rawHigh, scaleLow, scaleHigh, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue, const char* conversion) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue) {
|
||||
m_sensors.put(sensorName, sensorValue, conversion, NULL, NULL, NULL, NULL, calibrationValue);
|
||||
}
|
||||
|
||||
void TembooChoreo::addSensorInput(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh) {
|
||||
m_sensors.put(sensorName, sensorValue, NULL, rawLow, rawHigh, scaleLow, scaleHigh, NULL);
|
||||
}
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const char* outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::addOutputFilter(const String& outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
int TembooChoreo::run() {
|
||||
return run(INADDR_NONE, 80, TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS);
|
||||
}
|
||||
|
||||
int TembooChoreo::run(uint16_t timeoutSecs) {
|
||||
return run(INADDR_NONE, 80, timeoutSecs);
|
||||
}
|
||||
|
||||
int TembooChoreo::run(IPAddress addr, uint16_t port) {
|
||||
return run(addr, port, TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS);
|
||||
}
|
||||
|
||||
int TembooChoreo::run(IPAddress addr, uint16_t port, uint16_t timeoutSecs) {
|
||||
|
||||
m_nextChar = NULL;
|
||||
|
||||
if (m_accountName == NULL || *m_accountName == '\0') {
|
||||
return TEMBOO_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
|
||||
if (m_path == NULL || *m_path == '\0') {
|
||||
return TEMBOO_ERROR_CHOREO_MISSING;
|
||||
}
|
||||
|
||||
if (m_appKeyName == NULL || *m_appKeyName == '\0') {
|
||||
return TEMBOO_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
|
||||
if (m_appKeyValue == NULL || *m_appKeyValue == '\0') {
|
||||
return TEMBOO_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
|
||||
TembooSession session(m_client, addr, port);
|
||||
uint16_t httpCode = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
unsigned long timeoutBeginSecs = session.getTime();
|
||||
if (0 != session.executeChoreo(m_accountName, m_appKeyName, m_appKeyValue, m_path, m_inputs, m_expressions, m_sensors, m_outputs, m_preset, m_deviceType, m_deviceName)) {
|
||||
httpCode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
while(!m_client.available()) {
|
||||
if((session.getTime() - timeoutBeginSecs) >= timeoutSecs) {
|
||||
TEMBOO_TRACELN("Receive time out");
|
||||
m_client.stop();
|
||||
return TEMBOO_ERROR_STREAM_TIMEOUT;
|
||||
}
|
||||
if (!m_client.connected()) {
|
||||
TEMBOO_TRACELN("Disconnected");
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
if (!m_client.findUntil("HTTP/1.", HTTP_EOL)) {
|
||||
TEMBOO_TRACELN("No HTTP");
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
//Don't care if the next byte is a '1' or a '0'
|
||||
m_client.read();
|
||||
|
||||
//Read the HTTP status code
|
||||
httpCode = (uint16_t)m_client.parseInt();
|
||||
|
||||
// We expect HTTP response codes to be <= 599, but
|
||||
// we need to be prepared for anything.
|
||||
if (httpCode >= 600) {
|
||||
TEMBOO_TRACELN("Invalid HTTP");
|
||||
httpCode = 0;
|
||||
}
|
||||
|
||||
// if we get an auth error AND there was an x-temboo-time header,
|
||||
// update the session timeOffset
|
||||
if ((httpCode == 401) && (i == 0)) {
|
||||
if (m_client.findUntil("x-temboo-time:", HTTP_EOH)) {
|
||||
TembooSession::setTime((unsigned long)m_client.parseInt());
|
||||
while(m_client.available()) {
|
||||
m_client.read();
|
||||
}
|
||||
m_client.stop();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16toa(httpCode, m_httpCodeStr);
|
||||
strcat_P(m_httpCodeStr, PSTR("\x0A\x1E"));
|
||||
m_nextState = START;
|
||||
m_nextChar = HTTP_CODE;
|
||||
|
||||
if (httpCode < 200 || httpCode >= 300) {
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
|
||||
if (!m_client.find(HTTP_EOH)) {
|
||||
return TEMBOO_ERROR_HTTP_ERROR;
|
||||
}
|
||||
|
||||
return TEMBOO_ERROR_OK;
|
||||
}
|
||||
|
||||
void TembooChoreo::close() {
|
||||
m_client.stop();
|
||||
}
|
||||
|
||||
int TembooChoreo::available() {
|
||||
// If we're still sending the HTTP response code,
|
||||
// report at least one character available.
|
||||
if (m_nextChar != NULL) {
|
||||
return m_client.available() + 1;
|
||||
}
|
||||
|
||||
// Otherwise, return however many characters the client has.
|
||||
return m_client.available();
|
||||
}
|
||||
|
||||
|
||||
int TembooChoreo::peek() {
|
||||
// If we're still sending the HTTP response code,
|
||||
// return the next character in that sequence.
|
||||
if (m_nextChar != NULL) {
|
||||
if(m_nextState != HTTP_CODE_VALUE) {
|
||||
return (int)pgm_read_byte(m_nextChar);
|
||||
} else {
|
||||
return (int)*m_nextChar;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return whatever is in the client buffer.
|
||||
return m_client.peek();
|
||||
}
|
||||
|
||||
|
||||
int TembooChoreo::read() {
|
||||
|
||||
int c = 0;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
m_nextChar = HTTP_CODE;
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
m_nextState = HTTP_CODE_TAG;
|
||||
break;
|
||||
|
||||
case HTTP_CODE_TAG:
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = HTTP_CODE_VALUE;
|
||||
m_nextChar = m_httpCodeStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_VALUE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = END;
|
||||
m_nextChar = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
c = m_client.read();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
size_t TembooChoreo::write(uint8_t data) {
|
||||
return m_client.write(data);
|
||||
}
|
||||
|
||||
|
||||
void TembooChoreo::flush() {
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
m_client.flush();
|
||||
}
|
||||
|
||||
#endif //ARDUINO_AVR_YUN
|
||||
225
arduino/libraries/Temboo/src/Temboo.h
Normal file
225
arduino/libraries/Temboo/src/Temboo.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOO_H_
|
||||
#define TEMBOO_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO YUN AND TRE SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Process.h>
|
||||
|
||||
class TembooChoreo : public Process {
|
||||
|
||||
public:
|
||||
void begin() {Process::begin("temboo");}
|
||||
void setAccountName(const String& accountName) { addParameter("-a" + accountName);}
|
||||
void setAppKeyName(const String& appKeyName) { addParameter("-u" + appKeyName);}
|
||||
void setAppKey(const String& appKey) { addParameter("-p" + appKey);}
|
||||
void setChoreo(const String& choreo) { addParameter("-c" + choreo);}
|
||||
void setCredential(const String& credentialName) { addParameter("-e" + credentialName);}
|
||||
void setSavedInputs(const String& savedInputsName) { addParameter("-e" + savedInputsName);}
|
||||
void setProfile(const String& profileName) { addParameter("-e" + profileName);}
|
||||
void addInput(const String& inputName, const String& inputValue) { addParameter("-i" + inputName + ":" + inputValue);}
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName) { addParameter("-o" + filterName + ":" + filterPath + ":" + variableName);}
|
||||
void setSettingsFileToWrite(const String& filePath) { addParameter("-w" + filePath);}
|
||||
void setSettingsFileToRead(const String& filePath) { addParameter("-r" + filePath);}
|
||||
void setGatewayAddress(const String& addr) { addParameter("-s" + addr);}
|
||||
void addInputExpression(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addInputWithSensor(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void setDeviceName(const String& deviceName) {addParameter("-d" + deviceName);}
|
||||
void setDeviceType(const String& deviceType) {addParameter("-t" + deviceType);}
|
||||
};
|
||||
|
||||
#else //ARDUINO_AVR_YUN
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Client.h>
|
||||
#include <IPAddress.h>
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoInputExpressionSet.h"
|
||||
#include "utility/ChoreoSensorInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
#include "utility/ChoreoDevice.h"
|
||||
|
||||
#define TEMBOO_ERROR_OK (0)
|
||||
#define TEMBOO_ERROR_ACCOUNT_MISSING (201)
|
||||
#define TEMBOO_ERROR_CHOREO_MISSING (203)
|
||||
#define TEMBOO_ERROR_APPKEY_NAME_MISSING (205)
|
||||
#define TEMBOO_ERROR_APPKEY_MISSING (207)
|
||||
#define TEMBOO_ERROR_HTTP_ERROR (223)
|
||||
#define TEMBOO_ERROR_STREAM_TIMEOUT (225)
|
||||
#define TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS (901) //15 minutes and 1 second
|
||||
|
||||
class TembooChoreo : public Stream {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of an Arduino Client, usually an EthernetClient
|
||||
// or a WiFiClient. Used to communicate with Temboo.
|
||||
TembooChoreo(Client& client);
|
||||
|
||||
// Does nothing. Just for source compatibility with Yun code.
|
||||
void begin() {};
|
||||
|
||||
// Sets the account name to use when communicating with Temboo.
|
||||
// (required)
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
// Sets the application key name to use with choreo execution requests.
|
||||
// (required)
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
// Sets the application key value to use with choreo execution requests
|
||||
// (required)
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
// Sets the name of the choreo to be executed.
|
||||
// (required)
|
||||
void setChoreo(const String& choreoPath);
|
||||
void setChoreo(const char* choreoPath);
|
||||
|
||||
// Sets the name of the saved inputs to use when executing the choreo
|
||||
// (optional)
|
||||
void setSavedInputs(const String& savedInputsName);
|
||||
void setSavedInputs(const char* savedInputsName);
|
||||
|
||||
void setCredential(const String& credentialName);
|
||||
void setCredential(const char* credentialName);
|
||||
|
||||
void setProfile(const String& profileName);
|
||||
void setProfile(const char* profileName);
|
||||
|
||||
void setDeviceType(const String& deviceType);
|
||||
void setDeviceType(const char* deviceType);
|
||||
|
||||
void setDeviceName(const String& deviceName);
|
||||
void setDeviceName(const char* deviceName);
|
||||
|
||||
// Sets an input to be used when executing a choreo.
|
||||
// (optional or required, depending on the choreo being executed.)
|
||||
void addInput(const String& inputName, const String& inputValue);
|
||||
void addInput(const char* inputName, const char* inputValue);
|
||||
void addInput(const char* inputName, const String& inputValue);
|
||||
void addInput(const String& inputName, const char* inputValue);
|
||||
|
||||
// Sets a Choreo input that contains a sensor value to be converted by Temboo
|
||||
void addInputWithSensor(const String& inputName, const String& inputValue);
|
||||
void addInputWithSensor(const char* inputName, const String& inputValue);
|
||||
void addInputWithSensor(const char* inputName, const char* inputValue);
|
||||
// Keeping legacy methods
|
||||
void addInputExpression(const String& inputName, const String& inputValue);
|
||||
void addInputExpression(const char* inputName, const String& inputValue);
|
||||
void addInputExpression(const char* inputName, const char* inputValue);
|
||||
|
||||
// Sets in input that is using a sensor value. Different parameters are needed depending
|
||||
// on the type of sensor being used.
|
||||
void addSensorValue(const char* sensorName, int sensorValue, const char* conversion);
|
||||
void addSensorValue(const char* sensorName, int sensorValue);
|
||||
void addSensorValue(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue);
|
||||
void addSensorValue(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh);
|
||||
// Keeping legacy methods
|
||||
void addSensorInput(const char* sensorName, int sensorValue, const char* conversion);
|
||||
void addSensorInput(const char* sensorName, int sensorValue);
|
||||
void addSensorInput(const char* sensorName, int sensorValue, const char* conversion, const char* calibrationValue);
|
||||
void addSensorInput(const char* sensorName, int sensorValue, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh);
|
||||
|
||||
// Sets an output filter to be used to process the choreo output
|
||||
// (optional)
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName);
|
||||
|
||||
// Run the choreo using the current input info
|
||||
int run();
|
||||
// Run the choreo with a user specified timeout
|
||||
int run(uint16_t timeoutSecs);
|
||||
|
||||
// Run the choreo on the Temboo server at the given IP address and port
|
||||
int run(IPAddress addr, uint16_t port);
|
||||
int run(IPAddress addr, uint16_t port, uint16_t timeoutSecs);
|
||||
|
||||
void close();
|
||||
|
||||
// Stream interface - see the Arduino library documentation.
|
||||
int available();
|
||||
int read();
|
||||
int peek();
|
||||
void flush();
|
||||
|
||||
//Print interface - see the Arduino library documentation
|
||||
size_t write(uint8_t data);
|
||||
|
||||
|
||||
protected:
|
||||
ChoreoInputSet m_inputs;
|
||||
ChoreoInputExpressionSet m_expressions;
|
||||
ChoreoSensorInputSet m_sensors;
|
||||
ChoreoOutputSet m_outputs;
|
||||
ChoreoPreset m_preset;
|
||||
ChoreoDevice m_deviceType;
|
||||
ChoreoDevice m_deviceName;
|
||||
|
||||
const char* m_accountName;
|
||||
const char* m_appKeyValue;
|
||||
const char* m_appKeyName;
|
||||
const char* m_path;
|
||||
Client& m_client;
|
||||
char m_httpCodeStr[6];
|
||||
const char* m_nextChar;
|
||||
enum State {START, HTTP_CODE_TAG, HTTP_CODE_VALUE, END};
|
||||
State m_nextState;
|
||||
|
||||
};
|
||||
|
||||
#endif //ARDUINO_AVR_YUN
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
935
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.cpp
Normal file
935
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.cpp
Normal file
@@ -0,0 +1,935 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooCoAPSession.h"
|
||||
#include "utility/TembooTags.h"
|
||||
|
||||
#include "TembooCoAPEdgeDevice.h"
|
||||
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (0xFFFF)
|
||||
#endif
|
||||
|
||||
#ifndef UINT32_MAX
|
||||
#define UINT32_MAX (0xFFFFFFFF)
|
||||
#endif
|
||||
|
||||
//TODO: Maybe. Put these in PROGMEM and
|
||||
// modify any code that uses them.
|
||||
const char HTTP_CODE_PREFIX[] = "HTTP_CODE\x0A\x1F";
|
||||
const char HTTP_CODE_SUFFIX[] = "\x0A\x1E";
|
||||
const char TembooCoAPClient::URI_PATH[] = "exec";
|
||||
const char TIME_URI_PATH[] = "time";
|
||||
static char HEADER_TIME[] = "x-temboo-time:";
|
||||
|
||||
uint16_t TembooCoAPChoreo::s_nextRequestId = 0;
|
||||
|
||||
TembooCoAPClient::TembooCoAPClient(TembooCoAPIPStack& ipStack, IPAddress gatewayAddress, uint16_t gatewayPort) :
|
||||
m_messageLayer(m_rxBuffer, sizeof(m_rxBuffer), ipStack),
|
||||
m_rrLayer(m_messageLayer, m_rxBuffer, sizeof(m_rxBuffer)),
|
||||
m_gatewayAddress(gatewayAddress),
|
||||
m_gatewayPort(gatewayPort),
|
||||
m_messageID(0),
|
||||
m_state(STATE_IDLE),
|
||||
m_blockSize(MAX_BLOCK_SIZE),
|
||||
m_lastError(NO_ERROR),
|
||||
m_dataLen(0),
|
||||
m_txIndex(0),
|
||||
m_respLen(0),
|
||||
m_txByteCount(0),
|
||||
m_respHttpCode(0) {
|
||||
|
||||
memset(m_token, 0, sizeof(m_token));
|
||||
memset(m_dataBuffer, 0, sizeof(m_dataBuffer));
|
||||
memset(m_respBuffer, 0, sizeof(m_respBuffer));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPClient::resetChoreo() {
|
||||
memset(m_token, 0, sizeof(m_token));
|
||||
memset(m_dataBuffer, 0, sizeof(m_dataBuffer));
|
||||
memset(m_respBuffer, 0, sizeof(m_respBuffer));
|
||||
m_dataLen = 0;
|
||||
m_respLen = 0;
|
||||
m_txIndex = 0;
|
||||
m_txByteCount = 0;
|
||||
m_rxBlockNum = 0;
|
||||
|
||||
m_rrLayer.setState(CoapRRLayer::STATE_IDLE);
|
||||
m_messageLayer.setState(CoapMessageLayer::STATE_CLOSED);
|
||||
}
|
||||
|
||||
void TembooCoAPClient::begin(long seed){
|
||||
//RFC7252 "strongly recommends" that the initial
|
||||
//value of messageID be randomized. There's no good way
|
||||
//to do that reliably on many MCU boards. We will
|
||||
//use random(), and can instruct the user to call randomSeed
|
||||
//with input from an unused analog input if it's important to them.
|
||||
randomSeed(seed);
|
||||
m_messageID = random(0, UINT16_MAX);
|
||||
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::~TembooCoAPClient() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::write(uint8_t value) {
|
||||
if (m_dataLen < sizeof(m_dataBuffer)) {
|
||||
m_dataBuffer[m_dataLen] = value;
|
||||
m_dataLen++;
|
||||
m_txByteCount = 0;
|
||||
m_txIndex = 0;
|
||||
return NO_ERROR;
|
||||
}
|
||||
return ERROR_BUFFER_FULL;
|
||||
}
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::saveResponse(uint8_t* values, uint16_t len) {
|
||||
len = len < (sizeof(m_respBuffer) - m_respLen - 1) ? len : (sizeof(m_respBuffer) - m_respLen -1);
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Saving payload to the buffer");
|
||||
if ( len > 0) {
|
||||
memcpy(&m_respBuffer[m_respLen], values, len);
|
||||
m_respLen += len;
|
||||
return NO_ERROR;
|
||||
}
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Buffer full, payload not saved");
|
||||
return ERROR_BUFFER_FULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::write(uint8_t* values, uint16_t len) {
|
||||
Result rc = NO_ERROR;
|
||||
while(NO_ERROR == rc && len > 0) {
|
||||
rc = write(*values++);
|
||||
len--;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t TembooCoAPClient::getNextMessageID() {
|
||||
m_messageID++;
|
||||
if (m_messageID == 0) {
|
||||
m_messageID++;
|
||||
}
|
||||
return m_messageID;
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::generateToken() {
|
||||
// 5.3.1. Token suggests the tokenID should be a random value
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
m_token[i] = (rand() % 93) + 33;
|
||||
}
|
||||
m_token[8] = '\0';
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::sendBlockRequest(uint16_t msgID, uint32_t blockNum) {
|
||||
|
||||
CoapMsg msg(m_txBuffer, sizeof (m_txBuffer));
|
||||
msg.setCode(CoapMsg::COAP_POST);
|
||||
|
||||
if (msg.setToken((uint8_t*)m_token, strlen(m_token))) {
|
||||
TEMBOO_TRACELN("err: setToken");
|
||||
return ERROR_MSG_TOKEN;
|
||||
}
|
||||
|
||||
msg.setId(msgID);
|
||||
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_URI_PATH, (const uint8_t*)URI_PATH, strlen(URI_PATH))) {
|
||||
TEMBOO_TRACELN("err: setURI");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
|
||||
uint8_t optionValue[3];
|
||||
uint16_t optionLen = 0;
|
||||
|
||||
// If this is the last block in a series of blocks (or an only block)
|
||||
// include a block2 option to let the server know what our
|
||||
// desired block size is for the response.
|
||||
|
||||
optionValue[0] = (blockNum & 0xF000) >> 12;
|
||||
optionValue[1] = (blockNum & 0x0FF0) >> 4;
|
||||
optionValue[2] = (blockNum & 0x000F) << 4;
|
||||
optionValue[2] |= (0 ? 0x08 : 0);
|
||||
optionValue[2] |= (m_blockSize >> 5) & 0x07;
|
||||
|
||||
optionLen = 1;
|
||||
if (optionValue[0] > 0) {
|
||||
optionLen = 3;
|
||||
} else if (optionValue[1] > 0) {
|
||||
optionLen = 2;
|
||||
}
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_BLOCK2, (const uint8_t*)&optionValue[3 - optionLen], optionLen)) {
|
||||
TEMBOO_TRACELN("err: block2");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
|
||||
if (m_rrLayer.reliableSend(msg, m_token, m_gatewayAddress, m_gatewayPort) != CoapRRLayer::NO_ERROR) {
|
||||
TEMBOO_TRACELN("err: send");
|
||||
return ERROR_SENDING_MSG;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::sendBlock(uint16_t msgID, uint8_t* payload, size_t len, uint32_t blockNum, bool moreBlocks) {
|
||||
|
||||
CoapMsg msg(m_txBuffer, sizeof (m_txBuffer));
|
||||
msg.setCode(CoapMsg::COAP_POST);
|
||||
|
||||
if (msg.setToken((uint8_t*)m_token, strlen(m_token))) {
|
||||
TEMBOO_TRACELN("err: setToken");
|
||||
return ERROR_MSG_TOKEN;
|
||||
}
|
||||
|
||||
msg.setId(msgID);
|
||||
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_URI_PATH, (const uint8_t*)URI_PATH, strlen(URI_PATH))) {
|
||||
TEMBOO_TRACELN("err: setURI");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
|
||||
uint8_t optionValue[3];
|
||||
uint16_t optionLen = 0;
|
||||
|
||||
// If this is the last block in a series of blocks (or an only block)
|
||||
// include a block2 option to let the server know what our
|
||||
// desired block size is for the response.
|
||||
if (!moreBlocksToSend()) {
|
||||
optionValue[0] = (m_blockSize >> 5) & 0x07;
|
||||
optionLen = (optionValue[0] > 0) ? 1 : 0;
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_BLOCK2, (const uint8_t*)optionValue, optionLen)) {
|
||||
TEMBOO_TRACELN("err: block2");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is not the only block in the request,
|
||||
// include the block1 option.
|
||||
if (blockNum > 0 || moreBlocks) {
|
||||
|
||||
optionValue[0] = (blockNum & 0xF000) >> 12;
|
||||
optionValue[1] = (blockNum & 0x0FF0) >> 4;
|
||||
optionValue[2] = (blockNum & 0x000F) << 4;
|
||||
optionValue[2] |= (moreBlocks ? 0x08 : 0);
|
||||
optionValue[2] |= (m_blockSize >> 5) & 0x07;
|
||||
|
||||
optionLen = 1;
|
||||
if (optionValue[0] > 0) {
|
||||
optionLen = 3;
|
||||
} else if (optionValue[1] > 0) {
|
||||
optionLen = 2;
|
||||
}
|
||||
|
||||
if (msg.addOption(CoapMsg::COAP_OPTION_BLOCK1, (const uint8_t*)&optionValue[3 - optionLen], optionLen)) {
|
||||
TEMBOO_TRACELN("err: block1");
|
||||
return ERROR_MSG_OPTION;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.setPayload((uint8_t*)payload, len)) {
|
||||
TEMBOO_TRACELN("err: setPayload");
|
||||
return ERROR_MSG_PAYLOAD;
|
||||
}
|
||||
|
||||
if (m_rrLayer.reliableSend(msg, m_token, m_gatewayAddress, m_gatewayPort) != CoapRRLayer::NO_ERROR) {
|
||||
TEMBOO_TRACELN("err: send");
|
||||
return ERROR_SENDING_MSG;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPClient::adjustRequestBlockSize(CoapMsg& msg) {
|
||||
|
||||
// A block1 option in a response means the server is
|
||||
// requesting that we use a smaller block size.
|
||||
uint16_t newBlockSize = msg.getBlock1Size();
|
||||
if (newBlockSize > 0 && newBlockSize < m_blockSize) {
|
||||
m_blockSize = newBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::loop() {
|
||||
|
||||
m_lastResult = NO_ERROR;
|
||||
|
||||
switch (m_state) {
|
||||
|
||||
case STATE_IDLE:
|
||||
case STATE_RESPONSE_READY:
|
||||
// Pump the receiver.
|
||||
// We're not serving anything, so unless there's an outstanding
|
||||
// request (which would mean we would be in STATE_WAITING, not STATE_IDLE),
|
||||
// the R/R layer will reject or ignore any incoming traffic.
|
||||
m_rrLayer.loop();
|
||||
|
||||
break;
|
||||
case STATE_SEND_REQUEST:
|
||||
case STATE_RESPONSE_STARTED:
|
||||
case STATE_WAITING_FOR_RESPONSE:
|
||||
// We're waiting for a response to an earlier request.
|
||||
switch(m_rrLayer.loop()) {
|
||||
|
||||
case CoapRRLayer::NO_ERROR:
|
||||
// Nothing happened. Nothing to do.
|
||||
break;
|
||||
|
||||
case CoapRRLayer::RESPONSE_RECEIVED: {
|
||||
|
||||
// A response to our request was received.
|
||||
// It may have been a piggybacked ACK or a separate response
|
||||
CoapMsg msg(m_rxBuffer, sizeof(m_rxBuffer), m_messageLayer.getRXByteCount());
|
||||
|
||||
// See if it has a BLOCK1 option. If so, make sure the
|
||||
// block number matches the one we just sent. If the block
|
||||
// numbers don't match, we're FUBAR, so abort the request.
|
||||
// If they do match, adjust our request block size if the
|
||||
// server requested a different (smaller) size.
|
||||
|
||||
if (msg.getOptionCount(CoapMsg::COAP_OPTION_BLOCK1)) {
|
||||
uint32_t ackBlockNum = msg.getBlock1Num();
|
||||
if (ackBlockNum != m_txBlockNum) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Block1 message number does not match");
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
}
|
||||
resetChoreo();
|
||||
break;
|
||||
}
|
||||
adjustRequestBlockSize(msg);
|
||||
}
|
||||
|
||||
// Now deal with the response itself.
|
||||
switch(msg.getCode()) {
|
||||
case CoapMsg::COAP_CONTINUE: //2.31
|
||||
// 2.31 means the server is requesting the next block of the request.
|
||||
// If there are no more blocks to send, we're FUBAR, so abort the
|
||||
// request. Otherwise, send the next block.
|
||||
if (m_txIndex >= m_dataLen) {
|
||||
// no more data to send, bad news
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Gateway requested too many blocks");
|
||||
break;
|
||||
}
|
||||
if (sendChoreoRequest() != NO_ERROR) {
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Send Choreo request failed");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_REQUEST_ENTITY_INCOMPLETE: //4.08
|
||||
// 4.08 means the server is missing one or more blocks, so can't
|
||||
// service the request.
|
||||
// We're FUBAR, so abort the request.
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Gateway returned 4.08");
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_REQUEST_ENTITY_TOO_LARGE: //4.13
|
||||
// 4.13 means the server ran out of memory when receiving the
|
||||
// request.
|
||||
// We're FUBAR, so abort the request.
|
||||
resetChoreo();
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Gateway returned 4.13");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Any response code other than the special ones above means the
|
||||
// server has processed the request and is returning the final result,
|
||||
// which may be in one or more blocks. If we haven't finished sending
|
||||
// the request, we're FUBAR, so abort the request. Otherwise, process
|
||||
// the response.
|
||||
m_dataLen = 0;
|
||||
if (moreBlocksToSend()) {
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
}
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Response received before request finished");
|
||||
resetChoreo();
|
||||
m_state = STATE_IDLE;
|
||||
break;
|
||||
} else {
|
||||
if (msg.getOptionCount(CoapMsg::COAP_OPTION_BLOCK2)) {
|
||||
// The server is sending a multi-block response, make sure
|
||||
// it's sending the response block we're expecting.
|
||||
|
||||
uint32_t respBlockNum = msg.getBlock2Num();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Block2 opt recv");
|
||||
|
||||
if (respBlockNum > m_rxBlockNum) {
|
||||
// It sent a newer block than the one we're expecting,
|
||||
// (i.e. we've somehow missed a block)
|
||||
// that's an error.
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
}
|
||||
resetChoreo();
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Received block out of order");
|
||||
break;
|
||||
|
||||
} else if (respBlockNum < m_rxBlockNum) {
|
||||
// It resent a block we've already received,
|
||||
// (i.e. it didn't see our ACK),
|
||||
// just accept (ACK) it again.
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_messageLayer.acceptMsg(msg);
|
||||
}
|
||||
m_lastResult = NO_ERROR;
|
||||
sendBlockRequest(m_messageID, m_rxBlockNum);
|
||||
m_state = STATE_RESPONSE_STARTED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Received previous block");
|
||||
|
||||
|
||||
} else {
|
||||
// Server sent the next block we are expecting.
|
||||
// Accept it and add the payload to our buffer.
|
||||
bool block2More = msg.getBlock2More();
|
||||
m_respHttpCode = msg.getHTTPStatus();
|
||||
m_lastResult = saveResponse(msg.getPayload(), msg.getPayloadLen());
|
||||
if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
m_rxBlockNum = respBlockNum;
|
||||
m_messageLayer.acceptMsg(msg);
|
||||
}
|
||||
if (block2More) {
|
||||
m_rxBlockNum++;
|
||||
m_messageID++;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Request next block2 msg");
|
||||
sendBlockRequest(m_messageID, m_rxBlockNum);
|
||||
m_state = STATE_RESPONSE_STARTED;
|
||||
} else {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Final block2 msg recv");
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Response complete");
|
||||
m_state = STATE_RESPONSE_READY;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// There's no Block2 option, so is either
|
||||
// the one and only block in the response
|
||||
// or an empty ack.
|
||||
|
||||
// check if empty to handle final ack. If empty
|
||||
// wait for CON with matching token and then
|
||||
// request other blocks of response
|
||||
|
||||
if (msg.getCode() == CoapMsg::COAP_EMPTY) {
|
||||
m_rrLayer.setState(CoapRRLayer::STATE_WAITING);
|
||||
m_messageLayer.setState(CoapMessageLayer::STATE_WAITING_FOR_CON);
|
||||
m_state = STATE_WAITING_FOR_RESPONSE;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Empty ACK received, waiting for response");
|
||||
|
||||
} else {
|
||||
m_respHttpCode = msg.getHTTPStatus();
|
||||
m_lastResult = saveResponse(msg.getPayload(), msg.getPayloadLen());
|
||||
m_messageLayer.acceptMsg(msg);
|
||||
m_state = STATE_RESPONSE_READY;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Response complete");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Does it have a Block2 option?
|
||||
|
||||
uint32_t responseBlockNum = msg.getBlock2Num();
|
||||
if (responseBlockNum == m_rxBlockNum && msg.getOptionCount(CoapMsg::COAP_OPTION_BLOCK2)) {
|
||||
adjustRequestBlockSize(msg);
|
||||
m_rxBlockNum++;
|
||||
if (0 == m_rxBlockNum) {
|
||||
clearData();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapRRLayer::ERROR_RECEIVING_RESPONSE:
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Error receiving response");
|
||||
break;
|
||||
|
||||
case CoapRRLayer::RST_RECEIVED:
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("RST received");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Anything else indicates a failure of some sort. Check
|
||||
// the messageLayer lastResult for specifics.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Request failed");
|
||||
m_lastResult = ERROR_REQUEST_FAILED;
|
||||
m_state = STATE_IDLE;
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
void TembooCoAPClient::cancelWait() {
|
||||
if (STATE_WAITING_FOR_RESPONSE == m_state) {
|
||||
m_messageLayer.cancelReliableSend();
|
||||
m_dataLen = 0;
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TembooCoAPClient::moreBlocksToSend() {
|
||||
uint16_t payloadLength = (m_dataLen - m_txByteCount) < m_blockSize ? (m_dataLen - m_txByteCount) : m_blockSize;
|
||||
return ((m_txByteCount + payloadLength) < m_dataLen);
|
||||
}
|
||||
|
||||
TembooCoAPClient::Result TembooCoAPClient::sendChoreoRequest() {
|
||||
uint16_t payloadLength = 0;
|
||||
|
||||
generateToken();
|
||||
|
||||
payloadLength = (m_dataLen - m_txByteCount) < m_blockSize ? (m_dataLen - m_txByteCount) : m_blockSize;
|
||||
m_txBlockNum = m_txByteCount/m_blockSize;
|
||||
bool moreBlocks = (m_txByteCount + payloadLength) < m_dataLen;
|
||||
m_lastError = sendBlock(m_messageID, &m_dataBuffer[m_txIndex], payloadLength, m_txBlockNum, moreBlocks);
|
||||
m_messageID++;
|
||||
if (TembooCoAPClient::NO_ERROR == m_lastError) {
|
||||
m_state = STATE_SEND_REQUEST;
|
||||
m_txIndex += payloadLength;
|
||||
m_txByteCount += payloadLength;
|
||||
} else {
|
||||
m_lastError = ERROR_SENDING_MSG;
|
||||
m_state = STATE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
return m_lastError;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
TembooCoAPChoreo::TembooCoAPChoreo(TembooCoAPClient& client) :
|
||||
m_client(client),
|
||||
m_accountName(NULL),
|
||||
m_appKeyName(NULL),
|
||||
m_appKeyValue(NULL),
|
||||
m_path(NULL),
|
||||
m_requestId(0),
|
||||
m_availableChars(0),
|
||||
m_nextChar(NULL),
|
||||
m_nextState(END)
|
||||
{
|
||||
}
|
||||
|
||||
TembooCoAPChoreo::~TembooCoAPChoreo() {
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKey(const String& appKeyValue) {
|
||||
m_appKeyValue = appKeyValue.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setAppKey(const char* appKeyValue) {
|
||||
m_appKeyValue = appKeyValue;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setChoreo(const String& path) {
|
||||
m_path = path.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setChoreo(const char* path) {
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setSavedInputs(const String& savedInputsName) {
|
||||
m_preset.put(savedInputsName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setSavedInputs(const char* savedInputsName) {
|
||||
m_preset.put(savedInputsName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setCredential(const String& credentialName) {
|
||||
m_preset.put(credentialName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setCredential(const char* credentialName) {
|
||||
m_preset.put(credentialName);
|
||||
}
|
||||
|
||||
void TembooCoAPChoreo::setProfile(const String& profileName) {
|
||||
m_preset.put(profileName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::setProfile(const char* profileName) {
|
||||
m_preset.put(profileName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const String& inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const char* inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const char* inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addInput(const String& inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const char* outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::addOutputFilter(const String& outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
int TembooCoAPChoreo::waitForResponse(TembooTimer& timer) {
|
||||
|
||||
int rc = SUCCESS;
|
||||
while (m_client.getState() == TembooCoAPClient::STATE_RESPONSE_STARTED || m_client.getState() == TembooCoAPClient::STATE_WAITING_FOR_RESPONSE) {
|
||||
if (timer.expired()) {
|
||||
TEMBOO_TRACELN("ERROR: Choreo timeout");
|
||||
rc = TEMBOO_ERROR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
m_client.loop();
|
||||
// While the buffer may be full, we need to receive all of the data
|
||||
// from the gateway even though we discard it. We still return
|
||||
// the buffer error code, but the user is still able to see what
|
||||
// data was able to fit in the current buffer
|
||||
if (m_client.getMessageState() != TembooCoAPClient::NO_ERROR && m_client.getMessageState() != TembooCoAPClient::ERROR_BUFFER_FULL) {
|
||||
rc = FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TembooCoAPChoreo::run(uint16_t timeoutSecs) {
|
||||
m_nextChar = NULL;
|
||||
|
||||
if (IS_EMPTY(m_accountName)) {
|
||||
return TEMBOO_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_path)) {
|
||||
return TEMBOO_ERROR_CHOREO_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyName)) {
|
||||
return TEMBOO_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyValue)) {
|
||||
return TEMBOO_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
int rc = 0;
|
||||
|
||||
TembooTimer timer(timeoutSecs * 1000L);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
m_client.resetChoreo();
|
||||
|
||||
TembooCoAPSession session(m_client);
|
||||
m_requestId = s_nextRequestId++;
|
||||
|
||||
m_respData = NULL;
|
||||
m_availableChars = 0;
|
||||
m_nextState = START;
|
||||
uint16toa(0 , m_httpCodeStr);
|
||||
|
||||
m_client.getNextMessageID();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending request");
|
||||
rc = session.executeChoreo(m_requestId, m_accountName, m_appKeyName, m_appKeyValue, m_path, m_inputs, m_expressions, m_sensors, m_outputs, m_preset, m_deviceType, m_deviceName);
|
||||
if (SUCCESS != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
// finish sending Choreo request
|
||||
while (m_client.getState() == TembooCoAPClient::STATE_SEND_REQUEST) {
|
||||
if(m_client.loop() == TembooCoAPClient::ERROR_REQUEST_FAILED) {
|
||||
rc = m_client.getMessageState();
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Choreo request failed");
|
||||
goto ErrorExit;
|
||||
}
|
||||
}
|
||||
|
||||
// choreo request complete, wait for CON from gateway
|
||||
// and then request the rest of the response
|
||||
|
||||
rc = waitForResponse(timer);
|
||||
if (SUCCESS != rc){
|
||||
rc = m_client.getMessageState();
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Waiting for response failed");
|
||||
goto ErrorExit;
|
||||
} else {
|
||||
|
||||
m_respData = (char*)m_client.getPacketBuffer();
|
||||
uint16_t httpCode = m_client.getRespHttpCode();
|
||||
if (httpCode >= 700) {
|
||||
httpCode = 0;
|
||||
}
|
||||
|
||||
uint16toa(httpCode, m_httpCodeStr);
|
||||
m_availableChars = strlen(m_respData) + strlen(m_httpCodeStr) + strlen(HTTP_CODE_PREFIX) + strlen(HTTP_CODE_SUFFIX);
|
||||
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
|
||||
//Unauthroized, need to update the time
|
||||
if (httpCode == 401 && i == 0) {
|
||||
find(HEADER_TIME);
|
||||
TembooCoAPSession::setTime((unsigned long)this->parseInt());
|
||||
} else {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACE(m_availableChars);
|
||||
TEMBOO_TRACELN(" CHARS");
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Response buffer data:");
|
||||
TEMBOO_TRACELN(m_respData);
|
||||
|
||||
rc = m_client.getMessageState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorExit:
|
||||
if (SUCCESS != rc) {
|
||||
TEMBOO_TRACE(" ERROR:");
|
||||
TEMBOO_TRACELN(rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int TembooCoAPChoreo::available() {
|
||||
return m_availableChars;
|
||||
}
|
||||
|
||||
|
||||
int TembooCoAPChoreo::peek() {
|
||||
if (m_availableChars > 0) {
|
||||
return (int)*m_nextChar;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int TembooCoAPChoreo::read() {
|
||||
|
||||
if (m_availableChars > 0) {
|
||||
int c = 0;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
c = (int)(*m_nextChar++);
|
||||
m_nextState = HTTP_CODE_PRE;
|
||||
break;
|
||||
|
||||
case HTTP_CODE_PRE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ('\0' == *m_nextChar) {
|
||||
m_nextState = HTTP_CODE_VALUE;
|
||||
m_nextChar = m_httpCodeStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_VALUE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = HTTP_CODE_SUF;
|
||||
m_nextChar = HTTP_CODE_SUFFIX;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_SUF:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ('\0' == *m_nextChar) {
|
||||
m_nextState = RESP_DATA;
|
||||
m_nextChar = m_respData;
|
||||
}
|
||||
break;
|
||||
|
||||
case RESP_DATA:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ('\0' == *m_nextChar || m_availableChars <= 0) {
|
||||
m_nextState = END;
|
||||
}
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = -1;
|
||||
}
|
||||
if (m_availableChars > 0) {
|
||||
m_availableChars--;
|
||||
}
|
||||
return c;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t TembooCoAPChoreo::write(uint8_t __attribute__ ((unused)) data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPChoreo::flush() {
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
m_availableChars = 0;
|
||||
}
|
||||
|
||||
317
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.h
Normal file
317
arduino/libraries/Temboo/src/TembooCoAPEdgeDevice.h
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOCOAP_H_
|
||||
#define TEMBOOCOAP_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "utility/TembooTimer.h"
|
||||
#include "utility/TembooTags.h"
|
||||
|
||||
#include "utility/TembooCoAPIPStack.h"
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoInputExpressionSet.h"
|
||||
#include "utility/ChoreoSensorInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
#include "utility/ChoreoDevice.h"
|
||||
#include "utility/CoapMsg.h"
|
||||
#include "utility/CoapMessageLayer.h"
|
||||
#include "utility/CoapRRLayer.h"
|
||||
|
||||
|
||||
#define IS_EMPTY(s) (NULL == s || '\0' == *s)
|
||||
#define DEFAULT_CHOREO_TIMEOUT 900
|
||||
|
||||
class TembooCoAPChoreo;
|
||||
|
||||
class TembooCoAPClient {
|
||||
public:
|
||||
TembooCoAPClient(TembooCoAPIPStack& ipStack, IPAddress gatewayAddress, uint16_t gatewayPort = DEFAULT_COAP_PORT);
|
||||
virtual ~TembooCoAPClient();
|
||||
void begin(long seed);
|
||||
|
||||
enum Result {
|
||||
NO_ERROR = 0,
|
||||
ERROR_REQUEST_FAILED,
|
||||
ERROR_BAD_RESPONSE,
|
||||
ERROR_MSG_TOKEN,
|
||||
ERROR_MSG_OPTION,
|
||||
ERROR_MSG_PAYLOAD,
|
||||
ERROR_SENDING_MSG,
|
||||
ERROR_RECEIVING_MSG,
|
||||
ERROR_BUFFER_FULL,
|
||||
ERROR_INVALID_MSG,
|
||||
ERROR_RECEIVING_RESPONSE
|
||||
};
|
||||
|
||||
enum State {
|
||||
STATE_IDLE,
|
||||
STATE_TRANSMIT,
|
||||
STATE_WAITING_FOR_RESPONSE,
|
||||
STATE_RESPONSE_STARTED,
|
||||
STATE_RESPONSE_READY,
|
||||
STATE_NO_RESPONSE,
|
||||
STATE_SEND_REQUEST,
|
||||
STATE_ERROR
|
||||
};
|
||||
|
||||
Result write(uint8_t value);
|
||||
Result write(uint8_t* value, uint16_t len);
|
||||
void clearData() {m_dataLen = 0;}
|
||||
Result loop();
|
||||
Result sendChoreoRequest();
|
||||
uint8_t* getPacketBuffer() {return m_respBuffer;}
|
||||
int32_t getPacketBufferSize() {return m_respLen;}
|
||||
int32_t getPacketLength() {return 1000;}
|
||||
int16_t getRespHttpCode() {return m_respHttpCode;}
|
||||
State getState() {return m_state;}
|
||||
Result sendBlockRequest(uint16_t msgID, uint32_t blockNum);
|
||||
void resetChoreo();
|
||||
int getMessageState() {return m_lastResult;}
|
||||
Result requestTime(uint16_t msgID);
|
||||
|
||||
protected:
|
||||
static const char URI_PATH[];
|
||||
Result m_lastResult;
|
||||
bool moreBlocksToSend();
|
||||
|
||||
// MAX_BLOCK_SIZE *MUST* be one of the standard CoAP block sizes
|
||||
// This size should be set with consideration to any network or
|
||||
// hardware limitations on UDP packet size.
|
||||
// (16, 32, 64, 128, 256, 512, or 1024).
|
||||
static const int MAX_BLOCK_SIZE = 64;
|
||||
|
||||
// MAX_PACKET_SIZE should be at least big enough to hold:
|
||||
// for outgoing requests:
|
||||
// 4 header bytes
|
||||
// 6 token bytes (in our case, Spec says tokens can be up to 8 bytes).
|
||||
// 1 byte for the URI PATH option (I *think*)
|
||||
// 4 strlen(URI_PATH) bytes for the URI PATH option value.
|
||||
// 3 bytes for a block1 option
|
||||
// 3 bytes for a block1 option value
|
||||
// 1 bytes for a block2 option (early response size negotiation)
|
||||
// 1 bytes for a block2 option value
|
||||
// ? 5 bytes for a size1 or size2 option
|
||||
// ? 4 bytes for a sizeX option value
|
||||
// 1 byte for the FF payload marker
|
||||
// MAX_BLOCK_SIZE for the payload
|
||||
//
|
||||
// or 24 + MAX_BLOCK_SIZE
|
||||
//
|
||||
// HOWEVER... we need to consider the possibility that the server may
|
||||
// use more options and thus send more bytes. So we should add as much
|
||||
// extra space as we can reasonably afford so as to avoid buffer overflows.
|
||||
|
||||
static const size_t MAX_PACKET_SIZE = 90;
|
||||
|
||||
|
||||
static const size_t MAX_DATA_SIZE = 1000;
|
||||
|
||||
static const uint16_t DEFAULT_COAP_PORT = 5683;
|
||||
|
||||
CoapMessageLayer m_messageLayer;
|
||||
CoapRRLayer m_rrLayer;
|
||||
IPAddress m_gatewayAddress;
|
||||
uint16_t m_gatewayPort;
|
||||
uint16_t m_messageID;
|
||||
State m_state;
|
||||
uint16_t m_blockSize;
|
||||
Result m_lastError;
|
||||
|
||||
uint16_t m_dataLen;
|
||||
uint8_t m_dataBuffer[MAX_DATA_SIZE];
|
||||
uint8_t m_respBuffer[MAX_DATA_SIZE];
|
||||
|
||||
char m_token[9];
|
||||
uint8_t m_txBuffer[MAX_PACKET_SIZE];
|
||||
uint8_t m_rxBuffer[MAX_PACKET_SIZE];
|
||||
uint32_t m_rxBlockNum;
|
||||
int32_t m_txIndex;
|
||||
int32_t m_txByteCount;
|
||||
int32_t m_respLen;
|
||||
uint32_t m_txBlockNum;
|
||||
int16_t m_respHttpCode;
|
||||
|
||||
Result generateToken();
|
||||
uint16_t getNextMessageID();
|
||||
|
||||
Result sendBlock(uint16_t msgID, uint8_t* payload, size_t len, uint32_t blockNum, bool moreBlocks);
|
||||
void adjustRequestBlockSize(CoapMsg& msg);
|
||||
void cancelWait();
|
||||
|
||||
int send(CoapMsg* msg);
|
||||
int sendResetMsg(CoapMsg& msg);
|
||||
int sendEmptyAckMsg(CoapMsg& msg);
|
||||
int sendBlock2AckMsg(CoapMsg& msg);
|
||||
bool isMsgResponse(CoapMsg& msg);
|
||||
void handleBlockAck(CoapMsg& msg);
|
||||
void handleBlock1InResponse(CoapMsg& msg);
|
||||
Result saveResponse(uint8_t* values, uint16_t len);
|
||||
|
||||
friend class TembooCoAPChoreo;
|
||||
};
|
||||
|
||||
class TembooCoAPChoreo : public Stream {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of a TembooCoAPClient.
|
||||
// Used to communicate with a Temboo CoAP Gateway.
|
||||
TembooCoAPChoreo(TembooCoAPClient& client);
|
||||
~TembooCoAPChoreo();
|
||||
|
||||
// Does nothing. Just for source compatibility with Yun code.
|
||||
void begin() {;};
|
||||
|
||||
// Sets the account name to use when communicating with Temboo.
|
||||
// (required)
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
// Sets the application key name to use with choreo execution requests.
|
||||
// (required)
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
// Sets the application key value to use with choreo execution requests
|
||||
// (required)
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
// sets the name of the choreo to be executed.
|
||||
// (required)
|
||||
void setChoreo(const String& choreoPath);
|
||||
void setChoreo(const char* choreoPath);
|
||||
|
||||
|
||||
// sets the name of the saved inputs to use when executing the choreo
|
||||
// (optional)
|
||||
void setSavedInputs(const String& savedInputsName);
|
||||
void setSavedInputs(const char* savedInputsName);
|
||||
|
||||
void setCredential(const String& credentialName);
|
||||
void setCredential(const char* credentialName);
|
||||
|
||||
void setProfile(const String& profileName);
|
||||
void setProfile(const char* profileName);
|
||||
|
||||
// sets an input to be used when executing a choreo.
|
||||
// (optional or required, depending on the choreo being executed.)
|
||||
void addInput(const String& inputName, const String& inputValue);
|
||||
void addInput(const char* inputName, const char* inputValue);
|
||||
void addInput(const char* inputName, const String& inputValue);
|
||||
void addInput(const String& inputName, const char* inputValue);
|
||||
|
||||
// sets an output filter to be used to process the choreo output
|
||||
// (optional)
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName);
|
||||
|
||||
// run the choreo using the current input info
|
||||
int run(uint16_t timeoutSecs = DEFAULT_CHOREO_TIMEOUT);
|
||||
|
||||
char* getResponseData() {return m_respData;}
|
||||
char* getHTTPResponseCode() {return m_httpCodeStr;}
|
||||
|
||||
|
||||
// Stream interface
|
||||
void close() {};
|
||||
int available();
|
||||
int read();
|
||||
int peek();
|
||||
void flush();
|
||||
|
||||
//Print interface
|
||||
size_t write(uint8_t data);
|
||||
|
||||
enum Error {
|
||||
SUCCESS = 0,
|
||||
FAILURE,
|
||||
TEMBOO_ERROR_ACCOUNT_MISSING = 201,
|
||||
TEMBOO_ERROR_CHOREO_MISSING = 203,
|
||||
TEMBOO_ERROR_APPKEY_NAME_MISSING = 205,
|
||||
TEMBOO_ERROR_APPKEY_MISSING = 207,
|
||||
TEMBOO_ERROR_HTTP_ERROR = 223,
|
||||
TEMBOO_ERROR_TIMEOUT = 225,
|
||||
TEMBOO_ERROR_MEMORY = 900,
|
||||
TEMBOO_ERROR_TCPIP_CONNECT_FAIL = 901,
|
||||
TEMBOO_ERROR_NO_RESPONSE = 0xFFFF
|
||||
};
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
static const size_t MAX_RESPONSE_SIZE = 900;
|
||||
|
||||
TembooCoAPClient& m_client;
|
||||
const char* m_accountName;
|
||||
const char* m_appKeyName;
|
||||
const char* m_appKeyValue;
|
||||
const char* m_path;
|
||||
|
||||
ChoreoInputSet m_inputs;
|
||||
ChoreoInputExpressionSet m_expressions;
|
||||
ChoreoSensorInputSet m_sensors;
|
||||
ChoreoOutputSet m_outputs;
|
||||
ChoreoPreset m_preset;
|
||||
ChoreoDevice m_deviceType;
|
||||
ChoreoDevice m_deviceName;
|
||||
|
||||
char m_httpCodeStr[4];
|
||||
char* m_respData;
|
||||
|
||||
uint16_t m_requestId;
|
||||
static uint16_t s_nextRequestId;
|
||||
|
||||
// variables for the stream interface
|
||||
size_t m_availableChars;
|
||||
const char* m_nextChar;
|
||||
enum State {START,
|
||||
HTTP_CODE_PRE,
|
||||
HTTP_CODE_VALUE,
|
||||
HTTP_CODE_SUF,
|
||||
RESP_DATA,
|
||||
END};
|
||||
State m_nextState;
|
||||
|
||||
protected:
|
||||
int waitForResponse(TembooTimer& timer);
|
||||
uint16_t getRequestId() {return m_requestId;}
|
||||
};
|
||||
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
861
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.cpp
Normal file
861
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.cpp
Normal file
@@ -0,0 +1,861 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooMQTTSession.h"
|
||||
#include "utility/MQTTPacket.h"
|
||||
|
||||
#include "TembooMQTTEdgeDevice.h"
|
||||
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (0xFFFF)
|
||||
#endif
|
||||
|
||||
static const char HTTP_CODE_PREFIX[] PROGMEM = "HTTP_CODE\x0A\x1F";
|
||||
static const char HTTP_CODE_SUFFIX[] PROGMEM = "\x0A\x1E";
|
||||
|
||||
static const char REQUEST_TOPIC_PREFIX[] PROGMEM = "/temboo/req/";
|
||||
static const char REQUEST_ACK_TOPIC_PREFIX[] PROGMEM = "/temboo/ack/";
|
||||
static const char RESPONSE_TOPIC_PREFIX[] PROGMEM = "/temboo/resp/";
|
||||
static const char RESPONSE_DATA_TOPIC_PREFIX[] PROGMEM = "/temboo/resp-data/";
|
||||
|
||||
// TIME_TOPIC must NOT be PROGMEM
|
||||
static const char TIME_TOPIC[] = "/temboo/time";
|
||||
|
||||
static TembooMQTTChoreo* volatile g_currentChoreo = NULL;
|
||||
uint16_t TembooMQTTChoreo::s_nextRequestId = 0;
|
||||
|
||||
void handleDataMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("data: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
char* c;
|
||||
unsigned int count;
|
||||
for (c = (char*)msg.payload, count = 0; *c != '\0' && isdigit(*c) && count < msg.payloadlen; c++) {
|
||||
count++;
|
||||
if (count > 5) {
|
||||
TEMBOO_TRACELN(" long");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
TEMBOO_TRACELN(" short");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TAG_VALUE_SEPARATOR != *c) {
|
||||
TEMBOO_TRACELN(" bad msg");
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace the : with \0 so we can use strtoul.
|
||||
*c = '\0';
|
||||
|
||||
unsigned long requestId = strtoul((char*)msg.payload, NULL, 10);
|
||||
if (UINT16_MAX < requestId) {
|
||||
TEMBOO_TRACELN(" bad id");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) {
|
||||
TEMBOO_TRACE(" stale id: ");
|
||||
TEMBOO_TRACELN(requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
g_currentChoreo->setResponseData(c + 1, msg.payloadlen - strlen((char*)msg.payload) - 1);
|
||||
TEMBOO_TRACELN(" ok");
|
||||
|
||||
}
|
||||
|
||||
bool validateUint16PairMessage(const char* msg) {
|
||||
const char* c;
|
||||
int count = 0;
|
||||
for (c = msg; *c != '\0' && isdigit(*c); c++) {
|
||||
count++;
|
||||
if (count > 5)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TAG_VALUE_SEPARATOR != *c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
c++;
|
||||
for (count = 0; *c != '\0' && isdigit(*c); c++) {
|
||||
count++;
|
||||
if (count > 5) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void handleResponseMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("resp: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
// Expected max length is 9 (for 65535:599)
|
||||
if (msg.payloadlen > 9) {
|
||||
TEMBOO_TRACELN("long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.payloadlen < 3) {
|
||||
TEMBOO_TRACELN("short");
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the payload and nul terminate it;
|
||||
char respStr[10];
|
||||
memcpy(respStr, msg.payload, msg.payloadlen);
|
||||
respStr[msg.payloadlen] = '\0';
|
||||
|
||||
TEMBOO_TRACE(respStr);
|
||||
|
||||
if (!validateUint16PairMessage(respStr)) {
|
||||
TEMBOO_TRACELN(" bad msg");
|
||||
return;
|
||||
}
|
||||
|
||||
char* next;
|
||||
unsigned long respCode = 0;
|
||||
unsigned long requestId = strtoul(respStr, &next, 10);
|
||||
|
||||
// validate only checks that the request ID
|
||||
// has at least 1 but no more than 6 digits.
|
||||
// so we have to check the actual value here.
|
||||
if (UINT16_MAX < requestId) {
|
||||
TEMBOO_TRACELN(" bad id");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the request ID in the message doesn't match the
|
||||
// current request ID, then it's a stale ack.
|
||||
if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) {
|
||||
TEMBOO_TRACE(" stale id: ");
|
||||
TEMBOO_TRACELN(respStr);
|
||||
return;
|
||||
}
|
||||
|
||||
next++;
|
||||
respCode = strtoul(next, NULL, 10);
|
||||
|
||||
// Validate only checks that the ack code
|
||||
// has at least 1 but no more than 6 digits,
|
||||
// so we have to check the actual value here.
|
||||
if (1000 <= respCode) {
|
||||
TEMBOO_TRACELN(" bad code");
|
||||
return;
|
||||
}
|
||||
|
||||
// FINALLY, everything's OK.
|
||||
// pass the value to the waiting choreo.
|
||||
g_currentChoreo->setHTTPResponseCode(next);
|
||||
TEMBOO_TRACELN(" ok");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void handleTimeMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("time: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
// Time messages should be <= 10 characters long.
|
||||
if (msg.payloadlen > 10) {
|
||||
TEMBOO_TRACELN("long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.payloadlen == 0) {
|
||||
TEMBOO_TRACELN("short");
|
||||
return;
|
||||
}
|
||||
|
||||
// Payload should consist only of digits (0 - 9)
|
||||
for (unsigned int i = 0; i < msg.payloadlen; i++) {
|
||||
if (! isdigit(((char*)msg.payload)[i])) {
|
||||
TEMBOO_TRACELN("!digit");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char timeStr[11];
|
||||
memcpy(timeStr, msg.payload, msg.payloadlen);
|
||||
timeStr[msg.payloadlen] = '\0';
|
||||
|
||||
TEMBOO_TRACE(timeStr);
|
||||
|
||||
uint32_t t = strtoul(timeStr, NULL, 10);
|
||||
TembooMQTTSession::setTime(t);
|
||||
|
||||
TEMBOO_TRACELN(" ok");
|
||||
}
|
||||
|
||||
void handleAckMessage(MQTT::MessageData& md) {
|
||||
|
||||
TEMBOO_TRACE("ack: ");
|
||||
|
||||
MQTT::Message& msg = md.message;
|
||||
|
||||
// Expected max length is 11 (for 65535:65535)
|
||||
if (msg.payloadlen > 11) {
|
||||
TEMBOO_TRACELN("long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.payloadlen == 0) {
|
||||
TEMBOO_TRACELN("short");
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the payload and nul terminate it;
|
||||
char ackStr[12];
|
||||
memcpy(ackStr, msg.payload, msg.payloadlen);
|
||||
ackStr[msg.payloadlen] = '\0';
|
||||
|
||||
TEMBOO_TRACE(ackStr);
|
||||
|
||||
if (!validateUint16PairMessage(ackStr)) {
|
||||
TEMBOO_TRACELN(" bad msg");
|
||||
return;
|
||||
}
|
||||
|
||||
char* next;
|
||||
unsigned long ackCode = TEMBOO_ERROR_FAILURE;
|
||||
unsigned long requestId = strtoul(ackStr, &next, 10);
|
||||
|
||||
// validate only checks that the request ID
|
||||
// has at least 1 but no more than 6 digits.
|
||||
// so we have to check the actual value here.
|
||||
if (UINT16_MAX < requestId) {
|
||||
TEMBOO_TRACELN(" bad id");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the request ID in the message doesn't match the
|
||||
// current request ID, then it's a stale ack.
|
||||
if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) {
|
||||
TEMBOO_TRACE(" stale id: ");
|
||||
TEMBOO_TRACELN(ackStr);
|
||||
return;
|
||||
}
|
||||
|
||||
next++;
|
||||
ackCode = strtoul(next, NULL, 10);
|
||||
|
||||
// Validate only checks that the ack code
|
||||
// has at least 1 but no more than 6 digits,
|
||||
// so we have to check the actual value here.
|
||||
if (UINT16_MAX < ackCode) {
|
||||
TEMBOO_TRACELN(" bad code");
|
||||
return;
|
||||
}
|
||||
|
||||
// FINALLY, everything's OK.
|
||||
// pass the value to the waiting choreo.
|
||||
g_currentChoreo->setAckCode(ackCode);
|
||||
TEMBOO_TRACELN(" ok");
|
||||
|
||||
}
|
||||
|
||||
|
||||
TembooMQTTClient::TembooMQTTClient(TembooMQTTIPStack& ipStack, unsigned int commandTimeoutMs )
|
||||
: BaseClient (ipStack, commandTimeoutMs),
|
||||
m_ipStack(ipStack) {
|
||||
m_deviceId = NULL;
|
||||
m_requestTopic = NULL;
|
||||
m_ackTopic = NULL;
|
||||
m_responseTopic = NULL;
|
||||
m_dataTopic = NULL;
|
||||
}
|
||||
|
||||
void TembooMQTTClient::makeTopics() {
|
||||
m_requestTopic = strCatNew_P(REQUEST_TOPIC_PREFIX, m_deviceId);
|
||||
m_ackTopic = strCatNew_P(REQUEST_ACK_TOPIC_PREFIX, m_deviceId);
|
||||
m_responseTopic = strCatNew_P(RESPONSE_TOPIC_PREFIX, m_deviceId);
|
||||
m_dataTopic = strCatNew_P(RESPONSE_DATA_TOPIC_PREFIX, m_deviceId);
|
||||
}
|
||||
|
||||
int TembooMQTTClient::setDeviceId(char* id) {
|
||||
int rc = TEMBOO_ERROR_FAILURE;
|
||||
if (NULL == m_deviceId) {
|
||||
m_deviceId = new char[strlen(id) + 1];
|
||||
if (NULL != m_deviceId) {
|
||||
strcpy(m_deviceId, id);
|
||||
makeTopics();
|
||||
rc = TEMBOO_ERROR_OK;
|
||||
} else {
|
||||
rc = TEMBOO_ERROR_MEMORY;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TembooMQTTClient::setDeviceIdFromMac(byte (&mac)[6]) {
|
||||
int rc = TEMBOO_ERROR_FAILURE;
|
||||
|
||||
// Only do something if we don't already have a deviceId
|
||||
if (NULL == m_deviceId) {
|
||||
// generate the deviceId from the MAC address.
|
||||
char macStr[13];
|
||||
for (unsigned int i = 0; i < sizeof(mac)/sizeof(*mac); i++) {
|
||||
byte m = mac[i] >> 4;
|
||||
macStr[2 * i] = m < 10 ? ('0' + m):('A' + m - 10);
|
||||
m = mac[i] & 0x0F;
|
||||
macStr[2 * i + 1] = m < 10 ? ('0' + m):('A' + m - 10);
|
||||
}
|
||||
macStr[12] = '\0';
|
||||
rc = setDeviceId(macStr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
char* TembooMQTTClient::strCatNew_P(const char* s1, const char* s2) {
|
||||
size_t len = strlen_P(s1) + strlen(s2) + 1;
|
||||
char* result = new char[len];
|
||||
if (NULL != result) {
|
||||
strcpy_P(result, s1);
|
||||
strcat(result, s2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TembooMQTTClient::~TembooMQTTClient() {
|
||||
if (NULL != m_requestTopic) delete[] m_requestTopic;
|
||||
if (NULL != m_ackTopic) delete[] m_ackTopic;
|
||||
if (NULL != m_responseTopic) delete[] m_responseTopic;
|
||||
if (NULL != m_dataTopic) delete[] m_dataTopic;
|
||||
if (NULL != m_deviceId) delete[] m_deviceId;
|
||||
}
|
||||
|
||||
int TembooMQTTClient::connect(const char* hostname, int port) {
|
||||
int rc = TEMBOO_ERROR_FAILURE;
|
||||
|
||||
if (NULL == m_deviceId) {
|
||||
return TEMBOO_ERROR_DEVICE_ID_MISSING;
|
||||
}
|
||||
|
||||
TEMBOO_TRACE("DEVICE ID: ");
|
||||
TEMBOO_TRACELN(m_deviceId);
|
||||
|
||||
// Open a socket to the broker.
|
||||
TEMBOO_TRACE("IP: ");
|
||||
rc = m_ipStack.connect(hostname, port);
|
||||
if (1 != rc) {
|
||||
TEMBOO_TRACELN(rc);
|
||||
rc = TEMBOO_ERROR_TCPIP_CONNECT_FAIL;
|
||||
goto ErrorExit;
|
||||
} else {
|
||||
TEMBOO_TRACELN("OK");
|
||||
|
||||
// Establish an MQTT connection with the broker.
|
||||
TEMBOO_TRACE("MQ ");
|
||||
TEMBOO_TRACE(m_deviceId);
|
||||
TEMBOO_TRACE(": ");
|
||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||
data.MQTTVersion = 3;
|
||||
data.clientID.cstring = (char*)m_deviceId;
|
||||
rc = connect(data);
|
||||
if (MQTT::SUCCESS != rc) {
|
||||
TEMBOO_TRACELN(rc);
|
||||
rc = TEMBOO_ERROR_MQTT_CONNECT_FAIL;
|
||||
goto ErrorExit;
|
||||
}
|
||||
TEMBOO_TRACELN("OK");
|
||||
}
|
||||
|
||||
// Subscribe to the various topics we need to monitor.
|
||||
//NOTE: 'subscribe()' does NOT copy the topic strings,
|
||||
//so those strings must stay valid for the life of the connection.
|
||||
|
||||
TEMBOO_TRACE("SUB:");
|
||||
//Yes, it's slightly risky counting on MQTT::SUCCESS always being 0.
|
||||
rc = this->subscribe(m_ackTopic, MQTT::QOS1, handleAckMessage);
|
||||
rc |= this->subscribe(m_responseTopic, MQTT::QOS1, handleResponseMessage);
|
||||
rc |= this->subscribe(m_dataTopic, MQTT::QOS1, handleDataMessage);
|
||||
rc |= this->subscribe(TIME_TOPIC, MQTT::QOS1, handleTimeMessage);
|
||||
if (MQTT::SUCCESS != rc) {
|
||||
TEMBOO_TRACELN("NO");
|
||||
rc = TEMBOO_ERROR_MQTT_SUBSCRIBE_FAIL;
|
||||
goto ErrorExit;
|
||||
}
|
||||
TEMBOO_TRACELN("OK");
|
||||
|
||||
return TEMBOO_ERROR_OK;
|
||||
|
||||
ErrorExit:
|
||||
if (isConnected()) {
|
||||
disconnect();
|
||||
}
|
||||
m_ipStack.disconnect();
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool TembooMQTTClient::isConnected() {
|
||||
if (m_ipStack.isConnected()) {
|
||||
return BaseClient::isConnected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TembooMQTTClient::sendChoreoRequest(const char* request, size_t len) {
|
||||
MQTT::Message message;
|
||||
|
||||
message.qos = MQTT::QOS0;
|
||||
message.retained = false;
|
||||
message.dup = false;
|
||||
message.payload = (void*)request;
|
||||
message.payloadlen = len;
|
||||
int rc = this->publish(this->m_requestTopic, message);
|
||||
return rc == MQTT::SUCCESS ? TEMBOO_ERROR_OK : rc;
|
||||
}
|
||||
|
||||
TembooMQTTChoreo::TembooMQTTChoreo(TembooMQTTClient& client) :
|
||||
m_client(client),
|
||||
m_accountName(NULL),
|
||||
m_appKeyName(NULL),
|
||||
m_appKeyValue(NULL),
|
||||
m_path(NULL),
|
||||
m_haveHttpCode(false),
|
||||
m_ackCode(0),
|
||||
m_haveAckCode(false),
|
||||
m_haveData(false),
|
||||
m_requestId(0),
|
||||
m_availableChars(0),
|
||||
m_nextChar(NULL),
|
||||
m_nextState(END),
|
||||
m_packetStatus(TEMBOO_ERROR_OK)
|
||||
{
|
||||
}
|
||||
|
||||
TembooMQTTChoreo::~TembooMQTTChoreo() {
|
||||
memset(m_respData, '\0', sizeof(m_respData)/sizeof(m_respData[0]));
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKey(const String& appKeyValue) {
|
||||
m_appKeyValue = appKeyValue.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setAppKey(const char* appKeyValue) {
|
||||
m_appKeyValue = appKeyValue;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setChoreo(const String& path) {
|
||||
m_path = path.c_str();
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setChoreo(const char* path) {
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setSavedInputs(const String& savedInputsName) {
|
||||
m_preset.put(savedInputsName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setSavedInputs(const char* savedInputsName) {
|
||||
m_preset.put(savedInputsName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setCredential(const String& credentialName) {
|
||||
m_preset.put(credentialName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setCredential(const char* credentialName) {
|
||||
m_preset.put(credentialName);
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setProfile(const String& profileName) {
|
||||
m_preset.put(profileName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::setProfile(const char* profileName) {
|
||||
m_preset.put(profileName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const String& inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const char* inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName, inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const char* inputName, const String& inputValue) {
|
||||
m_inputs.put(inputName, inputValue.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addInput(const String& inputName, const char* inputValue) {
|
||||
m_inputs.put(inputName.c_str(), inputValue);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const char* filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const String& filterPath, const char* variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName);
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const char* filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath, variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const char* outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName, filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::addOutputFilter(const String& outputName, const String& filterPath, const String& variableName) {
|
||||
m_outputs.put(outputName.c_str(), filterPath.c_str(), variableName.c_str());
|
||||
}
|
||||
|
||||
int TembooMQTTChoreo::waitForResponse(volatile bool& var, TembooMQTTSession& session, ArduinoTimer& timer) {
|
||||
|
||||
int rc = TEMBOO_ERROR_OK;
|
||||
|
||||
while (!var) {
|
||||
if (timer.expired()) {
|
||||
rc = TEMBOO_ERROR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_client.isConnected()) {
|
||||
TEMBOO_TRACELN("DISCO");
|
||||
rc = TEMBOO_ERROR_MQTT_DISCONNECT;
|
||||
break;
|
||||
}
|
||||
m_client.yield(YIELD_TIME_MILLIS);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setHTTPResponseCode(char* respCodeStr) {
|
||||
//NOTE: if we run in a true multithreaded environment, this code
|
||||
//will have to be synchronized somehow. Checking the m_haveHttpCode
|
||||
//flag is insufficient to prevent race conditions.
|
||||
if (!m_haveHttpCode) {
|
||||
strncpy(m_httpCodeStr, respCodeStr, sizeof(m_httpCodeStr)-1);
|
||||
m_httpCodeStr[sizeof(m_httpCodeStr)-1] = '\0';
|
||||
m_haveHttpCode = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setResponseData(char* payload, size_t payloadLen) {
|
||||
|
||||
//NOTE: if we run in a true multithreaded environment, this code
|
||||
//will have to be synchronized somehow. Checking the m_haveData
|
||||
//flag is insufficient to prevent race conditions.
|
||||
if (!m_haveData) {
|
||||
size_t len = 0;
|
||||
if(payloadLen > MAX_MESSAGE_SIZE) {
|
||||
m_packetStatus = TEMBOO_ERROR_MQTT_BUFFER_OVERFLOW;
|
||||
len = sizeof(m_respData)/sizeof(m_respData[0])-1;
|
||||
} else if (payloadLen > MAX_RESPONSE_SIZE) {
|
||||
m_packetStatus = TEMBOO_ERROR_MQTT_DATA_TRUNCATED;
|
||||
len = sizeof(m_respData)/sizeof(m_respData[0])-1;
|
||||
} else {
|
||||
len = payloadLen;
|
||||
}
|
||||
memcpy(m_respData, payload, len);
|
||||
m_respData[len] = '\0';
|
||||
m_haveData = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMQTTChoreo::setAckCode(uint16_t ackCode) {
|
||||
//NOTE: if we run in a true multithreaded environment, this code
|
||||
//will have to be synchronized somehow. Checking the m_haveAckCode
|
||||
//flag is insufficient to prevent race conditions.
|
||||
if (!m_haveAckCode) {
|
||||
m_ackCode = ackCode;
|
||||
m_haveAckCode = true;
|
||||
}
|
||||
}
|
||||
|
||||
int TembooMQTTChoreo::run(uint16_t timeoutSecs) {
|
||||
m_nextChar = NULL;
|
||||
|
||||
if (!m_client.isConnected()) {
|
||||
return TEMBOO_ERROR_MQTT_DISCONNECT;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_accountName)) {
|
||||
return TEMBOO_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_path)) {
|
||||
return TEMBOO_ERROR_CHOREO_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyName)) {
|
||||
return TEMBOO_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
|
||||
if (IS_EMPTY(m_appKeyValue)) {
|
||||
return TEMBOO_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
|
||||
m_packetStatus = TEMBOO_ERROR_OK;
|
||||
|
||||
TembooMQTTSession session(m_client);
|
||||
m_requestId = s_nextRequestId++;
|
||||
|
||||
m_haveAckCode = false;
|
||||
m_ackCode = TEMBOO_ERROR_NO_RESPONSE;
|
||||
|
||||
m_haveHttpCode = false;
|
||||
memset(m_httpCodeStr, '\0', sizeof(m_httpCodeStr)/sizeof(m_httpCodeStr[0]));
|
||||
|
||||
m_haveData = false;
|
||||
memset(m_respData, '\0', sizeof(m_respData)/sizeof(m_respData[0]));
|
||||
|
||||
m_availableChars = 0;
|
||||
m_nextState = START;
|
||||
g_currentChoreo = this;
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACELN(" START");
|
||||
|
||||
ArduinoTimer timer(timeoutSecs * 1000L);
|
||||
|
||||
int rc = session.executeChoreo( m_requestId, m_accountName, m_appKeyName, m_appKeyValue, m_path, m_inputs, m_expressions, m_sensors, m_outputs, m_preset, m_deviceType, m_deviceName);
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
rc = waitForResponse(m_haveAckCode, session, timer);
|
||||
if (TEMBOO_ERROR_OK != rc){
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" ACK: ");
|
||||
TEMBOO_TRACELN(m_ackCode);
|
||||
|
||||
if (TEMBOO_ERROR_OK != m_ackCode) {
|
||||
rc = m_ackCode;
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
rc = waitForResponse(m_haveHttpCode, session, timer);
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" RESP: ");
|
||||
TEMBOO_TRACELN(m_httpCodeStr);
|
||||
|
||||
rc = waitForResponse(m_haveData, session, timer);
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
goto ErrorExit;
|
||||
}
|
||||
m_availableChars = strlen(m_respData) + strlen(m_httpCodeStr) + strlen_P(HTTP_CODE_PREFIX) + strlen_P(HTTP_CODE_SUFFIX);
|
||||
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" ");
|
||||
TEMBOO_TRACE(m_availableChars);
|
||||
TEMBOO_TRACELN(" CHARS");
|
||||
TEMBOO_TRACELN(m_respData);
|
||||
|
||||
rc = m_packetStatus;
|
||||
|
||||
ErrorExit:
|
||||
if (TEMBOO_ERROR_OK != rc) {
|
||||
TEMBOO_TRACE("RUN ");
|
||||
TEMBOO_TRACE(m_requestId);
|
||||
TEMBOO_TRACE(" ERROR:");
|
||||
TEMBOO_TRACELN(rc);
|
||||
}
|
||||
g_currentChoreo = NULL;
|
||||
m_client.yield(YIELD_TIME_MILLIS);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int TembooMQTTChoreo::available() {
|
||||
return m_availableChars;
|
||||
}
|
||||
|
||||
|
||||
int TembooMQTTChoreo::peek() {
|
||||
if (m_availableChars) {
|
||||
if (m_nextState == HTTP_CODE_VALUE || m_nextState == RESP_DATA || m_nextState == END) {
|
||||
return (int)*m_nextChar;
|
||||
} else {
|
||||
return (int)pgm_read_byte(m_nextChar);
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int TembooMQTTChoreo::read() {
|
||||
|
||||
if (m_haveData) {
|
||||
int c = 0;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
m_nextChar = HTTP_CODE_PREFIX;
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
m_nextState = HTTP_CODE_PRE;
|
||||
break;
|
||||
|
||||
case HTTP_CODE_PRE:
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = HTTP_CODE_VALUE;
|
||||
m_nextChar = m_httpCodeStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_VALUE:
|
||||
c = (int)(*m_nextChar++);
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = HTTP_CODE_SUF;
|
||||
m_nextChar = HTTP_CODE_SUFFIX;
|
||||
}
|
||||
break;
|
||||
|
||||
case HTTP_CODE_SUF:
|
||||
c = (int)pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = RESP_DATA;
|
||||
m_nextChar = m_respData;
|
||||
}
|
||||
break;
|
||||
|
||||
case RESP_DATA:
|
||||
c = (int)(*m_nextChar++);
|
||||
if ((m_availableChars - 1) <= 0) {
|
||||
m_nextState = END;
|
||||
}
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = -1;
|
||||
}
|
||||
if (m_availableChars > 0) {
|
||||
m_availableChars--;
|
||||
}
|
||||
return c;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t TembooMQTTChoreo::write(uint8_t data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTChoreo::flush() {
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
m_availableChars = 0;
|
||||
}
|
||||
|
||||
249
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.h
Normal file
249
arduino/libraries/Temboo/src/TembooMQTTEdgeDevice.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOMQTT_H_
|
||||
#define TEMBOOMQTT_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "utility/MQTTClient.h"
|
||||
#include "utility/ArduinoTimer.h"
|
||||
#include "utility/TembooTags.h"
|
||||
|
||||
#include "utility/TembooMQTTIPStack.h"
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoInputExpressionSet.h"
|
||||
#include "utility/ChoreoSensorInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
#include "utility/ChoreoDevice.h"
|
||||
|
||||
#define IS_EMPTY(s) (NULL == s || '\0' == *s)
|
||||
|
||||
#define TEMBOO_ERROR_OK (0)
|
||||
#define TEMBOO_ERROR_FAILURE (1)
|
||||
#define TEMBOO_ERROR_ACCOUNT_MISSING (201)
|
||||
#define TEMBOO_ERROR_CHOREO_MISSING (203)
|
||||
#define TEMBOO_ERROR_APPKEY_NAME_MISSING (205)
|
||||
#define TEMBOO_ERROR_APPKEY_MISSING (207)
|
||||
#define TEMBOO_ERROR_HTTP_ERROR (223)
|
||||
#define TEMBOO_ERROR_TIMEOUT (225)
|
||||
#define TEMBOO_ERROR_MEMORY (900)
|
||||
#define TEMBOO_ERROR_TCPIP_CONNECT_FAIL (901)
|
||||
#define TEMBOO_ERROR_MQTT_CONNECT_FAIL (902)
|
||||
#define TEMBOO_ERROR_MQTT_SUBSCRIBE_FAIL (903)
|
||||
#define TEMBOO_ERROR_MQTT_DISCONNECT (904)
|
||||
#define TEMBOO_ERROR_DEVICE_ID_MISSING (905)
|
||||
/*
|
||||
* The data from the MQTT is too large to fit in the buffer.
|
||||
* The MQTT read buffer read as much as it could and discarded
|
||||
* the rest of the packet data. Increase MAX_MESSAGE_SIZE
|
||||
* to read more data in MQTT's readbuf
|
||||
*/
|
||||
#define TEMBOO_ERROR_MQTT_BUFFER_OVERFLOW (906)
|
||||
/*
|
||||
* There was more data to be returned in the packet data than could
|
||||
* fit in the return buffer. Incease MAX_RESPONSE_SIZE to read more
|
||||
* of the packet response
|
||||
*/
|
||||
#define TEMBOO_ERROR_MQTT_DATA_TRUNCATED (907)
|
||||
#define TEMBOO_ERROR_NO_RESPONSE (0xFFFF)
|
||||
|
||||
|
||||
|
||||
static const int MAX_MESSAGE_SIZE = 512;
|
||||
static const int MAX_RESPONSE_SIZE = 100;
|
||||
static const int MAX_HANDLERS = 4;
|
||||
static const int YIELD_TIME_MILLIS = 200;
|
||||
|
||||
typedef MQTT::Client<TembooMQTTIPStack, ArduinoTimer, MAX_MESSAGE_SIZE, MAX_HANDLERS> BaseClient;
|
||||
|
||||
class TembooMQTTClient : public BaseClient {
|
||||
public:
|
||||
TembooMQTTClient(TembooMQTTIPStack& ipStack, unsigned int commandTimeoutMs = 30000 );
|
||||
virtual ~TembooMQTTClient();
|
||||
|
||||
using BaseClient::connect;
|
||||
int connect(const char* hostname, int port=1883);
|
||||
|
||||
using BaseClient::isConnected;
|
||||
bool isConnected();
|
||||
|
||||
int sendChoreoRequest(const char* request, size_t len);
|
||||
int setDeviceId(char* id);
|
||||
int setDeviceIdFromMac(byte (&mac)[6]);
|
||||
|
||||
protected:
|
||||
TembooMQTTIPStack& m_ipStack;
|
||||
char* m_deviceId;
|
||||
char* m_requestTopic;
|
||||
char* m_ackTopic;
|
||||
char* m_responseTopic;
|
||||
char* m_dataTopic;
|
||||
|
||||
char* strCatNew_P(const char*, const char*);
|
||||
void makeTopics();
|
||||
};
|
||||
|
||||
class TembooMQTTSession;
|
||||
|
||||
class TembooMQTTChoreo : public Stream {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of a TembooMQTTClient.
|
||||
// Used to communicate with a Temboo Gateway.
|
||||
TembooMQTTChoreo(TembooMQTTClient& client);
|
||||
~TembooMQTTChoreo();
|
||||
|
||||
// Does nothing. Just for source compatibility with Yun code.
|
||||
void begin() {};
|
||||
|
||||
// Sets the account name to use when communicating with Temboo.
|
||||
// (required)
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
// Sets the application key name to use with choreo execution requests.
|
||||
// (required)
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
// Sets the application key value to use with choreo execution requests
|
||||
// (required)
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
// sets the name of the choreo to be executed.
|
||||
// (required)
|
||||
void setChoreo(const String& choreoPath);
|
||||
void setChoreo(const char* choreoPath);
|
||||
|
||||
|
||||
// sets the name of the saved inputs to use when executing the choreo
|
||||
// (optional)
|
||||
void setSavedInputs(const String& savedInputsName);
|
||||
void setSavedInputs(const char* savedInputsName);
|
||||
|
||||
void setCredential(const String& credentialName);
|
||||
void setCredential(const char* credentialName);
|
||||
|
||||
void setProfile(const String& profileName);
|
||||
void setProfile(const char* profileName);
|
||||
|
||||
// sets an input to be used when executing a choreo.
|
||||
// (optional or required, depending on the choreo being executed.)
|
||||
void addInput(const String& inputName, const String& inputValue);
|
||||
void addInput(const char* inputName, const char* inputValue);
|
||||
void addInput(const char* inputName, const String& inputValue);
|
||||
void addInput(const String& inputName, const char* inputValue);
|
||||
|
||||
// sets an output filter to be used to process the choreo output
|
||||
// (optional)
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const char* variableName);
|
||||
void addOutputFilter(const char* filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const char* filterPath, const String& variableName);
|
||||
void addOutputFilter(const char* filterName, const String& filterPath, const String& variableName);
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName);
|
||||
|
||||
// run the choreo using the current input info
|
||||
int run(uint16_t timeoutSecs);
|
||||
|
||||
char* getResponseData() {return m_respData;}
|
||||
char* getHTTPResponseCode() {return m_httpCodeStr;}
|
||||
|
||||
|
||||
// Stream interface - see the Arduino library documentation.
|
||||
void close() {};
|
||||
int available();
|
||||
int read();
|
||||
int peek();
|
||||
void flush();
|
||||
|
||||
//Print interface - see the Arduino library documentation
|
||||
size_t write(uint8_t data);
|
||||
|
||||
|
||||
protected:
|
||||
TembooMQTTClient& m_client;
|
||||
const char* m_accountName;
|
||||
const char* m_appKeyName;
|
||||
const char* m_appKeyValue;
|
||||
const char* m_path;
|
||||
|
||||
ChoreoInputSet m_inputs;
|
||||
ChoreoInputExpressionSet m_expressions;
|
||||
ChoreoSensorInputSet m_sensors;
|
||||
ChoreoOutputSet m_outputs;
|
||||
ChoreoPreset m_preset;
|
||||
ChoreoDevice m_deviceType;
|
||||
ChoreoDevice m_deviceName;
|
||||
|
||||
char m_httpCodeStr[4];
|
||||
volatile bool m_haveHttpCode;
|
||||
|
||||
uint16_t m_ackCode;
|
||||
volatile bool m_haveAckCode;
|
||||
|
||||
char m_respData[MAX_RESPONSE_SIZE+1];
|
||||
volatile bool m_haveData;
|
||||
|
||||
uint16_t m_requestId;
|
||||
static uint16_t s_nextRequestId;
|
||||
|
||||
volatile uint16_t m_packetStatus;
|
||||
|
||||
// variables for the stream interface
|
||||
size_t m_availableChars;
|
||||
const char* m_nextChar;
|
||||
enum State {START, HTTP_CODE_PRE, HTTP_CODE_VALUE, HTTP_CODE_SUF, RESP_DATA, END};
|
||||
State m_nextState;
|
||||
|
||||
protected:
|
||||
uint16_t getRequestId() {return m_requestId;}
|
||||
void setAckCode(uint16_t ackCode);
|
||||
void setHTTPResponseCode(char* respCode);
|
||||
void setResponseData(char* data, size_t len);
|
||||
|
||||
int waitForResponse(volatile bool& var, TembooMQTTSession& session, ArduinoTimer& timer);
|
||||
|
||||
friend void handleDataMessage(MQTT::MessageData& md);
|
||||
friend void handleResponseMessage(MQTT::MessageData& md);
|
||||
friend void handleAckMessage(MQTT::MessageData& md);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
267
arduino/libraries/Temboo/src/TembooMonitoring.cpp
Normal file
267
arduino/libraries/Temboo/src/TembooMonitoring.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Mailbox.h>
|
||||
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
|
||||
#include "avr/dtostrf.h"
|
||||
#endif
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "TembooMonitoring.h"
|
||||
|
||||
const int MAX_MAILBOX_MESSAGE_SIZE = 128;
|
||||
|
||||
const unsigned long POLL_TIMEOUT = 50;
|
||||
const unsigned long INITIATE_TIMEOUT_MS = 5000;
|
||||
const unsigned long MCU_PING_PERIOD_MS = 5000;
|
||||
|
||||
void logTembooDebug(const char *c) {
|
||||
Console.print("Debug: ");
|
||||
Console.println(c);
|
||||
}
|
||||
|
||||
void addWebSocketPinData(int pin, int pinVal, bool requestResponse) {
|
||||
TembooMessaging::sendData(pin, pinVal, requestResponse);
|
||||
}
|
||||
|
||||
void updateIntervalTime(int intervalTime) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "Mi|V%i", intervalTime);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
TembooMessaging::TembooMessaging(TembooSensor** sensorTable, int sensorTableSize) {
|
||||
m_accountName = NULL;
|
||||
m_appKey = NULL;
|
||||
m_appKeyName = NULL;
|
||||
m_deviceID = NULL;
|
||||
m_connectionStatus = false;
|
||||
m_sensorTable = sensorTable;
|
||||
m_sensorTableSize = sensorTableSize;
|
||||
m_sensorTableDepth = 0;
|
||||
m_connectionAttemptTime = millis();
|
||||
m_lastPingTime = millis();
|
||||
// init sensor array
|
||||
int i = 0;
|
||||
for (i = 0; i < m_sensorTableSize; i++) {
|
||||
m_sensorTable[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int TembooMessaging::addTembooSensor(TembooSensor* sensor) {
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableSize; i++) {
|
||||
if (m_sensorTable[i] == sensor) {
|
||||
logTembooDebug("Sensor already added");
|
||||
return -1;
|
||||
}
|
||||
if (m_sensorTable[i] == NULL) {
|
||||
m_sensorTable[i] = sensor;
|
||||
m_sensorTableDepth++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
logTembooDebug("Sensor table full, sensor not added");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TembooMessaging::startMessaging() {
|
||||
if (!running()) {
|
||||
TEMBOO_TRACELN("starting messanger");
|
||||
m_connectionStatus = false;
|
||||
Process::begin("tembooMessaging");
|
||||
runAsynchronously();
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMessaging::setSensorsToDefaultState() {
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableDepth; i++) {
|
||||
if (m_sensorTable[i]->write != NULL) {
|
||||
m_sensorTable[i]->write(m_sensorTable[i]->sensorConfig, m_sensorTable[i]->defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TembooMessaging::begin() {
|
||||
Mailbox.begin();
|
||||
startMessaging();
|
||||
}
|
||||
|
||||
void TembooMessaging::setAccountName(const String& accountName) {
|
||||
m_accountName = accountName.c_str();
|
||||
}
|
||||
void TembooMessaging::setAccountName(const char* accountName) {
|
||||
m_accountName = accountName;
|
||||
}
|
||||
|
||||
void TembooMessaging::setAppKeyName(const String& appKeyName) {
|
||||
m_appKeyName = appKeyName.c_str();
|
||||
}
|
||||
void TembooMessaging::setAppKeyName(const char* appKeyName) {
|
||||
m_appKeyName = appKeyName;
|
||||
}
|
||||
|
||||
void TembooMessaging::setAppKey(const String& appKey) {
|
||||
m_appKey = appKey.c_str();
|
||||
}
|
||||
void TembooMessaging::setAppKey(const char* appKey) {
|
||||
m_appKey = appKey;
|
||||
}
|
||||
|
||||
void TembooMessaging::setDeviceID(const String& deviceID) {
|
||||
m_deviceID = deviceID.c_str();
|
||||
}
|
||||
void TembooMessaging::setDeviceID(const char* deviceID) {
|
||||
m_deviceID = deviceID;
|
||||
}
|
||||
|
||||
int TembooMessaging::initiateConnection() {
|
||||
unsigned long now = millis();
|
||||
if (now - m_connectionAttemptTime < INITIATE_TIMEOUT_MS) {
|
||||
poll();
|
||||
return TEMBOO_MONITORING_ERROR_NOT_CONNECTION_TIME;
|
||||
}
|
||||
if (m_accountName == NULL || *m_accountName == '\0') {
|
||||
return TEMBOO_MONITORING_ERROR_ACCOUNT_MISSING;
|
||||
}
|
||||
if (m_appKeyName == NULL || *m_appKeyName == '\0') {
|
||||
|
||||
return TEMBOO_MONITORING_ERROR_APPKEY_NAME_MISSING;
|
||||
}
|
||||
if (m_deviceID == NULL || *m_deviceID == '\0') {
|
||||
return TEMBOO_MONITORING_ERROR_DEVICEID_MISSING;
|
||||
}
|
||||
if (m_appKey == NULL || *m_appKey == '\0') {
|
||||
return TEMBOO_MONITORING_ERROR_APPKEY_MISSING;
|
||||
}
|
||||
startMessaging();
|
||||
int messageSize = strlen(m_accountName) + strlen(m_appKey) + strlen(m_appKeyName) + strlen(m_deviceID) + 11;
|
||||
uint8_t msg[messageSize];
|
||||
if (messageSize < MAX_MAILBOX_MESSAGE_SIZE) {
|
||||
messageSize = snprintf((char*)msg, messageSize, "MI|N%s|K%s|B%s|A%s", m_accountName, m_appKeyName, m_deviceID, m_appKey);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
m_connectionAttemptTime = now;
|
||||
} else {
|
||||
return TEMBOO_MONITORING_ERROR_REQUEST_TOO_LARGE;
|
||||
}
|
||||
return TEMBOO_MONITORING_ERROR_OK;
|
||||
}
|
||||
|
||||
WSMessageRequest TembooMessaging::poll() {
|
||||
startMessaging();
|
||||
long int now = millis();
|
||||
WSMessageRequest rc = WS_NO_MESSAGE;
|
||||
while (millis() - now < POLL_TIMEOUT) {
|
||||
if (millis() - m_lastPingTime >= MCU_PING_PERIOD_MS) {
|
||||
m_lastPingTime = millis();
|
||||
sendPing();
|
||||
}
|
||||
if (Mailbox.messageAvailable()) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int recvLen = Mailbox.readMessage(msg, MAX_MAILBOX_MESSAGE_SIZE);
|
||||
if (recvLen > 0) {
|
||||
rc = handleResponse(msg, m_sensorTable, m_sensorTableDepth, m_connectionStatus);
|
||||
if (rc == WS_UPDATE_CONNECTED) {
|
||||
//logTembooDebug("Connected to Temboo");
|
||||
m_connectionStatus = true;
|
||||
} else if (rc == WS_UPDATE_DISCONNECTED) {
|
||||
//logTembooDebug("Disconnected from Temboo");
|
||||
m_connectionStatus = false;
|
||||
} else if (rc == WS_REQUEST_ERROR) {
|
||||
// disconnect
|
||||
sendError("Message request error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void TembooMessaging::updatePinValue(int pinNum, int pinVal) {
|
||||
// save the data to the strcuture and then send to Temboo
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableDepth; i++) {
|
||||
if (m_sensorTable[i]->getSensorPin(m_sensorTable[i]->sensorConfig) == pinNum) {
|
||||
// if pin has pinWrite as NULL, it is an input
|
||||
// pin and needs to be stored. If not NULL,
|
||||
// pin is an actuator and should not be stored
|
||||
if(m_sensorTable[i]->write == NULL){
|
||||
sendData(pinNum, pinVal, false);
|
||||
} else {
|
||||
sendData(pinNum, pinVal, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
logTembooDebug("Unable to update pin");
|
||||
}
|
||||
|
||||
int TembooMessaging::retrievePinValue(int pinNum) {
|
||||
// search through pin structure and return the pin value
|
||||
int i = 0;
|
||||
for (; i < m_sensorTableDepth; i++) {
|
||||
if (m_sensorTable[i]->getSensorPin(m_sensorTable[i]->sensorConfig) == pinNum) {
|
||||
return m_sensorTable[i]->read(m_sensorTable[i]->sensorConfig);
|
||||
}
|
||||
}
|
||||
logTembooDebug("Unable to obtain pin value");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TembooMessaging::sendError(const char* errorText) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "ME|T%s", errorText);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
void TembooMessaging::disconnectWSConnection(int closeCode, const char* closeReason) {
|
||||
int messageSize = strlen(closeReason) + 11;
|
||||
uint8_t msg[messageSize];
|
||||
messageSize = snprintf((char*)msg, messageSize, "MF|O%i|r%s", closeCode, closeReason);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
void TembooMessaging::sendData(int pin, int pinVal, bool requestResponse) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "MD|P%i|V%i%s", pin, pinVal, requestResponse ? "|Q" : "");
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
void TembooMessaging::sendData(int pin, float pinVal) {
|
||||
uint8_t msg[MAX_MAILBOX_MESSAGE_SIZE] = {0};
|
||||
char floatStr[12] = {0};
|
||||
dtostrf(pinVal, 4, 2, floatStr);
|
||||
int messageSize = snprintf((char*)msg, MAX_MAILBOX_MESSAGE_SIZE, "MD|P%i|V%s", pin, floatStr);
|
||||
Mailbox.writeMessage(msg, messageSize);
|
||||
}
|
||||
|
||||
bool TembooMessaging::isConnected() {
|
||||
if (running()) {
|
||||
return m_connectionStatus;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TembooMessaging::sendPing() {
|
||||
Mailbox.writeMessage((uint8_t*)"P",1);
|
||||
}
|
||||
95
arduino/libraries/Temboo/src/TembooMonitoring.h
Normal file
95
arduino/libraries/Temboo/src/TembooMonitoring.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOMESSAGING_H_
|
||||
#define TEMBOOMESSAGING_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Process.h>
|
||||
|
||||
#include "utility/TembooWebSocketRequestHandles.h"
|
||||
|
||||
#define TEMBOO_MONITORING_ERROR_OK (0)
|
||||
#define TEMBOO_MONITORING_ERROR_ACCOUNT_MISSING (201)
|
||||
#define TEMBOO_MONITORING_ERROR_CHOREO_MISSING (203)
|
||||
#define TEMBOO_MONITORING_ERROR_APPKEY_NAME_MISSING (205)
|
||||
#define TEMBOO_MONITORING_ERROR_APPKEY_MISSING (207)
|
||||
#define TEMBOO_MONITORING_ERROR_DEVICEID_MISSING (209)
|
||||
|
||||
#define TEMBOO_MONITORING_ERROR_REQUEST_TOO_LARGE (251)
|
||||
#define TEMBOO_MONITORING_ERROR_NOT_CONNECTION_TIME (253)
|
||||
|
||||
class TembooMessaging : public Process {
|
||||
|
||||
public:
|
||||
TembooMessaging(TembooSensor** sensors, int pinTableSize);
|
||||
void begin();
|
||||
|
||||
void setAccountName(const String& accountName);
|
||||
void setAccountName(const char* accountName);
|
||||
|
||||
void setAppKeyName(const String& appKeyName);
|
||||
void setAppKeyName(const char* appKeyName);
|
||||
|
||||
void setAppKey(const String& appKey);
|
||||
void setAppKey(const char* appKey);
|
||||
|
||||
void setDeviceID(const String& appKey);
|
||||
void setDeviceID(const char* appKey);
|
||||
|
||||
int initiateConnection();
|
||||
|
||||
WSMessageRequest poll();
|
||||
|
||||
static void sendData(int pin, int pinVal, bool requestResponse=false);
|
||||
void sendData(int pin, float pinVal);
|
||||
void updatePinValue(int pinNum, int pinVal);
|
||||
int retrievePinValue(int pinNum);
|
||||
int addTembooSensor(TembooSensor* sensor);
|
||||
void setSensorsToDefaultState();
|
||||
|
||||
bool isConnected();
|
||||
|
||||
private:
|
||||
const char* m_accountName;
|
||||
const char* m_appKey;
|
||||
const char* m_appKeyName;
|
||||
const char* m_deviceID;
|
||||
bool m_connectionStatus;
|
||||
int m_sensorTableSize;
|
||||
int m_sensorTableDepth;
|
||||
long int m_connectionAttemptTime;
|
||||
TembooSensor** m_sensorTable;
|
||||
unsigned long m_lastPingTime;
|
||||
|
||||
void startMessaging();
|
||||
void disconnectWSConnection(int closeCode, const char* closeReason);
|
||||
void sendError(const char* errorText);
|
||||
void sendPing();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
51
arduino/libraries/Temboo/src/TembooSSL.cpp
Normal file
51
arduino/libraries/Temboo/src/TembooSSL.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
|
||||
#else
|
||||
|
||||
#include <string.h>
|
||||
#include <Client.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <TembooSSL.h>
|
||||
#include "utility/TembooGlobal.h"
|
||||
#include "utility/TembooSession.h"
|
||||
|
||||
TembooChoreoSSL::TembooChoreoSSL(Client& client) : TembooChoreo(client) {
|
||||
m_accountName = NULL;
|
||||
m_appKeyName = NULL;
|
||||
m_appKeyValue = NULL;
|
||||
m_path = NULL;
|
||||
m_nextChar = NULL;
|
||||
m_nextState = END;
|
||||
}
|
||||
|
||||
int TembooChoreoSSL::run() {
|
||||
return TembooChoreo::run(INADDR_NONE, 443, TEMBOO_CHOREO_DEFAULT_TIMEOUT_SECS);
|
||||
}
|
||||
|
||||
int TembooChoreoSSL::run(uint16_t timeoutSecs) {
|
||||
return TembooChoreo::run(INADDR_NONE, 443, timeoutSecs);
|
||||
}
|
||||
|
||||
#endif
|
||||
70
arduino/libraries/Temboo/src/TembooSSL.h
Normal file
70
arduino/libraries/Temboo/src/TembooSSL.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOSSL_H_
|
||||
#define TEMBOOSSL_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined (ARDUINO_AVR_YUN) || defined (ARDUINO_AVR_TRE)
|
||||
|
||||
#include <Temboo.h>
|
||||
|
||||
class TembooChoreoSSL : public TembooChoreo {};
|
||||
|
||||
#else
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO NON-YUN SUPPORT FOR SSL
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Client.h>
|
||||
#include <IPAddress.h>
|
||||
#include <Temboo.h>
|
||||
#include "utility/ChoreoInputSet.h"
|
||||
#include "utility/ChoreoOutputSet.h"
|
||||
#include "utility/ChoreoPreset.h"
|
||||
|
||||
class TembooChoreoSSL : public TembooChoreo {
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
// client - an instance of an Arduino Client that
|
||||
// allows HTTPS conntections, usually WiFiSSL
|
||||
TembooChoreoSSL(Client& client);
|
||||
|
||||
// run the choreo using the current input info
|
||||
// uses port 443
|
||||
int run();
|
||||
// run the choreo with a user specified timeout
|
||||
// uses port 443
|
||||
int run(uint16_t timeoutSecs);
|
||||
};
|
||||
|
||||
#endif //ARDUINO_AVR_YUN
|
||||
|
||||
#endif //TEMBOO_H_
|
||||
70
arduino/libraries/Temboo/src/TembooYunShield.h
Normal file
70
arduino/libraries/Temboo/src/TembooYunShield.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOYUNSHIELD_H_
|
||||
#define TEMBOOYUNSHIELD_H_
|
||||
|
||||
#ifndef TEMBOO_LIBRARY_VERSION
|
||||
#define TEMBOO_LIBRARY_VERSION 2
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// BEGIN ARDUINO YUN SHIELD SUPPORT
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#include <Process.h>
|
||||
|
||||
class TembooYunShieldChoreo : public Process {
|
||||
|
||||
public:
|
||||
void begin() {Process::begin("temboo");}
|
||||
void setAccountName(const String& accountName) { addParameter("-a" + accountName);}
|
||||
void setAppKeyName(const String& appKeyName) { addParameter("-u" + appKeyName);}
|
||||
void setAppKey(const String& appKey) { addParameter("-p" + appKey);}
|
||||
void setChoreo(const String& choreo) { addParameter("-c" + choreo);}
|
||||
void setCredential(const String& credentialName) { addParameter("-e" + credentialName);}
|
||||
void setSavedInputs(const String& savedInputsName) { addParameter("-e" + savedInputsName);}
|
||||
void setProfile(const String& profileName) { addParameter("-e" + profileName);}
|
||||
void addInput(const String& inputName, const String& inputValue) { addParameter("-i" + inputName + ":" + inputValue);}
|
||||
void addOutputFilter(const String& filterName, const String& filterPath, const String& variableName) { addParameter("-o" + filterName + ":" + filterPath + ":" + variableName);}
|
||||
void setSettingsFileToWrite(const String& filePath) { addParameter("-w" + filePath);}
|
||||
void setSettingsFileToRead(const String& filePath) { addParameter("-r" + filePath);}
|
||||
void setGatewayAddress(const String& addr) { addParameter("-s" + addr);}
|
||||
void addInputExpression(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addInputWithSensor(const String& inputName, const String& inputValue) { addParameter("-f" + inputName + ":" + inputValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorInput(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion) {addParameter("-n" + sensorName + ":" + String(sensorValue) + ":" + conversion);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue) {addParameter("-v" + sensorName + ":" + String(sensorValue));}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& conversion, const String& calibrationValue) {addParameter("-b" + sensorName + ":" + String(sensorValue) + ":" + conversion + ":" + calibrationValue);}
|
||||
void addSensorValue(const String& sensorName, long sensorValue, const String& rawLow, const String& rawHigh, const String& scaleLow, const String& scaleHigh) {addParameter("-m" + sensorName + ":" + String(sensorValue) + ":" + rawLow+ ":" + rawHigh+ ":" + scaleLow+ ":" + scaleHigh);}
|
||||
void setDeviceName(const String& deviceName) {addParameter("-d" + deviceName);}
|
||||
void setDeviceType(const String& deviceType) {addParameter("-t" + deviceType);}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //TEMBOOYUNSHIELD_H_
|
||||
62
arduino/libraries/Temboo/src/utility/ArduinoTimer.h
Normal file
62
arduino/libraries/Temboo/src/utility/ArduinoTimer.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT edge device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef ARDUINOTIMER_H_
|
||||
#define ARDUINOTIMER_H_
|
||||
|
||||
class ArduinoTimer {
|
||||
public:
|
||||
ArduinoTimer() {
|
||||
this->startTimeMillis = 0;
|
||||
this->durationMillis = 0;
|
||||
}
|
||||
|
||||
ArduinoTimer(unsigned long durationMillis) {
|
||||
this->countdown_ms(durationMillis);
|
||||
}
|
||||
|
||||
bool expired() {
|
||||
return left_ms() == 0;
|
||||
}
|
||||
|
||||
void countdown_ms(unsigned long durationMillis) {
|
||||
this->startTimeMillis = millis();
|
||||
this->durationMillis = durationMillis;
|
||||
}
|
||||
|
||||
void countdown(int durationSeconds) {
|
||||
countdown_ms((unsigned long)durationSeconds * 1000L);
|
||||
}
|
||||
|
||||
unsigned long left_ms() {
|
||||
unsigned long elapsedMillis = millis() - this->startTimeMillis;
|
||||
return elapsedMillis < this->durationMillis ? (this->durationMillis - elapsedMillis) : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned long startTimeMillis;
|
||||
unsigned long durationMillis;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* ARDUINOIMER_H_ */
|
||||
96
arduino/libraries/Temboo/src/utility/BaseFormatter.cpp
Normal file
96
arduino/libraries/Temboo/src/utility/BaseFormatter.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "BaseFormatter.h"
|
||||
|
||||
char BaseFormatter::escape(char c) {
|
||||
char outChar = c;
|
||||
switch(c) {
|
||||
case '\\':
|
||||
case '"':
|
||||
outChar = '\\';
|
||||
m_escapedChar = c;
|
||||
break;
|
||||
case '\b':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'f';
|
||||
break;
|
||||
case '\n':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
outChar = '\\';
|
||||
m_escapedChar = 't';
|
||||
break;
|
||||
default:
|
||||
m_escapedChar = '\0';
|
||||
}
|
||||
return outChar;
|
||||
}
|
||||
|
||||
char BaseFormatter::finishEscape() {
|
||||
char c = m_escapedChar;
|
||||
m_escapedChar = '\0';
|
||||
return c;
|
||||
}
|
||||
|
||||
char BaseFormatter::readTagChar(int nextState) {
|
||||
char c = pgm_read_byte(m_nextChar++);
|
||||
if (pgm_read_byte(m_nextChar) == '\0') {
|
||||
m_nextState = nextState;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
char BaseFormatter::readValueChar(int nextState) {
|
||||
char c;
|
||||
if (isEscaping()) {
|
||||
c = finishEscape();
|
||||
if (*m_nextChar == '\0') {
|
||||
m_nextState = nextState;
|
||||
}
|
||||
} else {
|
||||
c = escape(*m_nextChar++);
|
||||
if (!isEscaping()) {
|
||||
if(*m_nextChar == '\0') {
|
||||
m_nextState = nextState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
char BaseFormatter::readStartTagChar(const char* tag, int nextState) {
|
||||
m_nextChar = tag;
|
||||
char c = pgm_read_byte(m_nextChar++);
|
||||
m_nextState = nextState;
|
||||
return c;
|
||||
}
|
||||
46
arduino/libraries/Temboo/src/utility/BaseFormatter.h
Normal file
46
arduino/libraries/Temboo/src/utility/BaseFormatter.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef BASEFORMATTER_H_
|
||||
#define BASEFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class BaseFormatter {
|
||||
public:
|
||||
BaseFormatter() {m_escapedChar = '\0';}
|
||||
|
||||
protected:
|
||||
const char* m_nextChar;
|
||||
int m_nextState;
|
||||
char m_escapedChar;
|
||||
|
||||
char escape(char c);
|
||||
bool isEscaping() {return m_escapedChar != '\0';}
|
||||
char finishEscape();
|
||||
|
||||
char readTagChar(int nextState);
|
||||
char readValueChar(int nextState);
|
||||
char readStartTagChar(const char* tag, int nextState);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
23
arduino/libraries/Temboo/src/utility/ChoreoDevice.cpp
Normal file
23
arduino/libraries/Temboo/src/utility/ChoreoDevice.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "ChoreoDevice.h"
|
||||
42
arduino/libraries/Temboo/src/utility/ChoreoDevice.h
Normal file
42
arduino/libraries/Temboo/src/utility/ChoreoDevice.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREODEVICE_H_
|
||||
#define CHOREODEVICE_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
// Can be used for device type or device name
|
||||
class ChoreoDevice {
|
||||
public:
|
||||
ChoreoDevice() {m_name = NULL;}
|
||||
ChoreoDevice(const char* name) {put(name);}
|
||||
const char* getName() const {return m_name;}
|
||||
void put(const char* name) {m_name = name;}
|
||||
bool isEmpty() const {return m_name == NULL || *m_name == '\0';}
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoDeviceFormatter.h"
|
||||
#include "ChoreoDevice.h"
|
||||
|
||||
static const char TAG_DEVICE[] PROGMEM = "\"deviceType\":";
|
||||
static const char TAG_DEVICE_NAME[] PROGMEM = "\"device\":";
|
||||
|
||||
ChoreoDeviceFormatter::ChoreoDeviceFormatter(const ChoreoDevice* device, Type type) {
|
||||
m_device = device;
|
||||
m_type = type;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoDeviceFormatter::reset() {
|
||||
m_nextChar = NULL;
|
||||
if (m_device == NULL || m_device->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoDeviceFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoDeviceFormatter::next() {
|
||||
char c = '\0';
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
if (m_type == DEVICE_TYPE) {
|
||||
c = readStartTagChar(TAG_DEVICE, DEVICE_TAG);
|
||||
} else {
|
||||
c = readStartTagChar(TAG_DEVICE_NAME, DEVICE_TAG);
|
||||
}
|
||||
break;
|
||||
|
||||
case DEVICE_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_device->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = END;
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
55
arduino/libraries/Temboo/src/utility/ChoreoDeviceFormatter.h
Normal file
55
arduino/libraries/Temboo/src/utility/ChoreoDeviceFormatter.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREODEVICEFORMATTER_H_
|
||||
#define CHOREODEVICEFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoDevice.h"
|
||||
|
||||
class ChoreoDeviceFormatter : public BaseFormatter {
|
||||
public:
|
||||
enum Type {
|
||||
DEVICE_TYPE,
|
||||
DEVICE_NAME
|
||||
};
|
||||
|
||||
ChoreoDeviceFormatter(const ChoreoDevice* device, Type type);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
enum State {
|
||||
START,
|
||||
DEVICE_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
END
|
||||
};
|
||||
|
||||
const ChoreoDevice* m_device;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
#endif
|
||||
34
arduino/libraries/Temboo/src/utility/ChoreoInput.cpp
Normal file
34
arduino/libraries/Temboo/src/utility/ChoreoInput.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoInput.h"
|
||||
|
||||
ChoreoInput::ChoreoInput(ChoreoInput* prev, const char* name, const char* value) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
41
arduino/libraries/Temboo/src/utility/ChoreoInput.h
Normal file
41
arduino/libraries/Temboo/src/utility/ChoreoInput.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUT_H_
|
||||
#define CHOREOINPUT_H_
|
||||
#include "TembooGlobal.h"
|
||||
class ChoreoInput {
|
||||
public:
|
||||
ChoreoInput(ChoreoInput* prev, const char* name, const char* value);
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getValue() const {return m_value;}
|
||||
void setValue(const char* value) {m_value = value;}
|
||||
ChoreoInput* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoInput* m_next;
|
||||
const char* m_name;
|
||||
const char* m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoInputExpression.h"
|
||||
|
||||
ChoreoInputExpression::ChoreoInputExpression(ChoreoInputExpression* prev, const char* name, const char* value) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
41
arduino/libraries/Temboo/src/utility/ChoreoInputExpression.h
Normal file
41
arduino/libraries/Temboo/src/utility/ChoreoInputExpression.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTEXPRESSION_H_
|
||||
#define CHOREOINPUTEXPRESSION_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
class ChoreoInputExpression {
|
||||
public:
|
||||
ChoreoInputExpression(ChoreoInputExpression* prev, const char* name, const char* value);
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getValue() const {return m_value;}
|
||||
void setValue(const char* value) {m_value = value;}
|
||||
ChoreoInputExpression* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoInputExpression* m_next;
|
||||
const char* m_name;
|
||||
const char* m_value;
|
||||
};
|
||||
|
||||
#endif //CHOREOINPUTEXPRESSION_H_
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoInputExpressionFormatter.h"
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
|
||||
static const char TAG_INPUTS_START[] PROGMEM = "\"inputExpressions\":{";
|
||||
|
||||
ChoreoInputExpressionFormatter::ChoreoInputExpressionFormatter(const ChoreoInputExpressionSet* inputSet) {
|
||||
m_inputSet = inputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoInputExpressionFormatter::reset() {
|
||||
m_currentInput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_inputSet == NULL || m_inputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoInputExpressionFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoInputExpressionFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_INPUTS_START, INPUTS_TAG);
|
||||
break;
|
||||
|
||||
case INPUTS_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
if (m_nextState == NAME_START) {
|
||||
m_currentInput= m_inputSet->getFirstInput();
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = NAME_VALUE_SEPARATOR;
|
||||
break;
|
||||
|
||||
case NAME_VALUE_SEPARATOR:
|
||||
c = ':';
|
||||
m_nextState = VALUE_START;
|
||||
break;
|
||||
|
||||
case VALUE_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getValue();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VALUE_END;
|
||||
} else {
|
||||
m_nextState = VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
c = readValueChar(VALUE_END);
|
||||
break;
|
||||
|
||||
case VALUE_END:
|
||||
c = '"';
|
||||
m_currentInput = m_currentInput->getNext();
|
||||
if (m_currentInput != NULL) {
|
||||
m_nextState = NEXT_INPUT;
|
||||
} else {
|
||||
m_nextState = INPUTS_END;
|
||||
}
|
||||
break;
|
||||
case NEXT_INPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
m_nextState = NAME_START;
|
||||
break;
|
||||
|
||||
case INPUTS_END:
|
||||
c = '}';
|
||||
m_nextState = END;
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTEXPRESSIONFORMATTER_H_
|
||||
#define CHOREOINPUTEXPRESSIONFORMATTER_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
|
||||
class ChoreoInputExpressionFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoInputExpressionFormatter(const ChoreoInputExpressionSet* inputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoInputExpressionSet* m_inputSet;
|
||||
const ChoreoInputExpression* m_currentInput;
|
||||
|
||||
|
||||
enum State {
|
||||
START,
|
||||
INPUTS_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
NAME_VALUE_SEPARATOR,
|
||||
VALUE_START,
|
||||
VALUE,
|
||||
VALUE_END,
|
||||
NEXT_INPUT,
|
||||
INPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif //CHOREOINPUTEXPRESSIONFORMATTER_H_
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
|
||||
ChoreoInputExpressionSet::ChoreoInputExpressionSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoInputExpressionSet::~ChoreoInputExpressionSet() {
|
||||
ChoreoInputExpression* i = m_first;
|
||||
ChoreoInputExpression* next = NULL;
|
||||
while (i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoInputExpressionSet::put(const char* name, const char* value) {
|
||||
|
||||
// Haven't set ANY inputs yet?
|
||||
// Just create a new one.
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoInputExpression(NULL, name, value);
|
||||
} else {
|
||||
// Some inputs already set.
|
||||
// See if we already have this input.
|
||||
ChoreoInputExpression* last = NULL;
|
||||
ChoreoInputExpression* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
// We already have an input with this name.
|
||||
// Just update the value.
|
||||
i->setValue(value);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
// We don't have an input with this name
|
||||
// So we need to create a new one.
|
||||
if (i == NULL) {
|
||||
new ChoreoInputExpression(last, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* ChoreoInputExpressionSet::get(const char* name) const {
|
||||
ChoreoInputExpression* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i->getValue();
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTEXPRESSIONSET_H_
|
||||
#define CHOREOINPUTEXPRESSIONSET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoInputExpression.h"
|
||||
|
||||
class ChoreoInputExpressionSet {
|
||||
|
||||
public:
|
||||
ChoreoInputExpressionSet();
|
||||
~ChoreoInputExpressionSet();
|
||||
void put(const char* name, const char* value);
|
||||
const char* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoInputExpression* getFirstInput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoInputExpression* m_first;
|
||||
};
|
||||
|
||||
#endif //CHOREOINPUTEXPRESSIONSET_H_
|
||||
125
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.cpp
Normal file
125
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoInputFormatter.h"
|
||||
#include "ChoreoInputSet.h"
|
||||
|
||||
static const char TAG_INPUTS_START[] PROGMEM = "\"inputs\":{";
|
||||
|
||||
ChoreoInputFormatter::ChoreoInputFormatter(const ChoreoInputSet* inputSet) {
|
||||
m_inputSet = inputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoInputFormatter::reset() {
|
||||
m_currentInput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_inputSet == NULL || m_inputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoInputFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoInputFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_INPUTS_START, INPUTS_TAG);
|
||||
break;
|
||||
|
||||
case INPUTS_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
if (m_nextState == NAME_START) {
|
||||
m_currentInput= m_inputSet->getFirstInput();
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = NAME_VALUE_SEPARATOR;
|
||||
break;
|
||||
|
||||
case NAME_VALUE_SEPARATOR:
|
||||
c = ':';
|
||||
m_nextState = VALUE_START;
|
||||
break;
|
||||
|
||||
case VALUE_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getValue();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VALUE_END;
|
||||
} else {
|
||||
m_nextState = VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
c = readValueChar(VALUE_END);
|
||||
break;
|
||||
|
||||
case VALUE_END:
|
||||
c = '"';
|
||||
m_currentInput = m_currentInput->getNext();
|
||||
if (m_currentInput != NULL) {
|
||||
m_nextState = NEXT_INPUT;
|
||||
} else {
|
||||
m_nextState = INPUTS_END;
|
||||
}
|
||||
break;
|
||||
case NEXT_INPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
m_nextState = NAME_START;
|
||||
break;
|
||||
|
||||
case INPUTS_END:
|
||||
c = '}';
|
||||
m_nextState = END;
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
58
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.h
Normal file
58
arduino/libraries/Temboo/src/utility/ChoreoInputFormatter.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTFORMATTER_H_
|
||||
#define CHOREOINPUTFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoInputSet.h"
|
||||
|
||||
class ChoreoInputFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoInputFormatter(const ChoreoInputSet* inputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoInputSet* m_inputSet;
|
||||
const ChoreoInput* m_currentInput;
|
||||
|
||||
|
||||
enum State {
|
||||
START,
|
||||
INPUTS_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
NAME_VALUE_SEPARATOR,
|
||||
VALUE_START,
|
||||
VALUE,
|
||||
VALUE_END,
|
||||
NEXT_INPUT,
|
||||
INPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
80
arduino/libraries/Temboo/src/utility/ChoreoInputSet.cpp
Normal file
80
arduino/libraries/Temboo/src/utility/ChoreoInputSet.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ChoreoInputSet.h"
|
||||
|
||||
ChoreoInputSet::ChoreoInputSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoInputSet::~ChoreoInputSet() {
|
||||
ChoreoInput* i = m_first;
|
||||
ChoreoInput* next = NULL;
|
||||
while (i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoInputSet::put(const char* name, const char* value) {
|
||||
|
||||
// Haven't set ANY inputs yet?
|
||||
// Just create a new one.
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoInput(NULL, name, value);
|
||||
} else {
|
||||
// Some inputs already set.
|
||||
// See if we already have this input.
|
||||
ChoreoInput* last = NULL;
|
||||
ChoreoInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
// We already have an input with this name.
|
||||
// Just update the value.
|
||||
i->setValue(value);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
// We don't have an input with this name
|
||||
// So we need to create a new one.
|
||||
if (i == NULL) {
|
||||
new ChoreoInput(last, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* ChoreoInputSet::get(const char* name) const {
|
||||
ChoreoInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i->getValue();
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
43
arduino/libraries/Temboo/src/utility/ChoreoInputSet.h
Normal file
43
arduino/libraries/Temboo/src/utility/ChoreoInputSet.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOINPUTSET_H_
|
||||
#define CHOREOINPUTSET_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoInput.h"
|
||||
|
||||
class ChoreoInputSet {
|
||||
|
||||
public:
|
||||
ChoreoInputSet();
|
||||
~ChoreoInputSet();
|
||||
void put(const char* name, const char* value);
|
||||
const char* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoInput* getFirstInput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoInput* m_first;
|
||||
};
|
||||
|
||||
#endif
|
||||
37
arduino/libraries/Temboo/src/utility/ChoreoOutput.cpp
Normal file
37
arduino/libraries/Temboo/src/utility/ChoreoOutput.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoOutput.h"
|
||||
|
||||
|
||||
ChoreoOutput::ChoreoOutput(ChoreoOutput* prev, const char* name, const char* path, const char* var) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
m_path = path;
|
||||
m_var = var;
|
||||
}
|
||||
|
||||
|
||||
44
arduino/libraries/Temboo/src/utility/ChoreoOutput.h
Normal file
44
arduino/libraries/Temboo/src/utility/ChoreoOutput.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOOUTPUT_H_
|
||||
#define CHOREOOUTPUT_H_
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class ChoreoOutput {
|
||||
public:
|
||||
ChoreoOutput(ChoreoOutput* prev, const char* name, const char* path, const char* var);
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getPath() const {return m_path;}
|
||||
const char* getVariable() const {return m_var;}
|
||||
void setPath(const char* path) {m_path = path;}
|
||||
void setVariable(const char* variable) {m_var = variable;}
|
||||
ChoreoOutput* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoOutput* m_next;
|
||||
const char* m_name;
|
||||
const char* m_path;
|
||||
const char* m_var;
|
||||
};
|
||||
|
||||
#endif
|
||||
181
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.cpp
Normal file
181
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoOutputFormatter.h"
|
||||
#include "ChoreoOutputSet.h"
|
||||
|
||||
static const char TAG_OUTPUTS_START[] PROGMEM = "\"outputs\":[";
|
||||
static const char TAG_NAME[] PROGMEM = "\"name\":";
|
||||
static const char TAG_PATH[] PROGMEM = "\"path\":";
|
||||
static const char TAG_VAR[] PROGMEM = "\"variable\":";
|
||||
|
||||
|
||||
ChoreoOutputFormatter::ChoreoOutputFormatter(const ChoreoOutputSet* outputSet) {
|
||||
m_outputSet = outputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoOutputFormatter::reset() {
|
||||
m_currentOutput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_outputSet == NULL || m_outputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoOutputFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoOutputFormatter::next() {
|
||||
char c = '\0';
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_OUTPUTS_START, OUTPUTS_TAG);
|
||||
break;
|
||||
|
||||
case OUTPUTS_TAG:
|
||||
c = readTagChar(OUTPUT_START);
|
||||
if (m_nextState == OUTPUT_START) {
|
||||
m_currentOutput = m_outputSet->getFirstOutput();
|
||||
}
|
||||
break;
|
||||
|
||||
case OUTPUT_START:
|
||||
c = '{';
|
||||
m_nextChar = TAG_NAME;
|
||||
m_nextState = NAME_TAG;
|
||||
break;
|
||||
|
||||
case NAME_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentOutput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = NAME_PATH_SEPARATOR;
|
||||
break;
|
||||
|
||||
case NAME_PATH_SEPARATOR:
|
||||
c = ',';
|
||||
m_nextState = PATH_TAG;
|
||||
m_nextChar = TAG_PATH;
|
||||
break;
|
||||
|
||||
case PATH_TAG:
|
||||
c = readTagChar(PATH_START);
|
||||
break;
|
||||
|
||||
case PATH_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentOutput->getPath();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = PATH_END;
|
||||
} else {
|
||||
m_nextState = PATH;
|
||||
}
|
||||
break;
|
||||
|
||||
case PATH:
|
||||
c = readValueChar(PATH_END);
|
||||
break;
|
||||
|
||||
case PATH_END:
|
||||
c = '"';
|
||||
m_nextState = PATH_VAR_SEPARATOR;
|
||||
break;
|
||||
|
||||
case PATH_VAR_SEPARATOR:
|
||||
c = ',';
|
||||
m_nextState = VAR_TAG;
|
||||
m_nextChar = TAG_VAR;
|
||||
break;
|
||||
|
||||
case VAR_TAG:
|
||||
c = readTagChar(VAR_START);
|
||||
break;
|
||||
|
||||
case VAR_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentOutput->getVariable();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VAR_END;
|
||||
} else {
|
||||
m_nextState = VAR;
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR:
|
||||
c = readValueChar(VAR_END);
|
||||
break;
|
||||
|
||||
case VAR_END:
|
||||
c = '"';
|
||||
m_nextState = OUTPUT_END;
|
||||
break;
|
||||
|
||||
case OUTPUT_END:
|
||||
c = '}';
|
||||
m_currentOutput = m_currentOutput->getNext();
|
||||
if (m_currentOutput != NULL) {
|
||||
m_nextState = NEXT_OUTPUT;
|
||||
} else {
|
||||
m_nextState = OUTPUTS_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEXT_OUTPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentOutput->getName();
|
||||
m_nextState = OUTPUT_START;
|
||||
break;
|
||||
|
||||
case OUTPUTS_END:
|
||||
c = ']';
|
||||
m_nextState = END;
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
66
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.h
Normal file
66
arduino/libraries/Temboo/src/utility/ChoreoOutputFormatter.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOOUTPUTFORMATTER_H_
|
||||
#define CHOREOOUTPUTFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoOutputSet.h"
|
||||
|
||||
class ChoreoOutputFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoOutputFormatter(const ChoreoOutputSet* outputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoOutputSet* m_outputSet;
|
||||
const ChoreoOutput* m_currentOutput;
|
||||
|
||||
enum State {
|
||||
START,
|
||||
OUTPUTS_TAG,
|
||||
OUTPUT_START,
|
||||
NAME_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
NAME_PATH_SEPARATOR,
|
||||
PATH_TAG,
|
||||
PATH_START,
|
||||
PATH,
|
||||
PATH_END,
|
||||
PATH_VAR_SEPARATOR,
|
||||
VAR_TAG,
|
||||
VAR_START,
|
||||
VAR,
|
||||
VAR_END,
|
||||
OUTPUT_END,
|
||||
NEXT_OUTPUT,
|
||||
OUTPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
73
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.cpp
Normal file
73
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "ChoreoOutputSet.h"
|
||||
|
||||
|
||||
ChoreoOutputSet::ChoreoOutputSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoOutputSet::~ChoreoOutputSet() {
|
||||
ChoreoOutput* i = m_first;
|
||||
ChoreoOutput* next = NULL;
|
||||
while(i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoOutputSet::put(const char* name, const char* path, const char* variable) {
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoOutput(NULL, name, path, variable);
|
||||
} else {
|
||||
ChoreoOutput* last = NULL;
|
||||
ChoreoOutput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
i->setPath(path);
|
||||
i->setVariable(variable);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
if (i == NULL) {
|
||||
new ChoreoOutput(last, name, path, variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ChoreoOutput* ChoreoOutputSet::get(const char* name) const {
|
||||
ChoreoOutput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i;
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
43
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.h
Normal file
43
arduino/libraries/Temboo/src/utility/ChoreoOutputSet.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOOUTPUTSET_H_
|
||||
#define CHOREOOUTPUTSET_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoOutput.h"
|
||||
|
||||
class ChoreoOutputSet {
|
||||
|
||||
public:
|
||||
ChoreoOutputSet();
|
||||
~ChoreoOutputSet();
|
||||
void put(const char* name, const char* path, const char* variable);
|
||||
const ChoreoOutput* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoOutput* getFirstOutput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoOutput* m_first;
|
||||
};
|
||||
|
||||
#endif
|
||||
23
arduino/libraries/Temboo/src/utility/ChoreoPreset.cpp
Normal file
23
arduino/libraries/Temboo/src/utility/ChoreoPreset.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "ChoreoPreset.h"
|
||||
40
arduino/libraries/Temboo/src/utility/ChoreoPreset.h
Normal file
40
arduino/libraries/Temboo/src/utility/ChoreoPreset.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOPRESET_H_
|
||||
#define CHOREOPRESET_H_
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class ChoreoPreset {
|
||||
public:
|
||||
ChoreoPreset() {m_name = NULL;}
|
||||
ChoreoPreset(const char* name) {put(name);}
|
||||
const char* getName() const {return m_name;}
|
||||
void put(const char* name) {m_name = name;}
|
||||
bool isEmpty() const {return m_name == NULL || *m_name == '\0';}
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoPresetFormatter.h"
|
||||
#include "ChoreoPreset.h"
|
||||
|
||||
static const char TAG_PRESET[] PROGMEM = "\"preset\":";
|
||||
|
||||
ChoreoPresetFormatter::ChoreoPresetFormatter(const ChoreoPreset* preset) {
|
||||
m_preset = preset;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoPresetFormatter::reset() {
|
||||
m_nextChar = NULL;
|
||||
if (m_preset == NULL || m_preset->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoPresetFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoPresetFormatter::next() {
|
||||
char c = '\0';
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_PRESET, PRESET_TAG);
|
||||
break;
|
||||
|
||||
case PRESET_TAG:
|
||||
c = readTagChar(NAME_START);
|
||||
break;
|
||||
|
||||
case NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_preset->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = NAME_END;
|
||||
} else {
|
||||
m_nextState = NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME:
|
||||
c = readValueChar(NAME_END);
|
||||
break;
|
||||
|
||||
case NAME_END:
|
||||
c = '"';
|
||||
m_nextState = END;
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
50
arduino/libraries/Temboo/src/utility/ChoreoPresetFormatter.h
Normal file
50
arduino/libraries/Temboo/src/utility/ChoreoPresetFormatter.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOPROFILEFORMATTER_H_
|
||||
#define CHOREOPROFILEFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoPreset.h"
|
||||
|
||||
class ChoreoPresetFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoPresetFormatter(const ChoreoPreset* preset);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoPreset* m_preset;
|
||||
|
||||
enum State {
|
||||
START,
|
||||
PRESET_TAG,
|
||||
NAME_START,
|
||||
NAME,
|
||||
NAME_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
41
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.cpp
Normal file
41
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "ChoreoSensorInput.h"
|
||||
|
||||
ChoreoSensorInput::ChoreoSensorInput(ChoreoSensorInput* prev, const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration) {
|
||||
if (prev != NULL) {
|
||||
prev->m_next = this;
|
||||
}
|
||||
m_next = NULL;
|
||||
m_name = name;
|
||||
snprintf(m_value, TEMBOO_SENSOR_INPUT_ARRAY_SIZE, "%i",value);
|
||||
m_sensorConversion = sensorConversion;
|
||||
m_rawLow = rawLow;
|
||||
m_rawHigh = rawHigh;
|
||||
m_scaleLow = scaleLow;
|
||||
m_scaleHigh = scaleHigh;
|
||||
m_calibration = calibration;
|
||||
}
|
||||
|
||||
|
||||
63
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.h
Normal file
63
arduino/libraries/Temboo/src/utility/ChoreoSensorInput.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOSENSORINPUT_H_
|
||||
#define CHOREOSENSORINPUT_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
#define TEMBOO_SENSOR_INPUT_ARRAY_SIZE 8
|
||||
|
||||
class ChoreoSensorInput {
|
||||
public:
|
||||
|
||||
ChoreoSensorInput(ChoreoSensorInput* prev, const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration);
|
||||
|
||||
const char* getName() const {return m_name;}
|
||||
const char* getValue() const {return m_value;}
|
||||
const char* getConversion() const {return m_sensorConversion;}
|
||||
const char* getRawLow() const {return m_rawLow;}
|
||||
const char* getRawHigh() const {return m_rawHigh;}
|
||||
const char* getScaleLow() const {return m_scaleLow;}
|
||||
const char* getScaleHigh() const {return m_scaleHigh;}
|
||||
const char* getCalibration() const {return m_calibration;}
|
||||
void setValue(int value) {snprintf(m_value, TEMBOO_SENSOR_INPUT_ARRAY_SIZE, "%i",value);}
|
||||
void setConversion(const char* conversion) {m_sensorConversion = conversion;}
|
||||
void setRawLow(const char* value) {m_rawLow = value;}
|
||||
void setRawHigh(const char* value) {m_rawHigh = value;}
|
||||
void setScaleLow(const char* value) {m_scaleLow = value;}
|
||||
void setScaleHigh(const char* value) {m_scaleHigh = value;}
|
||||
void setCalibration(const char* value) {m_calibration = value;}
|
||||
ChoreoSensorInput* getNext() const {return m_next;}
|
||||
|
||||
private:
|
||||
ChoreoSensorInput* m_next;
|
||||
const char* m_name;
|
||||
char m_value[TEMBOO_SENSOR_INPUT_ARRAY_SIZE];
|
||||
const char* m_sensorConversion;
|
||||
const char* m_rawLow;
|
||||
const char* m_rawHigh;
|
||||
const char* m_scaleLow;
|
||||
const char* m_scaleHigh;
|
||||
const char* m_calibration;
|
||||
};
|
||||
|
||||
#endif //CHOREOSENSORINPUT_H_
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "ChoreoSensorInputFormatter.h"
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
|
||||
static const char TAG_INPUTS_START[] PROGMEM = "\"sensorInputs\":{";
|
||||
static const char TAG_SENSOR_NAME_SEPERATOR[] PROGMEM = "\":{";
|
||||
static const char TAG_VALUE_START[] PROGMEM = "\"value\":";
|
||||
static const char TAG_CALIBRATION_START[] PROGMEM = ",\"calibration\":";
|
||||
static const char TAG_RAW_LOW_START[] PROGMEM = ",\"rawLow\":";
|
||||
static const char TAG_RAW_HIGH_START[] PROGMEM = ",\"rawHigh\":";
|
||||
static const char TAG_SCALE_LOW_START[] PROGMEM = ",\"scaleLow\":";
|
||||
static const char TAG_SCALE_HIGH_START[] PROGMEM = ",\"scaleHigh\":";
|
||||
static const char TAG_CONVERSION_START[] PROGMEM = ",\"conversion\":";
|
||||
|
||||
ChoreoSensorInputFormatter::ChoreoSensorInputFormatter(const ChoreoSensorInputSet* inputSet) {
|
||||
m_inputSet = inputSet;
|
||||
reset();
|
||||
}
|
||||
|
||||
void ChoreoSensorInputFormatter::reset() {
|
||||
m_currentInput = NULL;
|
||||
m_nextChar = NULL;
|
||||
if (m_inputSet == NULL || m_inputSet->isEmpty()) {
|
||||
m_nextState = END;
|
||||
} else {
|
||||
m_nextState = START;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChoreoSensorInputFormatter::hasNext() {
|
||||
return m_nextState != END;
|
||||
}
|
||||
|
||||
char ChoreoSensorInputFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case START:
|
||||
c = readStartTagChar(TAG_INPUTS_START, SENSOR_INPUT_TAG);
|
||||
break;
|
||||
|
||||
case SENSOR_INPUT_TAG:
|
||||
c = readTagChar(SENSOR_NAME_START);
|
||||
if (m_nextState == SENSOR_NAME_START) {
|
||||
m_currentInput= m_inputSet->getFirstInput();
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_NAME_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = SENSOR_NAME_END;
|
||||
} else {
|
||||
m_nextState = SENSOR_NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_NAME:
|
||||
c = readValueChar(SENSOR_NAME_END);
|
||||
break;
|
||||
|
||||
case SENSOR_NAME_END:
|
||||
c = readStartTagChar(TAG_SENSOR_NAME_SEPERATOR, SENSOR_NAME_SEPERATOR);
|
||||
break;
|
||||
|
||||
case SENSOR_NAME_SEPERATOR:
|
||||
c = readTagChar(VALUE_TAG);
|
||||
if (m_nextState == VALUE_TAG) {
|
||||
m_nextChar= TAG_VALUE_START;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE_TAG:
|
||||
c = readTagChar(VALUE_START);
|
||||
break;
|
||||
|
||||
case VALUE_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getValue();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = VALUE_END;
|
||||
} else {
|
||||
m_nextState = VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
c = readValueChar(VALUE_END);
|
||||
break;
|
||||
|
||||
case VALUE_END:
|
||||
c = '"';
|
||||
// decide how the data set should be set up, with just the value, with
|
||||
// a conversion, or with the user defined scale
|
||||
if (m_currentInput->getCalibration() != NULL) {
|
||||
m_nextChar = TAG_CALIBRATION_START;
|
||||
m_nextState = CALIBRATION_TAG;
|
||||
} else {
|
||||
if (m_currentInput->getConversion() != NULL) {
|
||||
// use conversion
|
||||
m_nextState = CONVERSION_TAG;
|
||||
m_nextChar = TAG_CONVERSION_START;
|
||||
} else if (m_currentInput->getScaleHigh() != m_currentInput->getScaleLow()) {
|
||||
// use user defined scale
|
||||
m_nextState = RAW_LOW_TAG;
|
||||
m_nextChar = TAG_RAW_LOW_START;
|
||||
} else {
|
||||
// just the value is needed
|
||||
m_nextState = DATA_SET_END;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CALIBRATION_TAG:
|
||||
c = readTagChar(CALIBRATION_START);
|
||||
break;
|
||||
|
||||
case CALIBRATION_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getCalibration();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = CALIBRATION_END;
|
||||
} else {
|
||||
m_nextState = CALIBRATION;
|
||||
}
|
||||
break;
|
||||
|
||||
case CALIBRATION:
|
||||
c = readValueChar(CALIBRATION_END);
|
||||
break;
|
||||
|
||||
case CALIBRATION_END:
|
||||
c = '"';
|
||||
if (m_currentInput->getConversion() != NULL) {
|
||||
// use conversion
|
||||
m_nextState = CONVERSION_TAG;
|
||||
m_nextChar = TAG_CONVERSION_START;
|
||||
} else if (m_currentInput->getScaleHigh() != m_currentInput->getScaleLow()) {
|
||||
// use user defined scale
|
||||
m_nextState = RAW_LOW_TAG;
|
||||
m_nextChar = TAG_RAW_LOW_START;
|
||||
} else {
|
||||
// just the value is needed
|
||||
m_nextState = DATA_SET_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONVERSION_TAG:
|
||||
c = readTagChar(CONVERSION_START);
|
||||
break;
|
||||
|
||||
case CONVERSION_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getConversion();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = CONVERSION_END;
|
||||
} else {
|
||||
m_nextState = CONVERSION;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONVERSION:
|
||||
c = readValueChar(CONVERSION_END);
|
||||
break;
|
||||
|
||||
case CONVERSION_END:
|
||||
c = '"';
|
||||
m_nextState = DATA_SET_END;
|
||||
break;
|
||||
|
||||
case RAW_LOW_TAG:
|
||||
c = readTagChar(RAW_LOW_START);
|
||||
break;
|
||||
|
||||
case RAW_LOW_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getRawLow();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = RAW_LOW_END;
|
||||
} else {
|
||||
m_nextState = RAW_LOW;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_LOW:
|
||||
c = readValueChar(RAW_LOW_END);
|
||||
break;
|
||||
|
||||
case RAW_LOW_END:
|
||||
c = '"';
|
||||
m_nextState = RAW_HIGH_TAG;
|
||||
m_nextChar = TAG_RAW_HIGH_START;
|
||||
break;
|
||||
|
||||
case RAW_HIGH_TAG:
|
||||
c = readTagChar(RAW_HIGH_START);
|
||||
break;
|
||||
|
||||
case RAW_HIGH_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getRawHigh();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = RAW_HIGH_END;
|
||||
} else {
|
||||
m_nextState = RAW_HIGH;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_HIGH:
|
||||
c = readValueChar(RAW_HIGH_END);
|
||||
break;
|
||||
|
||||
case RAW_HIGH_END:
|
||||
c = '"';
|
||||
m_nextState = SCALE_LOW_TAG;
|
||||
m_nextChar = TAG_SCALE_LOW_START;
|
||||
break;
|
||||
|
||||
case SCALE_LOW_TAG:
|
||||
c = readTagChar(SCALE_LOW_START);
|
||||
break;
|
||||
|
||||
case SCALE_LOW_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getScaleLow();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = SCALE_LOW_END;
|
||||
} else {
|
||||
m_nextState = SCALE_LOW;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCALE_LOW:
|
||||
c = readValueChar(SCALE_LOW_END);
|
||||
break;
|
||||
|
||||
case SCALE_LOW_END:
|
||||
c = '"';
|
||||
m_nextState = SCALE_HIGH_TAG;
|
||||
m_nextChar = TAG_SCALE_HIGH_START;
|
||||
break;
|
||||
|
||||
case SCALE_HIGH_TAG:
|
||||
c = readTagChar(SCALE_HIGH_START);
|
||||
break;
|
||||
|
||||
case SCALE_HIGH_START:
|
||||
c = '"';
|
||||
m_nextChar = m_currentInput->getScaleHigh();
|
||||
if ((NULL == m_nextChar) || ('\0' == *m_nextChar)) {
|
||||
m_nextState = SCALE_HIGH_END;
|
||||
} else {
|
||||
m_nextState = SCALE_HIGH;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCALE_HIGH:
|
||||
c = readValueChar(SCALE_HIGH_END);
|
||||
break;
|
||||
|
||||
case SCALE_HIGH_END:
|
||||
c = '"';
|
||||
m_nextState = DATA_SET_END;
|
||||
break;
|
||||
|
||||
case DATA_SET_END:
|
||||
c = '}';
|
||||
m_currentInput = m_currentInput->getNext();
|
||||
if (NULL != m_currentInput) {
|
||||
m_nextState = NEXT_INPUT;
|
||||
} else {
|
||||
m_nextState = INPUTS_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEXT_INPUT:
|
||||
c = ',';
|
||||
m_nextChar = m_currentInput->getName();
|
||||
m_nextState = SENSOR_NAME_START;
|
||||
break;
|
||||
|
||||
case INPUTS_END:
|
||||
c = '}';
|
||||
m_nextState = END;
|
||||
break;
|
||||
|
||||
case END:
|
||||
default:
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOSENSORINPUTFORMATTER_H_
|
||||
#define CHOREOSENSORINPUTFORMATTER_H_
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
#include "BaseFormatter.h"
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
|
||||
class ChoreoSensorInputFormatter : public BaseFormatter {
|
||||
|
||||
public:
|
||||
ChoreoSensorInputFormatter(const ChoreoSensorInputSet* inputSet);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
const ChoreoSensorInputSet* m_inputSet;
|
||||
const ChoreoSensorInput* m_currentInput;
|
||||
|
||||
|
||||
enum State {
|
||||
// This is added to fix a compilation bug for the mkr1000
|
||||
START = 256,
|
||||
SENSOR_INPUT_TAG,
|
||||
SENSOR_NAME_START,
|
||||
SENSOR_NAME,
|
||||
SENSOR_NAME_END,
|
||||
SENSOR_NAME_SEPERATOR,
|
||||
VALUE_TAG,
|
||||
VALUE_START,
|
||||
VALUE,
|
||||
VALUE_END,
|
||||
CALIBRATION_TAG,
|
||||
CALIBRATION_START,
|
||||
CALIBRATION,
|
||||
CALIBRATION_END,
|
||||
CONVERSION_TAG,
|
||||
CONVERSION_START,
|
||||
CONVERSION,
|
||||
CONVERSION_END,
|
||||
RAW_LOW_TAG,
|
||||
RAW_LOW_START,
|
||||
RAW_LOW,
|
||||
RAW_LOW_END,
|
||||
RAW_HIGH_TAG,
|
||||
RAW_HIGH_START,
|
||||
RAW_HIGH,
|
||||
RAW_HIGH_END,
|
||||
SCALE_LOW_TAG,
|
||||
SCALE_LOW_START,
|
||||
SCALE_LOW,
|
||||
SCALE_LOW_END,
|
||||
SCALE_HIGH_TAG,
|
||||
SCALE_HIGH_START,
|
||||
SCALE_HIGH,
|
||||
SCALE_HIGH_END,
|
||||
DATA_SET_END,
|
||||
NEXT_INPUT,
|
||||
INPUTS_END,
|
||||
END
|
||||
};
|
||||
};
|
||||
|
||||
#endif //CHOREOSENSORINPUTFORMATTER_H_
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
|
||||
ChoreoSensorInputSet::ChoreoSensorInputSet() {
|
||||
m_first = NULL;
|
||||
}
|
||||
|
||||
ChoreoSensorInputSet::~ChoreoSensorInputSet() {
|
||||
ChoreoSensorInput* i = m_first;
|
||||
ChoreoSensorInput* next = NULL;
|
||||
while (i != NULL) {
|
||||
next = i->getNext();
|
||||
delete i;
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void ChoreoSensorInputSet::put(const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration) {
|
||||
|
||||
// Haven't set ANY inputs yet?
|
||||
// Just create a new one.
|
||||
if (m_first == NULL) {
|
||||
m_first = new ChoreoSensorInput(NULL, name, value, sensorConversion, rawLow, rawHigh, scaleLow, scaleHigh, calibration);
|
||||
} else {
|
||||
// Some inputs already set.
|
||||
// See if we already have this input.
|
||||
ChoreoSensorInput* last = NULL;
|
||||
ChoreoSensorInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
// We already have an input with this name.
|
||||
// Just update the value.
|
||||
i->setValue(value);
|
||||
i->setConversion(sensorConversion);
|
||||
i->setRawLow(rawLow);
|
||||
i->setRawHigh(rawHigh);
|
||||
i->setScaleLow(scaleLow);
|
||||
i->setScaleHigh(scaleHigh);
|
||||
i->setCalibration(calibration);
|
||||
break;
|
||||
}
|
||||
last = i;
|
||||
i = i->getNext();
|
||||
}
|
||||
|
||||
// We don't have an input with this name
|
||||
// So we need to create a new one.
|
||||
if (i == NULL) {
|
||||
new ChoreoSensorInput(last, name, value, sensorConversion, rawLow, rawHigh, scaleLow, scaleHigh, calibration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* ChoreoSensorInputSet::get(const char* name) const {
|
||||
ChoreoSensorInput* i = m_first;
|
||||
while(i != NULL) {
|
||||
if (strcmp(i->getName(), name) == 0) {
|
||||
return i->getValue();
|
||||
}
|
||||
i = i->getNext();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
44
arduino/libraries/Temboo/src/utility/ChoreoSensorInputSet.h
Normal file
44
arduino/libraries/Temboo/src/utility/ChoreoSensorInputSet.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef CHOREOSENSORINPUTSET_H_
|
||||
#define CHOREOSENSORINPUTSET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoSensorInput.h"
|
||||
|
||||
class ChoreoSensorInputSet {
|
||||
|
||||
public:
|
||||
ChoreoSensorInputSet();
|
||||
~ChoreoSensorInputSet();
|
||||
void put(const char* name, int value, const char* sensorConversion, const char* rawLow, const char* rawHigh, const char* scaleLow, const char* scaleHigh, const char* calibration);
|
||||
const char* get(const char* name) const;
|
||||
bool isEmpty() const {return m_first == NULL;}
|
||||
const ChoreoSensorInput* getFirstInput() const {return m_first;}
|
||||
|
||||
protected:
|
||||
ChoreoSensorInput* m_first;
|
||||
};
|
||||
|
||||
#endif //CHOREOSENSORINPUTSET_H_
|
||||
400
arduino/libraries/Temboo/src/utility/CoapMessageLayer.cpp
Normal file
400
arduino/libraries/Temboo/src/utility/CoapMessageLayer.cpp
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "utility/CoapMessageLayer.h"
|
||||
|
||||
CoapMessageLayer::CoapMessageLayer(uint8_t* rxBuffer, uint16_t rxLen, TembooCoAPIPStack& ipStack) :
|
||||
m_rxBuffer(rxBuffer),
|
||||
m_rxLen(rxLen),
|
||||
m_ipStack(ipStack),
|
||||
m_state(STATE_CLOSED),
|
||||
m_prevState(STATE_CLOSED),
|
||||
m_retransmitCount(0),
|
||||
m_lastResult(NO_ERROR),
|
||||
m_msgID(0) {
|
||||
}
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::rejectMsg(CoapMsg& msg) {
|
||||
return rejectMsg(msg, m_ipStack.getRemoteAddress(), m_ipStack.getRemotePort());
|
||||
}
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::rejectMsg(CoapMsg& msg, IPAddress addr, uint16_t port) {
|
||||
msg.convertToReset();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending RST");
|
||||
if (m_ipStack.sendDatagram(addr, port, msg.getMsgBytes(), msg.getMsgLen())) {
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_lastResult = NO_ERROR;
|
||||
if (STATE_ACK_PENDING == m_state) {
|
||||
// go back to previous state since the
|
||||
// message recv'd wasn't what we're expecting
|
||||
m_state = m_prevState;
|
||||
}
|
||||
}
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::acceptMsg(CoapMsg& msg) {
|
||||
return acceptMsg(msg, m_ipStack.getRemoteAddress(), m_ipStack.getRemotePort());
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::acceptMsg(CoapMsg& msg, IPAddress addr, uint16_t port) {
|
||||
if (m_state != STATE_ACK_PENDING) {
|
||||
m_lastResult = ERROR_IMPROPER_STATE;
|
||||
} else {
|
||||
msg.convertToEmptyAck();
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending ACK");
|
||||
if (m_ipStack.sendDatagram(addr, port, msg.getMsgBytes(), msg.getMsgLen())) {
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NO_ERROR;
|
||||
}
|
||||
}
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::reliableSend(CoapMsg& msg, IPAddress destAddr, uint16_t destPort) {
|
||||
|
||||
if (m_state != STATE_CLOSED) {
|
||||
m_lastResult = ERROR_IMPROPER_STATE;
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
m_rxByteCount = 0;
|
||||
m_msgID = msg.getId();
|
||||
m_msgBytes = msg.getMsgBytes();
|
||||
m_msgLen = msg.getMsgLen();
|
||||
m_destAddr = destAddr;
|
||||
m_destPort = destPort;
|
||||
msg.setType(CoapMsg::COAP_CONFIRMABLE);
|
||||
m_retransmitCount = 0;
|
||||
m_retransmitTimeoutMillis = random(ACK_TIMEOUT, MAX_ACK_TIMEOUT);
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Sending message");
|
||||
if (m_ipStack.sendDatagram(m_destAddr, m_destPort, msg.getMsgBytes(), msg.getMsgLen())) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_txSpanTimer.start(MAX_TRANSMIT_WAIT);
|
||||
m_retransmitTimer.start(m_retransmitTimeoutMillis);
|
||||
m_state = STATE_RELIABLE_TX;
|
||||
m_lastResult = NO_ERROR;
|
||||
}
|
||||
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::cancelReliableSend() {
|
||||
if (m_state != STATE_RELIABLE_TX) {
|
||||
m_lastResult = ERROR_IMPROPER_STATE;
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NO_ERROR;
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMessageLayer::Result CoapMessageLayer::loop() {
|
||||
|
||||
m_lastResult = NO_ERROR;
|
||||
|
||||
switch(m_state) {
|
||||
case STATE_RELIABLE_TX:
|
||||
// We have sent a CON request.
|
||||
// We're expecting an ACK or a response.
|
||||
|
||||
// See if it's time to give up all hope of getting an ACK.
|
||||
if (m_txSpanTimer.expired()) {
|
||||
m_lastResult = ERROR_TX_SPAN_TIME_EXCEEDED;
|
||||
m_state = STATE_CLOSED;
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("ACK not received within timeout");
|
||||
break;
|
||||
}
|
||||
|
||||
// See if any messages have come in.
|
||||
if (m_ipStack.recvDatagram(m_rxBuffer, m_rxLen, m_rxByteCount)) {
|
||||
m_lastResult = ERROR_RECEIVING_PACKET;
|
||||
m_state = STATE_CLOSED;
|
||||
break;
|
||||
}
|
||||
|
||||
// We've received something. See if it's relevant.
|
||||
if (m_rxByteCount > 0) {
|
||||
CoapMsg msg(m_rxBuffer, m_rxLen, m_rxByteCount);
|
||||
|
||||
// Make sure the message is valid
|
||||
if (!msg.isValid()) {
|
||||
rejectMsg(msg);
|
||||
|
||||
// We're only interested in messages coming from the host
|
||||
// we sent the original request to.
|
||||
} else if (m_ipStack.getRemoteAddress() == m_destAddr) {
|
||||
switch (msg.getType()) {
|
||||
case CoapMsg::COAP_ACK:
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("ACK Received");
|
||||
// Is it ACK'ing the last request we sent?
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ACK_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_RESET:
|
||||
// Is it rejecting the last request we sent?
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("RST Received");
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = RESET_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
m_prevState = m_state;
|
||||
m_state = STATE_ACK_PENDING;
|
||||
m_lastResult = CON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("CON Received");
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_NON_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
|
||||
// That's what Kovatsch et al. show in their FSM.
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("NON Received");
|
||||
break;
|
||||
}
|
||||
if (msg.getPayloadLen() > 0) {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Payload data:");
|
||||
uint8_t *payload = msg.getPayload();
|
||||
uint16_t len = msg.getPayloadLen();
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
TEMBOO_TRACE((char)payload[i]);
|
||||
}
|
||||
TEMBOO_TRACELN();
|
||||
}
|
||||
} else if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
// It's not from the host we sent the request to, but
|
||||
// the sending host expects a reply, so explicitly reject it.
|
||||
rejectMsg(msg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Nothing was received. See if it's time to retransmit.
|
||||
if (m_retransmitTimer.expired()) {
|
||||
if (m_retransmitCount >= MAX_RETRANSMIT) {
|
||||
// We've retried enough. Give up.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("Maximum retransmit reached");
|
||||
m_lastResult = ERROR_RETRANSMIT_COUNT_EXCEEDED;
|
||||
m_state = STATE_CLOSED;
|
||||
} else {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Retransmit message");
|
||||
m_retransmitCount++;
|
||||
m_retransmitTimeoutMillis *= 2;
|
||||
if (m_ipStack.sendDatagram(m_destAddr, m_destPort, m_msgBytes, m_msgLen)) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ERROR_SENDING_PACKET;
|
||||
} else {
|
||||
m_retransmitTimer.start(m_retransmitTimeoutMillis);
|
||||
m_state = STATE_RELIABLE_TX;
|
||||
m_lastResult = NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_ACK_PENDING:
|
||||
// Nothing to do here but wait for
|
||||
// the higher layer to accept or reject.
|
||||
break;
|
||||
|
||||
case STATE_WAITING_FOR_CON:
|
||||
// See if any messages have come in.
|
||||
if (m_ipStack.recvDatagram(m_rxBuffer, m_rxLen, m_rxByteCount)) {
|
||||
m_lastResult = ERROR_RECEIVING_PACKET;
|
||||
m_state = STATE_CLOSED;
|
||||
break;
|
||||
}
|
||||
|
||||
// We've received something. See if it's relevant.
|
||||
if (m_rxByteCount > 0) {
|
||||
CoapMsg msg(m_rxBuffer, m_rxLen, m_rxByteCount);
|
||||
|
||||
// Make sure the message is valid
|
||||
if (!msg.isValid()) {
|
||||
rejectMsg(msg);
|
||||
|
||||
// We're only interested in messages coming from the host
|
||||
// we sent the original request to.
|
||||
} else if (m_ipStack.getRemoteAddress() == m_destAddr) {
|
||||
switch (msg.getType()) {
|
||||
case CoapMsg::COAP_ACK:
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("ACK Received");
|
||||
// Is it ACK'ing the last request we sent?
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = ACK_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_RESET:
|
||||
// Is it rejecting the last request we sent?
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("RST Received");
|
||||
if (msg.getId() == m_msgID) {
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = RESET_RECEIVED;
|
||||
} else {
|
||||
// if not, just ignore it.
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("MID did not match");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
m_prevState = m_state;
|
||||
m_state = STATE_ACK_PENDING;
|
||||
m_lastResult = CON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("CON Received");
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_NON_CONFIRMABLE:
|
||||
// It COULD be the response to our request, or
|
||||
// just some unexpected message.
|
||||
// We'll let the upper layers decide.
|
||||
|
||||
// That's what Kovatsch et al. show in their FSM.
|
||||
m_state = STATE_CLOSED;
|
||||
m_lastResult = NON_RECEIVED;
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("NON Received");
|
||||
break;
|
||||
}
|
||||
if (msg.getPayloadLen() > 0) {
|
||||
TEMBOO_TRACE("DBG: ");
|
||||
TEMBOO_TRACELN("Payload data:");
|
||||
uint8_t *payload = msg.getPayload();
|
||||
uint16_t len = msg.getPayloadLen();
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
TEMBOO_TRACE((char)payload[i]);
|
||||
}
|
||||
TEMBOO_TRACELN();
|
||||
}
|
||||
} else if (msg.getType() == CoapMsg::COAP_CONFIRMABLE) {
|
||||
// It's not from the host we sent the request to, but
|
||||
// the sending host expects a reply, so explicitly reject it.
|
||||
rejectMsg(msg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case STATE_CLOSED:
|
||||
|
||||
// We haven't sent anything that we're expecting an answer to.
|
||||
// We haven't received anything that requires an answer.
|
||||
// Just pump the receiver.
|
||||
if (m_ipStack.recvDatagram(m_rxBuffer, m_rxLen, m_rxByteCount)) {
|
||||
m_lastResult = ERROR_RECEIVING_PACKET;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we received something, handle it.
|
||||
if (m_rxByteCount > 0) {
|
||||
CoapMsg msg(m_rxBuffer, m_rxLen, m_rxByteCount);
|
||||
switch (msg.getType()) {
|
||||
case CoapMsg::COAP_ACK:
|
||||
// Not expecting any ACKS, ignore it.
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_RESET:
|
||||
// Haven't sent any CONs and we don't send NONs, ignore it.
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_CONFIRMABLE:
|
||||
// Let the higher layers deal with this.
|
||||
m_prevState = m_state;
|
||||
m_state = STATE_ACK_PENDING;
|
||||
m_lastResult = CON_RECEIVED;
|
||||
break;
|
||||
|
||||
case CoapMsg::COAP_NON_CONFIRMABLE:
|
||||
// Let the higher layers deal with this.
|
||||
m_lastResult = NON_RECEIVED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
128
arduino/libraries/Temboo/src/utility/CoapMessageLayer.h
Normal file
128
arduino/libraries/Temboo/src/utility/CoapMessageLayer.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef COAPMESSAGELAYER_H_
|
||||
#define COAPMESSAGELAYER_H_
|
||||
|
||||
#include "TembooCoAPIPStack.h"
|
||||
#include "TembooTimer.h"
|
||||
#include "CoapMsg.h"
|
||||
|
||||
/**
|
||||
* CoapMessageLayer is the lowest layer of the CoAP stack. It is responsible for
|
||||
* transmitting and receiving messages. Specifically, this implementation is
|
||||
* responsible for handling confirmable (CON) messages and their ACKs.
|
||||
* It is intended to implement the Message Layer FSM described by Kovatsch et al. in
|
||||
* https://tools.ietf.org/html/draft-kovatsch-lwig-coap-01
|
||||
*
|
||||
* It can send reliable (confirmable or CON) messages and will maintain the necessary
|
||||
* state information to wait for an acknowledgement. It will handle any necessary
|
||||
* retransmissions and timeouts until a CON message has been ACK'd (or rejected.)
|
||||
*
|
||||
* It can not send unreliable (non-confirmable or NON) messages as currently designed
|
||||
* because our application does not use NON messages.
|
||||
*
|
||||
* It can receive CON and NON messages and will send ACKs or RESETs as required.
|
||||
*
|
||||
* Any non-rejected CON or NON messages received are passed up to the Request/Response
|
||||
* layer (CoapRRLayer) for processing.
|
||||
*
|
||||
* Be sure to note the difference between ACK's and Responses. This layer handles
|
||||
* ACK's, it does not handle Responses. (Responses are handled by the CoapRRLayer class.)
|
||||
*/
|
||||
|
||||
|
||||
class CoapMessageLayer {
|
||||
public:
|
||||
|
||||
enum State {
|
||||
STATE_CLOSED,
|
||||
STATE_RELIABLE_TX,
|
||||
STATE_ACK_PENDING,
|
||||
STATE_WAITING_FOR_CON
|
||||
};
|
||||
|
||||
enum Result {
|
||||
NO_ERROR = 0,
|
||||
CON_RECEIVED,
|
||||
NON_RECEIVED,
|
||||
ACK_RECEIVED,
|
||||
RESET_RECEIVED,
|
||||
ERROR_IMPROPER_STATE,
|
||||
ERROR_NULL_MESSAGE,
|
||||
ERROR_SENDING_PACKET,
|
||||
ERROR_RECEIVING_PACKET,
|
||||
ERROR_RETRANSMIT_COUNT_EXCEEDED,
|
||||
ERROR_TX_SPAN_TIME_EXCEEDED
|
||||
};
|
||||
|
||||
static const uint32_t ACK_TIMEOUT = 2000;
|
||||
|
||||
// MAX_ACK_TIMEOUT = ACK_TIMEOUT * ACK_RANDOM_FACTOR
|
||||
// RFC7252 suggests ACK_RANDOM_FACTOR = 1.5
|
||||
static const uint32_t MAX_ACK_TIMEOUT = 3000;
|
||||
static const uint8_t MAX_RETRANSMIT = 4;
|
||||
|
||||
// MAX_TRANSMIT_SPAN = ACK_TIMEOUT * ((2^MAX_RETRANSMIT) - 1) * ACK_RANDOM_FACTOR
|
||||
static const uint32_t MAX_TRANSMIT_SPAN = 45000;
|
||||
|
||||
// MAX_TRANSMIT_WAIT = ACK_TIMEOUT * (2^(MAX_RETRANSMIT + 1) - 1) * ACK_RANDOM_FACTOR
|
||||
static const uint32_t MAX_TRANSMIT_WAIT = 93000;
|
||||
|
||||
|
||||
CoapMessageLayer(uint8_t* rxBuffer, uint16_t rxLen, TembooCoAPIPStack& ipStack);
|
||||
Result reliableSend(CoapMsg& msg, IPAddress destAddr, uint16_t destPort);
|
||||
Result cancelReliableSend();
|
||||
Result acceptMsg(CoapMsg& msg);
|
||||
Result acceptMsg(CoapMsg& msg, IPAddress addr, uint16_t port);
|
||||
Result rejectMsg(CoapMsg& msg);
|
||||
Result rejectMsg(CoapMsg& msg, IPAddress addr, uint16_t port);
|
||||
Result loop();
|
||||
Result getLastResult() {return m_lastResult;}
|
||||
uint16_t getRXByteCount() {return m_rxByteCount;}
|
||||
void setState(State state) {m_state = state;}
|
||||
|
||||
|
||||
private:
|
||||
uint8_t* m_rxBuffer;
|
||||
uint16_t m_rxLen;
|
||||
TembooCoAPIPStack& m_ipStack;
|
||||
|
||||
State m_state;
|
||||
State m_prevState;
|
||||
int m_retransmitCount;
|
||||
Result m_lastResult;
|
||||
IPAddress m_destAddr;
|
||||
uint16_t m_destPort;
|
||||
|
||||
uint16_t m_msgID;
|
||||
uint8_t* m_msgBytes;
|
||||
uint16_t m_msgLen;
|
||||
int32_t m_rxByteCount;
|
||||
TembooTimer m_txSpanTimer;
|
||||
TembooTimer m_retransmitTimer;
|
||||
uint32_t m_retransmitTimeoutMillis;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
714
arduino/libraries/Temboo/src/utility/CoapMsg.cpp
Normal file
714
arduino/libraries/Temboo/src/utility/CoapMsg.cpp
Normal file
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "CoapMsg.h"
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
const uint8_t COAP_VERSION = 1;
|
||||
const uint8_t HEADER_LENGTH = 4;
|
||||
|
||||
|
||||
CoapMsg::CoapMsg(uint8_t* buffer, uint16_t bufferLen) : m_buffer(buffer), m_bufferLen(bufferLen) {
|
||||
|
||||
// Starting a new message. All messages have a header.
|
||||
m_msgLen = HEADER_LENGTH;
|
||||
memset(m_buffer, 0, m_bufferLen);
|
||||
m_buffer[0] = COAP_VERSION << 6;
|
||||
m_buildState = BUILD_BEGIN;
|
||||
m_lastOptionCode = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::CoapMsg(uint8_t* buffer, uint16_t bufferLen, uint16_t packetLen) : m_buffer(buffer), m_bufferLen(bufferLen) {
|
||||
m_msgLen = packetLen;
|
||||
m_buildState = BUILD_HAVE_PAYLOAD;
|
||||
m_lastOptionCode = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CoapMsg::setType(CoapMsg::Type msgType) {
|
||||
m_buffer[0] &= 0xCF; //11001111
|
||||
m_buffer[0] |= msgType << 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Type CoapMsg::getType() {
|
||||
return (CoapMsg::Type)((m_buffer[0] & 0x30) >> 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CoapMsg::setId(uint16_t msgId) {
|
||||
m_buffer[2] = msgId >> 8;
|
||||
m_buffer[3] = msgId & 0x00FF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getId() {
|
||||
return (m_buffer[2] << 8) + m_buffer[3];
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::setToken(const uint8_t* token, uint8_t tokenLen){
|
||||
|
||||
if (m_buildState >= BUILD_HAVE_TOKEN) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (tokenLen > 8){
|
||||
return COAP_RESULT_TOKEN_LENGTH;
|
||||
}
|
||||
|
||||
if ((tokenLen > 0) && (NULL == token)) {
|
||||
return COAP_RESULT_TOKEN_NULL;
|
||||
}
|
||||
|
||||
if ((m_msgLen + tokenLen) > m_bufferLen) {
|
||||
return COAP_RESULT_BUFFER_OVERRUN;
|
||||
}
|
||||
|
||||
memcpy(m_buffer + m_msgLen, token, tokenLen);
|
||||
m_msgLen += tokenLen;
|
||||
m_buffer[0] &= 0xF0;
|
||||
m_buffer[0] |= tokenLen;
|
||||
m_buildState = BUILD_HAVE_TOKEN;
|
||||
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getToken() {
|
||||
if (getTokenLen() > 0) {
|
||||
return m_buffer + HEADER_LENGTH;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t CoapMsg::getTokenLen() {
|
||||
return m_buffer[0] & 0x0F;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CoapMsg::setCode(CoapMsg::Code code) {
|
||||
m_buffer[1] = (uint8_t)code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Code CoapMsg::getCode() {
|
||||
return (CoapMsg::Code)(m_buffer[1]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getHTTPStatus() {
|
||||
uint8_t code = getCode();
|
||||
// 3 MSbs are the most significant digit (0 - 6)
|
||||
// 5 LSbs are the two least significant digits (0 - 31)
|
||||
return (code >> 5) * 100 + (code & 0x1F);
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::setPayload(const uint8_t* payload, uint16_t payloadLen) {
|
||||
|
||||
if (m_buildState >= BUILD_HAVE_PAYLOAD) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (payloadLen > 0 && payload == NULL) {
|
||||
return COAP_RESULT_PAYLOAD_NULL;
|
||||
}
|
||||
|
||||
if ((m_msgLen + payloadLen + 1) > m_bufferLen) {
|
||||
return COAP_RESULT_BUFFER_OVERRUN;
|
||||
}
|
||||
|
||||
// Add the special payload marker flag.
|
||||
m_buffer[m_msgLen++] = 0xFF;
|
||||
memcpy(m_buffer + m_msgLen, payload, payloadLen);
|
||||
m_msgLen += payloadLen;
|
||||
m_buildState = BUILD_HAVE_PAYLOAD;
|
||||
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getPayload() {
|
||||
if (m_buildState < BUILD_HAVE_PAYLOAD) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t* payload = NULL;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < m_buffer + m_msgLen) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
if ((optionDelta == 15) || (optionLen == 15)) {
|
||||
//Technically, if either optionDelta or optionLen is 15, then
|
||||
//both MUST be 15, else it's a malformed message. However,
|
||||
//we're going to be a little loose with the spec here.
|
||||
payload = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getPayloadLen() {
|
||||
if (m_buildState < BUILD_HAVE_PAYLOAD) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* payload = getPayload();
|
||||
if (NULL == payload) {
|
||||
return 0;
|
||||
}
|
||||
return m_buffer + m_msgLen - payload;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
0 1 2 3 4 5 6 7
|
||||
+---------------+---------------+
|
||||
| | |
|
||||
| Option Delta | Option Length | 1 byte
|
||||
| | |
|
||||
+---------------+---------------+
|
||||
\ \ 0 bytes if OptionDelta <= 12
|
||||
/ Option Delta / 1 byte if OptionDelte == 13
|
||||
\ (extended) \ 2 bytes if OptionDelta == 14
|
||||
+-------------------------------+
|
||||
\ \ 0 bytes if OptionLength <= 12
|
||||
/ Option Length / 1 byte if OptionLength == 13
|
||||
\ (extended) \ 2 bytes if OptionLength == 14
|
||||
+-------------------------------+
|
||||
\ \
|
||||
/ /
|
||||
\ \
|
||||
/ Option Value / 0 or more bytes (i.e. OptionLength bytes)
|
||||
\ \
|
||||
/ /
|
||||
\ \
|
||||
+-------------------------------+
|
||||
*/
|
||||
|
||||
CoapMsg::Result CoapMsg::addOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen) {
|
||||
|
||||
if (m_buildState > BUILD_HAVE_OPTIONS) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (m_lastOptionCode > optionCode) {
|
||||
return COAP_RESULT_BUILD_ORDER;
|
||||
}
|
||||
|
||||
if (optionLen > 0 && NULL == optionValue) {
|
||||
return COAP_RESULT_OPTION_NULL;
|
||||
}
|
||||
|
||||
CoapMsg::Result rc = validateOption(optionCode, optionValue, optionLen);
|
||||
if (COAP_RESULT_SUCCESS != rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// See if there's enough room in the buffer to add this option.
|
||||
uint16_t byteCount = 1;
|
||||
uint16_t optionDelta = optionCode - m_lastOptionCode;
|
||||
if (optionDelta >= 13) {
|
||||
byteCount++;
|
||||
}
|
||||
if (optionDelta >= 269) {
|
||||
byteCount++;
|
||||
}
|
||||
if (optionLen >= 13) {
|
||||
byteCount++;
|
||||
}
|
||||
if (optionLen >= 269) {
|
||||
byteCount++;
|
||||
}
|
||||
byteCount += optionLen;
|
||||
|
||||
if (m_msgLen + byteCount > m_bufferLen) {
|
||||
return COAP_RESULT_BUFFER_OVERRUN;
|
||||
}
|
||||
|
||||
// If we get this far, there's enough room.
|
||||
byteCount = 0;
|
||||
if (optionDelta >= 269) {
|
||||
m_buffer[m_msgLen] = 14 << 4;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionDelta - 269) >> 8;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionDelta - 269) & 0x00FF;
|
||||
} else if (optionDelta >= 13) {
|
||||
m_buffer[m_msgLen] = 13 << 4;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionDelta - 13);
|
||||
} else {
|
||||
m_buffer[m_msgLen] = optionDelta << 4;
|
||||
}
|
||||
|
||||
if (optionLen >= 269) {
|
||||
m_buffer[m_msgLen] = (m_buffer[m_msgLen] & 0XF0) | 14;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionLen - 269) >> 8;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionLen - 269) & 0xFF;
|
||||
} else if (optionLen >= 13) {
|
||||
m_buffer[m_msgLen] = (m_buffer[m_msgLen] & 0xF0) | 13;
|
||||
byteCount++;
|
||||
m_buffer[m_msgLen + byteCount] = (optionLen - 13);
|
||||
} else {
|
||||
m_buffer[m_msgLen] = (m_buffer[m_msgLen] & 0xF0) | optionLen;
|
||||
byteCount++;
|
||||
}
|
||||
|
||||
m_msgLen += byteCount;
|
||||
if (optionLen > 0) {
|
||||
memcpy(m_buffer + m_msgLen, optionValue, optionLen);
|
||||
}
|
||||
m_msgLen += optionLen;
|
||||
m_lastOptionCode = optionCode;
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::validateOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen) {
|
||||
|
||||
CoapMsg::Result rc = COAP_RESULT_SUCCESS;
|
||||
|
||||
switch(optionCode) {
|
||||
case COAP_OPTION_IF_MATCH:
|
||||
rc = validateOptionValue(0, 8, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_URI_HOST:
|
||||
rc = validateOptionValue(1, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_ETAG:
|
||||
rc = validateOptionValue(1, 8, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_IF_NONE_MATCH:
|
||||
rc = validateOptionValue(0, 0, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
//TODO: case COAP_OPTION_OBSERVE:
|
||||
// rc = validateOptionValue(0, 0, optionValue, optionLen);
|
||||
// break;
|
||||
|
||||
case COAP_OPTION_URI_PORT:
|
||||
rc = validateOptionValue(0, 2, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_LOCATION_PATH:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_URI_PATH:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_CONTENT_FORMAT:
|
||||
rc = validateOptionValue(0, 2, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_MAX_AGE:
|
||||
rc = validateOptionValue(0, 4, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_URI_QUERY:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_ACCEPT:
|
||||
rc = validateOptionValue(0, 2, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_LOCATION_QUERY:
|
||||
rc = validateOptionValue(0, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_BLOCK2:
|
||||
rc = validateOptionValue(0, 3, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_BLOCK1:
|
||||
rc = validateOptionValue(0, 3, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_SIZE2:
|
||||
rc = validateOptionValue(0, 4, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_PROXY_URI:
|
||||
rc = validateOptionValue(0, 1034, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_PROXY_SCHEME:
|
||||
rc = validateOptionValue(1, 255, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
case COAP_OPTION_SIZE1:
|
||||
rc = validateOptionValue(0, 4, optionValue, optionLen);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = COAP_RESULT_OPTION_UNKNOWN;
|
||||
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::validateOptionValue(uint16_t minLen, uint16_t maxLen, const uint8_t* optionValue, uint16_t optionLen) {
|
||||
|
||||
if (optionLen < minLen || optionLen > maxLen) {
|
||||
return COAP_RESULT_OPTION_LENGTH;
|
||||
}
|
||||
if (optionLen > 0 && NULL == optionValue) {
|
||||
return COAP_RESULT_OPTION_NULL;
|
||||
}
|
||||
|
||||
//TODO: Maybe. Validate value format (uint vs string vs opaque)
|
||||
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getMsgBytes() {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getMsgLen() {
|
||||
return m_msgLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::decodeOption(uint8_t* buffer, uint16_t* optionDelta, uint16_t* optionLen) {
|
||||
|
||||
*optionDelta = *buffer >> 4;
|
||||
*optionLen = *buffer & 0x0F;
|
||||
buffer++;
|
||||
|
||||
if (13 == *optionDelta) {
|
||||
*optionDelta = *buffer++ + 13;
|
||||
} else if (14 == *optionDelta) {
|
||||
*optionDelta = *buffer++ << 8;
|
||||
*optionDelta += *buffer++;
|
||||
*optionDelta += 269;
|
||||
}
|
||||
|
||||
if (13 == *optionLen) {
|
||||
*optionLen = *buffer++ + 13;
|
||||
} else if (14 == *optionLen) {
|
||||
*optionLen = *buffer++ << 8;
|
||||
*optionLen += *buffer++;
|
||||
*optionLen += 269;
|
||||
}
|
||||
|
||||
if (*optionLen != 15) {
|
||||
buffer += *optionLen;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getOptionCount(CoapMsg::Option optionCode) {
|
||||
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16_t CoapMsg::getOptionLen(CoapMsg::Option optionCode, uint16_t index) {
|
||||
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
if (count == index) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
optionLen = 0;
|
||||
}
|
||||
return optionLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t* CoapMsg::getOptionValue(CoapMsg::Option optionCode, uint16_t index) {
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
if (count == index) {
|
||||
return i - optionLen;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapMsg::Result CoapMsg::getOption(CoapMsg::Option optionCode, uint16_t index, uint8_t*& optionValue, uint16_t& optionLen) {
|
||||
|
||||
uint16_t count = 0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optLen);
|
||||
lastOption += optionDelta;
|
||||
if (lastOption == optionCode) {
|
||||
if (count == index) {
|
||||
optionValue = i - optLen;
|
||||
optionLen = optLen;
|
||||
return COAP_RESULT_SUCCESS;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return COAP_RESULT_OPTION_NOT_FOUND;
|
||||
|
||||
}
|
||||
|
||||
uint16_t CoapMsg::getBlock1Size() {
|
||||
return getBlockSize(COAP_OPTION_BLOCK1);
|
||||
}
|
||||
|
||||
uint16_t CoapMsg::getBlock2Size() {
|
||||
return getBlockSize(COAP_OPTION_BLOCK2);
|
||||
}
|
||||
|
||||
uint16_t CoapMsg::getBlockSize(CoapMsg::Option optionCode) {
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t optionLen;
|
||||
uint8_t* optionValue;
|
||||
if (getOption(optionCode, 0, optionValue, optionLen) != COAP_RESULT_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
uint16_t blockLen = 16 << (optionValue[optionLen - 1] & 0x07);
|
||||
if (blockLen > 1024) {
|
||||
return 0;
|
||||
}
|
||||
return blockLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t CoapMsg::getBlock1Num() {
|
||||
return getBlockNum(COAP_OPTION_BLOCK1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t CoapMsg::getBlock2Num() {
|
||||
return getBlockNum(COAP_OPTION_BLOCK2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t CoapMsg::getBlockNum(CoapMsg::Option optionCode) {
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t optionLen;
|
||||
uint8_t* optionValue;
|
||||
if (getOption(optionCode, 0, optionValue, optionLen) != COAP_RESULT_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
int32_t blockNum = 0;
|
||||
for (;optionLen > 0; optionLen--) {
|
||||
blockNum <<= 8;
|
||||
blockNum += *optionValue;
|
||||
optionValue++;
|
||||
}
|
||||
blockNum >>= 4;
|
||||
|
||||
return blockNum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapMsg::getBlock1More() {
|
||||
return getBlockMore(COAP_OPTION_BLOCK1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapMsg::getBlock2More() {
|
||||
return getBlockMore(COAP_OPTION_BLOCK2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapMsg::getBlockMore(CoapMsg::Option optionCode) {
|
||||
if (m_buildState < BUILD_HAVE_OPTIONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t optionLen;
|
||||
uint8_t* optionValue;
|
||||
if (getOption(optionCode, 0, optionValue, optionLen) != COAP_RESULT_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
if (optionLen == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (optionValue[optionLen-1] & 0x08) > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert this (existing received) message into a reset message.
|
||||
*/
|
||||
void CoapMsg::convertToReset() {
|
||||
setType(COAP_RESET);
|
||||
setCode(COAP_EMPTY);
|
||||
|
||||
// Set token length to 0
|
||||
m_buffer[0] &= 0xF0;
|
||||
|
||||
m_msgLen = HEADER_LENGTH;
|
||||
}
|
||||
|
||||
void CoapMsg::convertToEmptyAck() {
|
||||
setType(COAP_ACK);
|
||||
setCode(COAP_EMPTY);
|
||||
m_buffer[0] &= 0xF0;
|
||||
m_msgLen = HEADER_LENGTH;
|
||||
}
|
||||
|
||||
bool CoapMsg::isValid() {
|
||||
// check packet size
|
||||
if (m_msgLen < 4) {
|
||||
TEMBOO_TRACE("Packet must be a minimum of 4 bytes\n");
|
||||
return false;
|
||||
}
|
||||
// check token length
|
||||
if (getTokenLen() < 0 || getTokenLen() > 8) {
|
||||
TEMBOO_TRACE("Invalid token length\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t responseClass = m_buffer[1] >> 5;
|
||||
// check HTTP Code is between 000-599
|
||||
if (responseClass < 0 || responseClass > 5) {
|
||||
TEMBOO_TRACE("Invalid code\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HEADER_LENGTH + getTokenLen() == m_msgLen) {
|
||||
// nothing else in the packet
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t count =0;
|
||||
uint16_t lastOption = 0;
|
||||
uint16_t optionDelta = 0;
|
||||
uint16_t optionLen = 0;
|
||||
uint8_t* i = m_buffer + HEADER_LENGTH + getTokenLen();
|
||||
// validate options
|
||||
while (i < (m_buffer + m_msgLen) && *i != 0xFF) {
|
||||
i = decodeOption(i, &optionDelta, &optionLen);
|
||||
lastOption += optionDelta;
|
||||
if (validateOption((Option)lastOption, i - optionLen, optionLen)) {
|
||||
TEMBOO_TRACE("Invalid option\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if payload marker exists, make sure there is payload data
|
||||
if (*i == 0xFF && (i - m_buffer) > m_msgLen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
192
arduino/libraries/Temboo/src/utility/CoapMsg.h
Normal file
192
arduino/libraries/Temboo/src/utility/CoapMsg.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef COAPMSG_H_
|
||||
#define COAPMSG_H_
|
||||
|
||||
/*
|
||||
Byte 0 Byte 1 Byte 2 Byte 3
|
||||
MSB LSB MSB LSB MSB LSB MSB LSB
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
|Ver| T | TKL | | Code | | MsgID MSB | | MsgID LSB |
|
||||
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
||||
| Token (if any, TKL bytes) ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Options (if any) ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|1 1 1 1 1 1 1 1| Payload (if any) ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
#define RESPONSE_CODE(class, detail) ((class << 5) + detail)
|
||||
|
||||
class CoapMsg {
|
||||
|
||||
public:
|
||||
enum Type {
|
||||
COAP_CONFIRMABLE = 0,
|
||||
COAP_NON_CONFIRMABLE = 1,
|
||||
COAP_ACK = 2,
|
||||
COAP_RESET = 3
|
||||
};
|
||||
|
||||
enum Code {
|
||||
//REQUEST CODES
|
||||
COAP_EMPTY = 0,
|
||||
COAP_GET = 1,
|
||||
COAP_POST = 2,
|
||||
COAP_PUT = 3,
|
||||
COAP_DELETE = 4,
|
||||
|
||||
//RESPONSE CODES
|
||||
COAP_CREATED = RESPONSE_CODE(2,1),
|
||||
COAP_DELETED = RESPONSE_CODE(2,2),
|
||||
COAP_VALID = RESPONSE_CODE(2,3),
|
||||
COAP_CHANGED = RESPONSE_CODE(2,4),
|
||||
COAP_CONTENT = RESPONSE_CODE(2,5),
|
||||
COAP_CONTINUE = RESPONSE_CODE(2,31),
|
||||
COAP_BAD_REQUEST = RESPONSE_CODE(4,0),
|
||||
COAP_UNAUTHORIZED = RESPONSE_CODE(4,1),
|
||||
COAP_BAD_OPTION = RESPONSE_CODE(4,2),
|
||||
COAP_FORBIDDEN = RESPONSE_CODE(4,3),
|
||||
COAP_NOT_FOUND = RESPONSE_CODE(4,4),
|
||||
COAP_METHOD_NOT_ALLOWED = RESPONSE_CODE(4,5),
|
||||
COAP_NOT_ACCEPTABLE = RESPONSE_CODE(4,6),
|
||||
COAP_REQUEST_ENTITY_INCOMPLETE = RESPONSE_CODE(4,8),
|
||||
COAP_PRECONDITION_FAILED = RESPONSE_CODE(4,12),
|
||||
COAP_REQUEST_ENTITY_TOO_LARGE = RESPONSE_CODE(4,13),
|
||||
COAP_UNSUPPORTED_CONTENT_FORMAT = RESPONSE_CODE(4,15),
|
||||
COAP_INTERNAL_SERVER_ERROR = RESPONSE_CODE(5,0),
|
||||
COAP_NOT_IMPLEMENTED = RESPONSE_CODE(5,1),
|
||||
COAP_BAD_GATEWAY = RESPONSE_CODE(5,2),
|
||||
COAP_SERVICE_UNAVAILABLE = RESPONSE_CODE(5,3),
|
||||
COAP_GATEWAY_TIMEOUT = RESPONSE_CODE(5,4),
|
||||
COAP_PROXYING_NOT_SUPPORTED = RESPONSE_CODE(5,5)
|
||||
};
|
||||
|
||||
enum Option {
|
||||
COAP_OPTION_IF_MATCH = 1,
|
||||
COAP_OPTION_URI_HOST = 3,
|
||||
COAP_OPTION_ETAG = 4,
|
||||
COAP_OPTION_IF_NONE_MATCH = 5,
|
||||
//TODO: COAP_OPTION_OBSERVE = 6,
|
||||
COAP_OPTION_URI_PORT = 7,
|
||||
COAP_OPTION_LOCATION_PATH = 8,
|
||||
COAP_OPTION_URI_PATH = 11,
|
||||
COAP_OPTION_CONTENT_FORMAT = 12,
|
||||
COAP_OPTION_MAX_AGE = 14,
|
||||
COAP_OPTION_URI_QUERY = 15,
|
||||
COAP_OPTION_ACCEPT = 17,
|
||||
COAP_OPTION_LOCATION_QUERY = 20,
|
||||
COAP_OPTION_BLOCK2 = 23,
|
||||
COAP_OPTION_BLOCK1 = 27,
|
||||
COAP_OPTION_SIZE2 = 28,
|
||||
COAP_OPTION_PROXY_URI = 35,
|
||||
COAP_OPTION_PROXY_SCHEME = 39,
|
||||
COAP_OPTION_SIZE1 = 60
|
||||
};
|
||||
|
||||
enum Result {
|
||||
COAP_RESULT_SUCCESS = 0, // No error.
|
||||
COAP_RESULT_TOKEN_NULL, // Token length > 0 but NULL pointer given for token value.
|
||||
COAP_RESULT_TOKEN_LENGTH, // Illegal token length value (> 8).
|
||||
COAP_RESULT_PAYLOAD_NULL, // Payload length > 0 but NULL pointer given for payload value.
|
||||
COAP_RESULT_OPTION_UNKNOWN, // An unknown option code was specified.
|
||||
COAP_RESULT_OPTION_NULL, // Option length > 0 but NULL pointer given for option value.
|
||||
COAP_RESULT_OPTION_LENGTH, // Illegal length for option specified.
|
||||
COAP_RESULT_OPTION_NOT_FOUND,// The requested option was not found in the message.
|
||||
COAP_RESULT_BUFFER_OVERRUN, // Adding data would overrun the packet buffer
|
||||
COAP_RESULT_BUILD_ORDER, // Message build order incorrect.
|
||||
COAP_RESULT_INVALID_MSG, // Received message is malformed or invalid.
|
||||
COAP_RESULT_FAILURE // Operation failed or unspecified error
|
||||
};
|
||||
|
||||
CoapMsg(uint8_t* buffer, uint16_t bufferLen);
|
||||
CoapMsg(uint8_t* buffer, uint16_t bufferLen, uint16_t packetLen);
|
||||
|
||||
void setType(CoapMsg::Type msgType);
|
||||
CoapMsg::Type getType();
|
||||
|
||||
void setId(uint16_t msgId);
|
||||
uint16_t getId();
|
||||
|
||||
void setCode(CoapMsg::Code code);
|
||||
CoapMsg::Code getCode();
|
||||
uint16_t getHTTPStatus();
|
||||
|
||||
CoapMsg::Result setToken(const uint8_t* token, uint8_t tokenLen);
|
||||
uint8_t* getToken();
|
||||
uint8_t getTokenLen();
|
||||
|
||||
CoapMsg::Result addOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen);
|
||||
CoapMsg::Result getOption(CoapMsg::Option optionCode, uint16_t index, uint8_t*& optionValue, uint16_t& optionLen);
|
||||
uint16_t getOptionCount(CoapMsg::Option optionCode);
|
||||
uint16_t getOptionLen(CoapMsg::Option optionCode, uint16_t index);
|
||||
uint8_t* getOptionValue(CoapMsg::Option optionCode, uint16_t index);
|
||||
|
||||
CoapMsg::Result setPayload(const uint8_t* payload, uint16_t payloadLen);
|
||||
uint8_t* getPayload();
|
||||
uint16_t getPayloadLen();
|
||||
|
||||
uint8_t* getMsgBytes();
|
||||
uint16_t getMsgLen();
|
||||
|
||||
bool isValid();
|
||||
|
||||
uint16_t getBlock1Size();
|
||||
uint32_t getBlock1Num();
|
||||
bool getBlock1More();
|
||||
|
||||
uint16_t getBlock2Size();
|
||||
uint32_t getBlock2Num();
|
||||
bool getBlock2More();
|
||||
|
||||
void convertToReset();
|
||||
void convertToEmptyAck();
|
||||
|
||||
protected:
|
||||
uint8_t* m_buffer;
|
||||
uint16_t m_bufferLen;
|
||||
uint16_t m_msgLen;
|
||||
|
||||
enum BuildState {
|
||||
BUILD_BEGIN,
|
||||
BUILD_HAVE_TOKEN,
|
||||
BUILD_HAVE_OPTIONS,
|
||||
BUILD_HAVE_PAYLOAD
|
||||
};
|
||||
CoapMsg::BuildState m_buildState;
|
||||
uint16_t m_lastOptionCode;
|
||||
|
||||
protected:
|
||||
CoapMsg::Result validateOption(CoapMsg::Option optionCode, const uint8_t* optionValue, uint16_t optionLen);
|
||||
CoapMsg::Result validateOptionValue(uint16_t minLen, uint16_t maxLen, const uint8_t* optionValue, uint16_t optionLen);
|
||||
uint8_t* decodeOption(uint8_t* buffer, uint16_t* optionDelta, uint16_t* optionLen);
|
||||
uint16_t getBlockSize(CoapMsg::Option optionCode);
|
||||
uint32_t getBlockNum(CoapMsg::Option optionCode);
|
||||
bool getBlockMore(CoapMsg::Option optionCode);
|
||||
};
|
||||
|
||||
|
||||
#endif //TEMBOOCOAP_H_
|
||||
176
arduino/libraries/Temboo/src/utility/CoapRRLayer.cpp
Normal file
176
arduino/libraries/Temboo/src/utility/CoapRRLayer.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "CoapRRLayer.h"
|
||||
|
||||
CoapRRLayer::CoapRRLayer(CoapMessageLayer& messageLayer, uint8_t* rxBuffer, uint16_t rxBufferLen) :
|
||||
m_messageLayer(messageLayer),
|
||||
m_state(STATE_IDLE),
|
||||
m_lastResult(NO_ERROR),
|
||||
m_token(NULL),
|
||||
m_rxBuffer(rxBuffer),
|
||||
m_rxBufferLen(rxBufferLen) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapRRLayer::Result CoapRRLayer::reliableSend(CoapMsg& msg, char* token, IPAddress addr, uint16_t port) {
|
||||
if (m_state != STATE_IDLE) {
|
||||
return ERROR_IMPROPER_STATE;
|
||||
}
|
||||
|
||||
if (CoapMessageLayer::NO_ERROR != m_messageLayer.reliableSend(msg, addr, port)) {
|
||||
return ERROR_SENDING_MSG;
|
||||
}
|
||||
|
||||
m_token = token;
|
||||
m_state = STATE_WAITING;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CoapRRLayer::rxTokenMatches(CoapMsg& msg) {
|
||||
if (msg.getTokenLen() != strlen(m_token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 != memcmp(msg.getToken(), m_token, strlen(m_token))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CoapRRLayer::Result CoapRRLayer::loop() {
|
||||
|
||||
m_lastResult = NO_ERROR;
|
||||
|
||||
switch (m_state) {
|
||||
|
||||
case STATE_IDLE:
|
||||
|
||||
// Pump the receiver.
|
||||
// We're not serving anything, so unless there's an outstanding
|
||||
// request (which would mean we would be in STATE_WAITING, not STATE_IDLE),
|
||||
// we're going to reject or ignore any incoming traffic.
|
||||
switch(m_messageLayer.loop()) {
|
||||
case CON_RECEIVED:
|
||||
{
|
||||
// Explicitly reject any CON messages so the sender will
|
||||
// quit bugging us with retransmissions.
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Just ignore anything else.
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STATE_WAITING:
|
||||
// We're waiting for a response to an earlier request.
|
||||
switch(m_messageLayer.loop()) {
|
||||
|
||||
case CoapMessageLayer::NO_ERROR:
|
||||
// Nothing happened. Nothing to do.
|
||||
break;
|
||||
|
||||
case CoapMessageLayer::ACK_RECEIVED:
|
||||
{
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
|
||||
// If it wasn't an empty ack, it's a response.
|
||||
// And if the token matches, then it's the response we're waiting for.
|
||||
if (rxTokenMatches(msg)) {
|
||||
m_lastResult = RESPONSE_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
} else {
|
||||
// if ACK is not empty and tokens don't match, an error has occurred
|
||||
if (msg.getTokenLen() != 0) {
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("Error: ");
|
||||
TEMBOO_TRACELN("Msg token did not match");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapMessageLayer::RESET_RECEIVED:
|
||||
{
|
||||
// If it was a reset, the message should be empty
|
||||
// and there will be no token
|
||||
m_lastResult = RST_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapMessageLayer::CON_RECEIVED:
|
||||
{
|
||||
// See if this is our response or just some random message.
|
||||
// The message layer has already confirmed it's from the right host.
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
|
||||
// We only accept responses for the current request (i.e. the tokens must match)
|
||||
if (rxTokenMatches(msg)) {
|
||||
m_lastResult = RESPONSE_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
} else {
|
||||
m_messageLayer.rejectMsg(msg);
|
||||
TEMBOO_TRACE("Error: ");
|
||||
TEMBOO_TRACELN("Msg token did not match");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CoapMessageLayer::NON_RECEIVED:
|
||||
{
|
||||
CoapMsg msg(m_rxBuffer, m_rxBufferLen, m_messageLayer.getRXByteCount());
|
||||
if (rxTokenMatches(msg)) {
|
||||
m_lastResult = RESPONSE_RECEIVED;
|
||||
m_state = STATE_IDLE;
|
||||
} else {
|
||||
// if the token does not match, then an error occurred
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
m_state = STATE_IDLE;
|
||||
TEMBOO_TRACE("Error: ");
|
||||
TEMBOO_TRACELN("Msg token did not match");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Anything else indicates a failure of some sort. Check
|
||||
// the messageLayer lastResult for specifics.
|
||||
m_lastResult = ERROR_RECEIVING_RESPONSE;
|
||||
m_state = STATE_IDLE;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return m_lastResult;
|
||||
}
|
||||
82
arduino/libraries/Temboo/src/utility/CoapRRLayer.h
Normal file
82
arduino/libraries/Temboo/src/utility/CoapRRLayer.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef COAPRRLAYER_H_
|
||||
#define COAPRRLAYER_H_
|
||||
|
||||
#include "CoapMessageLayer.h"
|
||||
#include "CoapMsg.h"
|
||||
|
||||
/**
|
||||
* CoapRRLayer (Request/Response layer) is a middle layer of the CoAP stack. It is responsible for
|
||||
* sending requests and receiving responses to those requests. Note that it does not handle
|
||||
* ACKs or RESETs. Those are handled at the CoapMessageLayer.
|
||||
*
|
||||
* This class is intended to implement the CoAP Client Request/Response Layer FSM as described
|
||||
* by Kovatsch et al. in https://tools.ietf.org/html/draft-kovatsch-lwig-coap-01
|
||||
*
|
||||
* Note that this design only implements the client functionality as our application does not
|
||||
* serve anything.
|
||||
*/
|
||||
|
||||
class CoapRRLayer {
|
||||
|
||||
public:
|
||||
enum Result {
|
||||
NO_ERROR = 0,
|
||||
RESPONSE_RECEIVED,
|
||||
ACK_RECEIVED,
|
||||
CON_RECEIVED,
|
||||
ERROR_IMPROPER_STATE,
|
||||
ERROR_SENDING_MSG,
|
||||
ERROR_RECEIVING_RESPONSE,
|
||||
RST_RECEIVED
|
||||
};
|
||||
|
||||
enum State {
|
||||
STATE_IDLE,
|
||||
STATE_WAITING
|
||||
};
|
||||
|
||||
CoapRRLayer(CoapMessageLayer& messageLayer, uint8_t* rxBuffer, uint16_t rxBufferLen);
|
||||
|
||||
Result reliableSend(CoapMsg& msg, char* token, IPAddress addr, uint16_t port);
|
||||
Result send(CoapMsg& msg, char* token, IPAddress addr, uint16_t port);
|
||||
Result loop();
|
||||
Result getLastResult() {return m_lastResult;}
|
||||
void setState(State state) {m_state = state;}
|
||||
int16_t getRxByteCount() {return m_rxByteCount;}
|
||||
|
||||
protected:
|
||||
|
||||
CoapMessageLayer& m_messageLayer;
|
||||
State m_state;
|
||||
Result m_lastResult;
|
||||
char* m_token;
|
||||
uint8_t* m_rxBuffer;
|
||||
int16_t m_rxByteCount;
|
||||
uint16_t m_rxBufferLen;
|
||||
|
||||
bool rxTokenMatches(CoapMsg& msg);
|
||||
};
|
||||
|
||||
#endif
|
||||
226
arduino/libraries/Temboo/src/utility/DataFormatter.cpp
Normal file
226
arduino/libraries/Temboo/src/utility/DataFormatter.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "DataFormatter.h"
|
||||
|
||||
|
||||
DataFormatter::DataFormatter(const ChoreoInputSet* inputSet, const ChoreoInputExpressionSet* expressionSet, const ChoreoSensorInputSet* sensorSet, const ChoreoOutputSet* outputSet, const ChoreoPreset* preset, const ChoreoDevice* device, const ChoreoDevice* deviceName) :
|
||||
m_inputFormatter(inputSet),
|
||||
m_expressionFormatter(expressionSet),
|
||||
m_sensorFormatter(sensorSet),
|
||||
m_outputFormatter(outputSet),
|
||||
m_presetFormatter(preset),
|
||||
m_deviceTypeFormatter(device, ChoreoDeviceFormatter::DEVICE_TYPE),
|
||||
m_deviceNameFormatter(deviceName, ChoreoDeviceFormatter::DEVICE_NAME) {
|
||||
|
||||
m_inputSet = inputSet;
|
||||
m_expressionSet = expressionSet;
|
||||
m_sensorSet = sensorSet;
|
||||
m_outputSet = outputSet;
|
||||
m_preset = preset;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void DataFormatter::reset() {
|
||||
m_nextState = DATA_START;
|
||||
m_inputFormatter.reset();
|
||||
m_expressionFormatter.reset();
|
||||
m_sensorFormatter.reset();
|
||||
m_outputFormatter.reset();
|
||||
m_presetFormatter.reset();
|
||||
m_deviceTypeFormatter.reset();
|
||||
m_deviceNameFormatter.reset();
|
||||
}
|
||||
|
||||
bool DataFormatter::hasNext() {
|
||||
return m_nextState != DATA_END;
|
||||
}
|
||||
|
||||
char DataFormatter::next() {
|
||||
char c;
|
||||
switch(m_nextState) {
|
||||
case DATA_START:
|
||||
c = '{';
|
||||
// only add device type or device name if
|
||||
// sensor data is being sent with the Choreo
|
||||
// If device name is added, no need to send
|
||||
// device type
|
||||
if (m_sensorFormatter.hasNext()) {
|
||||
if (m_deviceNameFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_DEVICE_NAME;
|
||||
} else if (m_deviceTypeFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_DEVICE_TYPE;
|
||||
}
|
||||
} else {
|
||||
if (m_inputFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_INPUTS;
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
m_nextState = FORMATTING_EMPTY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_DEVICE_TYPE:
|
||||
if (m_deviceTypeFormatter.hasNext()) {
|
||||
c = m_deviceTypeFormatter.next();
|
||||
} else if (m_inputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_INPUTS;
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_DEVICE_NAME:
|
||||
if (m_deviceNameFormatter.hasNext()) {
|
||||
c = m_deviceNameFormatter.next();
|
||||
} else if (m_inputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_INPUTS;
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_INPUTS:
|
||||
if (m_inputFormatter.hasNext()) {
|
||||
c = m_inputFormatter.next();
|
||||
} else if (m_expressionFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_EXPRESSIONS;
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_EXPRESSIONS:
|
||||
if (m_expressionFormatter.hasNext()) {
|
||||
c = m_expressionFormatter.next();
|
||||
} else if (m_sensorFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_SENSORS;
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_SENSORS:
|
||||
if (m_sensorFormatter.hasNext()) {
|
||||
c = m_sensorFormatter.next();
|
||||
} else if (m_outputFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_OUTPUTS;
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_OUTPUTS:
|
||||
if (m_outputFormatter.hasNext()) {
|
||||
c = m_outputFormatter.next();
|
||||
} else if (m_presetFormatter.hasNext()) {
|
||||
c = ',';
|
||||
m_nextState = FORMATTING_PRESET;
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_PRESET:
|
||||
if (m_presetFormatter.hasNext()) {
|
||||
c = m_presetFormatter.next();
|
||||
} else {
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case FORMATTING_EMPTY:
|
||||
c = '}';
|
||||
m_nextState = DATA_END;
|
||||
break;
|
||||
|
||||
case DATA_END:
|
||||
default:
|
||||
c = '\0';
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
80
arduino/libraries/Temboo/src/utility/DataFormatter.h
Normal file
80
arduino/libraries/Temboo/src/utility/DataFormatter.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef DATAFORMATTER_H_
|
||||
#define DATAFORMATTER_H_
|
||||
#include "TembooGlobal.h"
|
||||
#include "ChoreoInputSet.h"
|
||||
#include "ChoreoInputExpressionSet.h"
|
||||
#include "ChoreoSensorInputSet.h"
|
||||
#include "ChoreoOutputSet.h"
|
||||
#include "ChoreoPreset.h"
|
||||
#include "ChoreoDevice.h"
|
||||
#include "ChoreoInputFormatter.h"
|
||||
#include "ChoreoInputExpressionFormatter.h"
|
||||
#include "ChoreoSensorInputFormatter.h"
|
||||
#include "ChoreoOutputFormatter.h"
|
||||
#include "ChoreoPresetFormatter.h"
|
||||
#include "ChoreoDeviceFormatter.h"
|
||||
|
||||
|
||||
|
||||
class DataFormatter {
|
||||
|
||||
public:
|
||||
DataFormatter(const ChoreoInputSet* inputSet, const ChoreoInputExpressionSet* expressionSet, const ChoreoSensorInputSet* sensorSet, const ChoreoOutputSet* outputSet, const ChoreoPreset* preset, const ChoreoDevice* device, const ChoreoDevice* deviceName);
|
||||
bool hasNext();
|
||||
char next();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
const ChoreoInputSet* m_inputSet;
|
||||
const ChoreoSensorInputSet* m_sensorSet;
|
||||
const ChoreoInputExpressionSet* m_expressionSet;
|
||||
const ChoreoOutputSet* m_outputSet;
|
||||
const ChoreoPreset* m_preset;
|
||||
|
||||
ChoreoInputFormatter m_inputFormatter;
|
||||
ChoreoInputExpressionFormatter m_expressionFormatter;
|
||||
ChoreoSensorInputFormatter m_sensorFormatter;
|
||||
ChoreoOutputFormatter m_outputFormatter;
|
||||
ChoreoPresetFormatter m_presetFormatter;
|
||||
ChoreoDeviceFormatter m_deviceTypeFormatter;
|
||||
ChoreoDeviceFormatter m_deviceNameFormatter;
|
||||
|
||||
enum State {
|
||||
DATA_START,
|
||||
FORMATTING_INPUTS,
|
||||
FORMATTING_EXPRESSIONS,
|
||||
FORMATTING_SENSORS,
|
||||
FORMATTING_OUTPUTS,
|
||||
FORMATTING_PRESET,
|
||||
FORMATTING_DEVICE_TYPE,
|
||||
FORMATTING_DEVICE_NAME,
|
||||
FORMATTING_EMPTY,
|
||||
DATA_END
|
||||
};
|
||||
|
||||
State m_nextState;
|
||||
|
||||
};
|
||||
#endif
|
||||
208
arduino/libraries/Temboo/src/utility/FP.h
Normal file
208
arduino/libraries/Temboo/src/utility/FP.h
Normal file
@@ -0,0 +1,208 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2013, 2014
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Sam Grove - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - added attached and detached member functions
|
||||
* Sam Grove - removed need for FP.cpp
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef FP_H
|
||||
#define FP_H
|
||||
|
||||
/** Example using the FP Class with global functions
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
* #include "FP.h"
|
||||
*
|
||||
* FP<void,bool>fp;
|
||||
* DigitalOut myled(LED1);
|
||||
*
|
||||
* void handler(bool value)
|
||||
* {
|
||||
* myled = value;
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* fp.attach(&handler);
|
||||
*
|
||||
* while(1)
|
||||
* {
|
||||
* fp(1);
|
||||
* wait(0.2);
|
||||
* fp(0);
|
||||
* wait(0.2);
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/** Example using the FP Class with different class member functions
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
* #include "FP.h"
|
||||
*
|
||||
* FP<void,bool>fp;
|
||||
* DigitalOut myled(LED4);
|
||||
*
|
||||
* class Wrapper
|
||||
* {
|
||||
* public:
|
||||
* Wrapper(){}
|
||||
*
|
||||
* void handler(bool value)
|
||||
* {
|
||||
* myled = value;
|
||||
* return;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* Wrapper wrapped;
|
||||
* fp.attach(&wrapped, &Wrapper::handler);
|
||||
*
|
||||
* while(1)
|
||||
* {
|
||||
* fp(1);
|
||||
* wait(0.2);
|
||||
* fp(0);
|
||||
* wait(0.2);
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/** Example using the FP Class with member FP and member function
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
* #include "FP.h"
|
||||
*
|
||||
* DigitalOut myled(LED2);
|
||||
*
|
||||
* class Wrapper
|
||||
* {
|
||||
* public:
|
||||
* Wrapper()
|
||||
* {
|
||||
* fp.attach(this, &Wrapper::handler);
|
||||
* }
|
||||
*
|
||||
* void handler(bool value)
|
||||
* {
|
||||
* myled = value;
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* FP<void,bool>fp;
|
||||
* };
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* Wrapper wrapped;
|
||||
*
|
||||
* while(1)
|
||||
* {
|
||||
* wrapped.fp(1);
|
||||
* wait(0.2);
|
||||
* wrapped.fp(0);
|
||||
* wait(0.2);
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class FP
|
||||
* @brief API for managing Function Pointers
|
||||
*/
|
||||
template<class retT, class argT>
|
||||
class FP
|
||||
{
|
||||
public:
|
||||
/** Create the FP object - only one callback can be attached to the object, that is
|
||||
* a member function or a global function, not both at the same time
|
||||
*/
|
||||
FP()
|
||||
{
|
||||
obj_callback = 0;
|
||||
c_callback = 0;
|
||||
}
|
||||
|
||||
/** Add a callback function to the object
|
||||
* @param item - Address of the initialized object
|
||||
* @param member - Address of the member function (dont forget the scope that the function is defined in)
|
||||
*/
|
||||
template<class T>
|
||||
void attach(T *item, retT (T::*method)(argT))
|
||||
{
|
||||
obj_callback = (FPtrDummy *)(item);
|
||||
method_callback = (retT (FPtrDummy::*)(argT))(method);
|
||||
return;
|
||||
}
|
||||
|
||||
/** Add a callback function to the object
|
||||
* @param function - The address of a globally defined function
|
||||
*/
|
||||
void attach(retT (*function)(argT))
|
||||
{
|
||||
c_callback = function;
|
||||
}
|
||||
|
||||
/** Invoke the function attached to the class
|
||||
* @param arg - An argument that is passed into the function handler that is called
|
||||
* @return The return from the function hanlder called by this class
|
||||
*/
|
||||
retT operator()(argT arg) const
|
||||
{
|
||||
if( 0 != c_callback ) {
|
||||
return obj_callback ? (obj_callback->*method_callback)(arg) : (*c_callback)(arg);
|
||||
}
|
||||
return (retT)0;
|
||||
}
|
||||
|
||||
/** Determine if an callback is currently hooked
|
||||
* @return 1 if a method is hooked, 0 otherwise
|
||||
*/
|
||||
bool attached()
|
||||
{
|
||||
return obj_callback || c_callback;
|
||||
}
|
||||
|
||||
/** Release a function from the callback hook
|
||||
*/
|
||||
void detach()
|
||||
{
|
||||
obj_callback = 0;
|
||||
c_callback = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// empty type used for casting
|
||||
class FPtrDummy;
|
||||
|
||||
FPtrDummy *obj_callback;
|
||||
|
||||
/**
|
||||
* @union Funciton
|
||||
* @brief Member or global callback function
|
||||
*/
|
||||
union {
|
||||
retT (*c_callback)(argT); /*!< Footprint for a global function */
|
||||
retT (FPtrDummy::*method_callback)(argT); /*!< Footprint for a member function */
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
954
arduino/libraries/Temboo/src/utility/MQTTClient.h
Normal file
954
arduino/libraries/Temboo/src/utility/MQTTClient.h
Normal file
@@ -0,0 +1,954 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014, 2015 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - fix for bug 458512 - QoS 2 messages
|
||||
* Ian Craggs - fix for bug 460389 - send loop uses wrong length
|
||||
* Ian Craggs - fix for bug 464169 - clearing subscriptions
|
||||
* Ian Craggs - fix for bug 464551 - enums and ints can be different size
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTCLIENT_H)
|
||||
#define MQTTCLIENT_H
|
||||
|
||||
#include "FP.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "stdio.h"
|
||||
#include "MQTTLogging.h"
|
||||
|
||||
#if !defined(MQTTCLIENT_QOS1)
|
||||
#define MQTTCLIENT_QOS1 1
|
||||
#endif
|
||||
#if !defined(MQTTCLIENT_QOS2)
|
||||
#define MQTTCLIENT_QOS2 0
|
||||
#endif
|
||||
|
||||
namespace MQTT
|
||||
{
|
||||
|
||||
|
||||
enum QoS { QOS0, QOS1, QOS2 };
|
||||
|
||||
// all failure return codes must be negative
|
||||
enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 };
|
||||
|
||||
|
||||
struct Message
|
||||
{
|
||||
enum QoS qos;
|
||||
bool retained;
|
||||
bool dup;
|
||||
unsigned short id;
|
||||
void *payload;
|
||||
size_t payloadlen;
|
||||
};
|
||||
|
||||
|
||||
struct MessageData
|
||||
{
|
||||
MessageData(MQTTString &aTopicName, struct Message &aMessage) : message(aMessage), topicName(aTopicName)
|
||||
{ }
|
||||
|
||||
struct Message &message;
|
||||
MQTTString &topicName;
|
||||
};
|
||||
|
||||
|
||||
class PacketId
|
||||
{
|
||||
public:
|
||||
PacketId()
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
|
||||
int getNext()
|
||||
{
|
||||
next = (next == MAX_PACKET_ID) ? 1 : next + 1;
|
||||
return next;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int MAX_PACKET_ID = 65535;
|
||||
int next;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class Client
|
||||
* @brief blocking, non-threaded MQTT client API
|
||||
*
|
||||
* This version of the API blocks on all method calls, until they are complete. This means that only one
|
||||
* MQTT request can be in process at any one time.
|
||||
* @param Network a network class which supports send, receive
|
||||
* @param Timer a timer class with the methods:
|
||||
*/
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 100, int MAX_MESSAGE_HANDLERS = 5>
|
||||
class Client
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
typedef void (*messageHandler)(MessageData&);
|
||||
|
||||
/** Construct the client
|
||||
* @param network - pointer to an instance of the Network class - must be connected to the endpoint
|
||||
* before calling MQTT connect
|
||||
* @param limits an instance of the Limit class - to alter limits as required
|
||||
*/
|
||||
Client(Network& network, unsigned int command_timeout_ms = 30000);
|
||||
|
||||
/** Set the default message handling callback - used for any message which does not match a subscription message handler
|
||||
* @param mh - pointer to the callback function
|
||||
*/
|
||||
void setDefaultMessageHandler(messageHandler mh)
|
||||
{
|
||||
defaultMessageHandler.attach(mh);
|
||||
}
|
||||
|
||||
/** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack
|
||||
* The nework object must be connected to the network endpoint before calling this
|
||||
* Default connect options are used
|
||||
* @return success code -
|
||||
*/
|
||||
int connect();
|
||||
|
||||
/** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack
|
||||
* The nework object must be connected to the network endpoint before calling this
|
||||
* @param options - connect options
|
||||
* @return success code -
|
||||
*/
|
||||
int connect(MQTTPacket_connectData& options);
|
||||
|
||||
/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
|
||||
* @param topic - the topic to publish to
|
||||
* @param message - the message to send
|
||||
* @return success code -
|
||||
*/
|
||||
int publish(const char* topicName, Message& message);
|
||||
|
||||
/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
|
||||
* @param topic - the topic to publish to
|
||||
* @param payload - the data to send
|
||||
* @param payloadlen - the length of the data
|
||||
* @param qos - the QoS to send the publish at
|
||||
* @param retained - whether the message should be retained
|
||||
* @return success code -
|
||||
*/
|
||||
int publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos = QOS0, bool retained = false);
|
||||
|
||||
/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
|
||||
* @param topic - the topic to publish to
|
||||
* @param payload - the data to send
|
||||
* @param payloadlen - the length of the data
|
||||
* @param id - the packet id used - returned
|
||||
* @param qos - the QoS to send the publish at
|
||||
* @param retained - whether the message should be retained
|
||||
* @return success code -
|
||||
*/
|
||||
int publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos = QOS1, bool retained = false);
|
||||
|
||||
/** MQTT Subscribe - send an MQTT subscribe packet and wait for the suback
|
||||
* @param topicFilter - a topic pattern which can include wildcards
|
||||
* @param qos - the MQTT QoS to subscribe at
|
||||
* @param mh - the callback function to be invoked when a message is received for this subscription
|
||||
* @return success code -
|
||||
*/
|
||||
int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh);
|
||||
|
||||
/** MQTT Unsubscribe - send an MQTT unsubscribe packet and wait for the unsuback
|
||||
* @param topicFilter - a topic pattern which can include wildcards
|
||||
* @return success code -
|
||||
*/
|
||||
int unsubscribe(const char* topicFilter);
|
||||
|
||||
/** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state
|
||||
* @return success code -
|
||||
*/
|
||||
int disconnect();
|
||||
|
||||
/** A call to this API must be made within the keepAlive interval to keep the MQTT connection alive
|
||||
* yield can be called if no other MQTT operation is needed. This will also allow messages to be
|
||||
* received.
|
||||
* @param timeout_ms the time to wait, in milliseconds
|
||||
* @return success code - on failure, this means the client has disconnected
|
||||
*/
|
||||
int yield(unsigned long timeout_ms = 1000L);
|
||||
|
||||
/** Is the client connected?
|
||||
* @return flag - is the client connected or not?
|
||||
*/
|
||||
bool isConnected()
|
||||
{
|
||||
return isconnected;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void cleanSession();
|
||||
int cycle(Timer& timer);
|
||||
int waitfor(int packet_type, Timer& timer);
|
||||
int keepalive();
|
||||
int publish(int len, Timer& timer, enum QoS qos);
|
||||
|
||||
int decodePacket(int* value, int timeout);
|
||||
int readPacket(Timer& timer);
|
||||
int sendPacket(int length, Timer& timer);
|
||||
int deliverMessage(MQTTString& topicName, Message& message);
|
||||
bool isTopicMatched(char* topicFilter, MQTTString& topicName);
|
||||
|
||||
Network& ipstack;
|
||||
unsigned long command_timeout_ms;
|
||||
|
||||
unsigned char sendbuf[MAX_MQTT_PACKET_SIZE];
|
||||
unsigned char readbuf[MAX_MQTT_PACKET_SIZE];
|
||||
|
||||
Timer last_sent, last_received;
|
||||
unsigned int keepAliveInterval;
|
||||
bool ping_outstanding;
|
||||
bool cleansession;
|
||||
|
||||
PacketId packetid;
|
||||
|
||||
struct MessageHandlers
|
||||
{
|
||||
const char* topicFilter;
|
||||
FP<void, MessageData&> fp;
|
||||
} messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic
|
||||
|
||||
FP<void, MessageData&> defaultMessageHandler;
|
||||
|
||||
bool isconnected;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
unsigned char pubbuf[MAX_MQTT_PACKET_SIZE]; // store the last publish for sending on reconnect
|
||||
int inflightLen;
|
||||
unsigned short inflightMsgid;
|
||||
enum QoS inflightQoS;
|
||||
#endif
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
bool pubrel;
|
||||
#if !defined(MAX_INCOMING_QOS2_MESSAGES)
|
||||
#define MAX_INCOMING_QOS2_MESSAGES 10
|
||||
#endif
|
||||
unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES];
|
||||
bool isQoS2msgidFree(unsigned short id);
|
||||
bool useQoS2msgid(unsigned short id);
|
||||
void freeQoS2msgid(unsigned short id);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
|
||||
void MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::cleanSession()
|
||||
{
|
||||
ping_outstanding = false;
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
messageHandlers[i].topicFilter = 0;
|
||||
isconnected = false;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
inflightMsgid = 0;
|
||||
inflightQoS = QOS0;
|
||||
#endif
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
pubrel = false;
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
incomingQoS2messages[i] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
|
||||
MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::Client(Network& network, unsigned int command_timeout_ms) : ipstack(network), packetid()
|
||||
{
|
||||
last_sent = Timer();
|
||||
last_received = Timer();
|
||||
this->command_timeout_ms = command_timeout_ms;
|
||||
cleanSession();
|
||||
}
|
||||
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
template<class Network, class Timer, int a, int b>
|
||||
bool MQTT::Client<Network, Timer, a, b>::isQoS2msgidFree(unsigned short id)
|
||||
{
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
{
|
||||
if (incomingQoS2messages[i] == id)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
bool MQTT::Client<Network, Timer, a, b>::useQoS2msgid(unsigned short id)
|
||||
{
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
{
|
||||
if (incomingQoS2messages[i] == 0)
|
||||
{
|
||||
incomingQoS2messages[i] = id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
void MQTT::Client<Network, Timer, a, b>::freeQoS2msgid(unsigned short id)
|
||||
{
|
||||
for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
|
||||
{
|
||||
if (incomingQoS2messages[i] == id)
|
||||
{
|
||||
incomingQoS2messages[i] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::sendPacket(int length, Timer& timer)
|
||||
{
|
||||
int rc = FAILURE,
|
||||
sent = 0;
|
||||
|
||||
while (sent < length && !timer.expired())
|
||||
{
|
||||
rc = ipstack.write(&sendbuf[sent], length - sent, timer.left_ms());
|
||||
if (rc < 0) // there was an error writing the data
|
||||
break;
|
||||
sent += rc;
|
||||
}
|
||||
if (sent == length)
|
||||
{
|
||||
if (this->keepAliveInterval > 0)
|
||||
last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet
|
||||
rc = SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
#if defined(MQTT_DEBUG)
|
||||
char printbuf[150];
|
||||
DEBUG("Rc %d from sending packet %s\n", rc, MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length));
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::decodePacket(int* value, int timeout)
|
||||
{
|
||||
unsigned char c;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;
|
||||
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = ipstack.read(&c, 1, timeout);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
exit:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If any read fails in this method, then we should disconnect from the network, as on reconnect
|
||||
* the packets can be retried.
|
||||
* @param timeout the max time to wait for the packet read to complete, in milliseconds
|
||||
* @return the MQTT packet type, or -1 if none
|
||||
*/
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::readPacket(Timer& timer)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
MQTTHeader header = {0};
|
||||
int len = 0;
|
||||
int rem_len = 0;
|
||||
|
||||
/* 1. read the header byte. This has the packet type in it */
|
||||
if (ipstack.read(readbuf, 1, timer.left_ms()) != 1)
|
||||
goto exit;
|
||||
|
||||
len = 1;
|
||||
/* 2. read the remaining length. This is variable in itself */
|
||||
decodePacket(&rem_len, timer.left_ms());
|
||||
len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */
|
||||
|
||||
if (rem_len > (MAX_MQTT_PACKET_SIZE - len))
|
||||
{
|
||||
rc = BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if (rc == BUFFER_OVERFLOW) {
|
||||
if (rem_len > 0 && (ipstack.read(readbuf + len, MAX_MQTT_PACKET_SIZE - len, timer.left_ms()) != (MAX_MQTT_PACKET_SIZE - len)))
|
||||
goto exit;
|
||||
readbuf[MAX_MQTT_PACKET_SIZE-1] = '\0';
|
||||
rem_len = rem_len - MAX_MQTT_PACKET_SIZE - len;
|
||||
while(rem_len--) {
|
||||
unsigned char c;
|
||||
ipstack.read(&c, 1, timer.left_ms() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len))
|
||||
goto exit;
|
||||
header.byte = readbuf[0];
|
||||
rc = header.bits.type;
|
||||
}
|
||||
|
||||
if (this->keepAliveInterval > 0)
|
||||
last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet
|
||||
exit:
|
||||
#if defined(MQTT_DEBUG)
|
||||
if (rc >= 0)
|
||||
{
|
||||
char printbuf[50];
|
||||
DEBUG("Rc %d from receiving packet %s\n", rc, MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len));
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// assume topic filter and name is in correct format
|
||||
// # can only be at end
|
||||
// + and # can only be next to separator
|
||||
template<class Network, class Timer, int a, int b>
|
||||
bool MQTT::Client<Network, Timer, a, b>::isTopicMatched(char* topicFilter, MQTTString& topicName)
|
||||
{
|
||||
char* curf = topicFilter;
|
||||
char* curn = topicName.lenstring.data;
|
||||
char* curn_end = curn + topicName.lenstring.len;
|
||||
|
||||
while (*curf && curn < curn_end)
|
||||
{
|
||||
if (*curn == '/' && *curf != '/')
|
||||
break;
|
||||
if (*curf != '+' && *curf != '#' && *curf != *curn)
|
||||
break;
|
||||
if (*curf == '+')
|
||||
{ // skip until we meet the next separator, or end of string
|
||||
char* nextpos = curn + 1;
|
||||
while (nextpos < curn_end && *nextpos != '/')
|
||||
nextpos = ++curn + 1;
|
||||
}
|
||||
else if (*curf == '#')
|
||||
curn = curn_end - 1; // skip until end of string
|
||||
curf++;
|
||||
curn++;
|
||||
};
|
||||
|
||||
return (curn == curn_end) && (*curf == '\0');
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
|
||||
int MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::deliverMessage(MQTTString& topicName, Message& message)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
// we have to find the right message handler - indexed by topic
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(&topicName, (char*)messageHandlers[i].topicFilter) ||
|
||||
isTopicMatched((char*)messageHandlers[i].topicFilter, topicName)))
|
||||
{
|
||||
if (messageHandlers[i].fp.attached())
|
||||
{
|
||||
MessageData md(topicName, message);
|
||||
messageHandlers[i].fp(md);
|
||||
rc = SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == FAILURE && defaultMessageHandler.attached())
|
||||
{
|
||||
MessageData md(topicName, message);
|
||||
defaultMessageHandler(md);
|
||||
rc = SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::yield(unsigned long timeout_ms)
|
||||
{
|
||||
int rc = SUCCESS;
|
||||
Timer timer = Timer();
|
||||
|
||||
timer.countdown_ms(timeout_ms);
|
||||
while (!timer.expired())
|
||||
{
|
||||
if (cycle(timer) < 0)
|
||||
{
|
||||
rc = FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::cycle(Timer& timer)
|
||||
{
|
||||
/* get one piece of work off the wire and one pass through */
|
||||
|
||||
// read the socket, see what work is due
|
||||
int packet_type = readPacket(timer);
|
||||
int rc = SUCCESS;
|
||||
if (packet_type == BUFFER_OVERFLOW) {
|
||||
MQTTHeader header = {0};
|
||||
header.byte = readbuf[0];
|
||||
packet_type = header.bits.type;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
switch (packet_type)
|
||||
{
|
||||
case FAILURE:
|
||||
case BUFFER_OVERFLOW:
|
||||
rc = packet_type;
|
||||
break;
|
||||
case CONNACK:
|
||||
case PUBACK:
|
||||
case SUBACK:
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
Message msg;
|
||||
int intQoS;
|
||||
if (MQTTDeserialize_publish((unsigned char*)&msg.dup, &intQoS, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName,
|
||||
(unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
goto exit;
|
||||
msg.qos = (enum QoS)intQoS;
|
||||
#if MQTTCLIENT_QOS2
|
||||
if (msg.qos != QOS2)
|
||||
#endif
|
||||
deliverMessage(topicName, msg);
|
||||
#if MQTTCLIENT_QOS2
|
||||
else if (isQoS2msgidFree(msg.id))
|
||||
{
|
||||
if (useQoS2msgid(msg.id))
|
||||
deliverMessage(topicName, msg);
|
||||
else
|
||||
WARN("Maximum number of incoming QoS2 messages exceeded");
|
||||
}
|
||||
#endif
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (msg.qos != QOS0)
|
||||
{
|
||||
if (msg.qos == QOS1)
|
||||
len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id);
|
||||
else if (msg.qos == QOS2)
|
||||
len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id);
|
||||
if (len <= 0)
|
||||
rc = FAILURE;
|
||||
else
|
||||
rc = sendPacket(len, timer);
|
||||
if (rc == FAILURE)
|
||||
goto exit; // there was a problem
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
#if MQTTCLIENT_QOS2
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
rc = FAILURE;
|
||||
else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE,
|
||||
(packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0)
|
||||
rc = FAILURE;
|
||||
else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet
|
||||
rc = FAILURE; // there was a problem
|
||||
if (rc == FAILURE)
|
||||
goto exit; // there was a problem
|
||||
if (packet_type == PUBREL)
|
||||
freeQoS2msgid(mypacketid);
|
||||
break;
|
||||
|
||||
case PUBCOMP:
|
||||
break;
|
||||
#endif
|
||||
case PINGRESP:
|
||||
ping_outstanding = false;
|
||||
break;
|
||||
}
|
||||
keepalive();
|
||||
exit:
|
||||
if (rc == SUCCESS)
|
||||
rc = packet_type;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::keepalive()
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
if (keepAliveInterval == 0)
|
||||
{
|
||||
rc = SUCCESS;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (last_sent.expired() || last_received.expired())
|
||||
{
|
||||
if (!ping_outstanding)
|
||||
{
|
||||
Timer timer = Timer(1000);
|
||||
int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE);
|
||||
if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet
|
||||
ping_outstanding = true;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// only used in single-threaded mode where one command at a time is in process
|
||||
template<class Network, class Timer, int a, int b>
|
||||
int MQTT::Client<Network, Timer, a, b>::waitfor(int packet_type, Timer& timer)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
do
|
||||
{
|
||||
if (timer.expired())
|
||||
break; // we timed out
|
||||
}
|
||||
while ((rc = cycle(timer)) != packet_type);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect(MQTTPacket_connectData& options)
|
||||
{
|
||||
Timer connect_timer = Timer(command_timeout_ms);
|
||||
int rc = FAILURE;
|
||||
int len = 0;
|
||||
|
||||
if (isconnected) // don't send connect packet again if we are already connected
|
||||
goto exit;
|
||||
|
||||
this->keepAliveInterval = options.keepAliveInterval;
|
||||
this->cleansession = options.cleansession;
|
||||
if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(len, connect_timer)) != SUCCESS) // send the connect packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (this->keepAliveInterval > 0)
|
||||
last_received.countdown(this->keepAliveInterval);
|
||||
// this will be a blocking call, wait for the connack
|
||||
if (waitfor(CONNACK, connect_timer) == CONNACK)
|
||||
{
|
||||
unsigned char connack_rc = 255;
|
||||
bool sessionPresent = false;
|
||||
if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
|
||||
rc = connack_rc;
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
#if MQTTCLIENT_QOS2
|
||||
// resend any inflight publish
|
||||
if (inflightMsgid > 0 && inflightQoS == QOS2 && pubrel)
|
||||
{
|
||||
if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0)
|
||||
rc = FAILURE;
|
||||
else
|
||||
rc = publish(len, connect_timer, inflightQoS);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (inflightMsgid > 0)
|
||||
{
|
||||
memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE);
|
||||
rc = publish(inflightLen, connect_timer, inflightQoS);
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (rc == SUCCESS)
|
||||
isconnected = true;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect()
|
||||
{
|
||||
MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
|
||||
return connect(default_options);
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::subscribe(const char* topicFilter, enum QoS qos, messageHandler messageHandler)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms);
|
||||
int len = 0;
|
||||
MQTTString topic = {(char*)topicFilter, {0, 0}};
|
||||
|
||||
if (!isconnected)
|
||||
goto exit;
|
||||
|
||||
len = MQTTSerialize_subscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(len, timer)) != SUCCESS) // send the subscribe packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (waitfor(SUBACK, timer) == SUBACK) // wait for suback
|
||||
{
|
||||
int count = 0, grantedQoS = -1;
|
||||
unsigned short mypacketid;
|
||||
if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
|
||||
rc = grantedQoS; // 0, 1, 2 or 0x80
|
||||
if (rc != 0x80)
|
||||
{
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (messageHandlers[i].topicFilter == 0)
|
||||
{
|
||||
messageHandlers[i].topicFilter = topicFilter;
|
||||
messageHandlers[i].fp.attach(messageHandler);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
exit:
|
||||
if (rc != SUCCESS)
|
||||
cleanSession();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::unsubscribe(const char* topicFilter)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms);
|
||||
MQTTString topic = {(char*)topicFilter, {0, 0}};
|
||||
int len = 0;
|
||||
|
||||
if (!isconnected)
|
||||
goto exit;
|
||||
|
||||
if ((len = MQTTSerialize_unsubscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic)) <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(len, timer)) != SUCCESS) // send the unsubscribe packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (waitfor(UNSUBACK, timer) == UNSUBACK)
|
||||
{
|
||||
unsigned short mypacketid; // should be the same as the packetid above
|
||||
if (MQTTDeserialize_unsuback(&mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
|
||||
{
|
||||
rc = 0;
|
||||
|
||||
// remove the subscription message handler associated with this topic, if there is one
|
||||
for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (strcmp(messageHandlers[i].topicFilter, topicFilter) == 0)
|
||||
{
|
||||
messageHandlers[i].topicFilter = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
exit:
|
||||
if (rc != SUCCESS)
|
||||
cleanSession();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(int len, Timer& timer, enum QoS qos)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = sendPacket(len, timer)) != SUCCESS) // send the publish packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
#if MQTTCLIENT_QOS1
|
||||
if (qos == QOS1)
|
||||
{
|
||||
if (waitfor(PUBACK, timer) == PUBACK)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
rc = FAILURE;
|
||||
else if (inflightMsgid == mypacketid)
|
||||
inflightMsgid = 0;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
#elif MQTTCLIENT_QOS2
|
||||
else if (qos == QOS2)
|
||||
{
|
||||
if (waitfor(PUBCOMP, timer) == PUBCOMP)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
|
||||
rc = FAILURE;
|
||||
else if (inflightMsgid == mypacketid)
|
||||
inflightMsgid = 0;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (rc != SUCCESS)
|
||||
cleanSession();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos, bool retained)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms);
|
||||
MQTTString topicString = MQTTString_initializer;
|
||||
int len = 0;
|
||||
|
||||
if (!isconnected)
|
||||
goto exit;
|
||||
|
||||
topicString.cstring = (char*)topicName;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (qos == QOS1 || qos == QOS2)
|
||||
id = packetid.getNext();
|
||||
#endif
|
||||
|
||||
len = MQTTSerialize_publish(sendbuf, MAX_MQTT_PACKET_SIZE, 0, qos, retained, id,
|
||||
topicString, (unsigned char*)payload, payloadlen);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
|
||||
#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
|
||||
if (!cleansession)
|
||||
{
|
||||
memcpy(pubbuf, sendbuf, len);
|
||||
inflightMsgid = id;
|
||||
inflightLen = len;
|
||||
inflightQoS = qos;
|
||||
#if MQTTCLIENT_QOS2
|
||||
pubrel = false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = publish(len, timer, qos);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos, bool retained)
|
||||
{
|
||||
unsigned short id = 0; // dummy - not used for anything
|
||||
return publish(topicName, payload, payloadlen, id, qos, retained);
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, Message& message)
|
||||
{
|
||||
return publish(topicName, message.payload, message.payloadlen, message.qos, message.retained);
|
||||
}
|
||||
|
||||
|
||||
template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
|
||||
int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::disconnect()
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer = Timer(command_timeout_ms); // we might wait for incomplete incoming publishes to complete
|
||||
int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE);
|
||||
if (len > 0)
|
||||
rc = sendPacket(len, timer); // send the disconnect packet
|
||||
|
||||
if (cleansession)
|
||||
cleanSession();
|
||||
else
|
||||
isconnected = false;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
136
arduino/libraries/Temboo/src/utility/MQTTConnect.h
Normal file
136
arduino/libraries/Temboo/src/utility/MQTTConnect.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTCONNECT_H_
|
||||
#define MQTTCONNECT_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char all; /**< all connect flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int username : 1; /**< 3.1 user name */
|
||||
unsigned int password : 1; /**< 3.1 password */
|
||||
unsigned int willRetain : 1; /**< will retain setting */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
unsigned int will : 1; /**< will flag */
|
||||
unsigned int cleansession : 1; /**< clean session flag */
|
||||
unsigned int : 1; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int : 1; /**< unused */
|
||||
unsigned int cleansession : 1; /**< cleansession flag */
|
||||
unsigned int will : 1; /**< will flag */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
unsigned int willRetain : 1; /**< will retain setting */
|
||||
unsigned int password : 1; /**< 3.1 password */
|
||||
unsigned int username : 1; /**< 3.1 user name */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTConnectFlags; /**< connect flags byte */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Defines the MQTT "Last Will and Testament" (LWT) settings for
|
||||
* the connect packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTW. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** The LWT topic to which the LWT message will be published. */
|
||||
MQTTString topicName;
|
||||
/** The LWT payload. */
|
||||
MQTTString message;
|
||||
/**
|
||||
* The retained flag for the LWT message (see MQTTAsync_message.retained).
|
||||
*/
|
||||
unsigned char retained;
|
||||
/**
|
||||
* The quality of service setting for the LWT message (see
|
||||
* MQTTAsync_message.qos and @ref qos).
|
||||
*/
|
||||
char qos;
|
||||
} MQTTPacket_willOptions;
|
||||
|
||||
|
||||
#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 }
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTC. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1
|
||||
*/
|
||||
unsigned char MQTTVersion;
|
||||
MQTTString clientID;
|
||||
unsigned short keepAliveInterval;
|
||||
unsigned char cleansession;
|
||||
unsigned char willFlag;
|
||||
MQTTPacket_willOptions will;
|
||||
MQTTString username;
|
||||
MQTTString password;
|
||||
} MQTTPacket_connectData;
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char all; /**< all connack flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int sessionpresent : 1; /**< session present flag */
|
||||
unsigned int : 7; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int : 7; /**< unused */
|
||||
unsigned int sessionpresent : 1; /**< session present flag */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTConnackFlags; /**< connack flags byte */
|
||||
|
||||
#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \
|
||||
MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} }
|
||||
|
||||
DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options);
|
||||
DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||
DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen);
|
||||
|
||||
DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen);
|
||||
DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen);
|
||||
|
||||
#endif /* MQTTCONNECT_H_ */
|
||||
214
arduino/libraries/Temboo/src/utility/MQTTConnectClient.c
Normal file
214
arduino/libraries/Temboo/src/utility/MQTTConnectClient.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
|
||||
* @param options the options to be used to build the connect packet
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
if (options->MQTTVersion == 3)
|
||||
len = 12; /* variable depending on MQTT or MQIsdp */
|
||||
else if (options->MQTTVersion == 4)
|
||||
len = 10;
|
||||
|
||||
len += MQTTstrlen(options->clientID)+2;
|
||||
if (options->willFlag)
|
||||
len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
|
||||
if (options->username.cstring || options->username.lenstring.data)
|
||||
len += MQTTstrlen(options->username)+2;
|
||||
if (options->password.cstring || options->password.lenstring.data)
|
||||
len += MQTTstrlen(options->password)+2;
|
||||
|
||||
FUNC_EXIT_RC(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the connect options into the buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param len the length in bytes of the supplied buffer
|
||||
* @param options the options to be used to build the connect packet
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
MQTTConnectFlags flags = {0};
|
||||
int len = 0;
|
||||
int rc = -1;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = CONNECT;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
|
||||
|
||||
if (options->MQTTVersion == 4)
|
||||
{
|
||||
writeCString(&ptr, "MQTT");
|
||||
writeChar(&ptr, (char) 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeCString(&ptr, "MQIsdp");
|
||||
writeChar(&ptr, (char) 3);
|
||||
}
|
||||
|
||||
flags.all = 0;
|
||||
flags.bits.cleansession = options->cleansession;
|
||||
flags.bits.will = (options->willFlag) ? 1 : 0;
|
||||
if (flags.bits.will)
|
||||
{
|
||||
flags.bits.willQoS = options->will.qos;
|
||||
flags.bits.willRetain = options->will.retained;
|
||||
}
|
||||
|
||||
if (options->username.cstring || options->username.lenstring.data)
|
||||
flags.bits.username = 1;
|
||||
if (options->password.cstring || options->password.lenstring.data)
|
||||
flags.bits.password = 1;
|
||||
|
||||
writeChar(&ptr, flags.all);
|
||||
writeInt(&ptr, options->keepAliveInterval);
|
||||
writeMQTTString(&ptr, options->clientID);
|
||||
if (options->willFlag)
|
||||
{
|
||||
writeMQTTString(&ptr, options->will.topicName);
|
||||
writeMQTTString(&ptr, options->will.message);
|
||||
}
|
||||
if (flags.bits.username)
|
||||
writeMQTTString(&ptr, options->username);
|
||||
if (flags.bits.password)
|
||||
writeMQTTString(&ptr, options->password);
|
||||
|
||||
rc = ptr - buf;
|
||||
|
||||
exit: FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into connack data - return code
|
||||
* @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
|
||||
* @param connack_rc returned integer value of the connack return code
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param len the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
MQTTConnackFlags flags = {0};
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != CONNACK)
|
||||
goto exit;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
|
||||
flags.all = readChar(&curdata);
|
||||
*sessionPresent = flags.bits.sessionpresent;
|
||||
*connack_rc = readChar(&curdata);
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @param packettype the message type
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = -1;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = packettype;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_disconnect(unsigned char* buf, int buflen)
|
||||
{
|
||||
return MQTTSerialize_zero(buf, buflen, DISCONNECT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pingreq(unsigned char* buf, int buflen)
|
||||
{
|
||||
return MQTTSerialize_zero(buf, buflen, PINGREQ);
|
||||
}
|
||||
148
arduino/libraries/Temboo/src/utility/MQTTConnectServer.c
Normal file
148
arduino/libraries/Temboo/src/utility/MQTTConnectServer.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include <string.h>
|
||||
|
||||
#define min(a, b) ((a < b) ? a : b)
|
||||
|
||||
|
||||
/**
|
||||
* Validates MQTT protocol name and version combinations
|
||||
* @param protocol the MQTT protocol name as an MQTTString
|
||||
* @param version the MQTT protocol version number, as in the connect packet
|
||||
* @return correct MQTT combination? 1 is true, 0 is false
|
||||
*/
|
||||
int MQTTPacket_checkVersion(MQTTString* protocol, int version)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp",
|
||||
min(6, protocol->lenstring.len)) == 0)
|
||||
rc = 1;
|
||||
else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT",
|
||||
min(4, protocol->lenstring.len)) == 0)
|
||||
rc = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into connect data structure
|
||||
* @param data the connect data structure to be filled out
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param len the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
MQTTConnectFlags flags = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = &buf[len];
|
||||
int rc = 0;
|
||||
MQTTString Protocol;
|
||||
int version;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != CONNECT)
|
||||
goto exit;
|
||||
|
||||
curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */
|
||||
|
||||
if (!readMQTTLenString(&Protocol, &curdata, enddata) ||
|
||||
enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
|
||||
goto exit;
|
||||
|
||||
version = (int)readChar(&curdata); /* Protocol version */
|
||||
/* If we don't recognize the protocol version, we don't parse the connect packet on the
|
||||
* basis that we don't know what the format will be.
|
||||
*/
|
||||
if (MQTTPacket_checkVersion(&Protocol, version))
|
||||
{
|
||||
flags.all = readChar(&curdata);
|
||||
data->cleansession = flags.bits.cleansession;
|
||||
data->keepAliveInterval = readInt(&curdata);
|
||||
if (!readMQTTLenString(&data->clientID, &curdata, enddata))
|
||||
goto exit;
|
||||
data->willFlag = flags.bits.will;
|
||||
if (flags.bits.will)
|
||||
{
|
||||
data->will.qos = flags.bits.willQoS;
|
||||
data->will.retained = flags.bits.willRetain;
|
||||
if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) ||
|
||||
!readMQTTLenString(&data->will.message, &curdata, enddata))
|
||||
goto exit;
|
||||
}
|
||||
if (flags.bits.username)
|
||||
{
|
||||
if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata))
|
||||
goto exit; /* username flag set, but no username supplied - invalid */
|
||||
if (flags.bits.password &&
|
||||
(enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata)))
|
||||
goto exit; /* password flag set, but no password supplied - invalid */
|
||||
}
|
||||
else if (flags.bits.password)
|
||||
goto exit; /* password flag set without username - invalid */
|
||||
rc = 1;
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the connack packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param connack_rc the integer connack return code to be used
|
||||
* @param sessionPresent the MQTT 3.1.1 sessionPresent flag
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
MQTTConnackFlags flags = {0};
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = CONNACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
|
||||
flags.all = 0;
|
||||
flags.bits.sessionpresent = sessionPresent;
|
||||
writeChar(&ptr, flags.all);
|
||||
writeChar(&ptr, connack_rc);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
107
arduino/libraries/Temboo/src/utility/MQTTDeserializePublish.c
Normal file
107
arduino/libraries/Temboo/src/utility/MQTTDeserializePublish.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include <string.h>
|
||||
|
||||
#define min(a, b) ((a < b) ? 1 : 0)
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into publish data
|
||||
* @param dup returned integer - the MQTT dup flag
|
||||
* @param qos returned integer - the MQTT QoS value
|
||||
* @param retained returned integer - the MQTT retained flag
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param topicName returned MQTTString - the MQTT topic in the publish
|
||||
* @param payload returned byte buffer - the MQTT publish payload
|
||||
* @param payloadlen returned integer - the length of the MQTT payload
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success
|
||||
*/
|
||||
int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||
unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != PUBLISH)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
*qos = header.bits.qos;
|
||||
*retained = header.bits.retain;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
if (!readMQTTLenString(topicName, &curdata, enddata) ||
|
||||
enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
|
||||
goto exit;
|
||||
|
||||
if (*qos > 0)
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*payloadlen = enddata - curdata;
|
||||
*payload = curdata;
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into an ack
|
||||
* @param packettype returned integer - the MQTT packet type
|
||||
* @param dup returned integer - the MQTT dup flag
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
*dup = header.bits.dup;
|
||||
*packettype = header.bits.type;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
256
arduino/libraries/Temboo/src/utility/MQTTFormat.c
Normal file
256
arduino/libraries/Temboo/src/utility/MQTTFormat.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
const char* MQTTPacket_names[] =
|
||||
{
|
||||
"RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
|
||||
"PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
|
||||
"PINGREQ", "PINGRESP", "DISCONNECT"
|
||||
};
|
||||
|
||||
|
||||
const char* MQTTPacket_getName(unsigned short packetid)
|
||||
{
|
||||
return MQTTPacket_names[packetid];
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data)
|
||||
{
|
||||
int strindex = 0;
|
||||
|
||||
strindex = snprintf(strbuf, strbuflen,
|
||||
"CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d",
|
||||
(int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data,
|
||||
(int)data->cleansession, data->keepAliveInterval);
|
||||
if (data->willFlag)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", will QoS %d, will retain %d, will topic %.*s, will message %.*s",
|
||||
data->will.qos, data->will.retained,
|
||||
data->will.topicName.lenstring.len, data->will.topicName.lenstring.data,
|
||||
data->will.message.lenstring.len, data->will.message.lenstring.data);
|
||||
if (data->username.lenstring.data && data->username.lenstring.len > 0)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", user name %.*s", data->username.lenstring.len, data->username.lenstring.data);
|
||||
if (data->password.lenstring.data && data->password.lenstring.len > 0)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", password %.*s", data->password.lenstring.len, data->password.lenstring.data);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
|
||||
unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen,
|
||||
"PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s",
|
||||
dup, qos, retained, packetid,
|
||||
(topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data,
|
||||
payloadlen, (payloadlen < 20) ? payloadlen : 20, payload);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid);
|
||||
if (dup)
|
||||
strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[])
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d",
|
||||
dup, packetid, count,
|
||||
topicFilters[0].lenstring.len, topicFilters[0].lenstring.data,
|
||||
requestedQoSs[0]);
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs)
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]);
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[])
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s",
|
||||
dup, packetid, count,
|
||||
topicFilters[0].lenstring.len, topicFilters[0].lenstring.data);
|
||||
}
|
||||
|
||||
|
||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
|
||||
{
|
||||
int index = 0;
|
||||
int rem_length = 0;
|
||||
MQTTHeader header = {0};
|
||||
|
||||
header.byte = buf[index++];
|
||||
index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
|
||||
|
||||
switch (header.bits.type)
|
||||
{
|
||||
case CONNACK:
|
||||
{
|
||||
unsigned char sessionPresent, connack_rc;
|
||||
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1)
|
||||
MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent);
|
||||
}
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
unsigned char dup, retained, *payload;
|
||||
unsigned short packetid;
|
||||
int qos, payloadlen;
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
|
||||
&payload, &payloadlen, buf, buflen) == 1)
|
||||
MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
|
||||
topicName, payload, payloadlen);
|
||||
}
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
{
|
||||
unsigned char packettype, dup;
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
|
||||
MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
|
||||
}
|
||||
break;
|
||||
case SUBACK:
|
||||
{
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
int grantedQoSs[1];
|
||||
if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1)
|
||||
MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs);
|
||||
}
|
||||
break;
|
||||
case UNSUBACK:
|
||||
{
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1)
|
||||
MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid);
|
||||
}
|
||||
break;
|
||||
case PINGREQ:
|
||||
case PINGRESP:
|
||||
case DISCONNECT:
|
||||
snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
|
||||
break;
|
||||
}
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
|
||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
|
||||
{
|
||||
int index = 0;
|
||||
int rem_length = 0;
|
||||
MQTTHeader header = {0};
|
||||
|
||||
header.byte = buf[index++];
|
||||
index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
|
||||
|
||||
switch (header.bits.type)
|
||||
{
|
||||
case CONNECT:
|
||||
{
|
||||
MQTTPacket_connectData data;
|
||||
int rc;
|
||||
if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1)
|
||||
MQTTStringFormat_connect(strbuf, strbuflen, &data);
|
||||
}
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
unsigned char dup, retained, *payload;
|
||||
unsigned short packetid;
|
||||
int qos, payloadlen;
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
|
||||
&payload, &payloadlen, buf, buflen) == 1)
|
||||
MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
|
||||
topicName, payload, payloadlen);
|
||||
}
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
{
|
||||
unsigned char packettype, dup;
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
|
||||
MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
|
||||
}
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
{
|
||||
unsigned char dup;
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
MQTTString topicFilters[1];
|
||||
int requestedQoSs[1];
|
||||
if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count,
|
||||
topicFilters, requestedQoSs, buf, buflen) == 1)
|
||||
MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);;
|
||||
}
|
||||
break;
|
||||
case UNSUBSCRIBE:
|
||||
{
|
||||
unsigned char dup;
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
MQTTString topicFilters[1];
|
||||
if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1)
|
||||
MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters);
|
||||
}
|
||||
break;
|
||||
case PINGREQ:
|
||||
case PINGRESP:
|
||||
case DISCONNECT:
|
||||
snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
|
||||
break;
|
||||
}
|
||||
strbuf[strbuflen] = '\0';
|
||||
return strbuf;
|
||||
}
|
||||
37
arduino/libraries/Temboo/src/utility/MQTTFormat.h
Normal file
37
arduino/libraries/Temboo/src/utility/MQTTFormat.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTFORMAT_H)
|
||||
#define MQTTFORMAT_H
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
const char* MQTTPacket_getName(unsigned short packetid);
|
||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data);
|
||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
|
||||
unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid);
|
||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[]);
|
||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[]);
|
||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||
|
||||
#endif
|
||||
41
arduino/libraries/Temboo/src/utility/MQTTLogging.h
Normal file
41
arduino/libraries/Temboo/src/utility/MQTTLogging.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Modified for Arduino environment by Temboo
|
||||
* Joe Planisky - replaced fancy fprintf stuff with Serial.print(ln)
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTT_LOGGING_H)
|
||||
#define MQTT_LOGGING_H
|
||||
|
||||
#if !defined(DEBUG)
|
||||
#define DEBUG(msg) {Serial.print("DEBUG: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#if !defined(LOG)
|
||||
#define LOG(msg) {Serial.print("LOG: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#if !defined(WARN)
|
||||
#define WARN(msg) {Serial.print("WARN: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#if !defined(ERROR)
|
||||
#define ERROR() {Serial.print("ERROR: "); Serial.println(msg);}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
410
arduino/libraries/Temboo/src/utility/MQTTPacket.c
Normal file
410
arduino/libraries/Temboo/src/utility/MQTTPacket.c
Normal file
@@ -0,0 +1,410 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - non-blocking packet read functions for stream transport
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Encodes the message length according to the MQTT algorithm
|
||||
* @param buf the buffer into which the encoded data is written
|
||||
* @param length the length to be encoded
|
||||
* @return the number of bytes written to buffer
|
||||
*/
|
||||
int MQTTPacket_encode(unsigned char* buf, int length)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
do
|
||||
{
|
||||
char d = length % 128;
|
||||
length /= 128;
|
||||
/* if there are more digits to encode, set the top bit of this digit */
|
||||
if (length > 0)
|
||||
d |= 0x80;
|
||||
buf[rc++] = d;
|
||||
} while (length > 0);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm
|
||||
* @param getcharfn pointer to function to read the next character from the data source
|
||||
* @param value the decoded length returned
|
||||
* @return the number of bytes read from the socket
|
||||
*/
|
||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
|
||||
{
|
||||
unsigned char c;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||
|
||||
FUNC_ENTRY;
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = (*getcharfn)(&c, 1);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
exit:
|
||||
FUNC_EXIT_RC(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPacket_len(int rem_len)
|
||||
{
|
||||
rem_len += 1; /* header byte */
|
||||
|
||||
/* now remaining_length field */
|
||||
if (rem_len < 128)
|
||||
rem_len += 1;
|
||||
else if (rem_len < 16384)
|
||||
rem_len += 2;
|
||||
else if (rem_len < 2097151)
|
||||
rem_len += 3;
|
||||
else
|
||||
rem_len += 4;
|
||||
return rem_len;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char* bufptr;
|
||||
|
||||
int bufchar(unsigned char* c, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
*c = *bufptr++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value)
|
||||
{
|
||||
bufptr = buf;
|
||||
return MQTTPacket_decode(bufchar, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates an integer from two bytes read from the input buffer
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the integer value calculated
|
||||
*/
|
||||
int readInt(unsigned char** pptr)
|
||||
{
|
||||
unsigned char* ptr = *pptr;
|
||||
int len = 256*(*ptr) + (*(ptr+1));
|
||||
*pptr += 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads one character from the input buffer.
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the character read
|
||||
*/
|
||||
char readChar(unsigned char** pptr)
|
||||
{
|
||||
char c = **pptr;
|
||||
(*pptr)++;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes one character to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param c the character to write
|
||||
*/
|
||||
void writeChar(unsigned char** pptr, char c)
|
||||
{
|
||||
**pptr = c;
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes an integer as 2 bytes to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param anInt the integer to write
|
||||
*/
|
||||
void writeInt(unsigned char** pptr, int anInt)
|
||||
{
|
||||
**pptr = (unsigned char)(anInt / 256);
|
||||
(*pptr)++;
|
||||
**pptr = (unsigned char)(anInt % 256);
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param string the C string to write
|
||||
*/
|
||||
void writeCString(unsigned char** pptr, const char* string)
|
||||
{
|
||||
int len = strlen(string);
|
||||
writeInt(pptr, len);
|
||||
memcpy(*pptr, string, len);
|
||||
*pptr += len;
|
||||
}
|
||||
|
||||
|
||||
int getLenStringLen(char* ptr)
|
||||
{
|
||||
int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
|
||||
{
|
||||
if (mqttstring.lenstring.len > 0)
|
||||
{
|
||||
writeInt(pptr, mqttstring.lenstring.len);
|
||||
memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
|
||||
*pptr += mqttstring.lenstring.len;
|
||||
}
|
||||
else if (mqttstring.cstring)
|
||||
writeCString(pptr, mqttstring.cstring);
|
||||
else
|
||||
writeInt(pptr, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mqttstring the MQTTString structure into which the data is to be read
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param enddata pointer to the end of the data: do not read beyond
|
||||
* @return 1 if successful, 0 if not
|
||||
*/
|
||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* the first two bytes are the length of the string */
|
||||
if (enddata - (*pptr) > 1) /* enough length to read the integer? */
|
||||
{
|
||||
mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */
|
||||
if (&(*pptr)[mqttstring->lenstring.len] <= enddata)
|
||||
{
|
||||
mqttstring->lenstring.data = (char*)*pptr;
|
||||
*pptr += mqttstring->lenstring.len;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
mqttstring->cstring = NULL;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string
|
||||
* @param mqttstring the string to return the length of
|
||||
* @return the length of the string
|
||||
*/
|
||||
int MQTTstrlen(MQTTString mqttstring)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (mqttstring.cstring)
|
||||
rc = strlen(mqttstring.cstring);
|
||||
else
|
||||
rc = mqttstring.lenstring.len;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares an MQTTString to a C string
|
||||
* @param a the MQTTString to compare
|
||||
* @param bptr the C string to compare
|
||||
* @return boolean - equal or not
|
||||
*/
|
||||
int MQTTPacket_equals(MQTTString* a, char* bptr)
|
||||
{
|
||||
int alen = 0,
|
||||
blen = 0;
|
||||
char *aptr;
|
||||
|
||||
if (a->cstring)
|
||||
{
|
||||
aptr = a->cstring;
|
||||
alen = strlen(a->cstring);
|
||||
}
|
||||
else
|
||||
{
|
||||
aptr = a->lenstring.data;
|
||||
alen = a->lenstring.len;
|
||||
}
|
||||
blen = strlen(bptr);
|
||||
|
||||
return (alen == blen) && (strncmp(aptr, bptr, alen) == 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to read packet data from some source into a buffer
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param getfn pointer to a function which will read any number of bytes from the needed source
|
||||
* @return integer MQTT packet type, or -1 on error
|
||||
* @note the whole message must fit into the caller's buffer
|
||||
*/
|
||||
int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
|
||||
{
|
||||
int rc = -1;
|
||||
MQTTHeader header = {0};
|
||||
int len = 0;
|
||||
int rem_len = 0;
|
||||
|
||||
/* 1. read the header byte. This has the packet type in it */
|
||||
if ((*getfn)(buf, 1) != 1)
|
||||
goto exit;
|
||||
|
||||
len = 1;
|
||||
/* 2. read the remaining length. This is variable in itself */
|
||||
MQTTPacket_decode(getfn, &rem_len);
|
||||
len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */
|
||||
|
||||
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if((rem_len + len) > buflen)
|
||||
goto exit;
|
||||
if ((*getfn)(buf + len, rem_len) != rem_len)
|
||||
goto exit;
|
||||
|
||||
header.byte = buf[0];
|
||||
rc = header.bits.type;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm, non-blocking
|
||||
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||
* @param value the decoded length returned
|
||||
* @return integer the number of bytes read from the socket, 0 for call again, or -1 on error
|
||||
*/
|
||||
static int MQTTPacket_decodenb(MQTTTransport *trp)
|
||||
{
|
||||
unsigned char c;
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if(trp->len == 0){ /* initialize on first call */
|
||||
trp->multiplier = 1;
|
||||
trp->rem_len = 0;
|
||||
}
|
||||
do {
|
||||
int frc;
|
||||
if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
goto exit;
|
||||
if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0){
|
||||
rc = 0;
|
||||
goto exit;
|
||||
}
|
||||
trp->rem_len += (c & 127) * trp->multiplier;
|
||||
trp->multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
rc = trp->len;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to read packet data from some source into a buffer, non-blocking
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||
* @return integer MQTT packet type, 0 for call again, or -1 on error
|
||||
* @note the whole message must fit into the caller's buffer
|
||||
*/
|
||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp)
|
||||
{
|
||||
int rc = -1, frc;
|
||||
MQTTHeader header = {0};
|
||||
|
||||
switch(trp->state){
|
||||
default:
|
||||
trp->state = 0;
|
||||
/*FALLTHROUGH*/
|
||||
case 0:
|
||||
/* read the header byte. This has the packet type in it */
|
||||
if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0)
|
||||
return 0;
|
||||
trp->len = 0;
|
||||
++trp->state;
|
||||
/*FALLTHROUGH*/
|
||||
/* read the remaining length. This is variable in itself */
|
||||
case 1:
|
||||
if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR)
|
||||
goto exit;
|
||||
if(frc == 0)
|
||||
return 0;
|
||||
trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */
|
||||
if((trp->rem_len + trp->len) > buflen)
|
||||
goto exit;
|
||||
++trp->state;
|
||||
/*FALLTHROUGH*/
|
||||
case 2:
|
||||
/* read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0)
|
||||
return 0;
|
||||
trp->rem_len -= frc;
|
||||
trp->len += frc;
|
||||
if(trp->rem_len)
|
||||
return 0;
|
||||
|
||||
header.byte = buf[0];
|
||||
rc = header.bits.type;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
trp->state = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
133
arduino/libraries/Temboo/src/utility/MQTTPacket.h
Normal file
133
arduino/libraries/Temboo/src/utility/MQTTPacket.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTPACKET_H_
|
||||
#define MQTTPACKET_H_
|
||||
|
||||
#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(WIN32_DLL) || defined(WIN64_DLL)
|
||||
#define DLLImport __declspec(dllimport)
|
||||
#define DLLExport __declspec(dllexport)
|
||||
#elif defined(LINUX_SO)
|
||||
#define DLLImport extern
|
||||
#define DLLExport __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define DLLImport
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
enum errors
|
||||
{
|
||||
MQTTPACKET_BUFFER_TOO_SHORT = -2,
|
||||
MQTTPACKET_READ_ERROR = -1,
|
||||
MQTTPACKET_READ_COMPLETE
|
||||
};
|
||||
|
||||
enum msgTypes
|
||||
{
|
||||
CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
|
||||
PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
|
||||
PINGREQ, PINGRESP, DISCONNECT
|
||||
};
|
||||
|
||||
/**
|
||||
* Bitfields for the MQTT header byte.
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
unsigned char byte; /**< the whole byte */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
unsigned int dup : 1; /**< DUP flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
unsigned int retain : 1; /**< retained flag bit */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int retain : 1; /**< retained flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
unsigned int dup : 1; /**< DUP flag bit */
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTHeader;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
char* data;
|
||||
} MQTTLenString;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* cstring;
|
||||
MQTTLenString lenstring;
|
||||
} MQTTString;
|
||||
|
||||
#define MQTTString_initializer {NULL, {0, NULL}}
|
||||
|
||||
int MQTTstrlen(MQTTString mqttstring);
|
||||
|
||||
#include "MQTTConnect.h"
|
||||
#include "MQTTPublish.h"
|
||||
#include "MQTTSubscribe.h"
|
||||
#include "MQTTUnsubscribe.h"
|
||||
#include "MQTTFormat.h"
|
||||
|
||||
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid);
|
||||
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen);
|
||||
|
||||
int MQTTPacket_len(int rem_len);
|
||||
int MQTTPacket_equals(MQTTString* a, char* b);
|
||||
|
||||
int MQTTPacket_encode(unsigned char* buf, int length);
|
||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value);
|
||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value);
|
||||
|
||||
int readInt(unsigned char** pptr);
|
||||
char readChar(unsigned char** pptr);
|
||||
void writeChar(unsigned char** pptr, char c);
|
||||
void writeInt(unsigned char** pptr, int anInt);
|
||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata);
|
||||
void writeCString(unsigned char** pptr, const char* string);
|
||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring);
|
||||
|
||||
DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int));
|
||||
|
||||
typedef struct {
|
||||
int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */
|
||||
void *sck; /* pointer to whatever the system may use to identify the transport */
|
||||
int multiplier;
|
||||
int rem_len;
|
||||
int len;
|
||||
char state;
|
||||
}MQTTTransport;
|
||||
|
||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp);
|
||||
|
||||
#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* MQTTPACKET_H_ */
|
||||
38
arduino/libraries/Temboo/src/utility/MQTTPublish.h
Normal file
38
arduino/libraries/Temboo/src/utility/MQTTPublish.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTPUBLISH_H_
|
||||
#define MQTTPUBLISH_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||
MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||
|
||||
DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||
unsigned char** payload, int* payloadlen, unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid);
|
||||
DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
|
||||
#endif /* MQTTPUBLISH_H_ */
|
||||
168
arduino/libraries/Temboo/src/utility/MQTTSerializePublish.c
Normal file
168
arduino/libraries/Temboo/src/utility/MQTTSerializePublish.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT publish packet that would be produced using the supplied parameters
|
||||
* @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
|
||||
* @param topicName the topic name to be used in the publish
|
||||
* @param payloadlen the length of the payload to be sent
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
len += 2 + MQTTstrlen(topicName) + payloadlen;
|
||||
if (qos > 0)
|
||||
len += 2; /* packetid */
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied publish data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param qos integer - the MQTT QoS value
|
||||
* @param retained integer - the MQTT retained flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param topicName MQTTString - the MQTT topic in the publish
|
||||
* @param payload byte buffer - the MQTT publish payload
|
||||
* @param payloadlen integer - the length of the MQTT payload
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||
MQTTString topicName, unsigned char* payload, int payloadlen)
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.bits.type = PUBLISH;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = qos;
|
||||
header.bits.retain = retained;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeMQTTString(&ptr, topicName);
|
||||
|
||||
if (qos > 0)
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
memcpy(ptr, payload, payloadlen);
|
||||
ptr += payloadlen;
|
||||
|
||||
rc = ptr - buf;
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the ack packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param type the MQTT packet type
|
||||
* @param dup the MQTT dup flag
|
||||
* @param packetid the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 4)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.bits.type = packettype;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 0;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
writeInt(&ptr, packetid);
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a puback packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBACK, packetid, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a pubrel packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBREL, packetid, dup);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a pubrel packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBCOMP, packetid, 0);
|
||||
}
|
||||
|
||||
|
||||
39
arduino/libraries/Temboo/src/utility/MQTTSubscribe.h
Normal file
39
arduino/libraries/Temboo/src/utility/MQTTSubscribe.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTSUBSCRIBE_H_
|
||||
#define MQTTSUBSCRIBE_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[], int requestedQoSs[]);
|
||||
|
||||
DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid,
|
||||
int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||
|
||||
DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len);
|
||||
|
||||
|
||||
#endif /* MQTTSUBSCRIBE_H_ */
|
||||
137
arduino/libraries/Temboo/src/utility/MQTTSubscribeClient.c
Normal file
137
arduino/libraries/Temboo/src/utility/MQTTSubscribeClient.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
|
||||
* @param count the number of topic filter strings in topicFilters
|
||||
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[])
|
||||
{
|
||||
int i;
|
||||
int len = 2; /* packetid */
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied subscribe data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied bufferr
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the topicFilters and reqQos arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param requestedQoSs - array of requested QoS
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[])
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = 0;
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = SUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
writeMQTTString(&ptr, topicFilters[i]);
|
||||
writeChar(&ptr, requestedQoSs[i]);
|
||||
}
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into suback data
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the grantedQoSs array
|
||||
* @param count returned integer - number of members in the grantedQoSs array
|
||||
* @param grantedQoSs returned array of integers - the granted qualities of service
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != SUBACK)
|
||||
goto exit;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (*count > maxcount)
|
||||
{
|
||||
rc = -1;
|
||||
goto exit;
|
||||
}
|
||||
grantedQoSs[(*count)++] = readChar(&curdata);
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
112
arduino/libraries/Temboo/src/utility/MQTTSubscribeServer.c
Normal file
112
arduino/libraries/Temboo/src/utility/MQTTSubscribeServer.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into subscribe data
|
||||
* @param dup integer returned - the MQTT dup flag
|
||||
* @param packetid integer returned - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
|
||||
* @param count - number of members in the topicFilters and requestedQoSs arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param requestedQoSs - array of requested QoS
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
|
||||
int requestedQoSs[], unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = -1;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != SUBSCRIBE)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
|
||||
goto exit;
|
||||
if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */
|
||||
goto exit;
|
||||
requestedQoSs[*count] = readChar(&curdata);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied suback data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the grantedQoSs array
|
||||
* @param grantedQoSs - array of granted QoS
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = -1;
|
||||
unsigned char *ptr = buf;
|
||||
int i;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2 + count)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = SUBACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
writeChar(&ptr, grantedQoSs[i]);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
38
arduino/libraries/Temboo/src/utility/MQTTUnsubscribe.h
Normal file
38
arduino/libraries/Temboo/src/utility/MQTTUnsubscribe.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTUNSUBSCRIBE_H_
|
||||
#define MQTTUNSUBSCRIBE_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[]);
|
||||
|
||||
DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[],
|
||||
unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
|
||||
DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len);
|
||||
|
||||
#endif /* MQTTUNSUBSCRIBE_H_ */
|
||||
106
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeClient.c
Normal file
106
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeClient.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
|
||||
* @param count the number of topic filter strings in topicFilters
|
||||
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[])
|
||||
{
|
||||
int i;
|
||||
int len = 2; /* packetid */
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the topicFilters array
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[])
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = -1;
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = UNSUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
writeMQTTString(&ptr, topicFilters[i]);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into unsuback data
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
|
||||
{
|
||||
unsigned char type = 0;
|
||||
unsigned char dup = 0;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen);
|
||||
if (type == UNSUBACK)
|
||||
rc = 1;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
102
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeServer.c
Normal file
102
arduino/libraries/Temboo/src/utility/MQTTUnsubscribeServer.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into unsubscribe data
|
||||
* @param dup integer returned - the MQTT dup flag
|
||||
* @param packetid integer returned - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
|
||||
* @param count - number of members in the topicFilters and requestedQoSs arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
|
||||
unsigned char* buf, int len)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != UNSUBSCRIBE)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
|
||||
goto exit;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied unsuback data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = UNSUBACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
78
arduino/libraries/Temboo/src/utility/StackTrace.h
Normal file
78
arduino/libraries/Temboo/src/utility/StackTrace.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - fix for bug #434081
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef STACKTRACE_H_
|
||||
#define STACKTRACE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#define NOSTACKTRACE 1
|
||||
|
||||
#if defined(NOSTACKTRACE)
|
||||
#define FUNC_ENTRY
|
||||
#define FUNC_ENTRY_NOLOG
|
||||
#define FUNC_ENTRY_MED
|
||||
#define FUNC_ENTRY_MAX
|
||||
#define FUNC_EXIT
|
||||
#define FUNC_EXIT_NOLOG
|
||||
#define FUNC_EXIT_MED
|
||||
#define FUNC_EXIT_MAX
|
||||
#define FUNC_EXIT_RC(x)
|
||||
#define FUNC_EXIT_MED_RC(x)
|
||||
#define FUNC_EXIT_MAX_RC(x)
|
||||
|
||||
#else
|
||||
|
||||
#if defined(WIN32)
|
||||
#define inline __inline
|
||||
#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
|
||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
|
||||
#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
|
||||
#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
|
||||
#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
|
||||
#else
|
||||
#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
|
||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
|
||||
#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
|
||||
#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
|
||||
#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
|
||||
|
||||
void StackTrace_entry(const char* name, int line, int trace);
|
||||
void StackTrace_exit(const char* name, int line, void* return_value, int trace);
|
||||
|
||||
void StackTrace_printStack(FILE* dest);
|
||||
char* StackTrace_get(unsigned long);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* STACKTRACE_H_ */
|
||||
101
arduino/libraries/Temboo/src/utility/TembooCoAPIPStack.h
Normal file
101
arduino/libraries/Temboo/src/utility/TembooCoAPIPStack.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOCOAPIPSTACK_H_
|
||||
#define TEMBOOCOAPIPSTACK_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <Udp.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
class TembooCoAPIPStack
|
||||
{
|
||||
public:
|
||||
enum Error {
|
||||
SUCCESS = 0,
|
||||
ERROR_RESOLVING,
|
||||
ERROR_WRITING,
|
||||
ERROR_SENDING,
|
||||
ERROR_RECEIVING
|
||||
|
||||
};
|
||||
|
||||
|
||||
TembooCoAPIPStack(UDP& udp) : m_udp(udp) {}
|
||||
|
||||
|
||||
int sendDatagram(IPAddress address, uint16_t port, uint8_t* data, size_t len) {
|
||||
if (0 == m_udp.beginPacket(address, port)) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP begin");
|
||||
return ERROR_RESOLVING;
|
||||
}
|
||||
|
||||
uint16_t c = m_udp.write(data, len);
|
||||
if (len != c) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP write");
|
||||
Serial.println(len);
|
||||
Serial.println(c);
|
||||
Serial.println((char*)data);
|
||||
|
||||
return ERROR_WRITING;
|
||||
}
|
||||
|
||||
if (0 == m_udp.endPacket()) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP send");
|
||||
return ERROR_SENDING;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int recvDatagram(uint8_t* buffer, size_t maxLen, int32_t& count) {
|
||||
memset(buffer, 0, maxLen);
|
||||
count = 0;
|
||||
if (m_udp.parsePacket() > 0) {
|
||||
count = m_udp.read(buffer, maxLen);
|
||||
if (count < 0) {
|
||||
TEMBOO_TRACE("ERROR: ");
|
||||
TEMBOO_TRACELN("UDP read");
|
||||
return ERROR_RECEIVING;
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
IPAddress getRemoteAddress() {
|
||||
return m_udp.remoteIP();
|
||||
}
|
||||
|
||||
uint16_t getRemotePort() {
|
||||
return m_udp.remotePort();
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
UDP& m_udp;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
203
arduino/libraries/Temboo/src/utility/TembooCoAPSession.cpp
Normal file
203
arduino/libraries/Temboo/src/utility/TembooCoAPSession.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "TembooCoAPSession.h"
|
||||
#include "tmbhmac.h"
|
||||
#include "DataFormatter.h"
|
||||
#include "TembooTags.h"
|
||||
|
||||
#define QSEND(x) if(!qsend((x))) { goto SendFailed;}
|
||||
|
||||
const size_t MAX_HOST_LEN = 64;
|
||||
const size_t MAX_URL_PATH_LEN = 100;
|
||||
|
||||
const char EOL[] = "\r\n";
|
||||
const char POST[] = "POST ";
|
||||
const char POSTAMBLE[] = " HTTP/1.0"; // Prevent host from using chunked encoding in response.
|
||||
const char HEADER_HOST[] = "Host: ";
|
||||
const char HEADER_ACCEPT[] = "Accept: application/xml";
|
||||
const char HEADER_ORG[] = "x-temboo-domain: /";
|
||||
const char HEADER_DOM[] = "/master";
|
||||
const char HEADER_CONTENT_LENGTH[] = "Content-Length: ";
|
||||
const char HEADER_TIME[] = "x-temboo-time: ";
|
||||
const char BASE_CHOREO_URI[] = "/arcturus-web/api-1.0/ar";
|
||||
const char HEADER_AUTH[] = "x-temboo-authentication: ";
|
||||
const char HEADER_CONTENT_TYPE[] = "Content-Type: text/plain";
|
||||
const char TEMBOO_DOMAIN[] = ".temboolive.com";
|
||||
const char SDK_ID[] = "?source_id=CoAPGateway_1";
|
||||
|
||||
|
||||
unsigned long TembooCoAPSession::s_timeOffset = 0;
|
||||
|
||||
TembooCoAPSession::TembooCoAPSession(TembooCoAPClient& client) : m_client(client) {
|
||||
}
|
||||
|
||||
|
||||
void TembooCoAPSession::setTime(unsigned long currentTime) {
|
||||
s_timeOffset = currentTime - (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
unsigned long TembooCoAPSession::getTime() {
|
||||
return s_timeOffset + (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int TembooCoAPSession::executeChoreo(
|
||||
uint16_t requestId,
|
||||
const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName){
|
||||
|
||||
DataFormatter fmt(&inputSet, &expressionSet, &sensorSet,&outputSet, &preset, &deviceType, &deviceName);
|
||||
char auth[HMAC_HEX_SIZE_BYTES + 1];
|
||||
char timeStr[11];
|
||||
char requestIdStr[5];
|
||||
uint16toa(requestId, requestIdStr);
|
||||
|
||||
// We use the current time-of-day as salt on the app key.
|
||||
// We keep track of time-of-day by getting the current time
|
||||
// from the server and applying an offset (the length of time
|
||||
// we've been running.)
|
||||
uint32toa((uint32_t)TembooCoAPSession::getTime(), timeStr);
|
||||
|
||||
getAuth(fmt, appKeyValue, timeStr, auth);
|
||||
|
||||
m_client.clearData();
|
||||
|
||||
QSEND(TAG_REQUEST_ID);
|
||||
QSEND(requestIdStr);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_ACCOUNT_NAME);
|
||||
QSEND(accountName);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_APP_KEY_NAME);
|
||||
QSEND(appKeyName);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_TIME);
|
||||
QSEND(timeStr);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_AUTH);
|
||||
QSEND(auth);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_CHOREO_ID);
|
||||
QSEND(path);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_DATA);
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
QSEND(fmt.next());
|
||||
}
|
||||
|
||||
QSEND(TAG_END_REQUEST);
|
||||
|
||||
return m_client.sendChoreoRequest();
|
||||
|
||||
SendFailed:
|
||||
TEMBOO_TRACELN("FAIL");
|
||||
return FAILURE;
|
||||
|
||||
}
|
||||
|
||||
uint16_t TembooCoAPSession::getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* result) const {
|
||||
|
||||
// We need the length of the data for other things, and
|
||||
// this method is a convenient place to calculate it.
|
||||
uint16_t len = 0;
|
||||
|
||||
HMAC hmac;
|
||||
|
||||
//combine the salt and the key and give it to the HMAC calculator
|
||||
size_t keyLength = strlen(appKeyValue) + strlen(salt);
|
||||
char key[keyLength + 1];
|
||||
strcpy(key, salt);
|
||||
strcat(key, appKeyValue);
|
||||
hmac.init((uint8_t*)key, keyLength);
|
||||
|
||||
// process the data a block at a time.
|
||||
uint8_t buffer[HMAC_BLOCK_SIZE_BYTES];
|
||||
int blockCount = 0;
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
uint8_t c = fmt.next();
|
||||
len++;
|
||||
buffer[blockCount++] = c;
|
||||
if (blockCount == HMAC_BLOCK_SIZE_BYTES) {
|
||||
hmac.process(buffer, blockCount);
|
||||
blockCount = 0;
|
||||
}
|
||||
}
|
||||
hmac.process(buffer, blockCount);
|
||||
|
||||
// Finalize the HMAC calculation and store the (ASCII HEX) value in *result.
|
||||
hmac.finishHex(result);
|
||||
|
||||
// Return the number of characters processed.
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
bool TembooCoAPSession::qsend(const char* s) {
|
||||
char c = *s++;
|
||||
bool rc = true;
|
||||
while(c != '\0' && rc) {
|
||||
rc = qsend(c);
|
||||
c = *s++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
bool TembooCoAPSession::qsend(char c) {
|
||||
// Never send a nul character.
|
||||
if ('\0' != c) {
|
||||
if (TembooCoAPClient::NO_ERROR == m_client.write(c)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
115
arduino/libraries/Temboo/src/utility/TembooCoAPSession.h
Normal file
115
arduino/libraries/Temboo/src/utility/TembooCoAPSession.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOCOAPSESSIONCLASS_H_
|
||||
#define TEMBOOCOAPSESSIONCLASS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
#include "TembooCoAPEdgeDevice.h"
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
#ifndef TEMBOO_SEND_QUEUE_SIZE
|
||||
#define TEMBOO_SEND_QUEUE_SIZE (1000)
|
||||
#endif
|
||||
|
||||
class ChoreoInputSet;
|
||||
class ChoreoOutputSet;
|
||||
class ChoreoPreset;
|
||||
class DataFormatter;
|
||||
|
||||
class TembooCoAPSession {
|
||||
public:
|
||||
|
||||
//TembooSession constructor
|
||||
//client: REQUIRED TembooCoAPClient client object.
|
||||
TembooCoAPSession(TembooCoAPClient& client);
|
||||
|
||||
//executeChoreo sends a choreo execution request to the Temboo system.
|
||||
// Does not wait for a response (that's a job for whoever owns the Client.)
|
||||
//accountName: the name of the user's account at Temboo.
|
||||
//appKeyName: the name of an application key in the user's account to use
|
||||
// for this execution (analogous to a user name).
|
||||
//appKeyValue: the value of the application key named in appKeyName.
|
||||
// Used to authenticate the user (analogous to a password)
|
||||
//path: The full path to the choreo to be executed (relative to the root of the
|
||||
// user's account.)
|
||||
//inputSet: the set of inputs needed by the choreo.
|
||||
// May be an empty ChoreoInputSet.
|
||||
//outputSet: the set of output filters to be applied to the choreo results.
|
||||
// May be an empty ChoreoOutputSet
|
||||
//preset: the ChoreoPreset to be used with the choreo execution.
|
||||
// May be an empty ChoreoPreset.
|
||||
int executeChoreo(
|
||||
uint16_t requestId,
|
||||
const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName);
|
||||
|
||||
// setTime sets the current time in Unix timestamp format. Needed for execution request authentication.
|
||||
// NOTE: This method is usually called by TembooChoreo.run() with the current time returned by
|
||||
// an error response from the Temboo system, thus automatically setting the time. However, it
|
||||
// MAY be called from user code if the particular board has a way of determining the current
|
||||
// time in the proper format.
|
||||
// currentTime: the number of seconds since 1970-01-01 00:00:00 UTC.
|
||||
static void setTime(unsigned long currentTime);
|
||||
|
||||
//getTime returns the current time in Unix timestamp format (seconds since 1970-01-01 00:00:00 UTC).
|
||||
// Only valid after setTime has been called.
|
||||
static unsigned long getTime();
|
||||
|
||||
enum Error {
|
||||
SUCCESS = 0,
|
||||
FAILURE
|
||||
};
|
||||
|
||||
private:
|
||||
TembooCoAPClient& m_client;
|
||||
static unsigned long s_timeOffset;
|
||||
|
||||
// calculate the authentication code value of the formatted request body
|
||||
// using the salted application key value as the key.
|
||||
// Returns the number of characters processed (i.e. the length of the request body)
|
||||
uint16_t getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* hexAuth) const;
|
||||
|
||||
|
||||
// queue an entire nul-terminated char array
|
||||
// from RAM one byte at a time.
|
||||
// returns true on success, false on failure
|
||||
bool qsend(const char*);
|
||||
|
||||
// queue a single character to be sent.
|
||||
// returns true on success, false on failure
|
||||
bool qsend(char);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
100
arduino/libraries/Temboo/src/utility/TembooDS18B20.cpp
Normal file
100
arduino/libraries/Temboo/src/utility/TembooDS18B20.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "TembooOneWire.h"
|
||||
#include "TembooDS18B20.h"
|
||||
|
||||
TembooOneWire* g_wire;
|
||||
|
||||
int tembooDS18B20Read(void* sensorConfig) {
|
||||
|
||||
uint8_t scratchPad[9];
|
||||
TembooDS18B20Config* config = (TembooDS18B20Config*) sensorConfig;
|
||||
|
||||
g_wire->reset();
|
||||
g_wire->select(config->channel);
|
||||
// tells device to convert temperature to scratchpad to read
|
||||
g_wire->write(0x44, config->parasite);
|
||||
// delay 750ms for conversion to occur, but wait for 1 second just in case
|
||||
delay(config->conversionDelayMillis);
|
||||
|
||||
// begin reading data
|
||||
g_wire->reset();
|
||||
// select the device we're requesting data from
|
||||
g_wire->select(config->channel);
|
||||
// send message to device requesting data
|
||||
g_wire->write(0xBE);
|
||||
|
||||
for(int i = 0; i < 9; i++) {
|
||||
scratchPad[i] = g_wire->read();
|
||||
}
|
||||
int16_t temp = (((int16_t)scratchPad[1]) << 8) + (int16_t)scratchPad[0];
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
void tembooDS18B20Begin(__attribute__((unused)) void* sensorConfig) {
|
||||
// all of the initializtion is done in init
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* tembooDS18B20GetSensorChannel(void* sensorConfig) {
|
||||
TembooDS18B20Config* config = (TembooDS18B20Config*) sensorConfig;
|
||||
return config->channel;
|
||||
}
|
||||
|
||||
int tembooDS18B20GetSensorPin(void* sensorConfig) {
|
||||
TembooDS18B20Config* config = (TembooDS18B20Config*) sensorConfig;
|
||||
return config->pin;
|
||||
}
|
||||
|
||||
void tembooDS18B20Init(TembooDS18B20Config* sensorConfig, TembooSensor* tembooSensor, int pin, int defaultValue) {
|
||||
sensorConfig->pin = pin;
|
||||
sensorConfig->conversionDelayMillis = 1000;
|
||||
|
||||
// create OneWire object
|
||||
if (NULL == g_wire) {
|
||||
g_wire = new TembooOneWire(pin);
|
||||
}
|
||||
// reset search before searching for device
|
||||
g_wire->reset_search();
|
||||
// find device connected to the wire
|
||||
g_wire->search(sensorConfig->channel);
|
||||
|
||||
if (g_wire->crc8(sensorConfig->channel, 7) != sensorConfig->channel[7]) {
|
||||
// invalid address
|
||||
}
|
||||
// begin to check if sensor is in parasite mode
|
||||
g_wire->reset();
|
||||
g_wire->select(sensorConfig->channel);
|
||||
g_wire->write(0xB4);
|
||||
sensorConfig->parasite = g_wire->read_bit() == 0;
|
||||
g_wire->reset();
|
||||
|
||||
tembooSensor->sensorConfig = sensorConfig;
|
||||
tembooSensor->read = tembooDS18B20Read;
|
||||
tembooSensor->begin = tembooDS18B20Begin;
|
||||
tembooSensor->write = NULL;
|
||||
tembooSensor->getSensorPin = tembooDS18B20GetSensorPin;
|
||||
tembooSensor->getSensorChannel = tembooDS18B20GetSensorChannel;
|
||||
tembooSensor->defaultValue = defaultValue;
|
||||
}
|
||||
42
arduino/libraries/Temboo/src/utility/TembooDS18B20.h
Normal file
42
arduino/libraries/Temboo/src/utility/TembooDS18B20.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef __TEMBOODS18B20__
|
||||
#define __TEMBOODS18B20__
|
||||
|
||||
#include "TembooWebSocketRequestHandles.h"
|
||||
|
||||
typedef struct TembooDS18B20Config{
|
||||
uint8_t channel[8];
|
||||
int pin;
|
||||
uint16_t conversionDelayMillis;
|
||||
bool parasite;
|
||||
} TembooDS18B20Config;
|
||||
|
||||
void tembooDS18B20Init(TembooDS18B20Config* sensorConfig, TembooSensor* tembooSensor, int pin, int defaultValue);
|
||||
void tembooDS18B20Begin(void* sensorConfig);
|
||||
uint8_t* tembooDS18B20GetSensorChannel(void* sensorConfig);
|
||||
int tembooDS18B20GetSensorPin(void* sensorConfig);
|
||||
int tembooDS18B20Read(void* sensorConfig);
|
||||
|
||||
|
||||
#endif /* defined(__TEMBOODS18B20__) */
|
||||
109
arduino/libraries/Temboo/src/utility/TembooGPIO.c
Normal file
109
arduino/libraries/Temboo/src/utility/TembooGPIO.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "TembooGPIO.h"
|
||||
|
||||
int tembooDigitalRead(void* sensorConfig) {
|
||||
TembooGPIOConfig* config = (TembooGPIOConfig*) sensorConfig;
|
||||
if (config->mode == OUTPUT) {
|
||||
return config->currentValue;
|
||||
}
|
||||
return digitalRead(config->pin);
|
||||
|
||||
}
|
||||
|
||||
void tembooDigitalWrite(void* sensorConfig, int val) {
|
||||
TembooGPIOConfig* config = (TembooGPIOConfig*) sensorConfig;
|
||||
digitalWrite(config->pin, val);
|
||||
config->currentValue = val;
|
||||
}
|
||||
|
||||
int tembooAnalogRead(void* sensorConfig) {
|
||||
TembooGPIOConfig* config = (TembooGPIOConfig*) sensorConfig;
|
||||
if (config->mode == OUTPUT) {
|
||||
return config->currentValue;
|
||||
}
|
||||
return analogRead(config->pin);
|
||||
}
|
||||
|
||||
void tembooAnalogWrite(void* sensorConfig, int val) {
|
||||
TembooGPIOConfig* config = (TembooGPIOConfig*) sensorConfig;
|
||||
analogWrite(config->pin, val);
|
||||
config->currentValue = val;
|
||||
}
|
||||
|
||||
void tembooGPIOBegin(__attribute__((unused)) void* sensorConfig) {
|
||||
// all of the initializtion is done in init
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* tembooGPIOGetSensorChannel(void* sensorConfig) {
|
||||
TembooGPIOConfig* config = (TembooGPIOConfig*) sensorConfig;
|
||||
return config->channel;
|
||||
}
|
||||
|
||||
int tembooGPIOGetSensorPin(void* sensorConfig) {
|
||||
TembooGPIOConfig* config = (TembooGPIOConfig*) sensorConfig;
|
||||
return config->pin;
|
||||
}
|
||||
|
||||
void tembooDigitalGPIOInit(TembooGPIOConfig* sensorConfig, TembooSensor* tembooSensor, int pin, int defaultValue, int mode) {
|
||||
sensorConfig->pin = pin;
|
||||
sensorConfig->channel = NULL;
|
||||
sensorConfig->mode = mode;
|
||||
pinMode(pin, mode);
|
||||
if (mode == OUTPUT) {
|
||||
// this will set the current value
|
||||
tembooDigitalWrite(sensorConfig, defaultValue);
|
||||
tembooSensor->write = tembooDigitalWrite;
|
||||
} else {
|
||||
sensorConfig->currentValue = tembooDigitalRead(sensorConfig);
|
||||
tembooSensor->write = NULL;
|
||||
}
|
||||
tembooSensor->sensorConfig = sensorConfig;
|
||||
tembooSensor->read = tembooDigitalRead;
|
||||
tembooSensor->begin = tembooGPIOBegin;
|
||||
tembooSensor->getSensorPin = tembooGPIOGetSensorPin;
|
||||
tembooSensor->getSensorChannel = tembooGPIOGetSensorChannel;
|
||||
tembooSensor->defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
void tembooAnalogGPIOInit(TembooGPIOConfig* sensorConfig, TembooSensor* tembooSensor, int pin, int defaultValue, int mode) {
|
||||
sensorConfig->pin = pin;
|
||||
sensorConfig->channel = NULL;
|
||||
sensorConfig->mode = mode;
|
||||
pinMode(pin, mode);
|
||||
if (mode == OUTPUT) {
|
||||
// this will set the current value
|
||||
tembooAnalogWrite(sensorConfig, defaultValue);
|
||||
tembooSensor->write = tembooAnalogWrite;
|
||||
} else {
|
||||
sensorConfig->currentValue = tembooAnalogRead(sensorConfig);
|
||||
tembooSensor->write = NULL;
|
||||
}
|
||||
tembooSensor->sensorConfig = sensorConfig;
|
||||
tembooSensor->read = tembooAnalogRead;
|
||||
tembooSensor->begin = tembooGPIOBegin;
|
||||
tembooSensor->getSensorPin = tembooGPIOGetSensorPin;
|
||||
tembooSensor->getSensorChannel = tembooGPIOGetSensorChannel;
|
||||
tembooSensor->defaultValue = defaultValue;
|
||||
}
|
||||
55
arduino/libraries/Temboo/src/utility/TembooGPIO.h
Normal file
55
arduino/libraries/Temboo/src/utility/TembooGPIO.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef __TEMBOOGPIO__
|
||||
#define __TEMBOOGPIO__
|
||||
|
||||
#include "TembooWebSocketRequestHandles.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct TembooGPIOConfig{
|
||||
uint8_t* channel;
|
||||
int pin;
|
||||
int currentValue;
|
||||
int mode;
|
||||
} TembooGPIOConfig;
|
||||
|
||||
void tembooGPIOBegin(void* sensorConfig);
|
||||
uint8_t* tembooGPIOGetSensorChannel(void* sensorConfig);
|
||||
int tembooGPIOGetSensorPin(void* sensorConfig);
|
||||
|
||||
void tembooDigitalGPIOInit(TembooGPIOConfig* sensorConfig, TembooSensor* tembooSensor, int pin, int defaultValue, int mode);
|
||||
int tembooDigitalRead(void* sensorConfig);
|
||||
void tembooDigitalWrite(void* sensorConfig, int val);
|
||||
|
||||
void tembooAnalogGPIOInit(TembooGPIOConfig* sensorConfig, TembooSensor* tembooSensor, int pin, int defaultValue, int mode);
|
||||
int tembooAnalogRead(void* sensorConfig);
|
||||
void tembooAnalogWrite(void* sensorConfig, int val);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* defined(__TEMBOOGPIO__) */
|
||||
48
arduino/libraries/Temboo/src/utility/TembooGlobal.c
Normal file
48
arduino/libraries/Temboo/src/utility/TembooGlobal.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
char* uint16toa(uint16_t value, char* dest) {
|
||||
return uint32toa(value, dest);
|
||||
}
|
||||
|
||||
char* uint32toa(uint32_t value, char* dest) {
|
||||
char* end = dest;
|
||||
do {
|
||||
*end++ = (value % 10) + '0';
|
||||
} while (value /= 10);
|
||||
*end = '\0';
|
||||
end--;
|
||||
|
||||
char c;
|
||||
char* begin = dest;
|
||||
while(end > begin) {
|
||||
c = *end;
|
||||
*end = *begin;
|
||||
*begin = c;
|
||||
end--;
|
||||
begin++;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
53
arduino/libraries/Temboo/src/utility/TembooGlobal.h
Normal file
53
arduino/libraries/Temboo/src/utility/TembooGlobal.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOGLOBAL_H_
|
||||
#define TEMBOOGLOBAL_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//#define TEMBOO_VERBOSE
|
||||
|
||||
#ifdef TEMBOO_VERBOSE
|
||||
#define TEMBOO_TRACE(x) Serial.print(x)
|
||||
#define TEMBOO_TRACE_BYTES(x,c) Serial.write((const uint8_t*)x,c)
|
||||
#define TEMBOO_TRACELN(x) Serial.println(x)
|
||||
#else
|
||||
#define TEMBOO_TRACE(x)
|
||||
#define TEMBOO_TRACE_BYTES(x,c)
|
||||
#define TEMBOO_TRACELN(x)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char* uint16toa(uint16_t value, char* dest);
|
||||
char* uint32toa(uint32_t value, char* dest);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
96
arduino/libraries/Temboo/src/utility/TembooMQTTIPStack.h
Normal file
96
arduino/libraries/Temboo/src/utility/TembooMQTTIPStack.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT edge device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOBASEIPSTACK_H_
|
||||
#define TEMBOOBASEIPSTACK_H_
|
||||
|
||||
#include <Client.h>
|
||||
|
||||
static const int WRITE_CHUNK_SIZE = 64;
|
||||
|
||||
class TembooMQTTIPStack
|
||||
{
|
||||
public:
|
||||
TembooMQTTIPStack(Client& client) : m_client(client) {
|
||||
}
|
||||
|
||||
int connect(const char* hostname, int port) {
|
||||
return m_client.connect(hostname, port);
|
||||
}
|
||||
|
||||
bool isConnected() {
|
||||
return m_client.connected();
|
||||
}
|
||||
|
||||
int disconnect() {
|
||||
m_client.stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read(unsigned char* buffer, int len, int timeoutMillis) {
|
||||
|
||||
m_client.setTimeout(timeoutMillis);
|
||||
size_t count = m_client.readBytes((char*)buffer, len);
|
||||
|
||||
// Not sure this is a totally good idea.
|
||||
// (Stream defaults to 1000 mS timeouts.)
|
||||
m_client.setTimeout(1000);
|
||||
return count;
|
||||
}
|
||||
|
||||
int write(unsigned char* buffer, int len, int timeout) {
|
||||
|
||||
// It's possible for write to get called with a 0 timeout,
|
||||
// in which case we want to make at least one attempt to send
|
||||
// the data.
|
||||
timeout = timeout == 0 ? 1 : timeout;
|
||||
m_client.setTimeout(timeout);
|
||||
|
||||
// Arduino's WiFiClient::write can accept only a limited
|
||||
// number of characters on each call, so we must make sure
|
||||
// we pass less than this limit. Note that this isn't necessary
|
||||
// for Arduino's EthernetClient. However, instead of trying to
|
||||
// differentiate between clients, we do it for both.
|
||||
// It SHOULDN'T impact performance in any significant way, but
|
||||
// if you're using a client that doesn't have this limitation and
|
||||
// the idea bothers you, feel free to eliminate the min() call or
|
||||
// else set WRITE_CHUNK_SIZE to a large number.
|
||||
|
||||
// Note that the caller (in this case, MQTT::Client::sendPacket)
|
||||
// is responsible for calling this method repeatedly until all
|
||||
// bytes have been written.
|
||||
|
||||
size_t count = m_client.write(buffer, min(len, WRITE_CHUNK_SIZE));
|
||||
|
||||
// Not sure this is a totally good idea.
|
||||
// (Stream defaults to 1000 mS timeouts.)
|
||||
m_client.setTimeout(1000);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
Client& m_client;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
193
arduino/libraries/Temboo/src/utility/TembooMQTTSession.cpp
Normal file
193
arduino/libraries/Temboo/src/utility/TembooMQTTSession.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT edge device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "TembooMQTTSession.h"
|
||||
#include "tmbhmac.h"
|
||||
#include "DataFormatter.h"
|
||||
#include "TembooTags.h"
|
||||
|
||||
#define QSEND(x) if(!qsend((x))) { goto SendFailed;}
|
||||
|
||||
unsigned long TembooMQTTSession::s_timeOffset = 0;
|
||||
|
||||
TembooMQTTSession::TembooMQTTSession(TembooMQTTClient& client) : m_client(client) {
|
||||
}
|
||||
|
||||
|
||||
void TembooMQTTSession::setTime(unsigned long currentTime) {
|
||||
s_timeOffset = currentTime - (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
unsigned long TembooMQTTSession::getTime() {
|
||||
return s_timeOffset + (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int TembooMQTTSession::executeChoreo(
|
||||
uint16_t requestId,
|
||||
const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName) {
|
||||
|
||||
DataFormatter fmt(&inputSet, &expressionSet, &sensorSet,&outputSet, &preset, &deviceType, &deviceName);
|
||||
char auth[HMAC_HEX_SIZE_BYTES + 1];
|
||||
char timeStr[11];
|
||||
char requestIdStr[5];
|
||||
uint16toa(requestId, requestIdStr);
|
||||
|
||||
// We use the current time-of-day as salt on the app key.
|
||||
// We keep track of time-of-day by getting the current time
|
||||
// from the server and applying an offset (the length of time
|
||||
// we've been running.)
|
||||
uint32toa((uint32_t)TembooMQTTSession::getTime(), timeStr);
|
||||
|
||||
getAuth(fmt, appKeyValue, timeStr, auth);
|
||||
|
||||
if (m_client.isConnected()) {
|
||||
|
||||
m_sendQueueDepth = 0;
|
||||
|
||||
QSEND(TAG_REQUEST_ID);
|
||||
QSEND(requestIdStr);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_ACCOUNT_NAME);
|
||||
QSEND(accountName);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_APP_KEY_NAME);
|
||||
QSEND(appKeyName);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_TIME);
|
||||
QSEND(timeStr);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_AUTH);
|
||||
QSEND(auth);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
QSEND(TAG_CHOREO_ID);
|
||||
QSEND(path);
|
||||
|
||||
QSEND(TAG_ELEMENT_SEPARATOR);
|
||||
|
||||
// Format and send the body of the request
|
||||
QSEND(TAG_DATA);
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
QSEND(fmt.next());
|
||||
}
|
||||
|
||||
QSEND(TAG_END_REQUEST);
|
||||
|
||||
return m_client.sendChoreoRequest(this->m_sendQueue, this->m_sendQueueDepth);
|
||||
|
||||
SendFailed:
|
||||
TEMBOO_TRACELN("FAIL");
|
||||
return TEMBOO_ERROR_FAILURE;
|
||||
|
||||
} else {
|
||||
TEMBOO_TRACELN("FAIL");
|
||||
return TEMBOO_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t TembooMQTTSession::getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* result) const {
|
||||
|
||||
// We need the length of the data for other things, and
|
||||
// this method is a convenient place to calculate it.
|
||||
uint16_t len = 0;
|
||||
|
||||
HMAC hmac;
|
||||
|
||||
//combine the salt and the key and give it to the HMAC calculator
|
||||
size_t keyLength = strlen(appKeyValue) + strlen(salt);
|
||||
char key[keyLength + 1];
|
||||
strcpy(key, salt);
|
||||
strcat(key, appKeyValue);
|
||||
hmac.init((uint8_t*)key, keyLength);
|
||||
|
||||
// process the data a block at a time.
|
||||
uint8_t buffer[HMAC_BLOCK_SIZE_BYTES];
|
||||
int blockCount = 0;
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
uint8_t c = fmt.next();
|
||||
len++;
|
||||
buffer[blockCount++] = c;
|
||||
if (blockCount == HMAC_BLOCK_SIZE_BYTES) {
|
||||
hmac.process(buffer, blockCount);
|
||||
blockCount = 0;
|
||||
}
|
||||
}
|
||||
hmac.process(buffer, blockCount);
|
||||
|
||||
// Finalize the HMAC calculation and store the (ASCII HEX) value in *result.
|
||||
hmac.finishHex(result);
|
||||
|
||||
// Return the number of characters processed.
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
bool TembooMQTTSession::qsend(const char* s) {
|
||||
char c = *s++;
|
||||
bool rc = true;
|
||||
while(c != '\0' && rc) {
|
||||
rc = qsend(c);
|
||||
c = *s++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
bool TembooMQTTSession::qsend(char c) {
|
||||
if (m_sendQueueDepth >= TEMBOO_SEND_QUEUE_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never send a nul character.
|
||||
if ('\0' != c) {
|
||||
m_sendQueue[m_sendQueueDepth++] = c;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
112
arduino/libraries/Temboo/src/utility/TembooMQTTSession.h
Normal file
112
arduino/libraries/Temboo/src/utility/TembooMQTTSession.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo MQTT edge device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOMQTTSESSIONCLASS_H_
|
||||
#define TEMBOOMQTTSESSIONCLASS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
#include "TembooMQTTEdgeDevice.h"
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
#ifndef TEMBOO_SEND_QUEUE_SIZE
|
||||
#define TEMBOO_SEND_QUEUE_SIZE (1000)
|
||||
#endif
|
||||
|
||||
class ChoreoInputSet;
|
||||
class ChoreoOutputSet;
|
||||
class ChoreoPreset;
|
||||
class DataFormatter;
|
||||
|
||||
class TembooMQTTSession {
|
||||
public:
|
||||
|
||||
//TembooMQTTSession constructor
|
||||
//client: REQUIRED TembooMQTTClient client object.
|
||||
TembooMQTTSession(TembooMQTTClient& client);
|
||||
|
||||
//executeChoreo sends a choreo execution request to the Temboo system.
|
||||
// Does not wait for a response (that's a job for whoever owns the Client.)
|
||||
//accountName: the name of the user's account at Temboo.
|
||||
//appKeyName: the name of an application key in the user's account to use
|
||||
// for this execution (analogous to a user name).
|
||||
//appKeyValue: the value of the application key named in appKeyName.
|
||||
// Used to authenticate the user (analogous to a password)
|
||||
//path: The full path to the choreo to be executed (relative to the root of the
|
||||
// user's account.)
|
||||
//inputSet: the set of inputs needed by the choreo.
|
||||
// May be an empty ChoreoInputSet.
|
||||
//outputSet: the set of output filters to be applied to the choreo results.
|
||||
// May be an empty ChoreoOutputSet
|
||||
//preset: the ChoreoPreset to be used with the choreo execution.
|
||||
// May be an empty ChoreoPreset.
|
||||
int executeChoreo(
|
||||
uint16_t requestId,
|
||||
const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName);
|
||||
|
||||
// setTime sets the current time in Unix timestamp format. Needed for execution request authentication.
|
||||
// NOTE: This method is usually called by TembooChoreo.run() with the current time returned by
|
||||
// an error response from the Temboo system, thus automatically setting the time. However, it
|
||||
// MAY be called from user code if the particular board has a way of determining the current
|
||||
// time in the proper format.
|
||||
// currentTime: the number of seconds since 1970-01-01 00:00:00 UTC.
|
||||
static void setTime(unsigned long currentTime);
|
||||
|
||||
//getTime returns the current time in Unix timestamp format (seconds since 1970-01-01 00:00:00 UTC).
|
||||
// Only valid after setTime has been called.
|
||||
static unsigned long getTime();
|
||||
|
||||
private:
|
||||
TembooMQTTClient& m_client;
|
||||
size_t m_sendQueueDepth;
|
||||
char m_sendQueue[TEMBOO_SEND_QUEUE_SIZE];
|
||||
static unsigned long s_timeOffset;
|
||||
|
||||
// calculate the authentication code value of the formatted request body
|
||||
// using the salted application key value as the key.
|
||||
// Returns the number of characters processed (i.e. the length of the request body)
|
||||
uint16_t getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* hexAuth) const;
|
||||
|
||||
|
||||
// queue an entire nul-terminated char array
|
||||
// from RAM one byte at a time.
|
||||
// returns true on success, false on failure
|
||||
bool qsend(const char*);
|
||||
|
||||
// queue a single character to be sent.
|
||||
// returns true on success, false on failure
|
||||
bool qsend(char);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
580
arduino/libraries/Temboo/src/utility/TembooOneWire.cpp
Normal file
580
arduino/libraries/Temboo/src/utility/TembooOneWire.cpp
Normal file
@@ -0,0 +1,580 @@
|
||||
/*
|
||||
Copyright (c) 2007, Jim Studt (original old version - many contributors since)
|
||||
|
||||
The latest version of this library may be found at:
|
||||
http://www.pjrc.com/teensy/td_libs_OneWire.html
|
||||
|
||||
OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since
|
||||
January 2010. At the time, it was in need of many bug fixes, but had
|
||||
been abandoned the original author (Jim Studt). None of the known
|
||||
contributors were interested in maintaining OneWire. Paul typically
|
||||
works on OneWire every 6 to 12 months. Patches usually wait that
|
||||
long. If anyone is interested in more actively maintaining OneWire,
|
||||
please contact Paul.
|
||||
|
||||
Temboo Version:
|
||||
Class renamed to resolve a distribution issue.
|
||||
|
||||
Version 2.3:
|
||||
Unknonw chip fallback mode, Roger Clark
|
||||
Teensy-LC compatibility, Paul Stoffregen
|
||||
Search bug fix, Love Nystrom
|
||||
|
||||
Version 2.2:
|
||||
Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com
|
||||
Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030
|
||||
Fix DS18B20 example negative temperature
|
||||
Fix DS18B20 example's low res modes, Ken Butcher
|
||||
Improve reset timing, Mark Tillotson
|
||||
Add const qualifiers, Bertrik Sikken
|
||||
Add initial value input to crc16, Bertrik Sikken
|
||||
Add target_search() function, Scott Roberts
|
||||
|
||||
Version 2.1:
|
||||
Arduino 1.0 compatibility, Paul Stoffregen
|
||||
Improve temperature example, Paul Stoffregen
|
||||
DS250x_PROM example, Guillermo Lovato
|
||||
PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com
|
||||
Improvements from Glenn Trewitt:
|
||||
- crc16() now works
|
||||
- check_crc16() does all of calculation/checking work.
|
||||
- Added read_bytes() and write_bytes(), to reduce tedious loops.
|
||||
- Added ds2408 example.
|
||||
Delete very old, out-of-date readme file (info is here)
|
||||
|
||||
Version 2.0: Modifications by Paul Stoffregen, January 2010:
|
||||
http://www.pjrc.com/teensy/td_libs_OneWire.html
|
||||
Search fix from Robin James
|
||||
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27
|
||||
Use direct optimized I/O in all cases
|
||||
Disable interrupts during timing critical sections
|
||||
(this solves many random communication errors)
|
||||
Disable interrupts during read-modify-write I/O
|
||||
Reduce RAM consumption by eliminating unnecessary
|
||||
variables and trimming many to 8 bits
|
||||
Optimize both crc8 - table version moved to flash
|
||||
|
||||
Modified to work with larger numbers of devices - avoids loop.
|
||||
Tested in Arduino 11 alpha with 12 sensors.
|
||||
26 Sept 2008 -- Robin James
|
||||
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27
|
||||
|
||||
Updated to work with arduino-0008 and to include skip() as of
|
||||
2007/07/06. --RJL20
|
||||
|
||||
Modified to calculate the 8-bit CRC directly, avoiding the need for
|
||||
the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010
|
||||
-- Tom Pollard, Jan 23, 2008
|
||||
|
||||
Jim Studt's original library was modified by Josh Larios.
|
||||
|
||||
Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008
|
||||
|
||||
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.
|
||||
|
||||
Much of the code was inspired by Derek Yerger's code, though I don't
|
||||
think much of that remains. In any event that was..
|
||||
(copyleft) 2006 by Derek Yerger - Free to distribute freely.
|
||||
|
||||
The CRC code was excerpted and inspired by the Dallas Semiconductor
|
||||
sample code bearing this copyright.
|
||||
//---------------------------------------------------------------------------
|
||||
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
|
||||
//
|
||||
// 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 DALLAS SEMICONDUCTOR 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.
|
||||
//
|
||||
// Except as contained in this notice, the name of Dallas Semiconductor
|
||||
// shall not be used except as stated in the Dallas Semiconductor
|
||||
// Branding Policy.
|
||||
//--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "TembooOneWire.h"
|
||||
|
||||
|
||||
TembooOneWire::TembooOneWire(uint8_t pin)
|
||||
{
|
||||
pinMode(pin, INPUT);
|
||||
bitmask = PIN_TO_BITMASK(pin);
|
||||
baseReg = PIN_TO_BASEREG(pin);
|
||||
#if ONEWIRE_SEARCH
|
||||
reset_search();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Perform the onewire reset function. We will wait up to 250uS for
|
||||
// the bus to come high, if it doesn't then it is broken or shorted
|
||||
// and we return a 0;
|
||||
//
|
||||
// Returns 1 if a device asserted a presence pulse, 0 otherwise.
|
||||
//
|
||||
uint8_t TembooOneWire::reset(void)
|
||||
{
|
||||
IO_REG_TYPE mask = bitmask;
|
||||
volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
|
||||
uint8_t r;
|
||||
uint8_t retries = 125;
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(reg, mask);
|
||||
interrupts();
|
||||
// wait until the wire is high... just in case
|
||||
do {
|
||||
if (--retries == 0) return 0;
|
||||
delayMicroseconds(2);
|
||||
} while ( !DIRECT_READ(reg, mask));
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(reg, mask);
|
||||
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
|
||||
interrupts();
|
||||
delayMicroseconds(480);
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(reg, mask); // allow it to float
|
||||
delayMicroseconds(70);
|
||||
r = !DIRECT_READ(reg, mask);
|
||||
interrupts();
|
||||
delayMicroseconds(410);
|
||||
return r;
|
||||
}
|
||||
|
||||
//
|
||||
// Write a bit. Port and bit is used to cut lookup time and provide
|
||||
// more certain timing.
|
||||
//
|
||||
void TembooOneWire::write_bit(uint8_t v)
|
||||
{
|
||||
IO_REG_TYPE mask=bitmask;
|
||||
volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
|
||||
|
||||
if (v & 1) {
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(reg, mask);
|
||||
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
|
||||
delayMicroseconds(10);
|
||||
DIRECT_WRITE_HIGH(reg, mask); // drive output high
|
||||
interrupts();
|
||||
delayMicroseconds(55);
|
||||
} else {
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(reg, mask);
|
||||
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
|
||||
delayMicroseconds(65);
|
||||
DIRECT_WRITE_HIGH(reg, mask); // drive output high
|
||||
interrupts();
|
||||
delayMicroseconds(5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Read a bit. Port and bit is used to cut lookup time and provide
|
||||
// more certain timing.
|
||||
//
|
||||
uint8_t TembooOneWire::read_bit(void)
|
||||
{
|
||||
IO_REG_TYPE mask=bitmask;
|
||||
volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
|
||||
uint8_t r;
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_MODE_OUTPUT(reg, mask);
|
||||
DIRECT_WRITE_LOW(reg, mask);
|
||||
delayMicroseconds(3);
|
||||
DIRECT_MODE_INPUT(reg, mask); // let pin float, pull up will raise
|
||||
delayMicroseconds(10);
|
||||
r = DIRECT_READ(reg, mask);
|
||||
interrupts();
|
||||
delayMicroseconds(53);
|
||||
return r;
|
||||
}
|
||||
|
||||
//
|
||||
// Write a byte. The writing code uses the active drivers to raise the
|
||||
// pin high, if you need power after the write (e.g. DS18S20 in
|
||||
// parasite power mode) then set 'power' to 1, otherwise the pin will
|
||||
// go tri-state at the end of the write to avoid heating in a short or
|
||||
// other mishap.
|
||||
//
|
||||
void TembooOneWire::write(uint8_t v, uint8_t power /* = 0 */) {
|
||||
uint8_t bitMask;
|
||||
|
||||
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
||||
TembooOneWire::write_bit( (bitMask & v)?1:0);
|
||||
}
|
||||
if ( !power) {
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(baseReg, bitmask);
|
||||
DIRECT_WRITE_LOW(baseReg, bitmask);
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void TembooOneWire::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) {
|
||||
for (uint16_t i = 0 ; i < count ; i++)
|
||||
write(buf[i]);
|
||||
if (!power) {
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(baseReg, bitmask);
|
||||
DIRECT_WRITE_LOW(baseReg, bitmask);
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Read a byte
|
||||
//
|
||||
uint8_t TembooOneWire::read() {
|
||||
uint8_t bitMask;
|
||||
uint8_t r = 0;
|
||||
|
||||
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
||||
if ( TembooOneWire::read_bit()) r |= bitMask;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void TembooOneWire::read_bytes(uint8_t *buf, uint16_t count) {
|
||||
for (uint16_t i = 0 ; i < count ; i++)
|
||||
buf[i] = read();
|
||||
}
|
||||
|
||||
//
|
||||
// Do a ROM select
|
||||
//
|
||||
void TembooOneWire::select(const uint8_t rom[8])
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
write(0x55); // Choose ROM
|
||||
|
||||
for (i = 0; i < 8; i++) write(rom[i]);
|
||||
}
|
||||
|
||||
//
|
||||
// Do a ROM skip
|
||||
//
|
||||
void TembooOneWire::skip()
|
||||
{
|
||||
write(0xCC); // Skip ROM
|
||||
}
|
||||
|
||||
void TembooOneWire::depower()
|
||||
{
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(baseReg, bitmask);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
#if ONEWIRE_SEARCH
|
||||
|
||||
//
|
||||
// You need to use this function to start a search again from the beginning.
|
||||
// You do not need to do it for the first search, though you could.
|
||||
//
|
||||
void TembooOneWire::reset_search()
|
||||
{
|
||||
// reset the search state
|
||||
LastDiscrepancy = 0;
|
||||
LastDeviceFlag = FALSE;
|
||||
LastFamilyDiscrepancy = 0;
|
||||
for(int i = 7; ; i--) {
|
||||
ROM_NO[i] = 0;
|
||||
if ( i == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the search to find the device type 'family_code' on the next call
|
||||
// to search(*newAddr) if it is present.
|
||||
//
|
||||
void TembooOneWire::target_search(uint8_t family_code)
|
||||
{
|
||||
// set the search state to find SearchFamily type devices
|
||||
ROM_NO[0] = family_code;
|
||||
for (uint8_t i = 1; i < 8; i++)
|
||||
ROM_NO[i] = 0;
|
||||
LastDiscrepancy = 64;
|
||||
LastFamilyDiscrepancy = 0;
|
||||
LastDeviceFlag = FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform a search. If this function returns a '1' then it has
|
||||
// enumerated the next device and you may retrieve the ROM from the
|
||||
// OneWire::address variable. If there are no devices, no further
|
||||
// devices, or something horrible happens in the middle of the
|
||||
// enumeration then a 0 is returned. If a new device is found then
|
||||
// its address is copied to newAddr. Use OneWire::reset_search() to
|
||||
// start over.
|
||||
//
|
||||
// --- Replaced by the one from the Dallas Semiconductor web site ---
|
||||
//--------------------------------------------------------------------------
|
||||
// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
|
||||
// search state.
|
||||
// Return TRUE : device found, ROM number in ROM_NO buffer
|
||||
// FALSE : device not found, end of search
|
||||
//
|
||||
uint8_t TembooOneWire::search(uint8_t *newAddr, bool search_mode /* = true */)
|
||||
{
|
||||
uint8_t id_bit_number;
|
||||
uint8_t last_zero, rom_byte_number, search_result;
|
||||
uint8_t id_bit, cmp_id_bit;
|
||||
|
||||
unsigned char rom_byte_mask, search_direction;
|
||||
|
||||
// initialize for search
|
||||
id_bit_number = 1;
|
||||
last_zero = 0;
|
||||
rom_byte_number = 0;
|
||||
rom_byte_mask = 1;
|
||||
search_result = 0;
|
||||
|
||||
// if the last call was not the last one
|
||||
if (!LastDeviceFlag)
|
||||
{
|
||||
// 1-Wire reset
|
||||
if (!reset())
|
||||
{
|
||||
// reset the search
|
||||
LastDiscrepancy = 0;
|
||||
LastDeviceFlag = FALSE;
|
||||
LastFamilyDiscrepancy = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// issue the search command
|
||||
if (search_mode == true) {
|
||||
write(0xF0); // NORMAL SEARCH
|
||||
} else {
|
||||
write(0xEC); // CONDITIONAL SEARCH
|
||||
}
|
||||
|
||||
// loop to do the search
|
||||
do
|
||||
{
|
||||
// read a bit and its complement
|
||||
id_bit = read_bit();
|
||||
cmp_id_bit = read_bit();
|
||||
|
||||
// check for no devices on 1-wire
|
||||
if ((id_bit == 1) && (cmp_id_bit == 1))
|
||||
break;
|
||||
else
|
||||
{
|
||||
// all devices coupled have 0 or 1
|
||||
if (id_bit != cmp_id_bit)
|
||||
search_direction = id_bit; // bit write value for search
|
||||
else
|
||||
{
|
||||
// if this discrepancy if before the Last Discrepancy
|
||||
// on a previous next then pick the same as last time
|
||||
if (id_bit_number < LastDiscrepancy)
|
||||
search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
|
||||
else
|
||||
// if equal to last pick 1, if not then pick 0
|
||||
search_direction = (id_bit_number == LastDiscrepancy);
|
||||
|
||||
// if 0 was picked then record its position in LastZero
|
||||
if (search_direction == 0)
|
||||
{
|
||||
last_zero = id_bit_number;
|
||||
|
||||
// check for Last discrepancy in family
|
||||
if (last_zero < 9)
|
||||
LastFamilyDiscrepancy = last_zero;
|
||||
}
|
||||
}
|
||||
|
||||
// set or clear the bit in the ROM byte rom_byte_number
|
||||
// with mask rom_byte_mask
|
||||
if (search_direction == 1)
|
||||
ROM_NO[rom_byte_number] |= rom_byte_mask;
|
||||
else
|
||||
ROM_NO[rom_byte_number] &= ~rom_byte_mask;
|
||||
|
||||
// serial number search direction write bit
|
||||
write_bit(search_direction);
|
||||
|
||||
// increment the byte counter id_bit_number
|
||||
// and shift the mask rom_byte_mask
|
||||
id_bit_number++;
|
||||
rom_byte_mask <<= 1;
|
||||
|
||||
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
|
||||
if (rom_byte_mask == 0)
|
||||
{
|
||||
rom_byte_number++;
|
||||
rom_byte_mask = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
|
||||
|
||||
// if the search was successful then
|
||||
if (!(id_bit_number < 65))
|
||||
{
|
||||
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
|
||||
LastDiscrepancy = last_zero;
|
||||
|
||||
// check for last device
|
||||
if (LastDiscrepancy == 0)
|
||||
LastDeviceFlag = TRUE;
|
||||
|
||||
search_result = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// if no device found then reset counters so next 'search' will be like a first
|
||||
if (!search_result || !ROM_NO[0])
|
||||
{
|
||||
LastDiscrepancy = 0;
|
||||
LastDeviceFlag = FALSE;
|
||||
LastFamilyDiscrepancy = 0;
|
||||
search_result = FALSE;
|
||||
} else {
|
||||
for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i];
|
||||
}
|
||||
return search_result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ONEWIRE_CRC
|
||||
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
|
||||
// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
|
||||
//
|
||||
|
||||
#if ONEWIRE_CRC8_TABLE
|
||||
// This table comes from Dallas sample code where it is freely reusable,
|
||||
// though Copyright (C) 2000 Dallas Semiconductor Corporation
|
||||
static const uint8_t PROGMEM dscrc_table[] = {
|
||||
0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
|
||||
157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
|
||||
35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
|
||||
190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
|
||||
70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
|
||||
219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
|
||||
101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
|
||||
248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
|
||||
140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
|
||||
17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
|
||||
175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
|
||||
50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
|
||||
202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
|
||||
87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
|
||||
233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
|
||||
116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
|
||||
|
||||
//
|
||||
// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
|
||||
// and the registers. (note: this might better be done without to
|
||||
// table, it would probably be smaller and certainly fast enough
|
||||
// compared to all those delayMicrosecond() calls. But I got
|
||||
// confused, so I use this table from the examples.)
|
||||
//
|
||||
uint8_t TembooOneWire::crc8(const uint8_t *addr, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
|
||||
while (len--) {
|
||||
crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
#else
|
||||
//
|
||||
// Compute a Dallas Semiconductor 8 bit CRC directly.
|
||||
// this is much slower, but much smaller, than the lookup table.
|
||||
//
|
||||
uint8_t TembooOneWire::crc8(const uint8_t *addr, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
|
||||
while (len--) {
|
||||
#if defined(__AVR__)
|
||||
crc = _crc_ibutton_update(crc, *addr++);
|
||||
#else
|
||||
uint8_t inbyte = *addr++;
|
||||
for (uint8_t i = 8; i; i--) {
|
||||
uint8_t mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix) crc ^= 0x8C;
|
||||
inbyte >>= 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ONEWIRE_CRC16
|
||||
bool TembooOneWire::check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc)
|
||||
{
|
||||
crc = ~crc16(input, len, crc);
|
||||
return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1];
|
||||
}
|
||||
|
||||
uint16_t TembooOneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc)
|
||||
{
|
||||
#if defined(__AVR__)
|
||||
for (uint16_t i = 0 ; i < len ; i++) {
|
||||
crc = _crc16_update(crc, input[i]);
|
||||
}
|
||||
#else
|
||||
static const uint8_t oddparity[16] =
|
||||
{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
|
||||
|
||||
for (uint16_t i = 0 ; i < len ; i++) {
|
||||
// Even though we're just copying a byte from the input,
|
||||
// we'll be doing 16-bit computation with it.
|
||||
uint16_t cdata = input[i];
|
||||
cdata = (cdata ^ crc) & 0xff;
|
||||
crc >>= 8;
|
||||
|
||||
if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4])
|
||||
crc ^= 0xC001;
|
||||
|
||||
cdata <<= 6;
|
||||
crc ^= cdata;
|
||||
cdata <<= 1;
|
||||
crc ^= cdata;
|
||||
}
|
||||
#endif
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
371
arduino/libraries/Temboo/src/utility/TembooOneWire.h
Normal file
371
arduino/libraries/Temboo/src/utility/TembooOneWire.h
Normal file
@@ -0,0 +1,371 @@
|
||||
#ifndef TembooOneWire_h
|
||||
#define TembooOneWire_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if defined(__AVR__)
|
||||
#include <util/crc16.h>
|
||||
#endif
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h" // for delayMicroseconds, digitalPinToBitMask, etc
|
||||
#else
|
||||
#include "WProgram.h" // for delayMicroseconds
|
||||
#include "pins_arduino.h" // for digitalPinToBitMask, etc
|
||||
#endif
|
||||
|
||||
// You can exclude certain features from OneWire. In theory, this
|
||||
// might save some space. In practice, the compiler automatically
|
||||
// removes unused code (technically, the linker, using -fdata-sections
|
||||
// and -ffunction-sections when compiling, and Wl,--gc-sections
|
||||
// when linking), so most of these will not result in any code size
|
||||
// reduction. Well, unless you try to use the missing features
|
||||
// and redesign your program to not need them! ONEWIRE_CRC8_TABLE
|
||||
// is the exception, because it selects a fast but large algorithm
|
||||
// or a small but slow algorithm.
|
||||
|
||||
// you can exclude onewire_search by defining that to 0
|
||||
#ifndef ONEWIRE_SEARCH
|
||||
#define ONEWIRE_SEARCH 1
|
||||
#endif
|
||||
|
||||
// You can exclude CRC checks altogether by defining this to 0
|
||||
#ifndef ONEWIRE_CRC
|
||||
#define ONEWIRE_CRC 1
|
||||
#endif
|
||||
|
||||
// Select the table-lookup method of computing the 8-bit CRC
|
||||
// by setting this to 1. The lookup table enlarges code size by
|
||||
// about 250 bytes. It does NOT consume RAM (but did in very
|
||||
// old versions of OneWire). If you disable this, a slower
|
||||
// but very compact algorithm is used.
|
||||
#ifndef ONEWIRE_CRC8_TABLE
|
||||
#define ONEWIRE_CRC8_TABLE 1
|
||||
#endif
|
||||
|
||||
// You can allow 16-bit CRC checks by defining this to 1
|
||||
// (Note that ONEWIRE_CRC must also be 1.)
|
||||
#ifndef ONEWIRE_CRC16
|
||||
#define ONEWIRE_CRC16 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
// Platform specific I/O definitions
|
||||
|
||||
#if defined(__AVR__)
|
||||
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint8_t
|
||||
#define IO_REG_ASM asm("r30")
|
||||
#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)
|
||||
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask))
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask))
|
||||
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask))
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask))
|
||||
|
||||
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
|
||||
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
|
||||
#define PIN_TO_BITMASK(pin) (1)
|
||||
#define IO_REG_TYPE uint8_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) (*((base)+512))
|
||||
#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0)
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1)
|
||||
#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1)
|
||||
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1)
|
||||
|
||||
#elif defined(__MKL26Z64__)
|
||||
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint8_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0)
|
||||
#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask))
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask))
|
||||
#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask))
|
||||
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask))
|
||||
|
||||
#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
|
||||
// Arduino 1.5.1 may have a bug in delayMicroseconds() on Arduino Due.
|
||||
// http://arduino.cc/forum/index.php/topic,141030.msg1076268.html#msg1076268
|
||||
// If you have trouble with OneWire on Arduino Due, please check the
|
||||
// status of delayMicroseconds() before reporting a bug in OneWire!
|
||||
#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0)
|
||||
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask))
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask))
|
||||
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask))
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask))
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM
|
||||
#endif
|
||||
#ifndef pgm_read_byte
|
||||
#define pgm_read_byte(addr) (*(const uint8_t *)(addr))
|
||||
#endif
|
||||
|
||||
#elif defined(__PIC32MX__)
|
||||
#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin)))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10
|
||||
#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04
|
||||
#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28
|
||||
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
#define PIN_TO_BASEREG(pin) ((volatile uint32_t*) GPO)
|
||||
#define PIN_TO_BITMASK(pin) (1 << pin)
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) ((GPI & (mask)) ? 1 : 0) //GPIO_IN_ADDRESS
|
||||
#define DIRECT_MODE_INPUT(base, mask) (GPE &= ~(mask)) //GPIO_ENABLE_W1TC_ADDRESS
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) (GPE |= (mask)) //GPIO_ENABLE_W1TS_ADDRESS
|
||||
#define DIRECT_WRITE_LOW(base, mask) (GPOC = (mask)) //GPIO_OUT_W1TC_ADDRESS
|
||||
#define DIRECT_WRITE_HIGH(base, mask) (GPOS = (mask)) //GPIO_OUT_W1TS_ADDRESS
|
||||
|
||||
#elif defined(__SAMD21G18A__)
|
||||
#define PIN_TO_BASEREG(pin) portModeRegister(digitalPinToPort(pin))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) (((*((base)+8)) & (mask)) ? 1 : 0)
|
||||
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) = (mask))
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+2)) = (mask))
|
||||
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+5)) = (mask))
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+6)) = (mask))
|
||||
|
||||
#elif defined(RBL_NRF51822)
|
||||
#define PIN_TO_BASEREG(pin) (0)
|
||||
#define PIN_TO_BITMASK(pin) (pin)
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, pin) nrf_gpio_pin_read(pin)
|
||||
#define DIRECT_WRITE_LOW(base, pin) nrf_gpio_pin_clear(pin)
|
||||
#define DIRECT_WRITE_HIGH(base, pin) nrf_gpio_pin_set(pin)
|
||||
#define DIRECT_MODE_INPUT(base, pin) nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL)
|
||||
#define DIRECT_MODE_OUTPUT(base, pin) nrf_gpio_cfg_output(pin)
|
||||
|
||||
#elif defined(__arc__) /* Arduino101/Genuino101 specifics */
|
||||
|
||||
#include "scss_registers.h"
|
||||
#include "portable.h"
|
||||
#include "avr/pgmspace.h"
|
||||
|
||||
#define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId)
|
||||
#define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType)
|
||||
#define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase)
|
||||
#define DIR_OFFSET_SS 0x01
|
||||
#define DIR_OFFSET_SOC 0x04
|
||||
#define EXT_PORT_OFFSET_SS 0x0A
|
||||
#define EXT_PORT_OFFSET_SOC 0x50
|
||||
|
||||
/* GPIO registers base address */
|
||||
#define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase)
|
||||
#define PIN_TO_BITMASK(pin) pin
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
IO_REG_TYPE directRead(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
|
||||
{
|
||||
IO_REG_TYPE ret;
|
||||
if (SS_GPIO == GPIO_TYPE(pin)) {
|
||||
ret = READ_ARC_REG(((IO_REG_TYPE)base + EXT_PORT_OFFSET_SS));
|
||||
} else {
|
||||
ret = MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, EXT_PORT_OFFSET_SOC);
|
||||
}
|
||||
return ((ret >> GPIO_ID(pin)) & 0x01);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void directModeInput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
|
||||
{
|
||||
if (SS_GPIO == GPIO_TYPE(pin)) {
|
||||
WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)),
|
||||
((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
|
||||
} else {
|
||||
MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin));
|
||||
}
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void directModeOutput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
|
||||
{
|
||||
if (SS_GPIO == GPIO_TYPE(pin)) {
|
||||
WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)),
|
||||
((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
|
||||
} else {
|
||||
MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin));
|
||||
}
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void directWriteLow(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
|
||||
{
|
||||
if (SS_GPIO == GPIO_TYPE(pin)) {
|
||||
WRITE_ARC_REG(READ_ARC_REG(base) & ~(0x01 << GPIO_ID(pin)), base);
|
||||
} else {
|
||||
MMIO_REG_VAL(base) &= ~(0x01 << GPIO_ID(pin));
|
||||
}
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
|
||||
{
|
||||
if (SS_GPIO == GPIO_TYPE(pin)) {
|
||||
WRITE_ARC_REG(READ_ARC_REG(base) | (0x01 << GPIO_ID(pin)), base);
|
||||
} else {
|
||||
MMIO_REG_VAL(base) |= (0x01 << GPIO_ID(pin));
|
||||
}
|
||||
}
|
||||
|
||||
#define DIRECT_READ(base, pin) directRead(base, pin)
|
||||
#define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin)
|
||||
#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin)
|
||||
#define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin)
|
||||
#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin)
|
||||
|
||||
#else
|
||||
#define PIN_TO_BASEREG(pin) (0)
|
||||
#define PIN_TO_BITMASK(pin) (pin)
|
||||
#define IO_REG_TYPE unsigned int
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, pin) digitalRead(pin)
|
||||
#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW)
|
||||
#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH)
|
||||
#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT)
|
||||
#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT)
|
||||
#warning "OneWire. Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite. Operation of this library is not guaranteed on this architecture."
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
class TembooOneWire
|
||||
{
|
||||
private:
|
||||
IO_REG_TYPE bitmask;
|
||||
volatile IO_REG_TYPE *baseReg;
|
||||
|
||||
#if ONEWIRE_SEARCH
|
||||
// global search state
|
||||
unsigned char ROM_NO[8];
|
||||
uint8_t LastDiscrepancy;
|
||||
uint8_t LastFamilyDiscrepancy;
|
||||
uint8_t LastDeviceFlag;
|
||||
#endif
|
||||
|
||||
public:
|
||||
TembooOneWire( uint8_t pin);
|
||||
|
||||
// Perform a 1-Wire reset cycle. Returns 1 if a device responds
|
||||
// with a presence pulse. Returns 0 if there is no device or the
|
||||
// bus is shorted or otherwise held low for more than 250uS
|
||||
uint8_t reset(void);
|
||||
|
||||
// Issue a 1-Wire rom select command, you do the reset first.
|
||||
void select(const uint8_t rom[8]);
|
||||
|
||||
// Issue a 1-Wire rom skip command, to address all on bus.
|
||||
void skip(void);
|
||||
|
||||
// Write a byte. If 'power' is one then the wire is held high at
|
||||
// the end for parasitically powered devices. You are responsible
|
||||
// for eventually depowering it by calling depower() or doing
|
||||
// another read or write.
|
||||
void write(uint8_t v, uint8_t power = 0);
|
||||
|
||||
void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0);
|
||||
|
||||
// Read a byte.
|
||||
uint8_t read(void);
|
||||
|
||||
void read_bytes(uint8_t *buf, uint16_t count);
|
||||
|
||||
// Write a bit. The bus is always left powered at the end, see
|
||||
// note in write() about that.
|
||||
void write_bit(uint8_t v);
|
||||
|
||||
// Read a bit.
|
||||
uint8_t read_bit(void);
|
||||
|
||||
// Stop forcing power onto the bus. You only need to do this if
|
||||
// you used the 'power' flag to write() or used a write_bit() call
|
||||
// and aren't about to do another read or write. You would rather
|
||||
// not leave this powered if you don't have to, just in case
|
||||
// someone shorts your bus.
|
||||
void depower(void);
|
||||
|
||||
#if ONEWIRE_SEARCH
|
||||
// Clear the search state so that if will start from the beginning again.
|
||||
void reset_search();
|
||||
|
||||
// Setup the search to find the device type 'family_code' on the next call
|
||||
// to search(*newAddr) if it is present.
|
||||
void target_search(uint8_t family_code);
|
||||
|
||||
// Look for the next device. Returns 1 if a new address has been
|
||||
// returned. A zero might mean that the bus is shorted, there are
|
||||
// no devices, or you have already retrieved all of them. It
|
||||
// might be a good idea to check the CRC to make sure you didn't
|
||||
// get garbage. The order is deterministic. You will always get
|
||||
// the same devices in the same order.
|
||||
uint8_t search(uint8_t *newAddr, bool search_mode = true);
|
||||
#endif
|
||||
|
||||
#if ONEWIRE_CRC
|
||||
// Compute a Dallas Semiconductor 8 bit CRC, these are used in the
|
||||
// ROM and scratchpad registers.
|
||||
static uint8_t crc8(const uint8_t *addr, uint8_t len);
|
||||
|
||||
#if ONEWIRE_CRC16
|
||||
// Compute the 1-Wire CRC16 and compare it against the received CRC.
|
||||
// Example usage (reading a DS2408):
|
||||
// // Put everything in a buffer so we can compute the CRC easily.
|
||||
// uint8_t buf[13];
|
||||
// buf[0] = 0xF0; // Read PIO Registers
|
||||
// buf[1] = 0x88; // LSB address
|
||||
// buf[2] = 0x00; // MSB address
|
||||
// WriteBytes(net, buf, 3); // Write 3 cmd bytes
|
||||
// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16
|
||||
// if (!CheckCRC16(buf, 11, &buf[11])) {
|
||||
// // Handle error.
|
||||
// }
|
||||
//
|
||||
// @param input - Array of bytes to checksum.
|
||||
// @param len - How many bytes to use.
|
||||
// @param inverted_crc - The two CRC16 bytes in the received data.
|
||||
// This should just point into the received data,
|
||||
// *not* at a 16-bit integer.
|
||||
// @param crc - The crc starting value (optional)
|
||||
// @return True, iff the CRC matches.
|
||||
static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0);
|
||||
|
||||
// Compute a Dallas Semiconductor 16 bit CRC. This is required to check
|
||||
// the integrity of data received from many 1-Wire devices. Note that the
|
||||
// CRC computed here is *not* what you'll get from the 1-Wire network,
|
||||
// for two reasons:
|
||||
// 1) The CRC is transmitted bitwise inverted.
|
||||
// 2) Depending on the endian-ness of your processor, the binary
|
||||
// representation of the two-byte return value may have a different
|
||||
// byte order than the two bytes you get from 1-Wire.
|
||||
// @param input - Array of bytes to checksum.
|
||||
// @param len - How many bytes to use.
|
||||
// @param crc - The crc starting value (optional)
|
||||
// @return The CRC16, as defined by Dallas Semiconductor.
|
||||
static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
273
arduino/libraries/Temboo/src/utility/TembooSession.cpp
Normal file
273
arduino/libraries/Temboo/src/utility/TembooSession.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "TembooSession.h"
|
||||
#include "tmbhmac.h"
|
||||
#include "DataFormatter.h"
|
||||
|
||||
static const char EOL[] PROGMEM = "\r\n";
|
||||
static const char POST[] PROGMEM = "POST ";
|
||||
static const char POSTAMBLE[] PROGMEM = " HTTP/1.0"; // Prevent host from using chunked encoding in response.
|
||||
static const char HEADER_HOST[] PROGMEM = "Host: ";
|
||||
static const char HEADER_ACCEPT[] PROGMEM = "Accept: application/xml";
|
||||
static const char HEADER_ORG[] PROGMEM = "x-temboo-domain: /";
|
||||
static const char HEADER_DOM[] PROGMEM = "/master";
|
||||
static const char HEADER_CONTENT_LENGTH[] PROGMEM = "Content-Length: ";
|
||||
static const char HEADER_TIME[] PROGMEM = "x-temboo-time: ";
|
||||
static const char BASE_CHOREO_URI[] PROGMEM = "/arcturus-web/api-1.0/ar";
|
||||
static const char HEADER_AUTH[] PROGMEM = "x-temboo-authentication: ";
|
||||
static const char HEADER_CONTENT_TYPE[] PROGMEM = "Content-Type: text/plain";
|
||||
static const char TEMBOO_DOMAIN[] PROGMEM = ".temboolive.com";
|
||||
static const char SDK_ID[] PROGMEM = "?source_id=";
|
||||
static const char SDK_ID_DEFAULT[] PROGMEM = "ArduinoSDK1.2.0";
|
||||
static const char HTTP[] PROGMEM = "http://";
|
||||
|
||||
unsigned long TembooSession::s_timeOffset = 0;
|
||||
|
||||
TembooSession::TembooSession(Client& client,
|
||||
IPAddress serverAddr,
|
||||
uint16_t port) : m_client(client) {
|
||||
m_addr = serverAddr;
|
||||
m_port = port;
|
||||
m_sendQueueDepth = 0;
|
||||
}
|
||||
|
||||
|
||||
void TembooSession::setTime(unsigned long currentTime) {
|
||||
s_timeOffset = currentTime - (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
unsigned long TembooSession::getTime() {
|
||||
return s_timeOffset + (millis()/1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int TembooSession::executeChoreo(
|
||||
const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName) {
|
||||
|
||||
DataFormatter fmt(&inputSet, &expressionSet, &sensorSet,&outputSet, &preset, &deviceType, &deviceName);
|
||||
char auth[HMAC_HEX_SIZE_BYTES + 1];
|
||||
char buffer[11];
|
||||
|
||||
// We use the current time-of-day as salt on the app key.
|
||||
// We keep track of time-of-day by getting the current time
|
||||
// from the server and applying an offset (the length of time
|
||||
// we've been running.)
|
||||
uint32toa((uint32_t)TembooSession::getTime(), buffer);
|
||||
|
||||
uint16_t contentLength = getAuth(fmt, appKeyValue, buffer, auth);
|
||||
if (m_client.connected()) {
|
||||
m_client.stop();
|
||||
}
|
||||
m_client.flush();
|
||||
|
||||
int connected = 0;
|
||||
TEMBOO_TRACE("Connecting: ");
|
||||
|
||||
// reserve space for the "host" string sufficient to hold either the
|
||||
// (dotted-quad) IP address + port, or the default <account>.temboolive.com
|
||||
// host string.
|
||||
int hostLen = strlen_P(TEMBOO_DOMAIN) + strlen(accountName) + 1;
|
||||
char host[hostLen];
|
||||
|
||||
// Construct the "host" string from the account name and the temboo domain name.
|
||||
|
||||
strcpy(host, accountName);
|
||||
strcat_P(host, TEMBOO_DOMAIN);
|
||||
|
||||
bool useProxy = false;
|
||||
|
||||
if (m_addr == INADDR_NONE) {
|
||||
TEMBOO_TRACELN(host);
|
||||
connected = m_client.connect(host, m_port);
|
||||
} else {
|
||||
|
||||
TEMBOO_TRACELN(host);
|
||||
connected = m_client.connect(m_addr, m_port);
|
||||
|
||||
useProxy = true;
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
|
||||
TEMBOO_TRACELN("OK. req:");
|
||||
qsendProgmem(POST);
|
||||
if(useProxy) {
|
||||
qsendProgmem(HTTP);
|
||||
qsend(host);
|
||||
}
|
||||
qsendProgmem(BASE_CHOREO_URI);
|
||||
qsend(path);
|
||||
qsendProgmem(SDK_ID);
|
||||
if (deviceType.isEmpty()) {
|
||||
qsendProgmem(SDK_ID_DEFAULT);
|
||||
} else {
|
||||
qsend(deviceType.getName());
|
||||
}
|
||||
qsendlnProgmem(POSTAMBLE);
|
||||
|
||||
// Send our custom authentication header
|
||||
// (app-key-name:hmac)
|
||||
qsendProgmem(HEADER_AUTH);
|
||||
qsend(appKeyName);
|
||||
qsend(":");
|
||||
qsendln(auth);
|
||||
|
||||
// send the standard host header
|
||||
qsendProgmem(HEADER_HOST);
|
||||
qsendln(host);
|
||||
|
||||
// send the standard accept header
|
||||
qsendlnProgmem(HEADER_ACCEPT);
|
||||
|
||||
// send our custom account name neader
|
||||
qsendProgmem(HEADER_ORG);
|
||||
qsend(accountName);
|
||||
qsendlnProgmem(HEADER_DOM);
|
||||
|
||||
// send the standard content type header
|
||||
qsendlnProgmem(HEADER_CONTENT_TYPE);
|
||||
|
||||
// send our custom client time header
|
||||
qsendProgmem(HEADER_TIME);
|
||||
qsendln(buffer);
|
||||
|
||||
// send the standard content length header
|
||||
qsendProgmem(HEADER_CONTENT_LENGTH);
|
||||
qsendln(uint16toa(contentLength, buffer));
|
||||
|
||||
qsendProgmem(EOL);
|
||||
|
||||
// Format and send the body of the request
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
qsend(fmt.next());
|
||||
}
|
||||
|
||||
qsendProgmem(EOL);
|
||||
qflush();
|
||||
return 0;
|
||||
} else {
|
||||
TEMBOO_TRACELN("FAIL");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint16_t TembooSession::getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* result) const {
|
||||
|
||||
// We need the length of the data for other things, and
|
||||
// this method is a convenient place to calculate it.
|
||||
uint16_t len = 0;
|
||||
|
||||
HMAC hmac;
|
||||
|
||||
//combine the salt and the key and give it to the HMAC calculator
|
||||
size_t keyLength = strlen(appKeyValue) + strlen(salt);
|
||||
char key[keyLength + 1];
|
||||
strcpy(key, salt);
|
||||
strcat(key, appKeyValue);
|
||||
hmac.init((uint8_t*)key, keyLength);
|
||||
|
||||
// process the data a block at a time.
|
||||
uint8_t buffer[HMAC_BLOCK_SIZE_BYTES];
|
||||
int blockCount = 0;
|
||||
fmt.reset();
|
||||
while(fmt.hasNext()) {
|
||||
uint8_t c = fmt.next();
|
||||
len++;
|
||||
buffer[blockCount++] = c;
|
||||
if (blockCount == HMAC_BLOCK_SIZE_BYTES) {
|
||||
hmac.process(buffer, blockCount);
|
||||
blockCount = 0;
|
||||
}
|
||||
}
|
||||
hmac.process(buffer, blockCount);
|
||||
|
||||
// Finalize the HMAC calculation and store the (ASCII HEX) value in *result.
|
||||
hmac.finishHex(result);
|
||||
|
||||
// Return the number of characters processed.
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void TembooSession::qsend(const char* s) {
|
||||
char c = *s++;
|
||||
while(c != '\0') {
|
||||
qsend(c);
|
||||
c = *s++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TembooSession::qsendProgmem(const char* s) {
|
||||
char c = pgm_read_byte(s++);
|
||||
while(c != '\0') {
|
||||
qsend(c);
|
||||
c = pgm_read_byte(s++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TembooSession::qsend(char c) {
|
||||
m_sendQueue[m_sendQueueDepth++] = c;
|
||||
if (m_sendQueueDepth >= TEMBOO_SEND_QUEUE_SIZE) {
|
||||
qflush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TembooSession::qflush() {
|
||||
m_client.write((const uint8_t*)m_sendQueue, m_sendQueueDepth);
|
||||
TEMBOO_TRACE_BYTES(m_sendQueue, m_sendQueueDepth);
|
||||
m_sendQueueDepth = 0;
|
||||
}
|
||||
|
||||
|
||||
void TembooSession::qsendln(const char* s) {
|
||||
qsend(s);
|
||||
qsendProgmem(EOL);
|
||||
}
|
||||
|
||||
|
||||
void TembooSession::qsendlnProgmem(const char* s) {
|
||||
qsendProgmem(s);
|
||||
qsendProgmem(EOL);
|
||||
}
|
||||
|
||||
|
||||
141
arduino/libraries/Temboo/src/utility/TembooSession.h
Normal file
141
arduino/libraries/Temboo/src/utility/TembooSession.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOSESSIONCLASS_H_
|
||||
#define TEMBOOSESSIONCLASS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
#include <Client.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
#ifndef TEMBOO_SEND_QUEUE_SIZE
|
||||
|
||||
// Some network interfaces (i.e. ethernet or WiFi shields) can only accept
|
||||
// a limited amount of data. If you try to send more than the limit, the excess
|
||||
// is just lost. However, sending one character at a time is very inefficient.
|
||||
// To deal with this situation, we queue up TEMBOO_SEND_QUEUE_SIZE bytes to send
|
||||
// to the network device at one time. This is a compromise between RAM usage
|
||||
// and performance.
|
||||
#define TEMBOO_SEND_QUEUE_SIZE (32)
|
||||
#endif
|
||||
|
||||
class ChoreoInputSet;
|
||||
class ChoreoInputExpressionSet;
|
||||
class ChoreoSensorInputSet;
|
||||
class ChoreoOutputSet;
|
||||
class ChoreoPreset;
|
||||
class ChoreoDevice;
|
||||
class DataFormatter;
|
||||
|
||||
class TembooSession {
|
||||
public:
|
||||
|
||||
//TembooSession constructor
|
||||
//client: REQUIRED TCP/IP client object. Usually either an EthernetClient or a WiFiClient
|
||||
//IPAddress: OPTIONAL IP address of the server to connect to. Usually only used for testing.
|
||||
//port: OPTIONAL port number to use with the IPAddress. Usually only used for testing.
|
||||
TembooSession(Client& client, IPAddress serverAddr=INADDR_NONE, uint16_t port=80);
|
||||
|
||||
//executeChoreo sends a choreo execution request to the Temboo system.
|
||||
// Does not wait for a response (that's a job for whoever owns the Client.)
|
||||
//accountName: the name of the user's account at Temboo.
|
||||
//appKeyName: the name of an application key in the user's account to use
|
||||
// for this execution (analogous to a user name).
|
||||
//appKeyValue: the value of the application key named in appKeyName.
|
||||
// Used to authenticate the user (analogous to a password)
|
||||
//path: The full path to the choreo to be executed (relative to the root of the
|
||||
// user's account.)
|
||||
//inputSet: the set of inputs needed by the choreo.
|
||||
// May be an empty ChoreoInputSet.
|
||||
//outputSet: the set of output filters to be applied to the choreo results.
|
||||
// May be an empty ChoreoOutputSet
|
||||
//preset: the ChoreoPreset to be used with the choreo execution.
|
||||
// May be an empty ChoreoPreset.
|
||||
int executeChoreo(const char* accountName,
|
||||
const char* appKeyName,
|
||||
const char* appKeyValue,
|
||||
const char* path,
|
||||
const ChoreoInputSet& inputSet,
|
||||
const ChoreoInputExpressionSet& expressionSet,
|
||||
const ChoreoSensorInputSet& sensorSet,
|
||||
const ChoreoOutputSet& outputSet,
|
||||
const ChoreoPreset& preset,
|
||||
const ChoreoDevice& deviceType,
|
||||
const ChoreoDevice& deviceName);
|
||||
|
||||
// setTime sets the current time in Unix timestamp format. Needed for execution request authentication.
|
||||
// NOTE: This method is usually called by TembooChoreo.run() with the current time returned by
|
||||
// an error response from the Temboo system, thus automatically setting the time. However, it
|
||||
// MAY be called from user code if the particular board has a way of determining the current
|
||||
// time in the proper format.
|
||||
// currentTime: the number of seconds since 1970-01-01 00:00:00 UTC.
|
||||
static void setTime(unsigned long currentTime);
|
||||
|
||||
//getTime returns the current time in Unix timestamp format (seconds since 1970-01-01 00:00:00 UTC).
|
||||
// Only valid after setTime has been called.
|
||||
static unsigned long getTime();
|
||||
|
||||
private:
|
||||
static unsigned long s_timeOffset;
|
||||
|
||||
IPAddress m_addr;
|
||||
uint16_t m_port;
|
||||
|
||||
Client& m_client;
|
||||
char m_sendQueue[TEMBOO_SEND_QUEUE_SIZE];
|
||||
size_t m_sendQueueDepth;
|
||||
|
||||
// calculate the authentication code value of the formatted request body
|
||||
// using the salted application key value as the key.
|
||||
// Returns the number of characters processed (i.e. the length of the request body)
|
||||
uint16_t getAuth(DataFormatter& fmt, const char* appKeyValue, const char* salt, char* hexAuth) const;
|
||||
|
||||
|
||||
// queue an entire nul-terminated char array
|
||||
// from RAM followed by a newline.
|
||||
void qsendln(const char* str);
|
||||
|
||||
// queue an entire nul-terminated char array
|
||||
// from flash memory (PROGMEM) one byte at a time,
|
||||
// followed by a newline.
|
||||
void qsendlnProgmem(const char* str);
|
||||
|
||||
// queue an entire nul-terminated char array
|
||||
// from RAM one byte at a time.
|
||||
void qsend(const char*);
|
||||
|
||||
// queue an entire nul-terminated char array
|
||||
// from flash memory (PROGMEM) one byte at a time.
|
||||
void qsendProgmem(const char*);
|
||||
|
||||
// queue a single character to be sent when the queue is full.
|
||||
void qsend(char);
|
||||
|
||||
// send the current contents of the send queue to the client.
|
||||
void qflush();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
37
arduino/libraries/Temboo/src/utility/TembooTags.h
Normal file
37
arduino/libraries/Temboo/src/utility/TembooTags.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library for Arduino
|
||||
#
|
||||
# Copyright (C) 2017 Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOTAGS_H_
|
||||
#define TEMBOOTAGS_H_
|
||||
|
||||
#define TAG_REQUEST_ID ('R')
|
||||
#define TAG_ACCOUNT_NAME ('N')
|
||||
#define TAG_APP_KEY_NAME ('K')
|
||||
#define TAG_TIME ('T')
|
||||
#define TAG_AUTH ('A')
|
||||
#define TAG_CHOREO_ID ('C')
|
||||
#define TAG_DATA ('X')
|
||||
#define TAG_END_REQUEST ('!')
|
||||
#define TAG_ELEMENT_SEPARATOR ('|')
|
||||
#define TAG_VALUE_SEPARATOR (':')
|
||||
|
||||
#endif //TEMBOOTAGS_H_
|
||||
62
arduino/libraries/Temboo/src/utility/TembooTimer.h
Normal file
62
arduino/libraries/Temboo/src/utility/TembooTimer.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo CoAP Edge Device library
|
||||
#
|
||||
# Copyright (C) 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TEMBOOTIMER_H_
|
||||
#define TEMBOOTIMER_H_
|
||||
|
||||
class TembooTimer {
|
||||
public:
|
||||
TembooTimer() {
|
||||
this->m_startTimeMillis = 0;
|
||||
this->m_durationMillis = 0;
|
||||
}
|
||||
|
||||
TembooTimer(uint32_t m_durationMillis) {
|
||||
this->start(m_durationMillis);
|
||||
}
|
||||
|
||||
bool expired() {
|
||||
return leftMillis() == 0;
|
||||
}
|
||||
|
||||
void start(uint32_t m_durationMillis) {
|
||||
this->m_startTimeMillis = getMillis();
|
||||
this->m_durationMillis = m_durationMillis;
|
||||
}
|
||||
|
||||
uint32_t leftMillis() {
|
||||
uint32_t elapsedMillis = getMillis() - this->m_startTimeMillis;
|
||||
return elapsedMillis < this->m_durationMillis ? (this->m_durationMillis - elapsedMillis) : 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t getMillis() {
|
||||
return millis();
|
||||
}
|
||||
|
||||
uint32_t m_startTimeMillis;
|
||||
uint32_t m_durationMillis;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* TEMBOOTIMER_H_ */
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "TembooWebSocketRequestHandles.h"
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
static WSMessageRequest handleMessageRequest(char** saveptr);
|
||||
static WSMessageRequest handleIntervalRequest(char** saveptr);
|
||||
static WSMessageRequest handleSetRequest(char** saveptr, TembooSensor** sensorTable, int sensorTableDepth);
|
||||
static WSMessageRequest handleGetRequest(char** saveptr, TembooSensor** sensorTable, int sensorTableDepth);
|
||||
static WSMessageRequest handleMonitoringUpdateRequest(char**saveptr);
|
||||
|
||||
static const uint16_t WS_MAX_PAYLOAD_SIZE = 126;
|
||||
|
||||
|
||||
|
||||
WSMessageRequest handleGetRequest(char** saveptr, TembooSensor** sensorTable, int sensorTableDepth) {
|
||||
char* textField = strtok_r(NULL, "|", &(*saveptr));
|
||||
int pinNum = -1;
|
||||
int pinVal = -1;
|
||||
WSMessageRequest rc = WS_REQUEST_ERROR;
|
||||
// strlen should catch the + sign
|
||||
while (textField != NULL && strlen(textField) >= 2) {
|
||||
if (*textField == 'P') {
|
||||
// if pinNum is already set, then an extra pin number
|
||||
// has been added to the request incorrectly
|
||||
if (pinNum != -1) {
|
||||
logTembooDebug("Invalid Get request\n");
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
size_t i = 0;
|
||||
// make sure the pin value is made of digits
|
||||
while (i < strlen(&textField[1])) {
|
||||
if (isdigit(textField[1 + i++]) == 0) {
|
||||
logTembooDebug(("Invalid pin number\n"));
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
}
|
||||
pinNum = atoi(&textField[1]);
|
||||
// make sure pin number is valid, set to -1 if error
|
||||
if (pinNum > MAX_PIN_NUMBER) {
|
||||
logTembooDebug(("Invalid pin number\n"));
|
||||
pinNum = -1;
|
||||
}
|
||||
} else if (*textField == '+') {
|
||||
break;
|
||||
} else {
|
||||
logTembooDebug("Invalid message key\n");
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
textField = strtok_r(NULL, "|", &(*saveptr));
|
||||
}
|
||||
|
||||
if (pinNum >= 0) {
|
||||
//obtain the data and send to Temboo
|
||||
int i = 0;
|
||||
for (i = 0; i < sensorTableDepth; i++) {
|
||||
if (sensorTable[i]->getSensorPin(sensorTable[i]->sensorConfig) == pinNum) {
|
||||
pinVal = sensorTable[i]->read(sensorTable[i]->sensorConfig);
|
||||
rc = WS_GET_REQUEST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc != WS_REQUEST_ERROR) {
|
||||
// Send pinVal
|
||||
addWebSocketPinData(pinNum, pinVal, true);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
WSMessageRequest handleSetRequest(char** saveptr, TembooSensor** sensorTable, int sensorTableDepth) {
|
||||
int pinNum = -1;
|
||||
int pinVal = -1;
|
||||
bool pinValSet = false;
|
||||
WSMessageRequest rc = WS_REQUEST_ERROR;
|
||||
char* textField = strtok_r(NULL, "|", &(*saveptr));
|
||||
while (textField != NULL && strlen(textField) >= 2) {
|
||||
if (*textField == 'P') {
|
||||
// if pinNum is already set, then an extra pin number
|
||||
// has been added to the request incorrectly
|
||||
if (pinNum != -1) {
|
||||
logTembooDebug("Invalid Set request\n");
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
size_t i = 0;
|
||||
// make sure the pin value is made of digits
|
||||
while (i < strlen(&textField[1])) {
|
||||
if (isdigit(textField[1 + i++]) == 0) {
|
||||
logTembooDebug(("Invalid pin number\n"));
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
}
|
||||
pinNum = atoi(&textField[1]);
|
||||
// make sure pin number is valid, set to -1 if error
|
||||
if (pinNum > MAX_PIN_NUMBER) {
|
||||
logTembooDebug(("Invalid pin number\n"));
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
} else if (*textField == 'V') {
|
||||
// if pinVal is already set, then an extra pin value
|
||||
// has been added to the request incorrectly
|
||||
if (pinValSet) {
|
||||
logTembooDebug("Invalid Set request\n");
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
size_t i = 0;
|
||||
// make sure the pin value is made of digits
|
||||
while (i < strlen(&textField[1])) {
|
||||
if (isdigit(textField[1 + i++]) == 0) {
|
||||
logTembooDebug(("Invalid pin value\n"));
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
}
|
||||
pinVal = atoi(&textField[1]);
|
||||
// make sure pin value is valid, pinValSet will remain false
|
||||
if (pinVal > MAX_PIN_VALUE) {
|
||||
logTembooDebug(("Invalid pin value\n"));
|
||||
return WS_REQUEST_ERROR;
|
||||
} else {
|
||||
pinValSet = true;
|
||||
}
|
||||
|
||||
} else if (*textField == '+') {
|
||||
break;
|
||||
} else {
|
||||
logTembooDebug("Invalid message key\n");
|
||||
return WS_REQUEST_ERROR;
|
||||
}
|
||||
textField = strtok_r(NULL, "|", &(*saveptr));
|
||||
}
|
||||
|
||||
if (pinNum >= 0 && pinValSet) {
|
||||
int i = 0;
|
||||
for (; i < sensorTableDepth; i++) {
|
||||
if (sensorTable[i]->getSensorPin(sensorTable[i]->sensorConfig) == pinNum) {
|
||||
if (sensorTable[i]->write != NULL) {
|
||||
sensorTable[i]->write(sensorTable[i]->sensorConfig, pinVal);
|
||||
rc = WS_SET_REQUEST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc != WS_REQUEST_ERROR) {
|
||||
// with pinval found, send the data to Temboo
|
||||
addWebSocketPinData(pinNum, pinVal, true);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Parse out the error message while ignoring escaped characters.
|
||||
// This functions acts like strstr where it will move saveptr
|
||||
// to the index past the delimeter ('|' in this case), add '\0'
|
||||
// where the delimeter is, and return the pointer of the error message
|
||||
char* getErrorMessage(char** saveptr) {
|
||||
if (saveptr == NULL || *saveptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char* tok = *saveptr;
|
||||
size_t len = strlen(*saveptr);
|
||||
(*saveptr)++;
|
||||
if (**saveptr == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
while (**saveptr != '\0') {
|
||||
// check for delimiter
|
||||
if (**saveptr == '\\') {
|
||||
// check if we're escaping a special character
|
||||
switch (*(*saveptr+1)) {
|
||||
case '+':
|
||||
case '|':
|
||||
case '\\':
|
||||
// shift string over 1 and increment
|
||||
memmove(*saveptr,*saveptr+1, --len);
|
||||
(*saveptr)++;
|
||||
break;
|
||||
default:
|
||||
// increment pointer and decrement str length
|
||||
(*saveptr)++;
|
||||
len--;
|
||||
break;
|
||||
}
|
||||
} else if (**saveptr == '|') {
|
||||
// end of message
|
||||
**saveptr = '\0';
|
||||
(*saveptr)++;
|
||||
return tok;
|
||||
} else {
|
||||
// increment pointer and decrement str length
|
||||
(*saveptr)++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
// return string when if the end of the text is found
|
||||
return tok;
|
||||
}
|
||||
|
||||
WSMessageRequest handleMessageRequest(char** saveptr) {
|
||||
char* textField = getErrorMessage(&(*saveptr));
|
||||
WSMessageRequest rc = WS_REQUEST_ERROR;
|
||||
if (textField != NULL && strlen(textField) >= 2) {
|
||||
if (*textField == 'T') {
|
||||
logTembooDebug(&textField[1]);
|
||||
rc = WS_MESSAGE_REQUEST;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
WSMessageRequest handleMonitoringUpdateRequest(char** saveptr) {
|
||||
char* textField = strtok_r(NULL, "|", &(*saveptr));
|
||||
WSMessageRequest rc = WS_REQUEST_ERROR;
|
||||
if (textField != NULL && strlen(textField) >= 2) {
|
||||
if (*textField == 'T') {
|
||||
if (!strcmp(&textField[1], "true")) {
|
||||
rc = WS_UPDATE_CONNECTED;
|
||||
} else if (!strcmp(&textField[1], "false")) {
|
||||
rc = WS_UPDATE_DISCONNECTED;
|
||||
} else {
|
||||
rc = WS_REQUEST_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
WSMessageRequest handleIntervalRequest(char** saveptr) {
|
||||
char* textField = strtok_r(NULL, "|", &(*saveptr));
|
||||
WSMessageRequest rc = WS_REQUEST_ERROR;
|
||||
if (textField != NULL && strlen(textField) >= 2) {
|
||||
if (*textField == 'V') {
|
||||
if (isdigit(textField[1]) && strlen(&textField[1]) < 6) {
|
||||
updateIntervalTime(atoi(&textField[1]));
|
||||
rc = WS_INTERVAL_REQUEST;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
WSMessageRequest handleResponse(uint8_t* request, TembooSensor** sensorTable, int sensorTableDepth, bool connectionStatus) {
|
||||
// parse response to find request type
|
||||
char* saveptr = NULL;
|
||||
char* requestField = strtok_r((char*)request, "|", &saveptr);
|
||||
WSMessageRequest rc = WS_REQUEST_ERROR;
|
||||
while (requestField != NULL) {
|
||||
if (*requestField == 'M') {
|
||||
switch (requestField[1]) {
|
||||
// only read web socket messages if device is connected
|
||||
// to the web socket
|
||||
case 'G':
|
||||
// send pin data to Temboo
|
||||
if (connectionStatus) {
|
||||
rc = handleGetRequest(&saveptr, sensorTable, sensorTableDepth);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
// change state of a pin
|
||||
if (connectionStatus) {
|
||||
rc = handleSetRequest(&saveptr, sensorTable, sensorTableDepth);
|
||||
}
|
||||
break;
|
||||
case 'E':
|
||||
// print message
|
||||
rc = handleMessageRequest(&saveptr);
|
||||
break;
|
||||
case 'U':
|
||||
rc = handleMonitoringUpdateRequest(&saveptr);
|
||||
break;
|
||||
case 'I':
|
||||
rc = handleIntervalRequest(&saveptr);
|
||||
break;
|
||||
default:
|
||||
// return error
|
||||
rc = WS_REQUEST_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rc == WS_REQUEST_ERROR) {
|
||||
break;
|
||||
}
|
||||
requestField = strtok_r(NULL, "|", &saveptr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef __TembooWebSocketRequestHandles__
|
||||
#define __TembooWebSocketRequestHandles__
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PIN_NUMBER
|
||||
#define MAX_PIN_NUMBER 256
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PIN_VALUE
|
||||
#define MAX_PIN_VALUE 255
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
WS_GET_REQUEST = 0,
|
||||
WS_SET_REQUEST,
|
||||
WS_DATA_REQUEST,
|
||||
WS_MESSAGE_REQUEST,
|
||||
WS_RR_REQUEST,
|
||||
WS_SESSION_ID_REQUEST,
|
||||
WS_REQUEST_ERROR,
|
||||
WS_UPDATE_CONNECTED,
|
||||
WS_UPDATE_DISCONNECTED,
|
||||
WS_NO_MESSAGE,
|
||||
WS_INTERVAL_REQUEST
|
||||
} WSMessageRequest;
|
||||
|
||||
typedef struct TembooSensor{
|
||||
void* sensorConfig;
|
||||
int (*read)(void* sensorConfig);
|
||||
void (*write)(void* sensorConfig, int val);
|
||||
void (*begin)(void* sensorConfig);
|
||||
int (*getSensorPin)(void*sensorConfig);
|
||||
uint8_t* (*getSensorChannel)(void*sensorConfig);
|
||||
int defaultValue;
|
||||
} TembooSensor;
|
||||
|
||||
typedef TembooSensor TembooActuator;
|
||||
|
||||
void addWebSocketPinData(int pin, int pinVal, bool requestResponse);
|
||||
void updateIntervalTime(int intervalTime);
|
||||
|
||||
// Send frame
|
||||
WSMessageRequest handleResponse(uint8_t* request, TembooSensor** sensors, int sensorTableDepth, bool connectionStatus);
|
||||
void logTembooDebug(const char *c);
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* defined(__TembooWebSocketRequestHandles__) */
|
||||
107
arduino/libraries/Temboo/src/utility/tmbhmac.cpp
Normal file
107
arduino/libraries/Temboo/src/utility/tmbhmac.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "tmbhmac.h"
|
||||
|
||||
HMAC::HMAC() {
|
||||
}
|
||||
|
||||
HMAC::HMAC(const uint8_t* key, uint32_t keyLength) {
|
||||
init(key, keyLength);
|
||||
}
|
||||
|
||||
void HMAC::init(const uint8_t* key, uint32_t keyLength) {
|
||||
|
||||
m_key = key;
|
||||
m_keyLength = keyLength;
|
||||
|
||||
uint8_t iKeyPad[HMAC_BLOCK_SIZE_BYTES];
|
||||
|
||||
constructKeyPad(iKeyPad, key, keyLength, (uint8_t)0x36);
|
||||
|
||||
m_md5.init();
|
||||
m_md5.process(iKeyPad, HMAC_BLOCK_SIZE_BYTES);
|
||||
}
|
||||
|
||||
void HMAC::process(const uint8_t* msg, uint32_t msgLength) {
|
||||
// hmac = hash(o_key_pad + hash(i_key_pad + message))
|
||||
// continue hashing the message
|
||||
m_md5.process(msg, msgLength);
|
||||
}
|
||||
|
||||
void HMAC::finish(uint8_t* dest) {
|
||||
//hmac = hash(o_key_pad + hash(i_key_pad + message))
|
||||
//
|
||||
// Construct the o_key_pad
|
||||
uint8_t finalBlock[HMAC_BLOCK_SIZE_BYTES + HMAC_HASH_SIZE_BYTES];
|
||||
constructKeyPad(finalBlock, m_key, m_keyLength, (uint8_t)0x5C);
|
||||
m_md5.finish(finalBlock + HMAC_BLOCK_SIZE_BYTES);
|
||||
|
||||
m_md5.init();
|
||||
m_md5.process(finalBlock, HMAC_BLOCK_SIZE_BYTES + HMAC_HASH_SIZE_BYTES);
|
||||
m_md5.finish(dest);
|
||||
}
|
||||
|
||||
void HMAC::finishHex(char* dest) {
|
||||
uint8_t binDest[HMAC_HASH_SIZE_BYTES];
|
||||
finish(binDest);
|
||||
toHex(binDest, dest);
|
||||
}
|
||||
|
||||
void HMAC::toHex(uint8_t* hmac, char* dest) {
|
||||
static const char hex[17] PROGMEM = "0123456789abcdef";
|
||||
uint16_t i;
|
||||
for (i = 0; i < HMAC_HASH_SIZE_BYTES; i++) {
|
||||
dest[i*2] = pgm_read_byte(&hex[hmac[i] >> 4]);
|
||||
dest[(i*2) + 1] = pgm_read_byte(&hex[hmac[i] & 0x0F]);
|
||||
}
|
||||
dest[HMAC_HASH_SIZE_BYTES * 2] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* dest MUST be big enough to hold HMAC_BLOCK_SIZE_BYTES
|
||||
*/
|
||||
void HMAC::constructKeyPad(uint8_t* dest, const uint8_t* key, uint32_t keyLength, uint8_t padByte) {
|
||||
|
||||
if (keyLength > HMAC_BLOCK_SIZE_BYTES) {
|
||||
// If the key is bigger than 1 block,
|
||||
// replace the key with the hash of the key.
|
||||
MD5 md5;
|
||||
md5.process(key, keyLength);
|
||||
md5.finish(dest);
|
||||
keyLength = HMAC_HASH_SIZE_BYTES;
|
||||
} else {
|
||||
// If the key length is <= to the HMAC block length,
|
||||
// just use the key as-is.
|
||||
memcpy(dest, key, keyLength);
|
||||
}
|
||||
// pad the remaining space with 0s
|
||||
if (keyLength < HMAC_BLOCK_SIZE_BYTES) {
|
||||
memset(dest + keyLength, 0, HMAC_BLOCK_SIZE_BYTES-keyLength);
|
||||
}
|
||||
|
||||
for (int i = 0; i < HMAC_BLOCK_SIZE_BYTES; i++) {
|
||||
dest[i] ^= padByte;
|
||||
}
|
||||
}
|
||||
57
arduino/libraries/Temboo/src/utility/tmbhmac.h
Normal file
57
arduino/libraries/Temboo/src/utility/tmbhmac.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TMBHMAC_H_
|
||||
#define TMBHMAC_H_
|
||||
#include "tmbmd5.h"
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
#define HMAC_HASH_SIZE_BYTES (MD5_HASH_SIZE_BYTES)
|
||||
#define HMAC_BLOCK_SIZE_BYTES (MD5_BLOCK_SIZE_BYTES)
|
||||
|
||||
#define HMAC_HEX_SIZE_BYTES (HMAC_HASH_SIZE_BYTES * 2)
|
||||
|
||||
class HMAC
|
||||
{
|
||||
public:
|
||||
HMAC();
|
||||
HMAC(const uint8_t* key, uint32_t keyLength);
|
||||
void init(const uint8_t* key, uint32_t keyLength);
|
||||
void process(const uint8_t* msg, uint32_t msgLength);
|
||||
void finish(uint8_t* dest);
|
||||
void finishHex(char* dest);
|
||||
enum {
|
||||
HMAC_OK = 0,
|
||||
HMAC_ERROR,
|
||||
HMAC_FAIL_TESTVECTOR
|
||||
};
|
||||
|
||||
private:
|
||||
MD5 m_md5;
|
||||
const uint8_t* m_key;
|
||||
uint32_t m_keyLength;
|
||||
|
||||
void constructKeyPad(uint8_t* dest, const uint8_t* key, uint32_t keyLength, uint8_t padByte);
|
||||
void toHex(uint8_t* hmac, char* dest);
|
||||
};
|
||||
#endif
|
||||
|
||||
213
arduino/libraries/Temboo/src/utility/tmbmd5.cpp
Normal file
213
arduino/libraries/Temboo/src/utility/tmbmd5.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "tmbmd5.h"
|
||||
|
||||
|
||||
static const uint8_t Worder[64] PROGMEM = {
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
|
||||
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
|
||||
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
|
||||
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9
|
||||
};
|
||||
|
||||
static const uint8_t Rorder[64] PROGMEM = {
|
||||
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
|
||||
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
|
||||
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
|
||||
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
|
||||
};
|
||||
|
||||
static const uint32_t Korder[64] PROGMEM = {
|
||||
0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL,
|
||||
0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL,
|
||||
0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL,
|
||||
0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL,
|
||||
0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL,
|
||||
0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL,
|
||||
0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL,
|
||||
0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL
|
||||
};
|
||||
|
||||
|
||||
MD5::MD5() {
|
||||
init();
|
||||
}
|
||||
|
||||
void MD5::init() {
|
||||
m_state[0] = 0x67452301UL;
|
||||
m_state[1] = 0xefcdab89UL;
|
||||
m_state[2] = 0x98badcfeUL;
|
||||
m_state[3] = 0x10325476UL;
|
||||
m_bufLength = 0;
|
||||
m_msgLengthBits = 0;
|
||||
}
|
||||
|
||||
int MD5::compress(const uint8_t* buf) {
|
||||
uint32_t a;
|
||||
uint32_t b;
|
||||
uint32_t c;
|
||||
uint32_t d;
|
||||
uint32_t i;
|
||||
uint32_t W[16];
|
||||
uint32_t t;
|
||||
|
||||
// Copy data into W[0..15] in an endian-agnostic way
|
||||
for (i = 0; i < 16; i++) {
|
||||
W[i] = ((uint32_t)(buf[3])) << 24
|
||||
| ((uint32_t)(buf[2])) << 16
|
||||
| ((uint32_t)(buf[1])) << 8
|
||||
| ((uint32_t)(buf[0]));
|
||||
buf += 4;
|
||||
}
|
||||
|
||||
// copy current state
|
||||
a = m_state[0];
|
||||
b = m_state[1];
|
||||
c = m_state[2];
|
||||
d = m_state[3];
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
FF(&a,b,c,d,W[pgm_read_byte(&Worder[i])],pgm_read_byte(&Rorder[i]),pgm_read_dword(&Korder[i]));
|
||||
t = d; d = c; c = b; b = a; a = t;
|
||||
}
|
||||
|
||||
for (; i < 32; ++i) {
|
||||
GG(&a,b,c,d,W[pgm_read_byte(&Worder[i])],pgm_read_byte(&Rorder[i]),pgm_read_dword(&Korder[i]));
|
||||
t = d; d = c; c = b; b = a; a = t;
|
||||
}
|
||||
|
||||
for (; i < 48; ++i) {
|
||||
HH(&a,b,c,d,W[pgm_read_byte(&Worder[i])],pgm_read_byte(&Rorder[i]),pgm_read_dword(&Korder[i]));
|
||||
t = d; d = c; c = b; b = a; a = t;
|
||||
}
|
||||
|
||||
for (; i < 64; ++i) {
|
||||
II(&a,b,c,d,W[pgm_read_byte(&Worder[i])],pgm_read_byte(&Rorder[i]),pgm_read_dword(&Korder[i]));
|
||||
t = d; d = c; c = b; b = a; a = t;
|
||||
}
|
||||
|
||||
m_state[0] = m_state[0] + a;
|
||||
m_state[1] = m_state[1] + b;
|
||||
m_state[2] = m_state[2] + c;
|
||||
m_state[3] = m_state[3] + d;
|
||||
|
||||
return MD5::MD5_OK;
|
||||
}
|
||||
|
||||
int MD5::process (const uint8_t* msg, uint32_t msgLengthBytes) {
|
||||
uint32_t n;
|
||||
int err;
|
||||
|
||||
if (m_bufLength >= sizeof(m_buf)) {
|
||||
return MD5::MD5_INVALID_ARG;
|
||||
}
|
||||
|
||||
while (msgLengthBytes > 0) {
|
||||
|
||||
// Process the input msg in 64 byte chunks
|
||||
if (m_bufLength == 0 && msgLengthBytes >= 64) {
|
||||
err = compress (msg);
|
||||
if (err != MD5::MD5_OK) {
|
||||
return err;
|
||||
}
|
||||
m_msgLengthBits += 64 * 8;
|
||||
msg += 64;
|
||||
msgLengthBytes -= 64;
|
||||
} else {
|
||||
n = 64 - m_bufLength;
|
||||
if (msgLengthBytes < n) {
|
||||
n = msgLengthBytes;
|
||||
}
|
||||
memcpy(m_buf + m_bufLength, msg, (size_t)n);
|
||||
m_bufLength += n;
|
||||
msg += n;
|
||||
msgLengthBytes -= n;
|
||||
if (m_bufLength == 64) {
|
||||
err = compress (m_buf);
|
||||
if (err != MD5::MD5_OK) {
|
||||
return err;
|
||||
}
|
||||
m_msgLengthBits += 64 * 8;
|
||||
m_bufLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MD5::MD5_OK;
|
||||
}
|
||||
|
||||
|
||||
int MD5::finish(uint8_t* out) {
|
||||
int i;
|
||||
|
||||
if (m_bufLength >= sizeof(m_buf)) {
|
||||
return MD5::MD5_INVALID_ARG;
|
||||
}
|
||||
|
||||
m_msgLengthBits += m_bufLength * 8;
|
||||
|
||||
// append a '1' bit (right-padded with zeros)
|
||||
m_buf[m_bufLength++] = (uint8_t)0x80;
|
||||
|
||||
// if the bufLength is > 56 bytes, pad with zeros then compress.
|
||||
// Then fall back to padding with zeros and length encoding like normal.
|
||||
if (m_bufLength > 56) {
|
||||
while (m_bufLength < 64) {
|
||||
m_buf[m_bufLength++] = (uint8_t)0;
|
||||
}
|
||||
compress(m_buf);
|
||||
m_bufLength = 0;
|
||||
}
|
||||
|
||||
// pad with zeroes up to 56 bytes.
|
||||
// (Why 56? because we store the 8-byte length at the end.)
|
||||
// (What if bufLength == 56? Perfect! No padding prior to 8-byte length needed.)
|
||||
while (m_bufLength < 56) {
|
||||
m_buf[m_bufLength++] = (uint8_t)0;
|
||||
}
|
||||
|
||||
// add the length in an endian-agnostic way
|
||||
m_buf[56] = (uint8_t)((m_msgLengthBits ) & 255);
|
||||
m_buf[57] = (uint8_t)((m_msgLengthBits >> 8) & 255);
|
||||
m_buf[58] = (uint8_t)((m_msgLengthBits >> 16) & 255);
|
||||
m_buf[59] = (uint8_t)((m_msgLengthBits >> 24) & 255);
|
||||
m_buf[60] = (uint8_t)((m_msgLengthBits >> 32) & 255);
|
||||
m_buf[61] = (uint8_t)((m_msgLengthBits >> 40) & 255);
|
||||
m_buf[62] = (uint8_t)((m_msgLengthBits >> 48) & 255);
|
||||
m_buf[63] = (uint8_t)((m_msgLengthBits >> 56) & 255);
|
||||
|
||||
compress(m_buf);
|
||||
|
||||
// copy the state to the output in an endian-agnostic way
|
||||
for (i = 0; i < 4; i++) {
|
||||
out[0] = m_state[i] & 255;
|
||||
out[1] = (m_state[i] >> 8) & 255;
|
||||
out[2] = (m_state[i] >> 16) & 255;
|
||||
out[3] = (m_state[i] >> 24) & 255;
|
||||
out += 4;
|
||||
}
|
||||
return MD5::MD5_OK;
|
||||
}
|
||||
|
||||
72
arduino/libraries/Temboo/src/utility/tmbmd5.h
Normal file
72
arduino/libraries/Temboo/src/utility/tmbmd5.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
###############################################################################
|
||||
#
|
||||
# Temboo Arduino library
|
||||
#
|
||||
# Copyright 2017, Temboo Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
# either express or implied. See the License for the specific
|
||||
# language governing permissions and limitations under the License.
|
||||
#
|
||||
###############################################################################
|
||||
*/
|
||||
|
||||
#ifndef TMBMD5_H_
|
||||
#define TMBMD5_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "TembooGlobal.h"
|
||||
|
||||
#define MD5_HASH_SIZE_BITS (128)
|
||||
#define MD5_HASH_SIZE_BYTES (MD5_HASH_SIZE_BITS/8)
|
||||
|
||||
#define MD5_BLOCK_SIZE_BITS (512)
|
||||
#define MD5_BLOCK_SIZE_BYTES (MD5_BLOCK_SIZE_BITS/8)
|
||||
|
||||
|
||||
#define ROL(x, y) ( (((uint32_t)(x)<<(uint32_t)((y)&31)) | (((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)(32-((y)&31)))) & 0xFFFFFFFFUL)
|
||||
class MD5 {
|
||||
|
||||
public:
|
||||
MD5();
|
||||
void init();
|
||||
int process(const uint8_t* in, uint32_t inlen);
|
||||
int finish(uint8_t* hash);
|
||||
enum {
|
||||
MD5_OK = 0,
|
||||
MD5_ERROR,
|
||||
MD5_INVALID_ARG,
|
||||
MD5_FAIL_TESTVECTOR
|
||||
};
|
||||
|
||||
private:
|
||||
uint64_t m_msgLengthBits;
|
||||
uint32_t m_state[4];
|
||||
uint32_t m_bufLength;
|
||||
uint8_t m_buf[64];
|
||||
|
||||
int compress(const uint8_t* buf);
|
||||
|
||||
inline uint32_t F_(uint32_t x, uint32_t y, uint32_t z) { return (z ^ (x & (y ^ z))); }
|
||||
inline uint32_t G_(uint32_t x, uint32_t y, uint32_t z) { return (y ^ (z & (y ^ x))); }
|
||||
inline uint32_t H_(uint32_t x, uint32_t y, uint32_t z) { return (x^y^z); }
|
||||
inline uint32_t I_(uint32_t x, uint32_t y, uint32_t z) { return (y^(x|(~z))); }
|
||||
|
||||
|
||||
inline void FF(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t s, uint32_t t) { *a = (*a + F_(b,c,d) + M + t); *a = ROL(*a, s) + b; }
|
||||
inline void GG(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t s, uint32_t t) { *a = (*a + G_(b,c,d) + M + t); *a = ROL(*a, s) + b; }
|
||||
inline void HH(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t s, uint32_t t) { *a = (*a + H_(b,c,d) + M + t); *a = ROL(*a, s) + b; }
|
||||
inline void II(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t s, uint32_t t) { *a = (*a + I_(b,c,d) + M + t); *a = ROL(*a, s) + b; }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user