mbedOS5 and ESP8266/ESP8285 support

This commit is contained in:
Oguz Bastemur 2019-01-24 16:46:05 -08:00
Родитель bcc2027d04
Коммит 28ea9de0c9
63 изменённых файлов: 13252 добавлений и 2 удалений

13
.gitignore поставляемый
Просмотреть файл

@ -298,4 +298,15 @@ __pycache__/
.arduino15/
BUILD/
.iotz.mxchip.tweak
devkit-sdk/
devkit-sdk/
.DS_Store
MBED_OS/iotz-mbed-deps/
MBED_OS/mbed_config.h
MBED_OS/mbed_settings.py
MBED_OS/Makefile
MBED_OS/GettingStarted.html
MBED_OS/.mbedignore
MBED_OS/.mbed
MBED_OS/mbed-os/

49
ESP8266/README.md Normal file
Просмотреть файл

@ -0,0 +1,49 @@
# ESP8266 basic example for Azure IoT Central
- Visit [AzureIoTCentral](https://apps.azureiotcentral.com) and create a `new application`.
- Select `Sample Devkits`
- Add a new `mxchip` device. (real device) (Under the `Device Explorer`)
- Browse into device window and click/open `Connect` on the top-right of the device screen
- Grab `scopeId`, `device Id` and `primary key` and fill the necessary parts under `esp8266_arduino.ino`
```
// #define WIFI_SSID "<ENTER WIFI SSID HERE>"
// #define WIFI_PASSWORD "<ENTER WIFI PASSWORD HERE>"
// const char* scopeId = "<ENTER SCOPE ID HERE>";
// const char* deviceId = "<ENTER DEVICE ID HERE>";
// const char* deviceKey = "<ENTER DEVICE primary/secondary KEY HERE>";
```
Create a `src/` folder and copy `../iotc` folder into it.
Compile it! and deploy to your device.
- Download Arduino-CLI from [this link](https://github.com/arduino/arduino-cli#download-the-latest-stable-release)
Setup the environment; (under the project folder)
```
arduino-cli-0.3.3 core install esp8266:esp8266
arduino-cli-0.3.3 board attach esp8266:esp8266:nodemcu
```
Compile!
```
arduino-cli-0.3.3 compile
```
Upload
```
arduino-cli-0.3.3 upload -p <PORT / DEV?? i.e. => /dev/cu.SLAB_USBtoUART >
```
Monitoring?
```
npm install -g nodemcu-tool
```
Assuming the port/dev for the board is `/dev/cu.SLAB_USBtoUART`
```
nodemcu-tool -p /dev/cu.SLAB_USBtoUART -b 9600 terminal
```

102
ESP8266/esp8266_arduino.ino Normal file
Просмотреть файл

@ -0,0 +1,102 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "src/iotc/iotc.h"
#include "src/iotc/common/string_buffer.h"
#include <ESP8266WiFi.h>
// #define WIFI_SSID "<ENTER WIFI SSID HERE>"
// #define WIFI_PASSWORD "<ENTER WIFI PASSWORD HERE>"
// const char* scopeId = "<ENTER SCOPE ID HERE>";
// const char* deviceId = "<ENTER DEVICE ID HERE>";
// const char* deviceKey = "<ENTER DEVICE primary/secondary KEY HERE>";
static IOTContext context = NULL;
void connect_wifi() {
Serial.begin(9600);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
LOG_VERBOSE("Connecting WiFi..");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
static bool isConnected = false;
void onEvent(IOTContext ctx, IOTCallbackInfo *callbackInfo) {
if (strcmp(callbackInfo->eventName, "ConnectionStatus") == 0) {
LOG_VERBOSE("Is connected ? %s (%d)", callbackInfo->statusCode == IOTC_CONNECTION_OK ? "YES" : "NO", callbackInfo->statusCode);
isConnected = callbackInfo->statusCode == IOTC_CONNECTION_OK;
}
AzureIOT::StringBuffer buffer;
if (callbackInfo->payloadLength > 0) {
buffer.initialize(callbackInfo->payload, callbackInfo->payloadLength);
}
LOG_VERBOSE("- [%s] event was received. Payload => %s", callbackInfo->eventName, buffer.getLength() ? *buffer : "EMPTY");
if (strcmp(callbackInfo->eventName, "Command") == 0) {
LOG_VERBOSE("- Command name was => %s\r\n", callbackInfo->tag);
}
}
static unsigned prevMillis = 0, loopId = 0;
void setup()
{
connect_wifi();
// Azure IOT Central setup
int errorCode = iotc_init_context(&context);
if (errorCode != 0) {
LOG_ERROR("Error initializing IOTC. Code %d", errorCode);
return;
}
iotc_set_logging(IOTC_LOGGING_API_ONLY);
// for the simplicity of this sample, used same callback for all the events below
iotc_on(context, "MessageSent", onEvent, NULL);
iotc_on(context, "Command", onEvent, NULL);
iotc_on(context, "ConnectionStatus", onEvent, NULL);
iotc_on(context, "SettingsUpdated", onEvent, NULL);
iotc_on(context, "Error", onEvent, NULL);
errorCode = iotc_connect(context, scopeId, deviceKey, deviceId, IOTC_CONNECT_SYMM_KEY);
if (errorCode != 0) {
LOG_ERROR("Error @ iotc_connect. Code %d", errorCode);
return;
}
prevMillis = millis();
}
void loop()
{
if (isConnected) {
unsigned long ms = millis();
if (ms - prevMillis > 15000) { // send telemetry every 15 seconds
char msg[64] = {0};
int pos = 0, errorCode = 0;
prevMillis = ms;
if (loopId++ % 2 == 0) {
pos = snprintf(msg, sizeof(msg) - 1, "{\"accelerometerX\": %d}", 10 + (rand() % 20));
errorCode = iotc_send_telemetry(context, msg, pos);
} else {
pos = snprintf(msg, sizeof(msg) - 1, "{\"dieNumber\":%d}", 1 + (rand() % 5));
errorCode = iotc_send_property(context, msg, pos);
}
msg[pos] = 0;
if (errorCode != 0) {
LOG_ERROR("Sending message has failed with error code %d", errorCode);
}
}
iotc_do_work(context); // do background work for iotc
}
}

1
ESP8266/sketch.json Normal file
Просмотреть файл

@ -0,0 +1 @@
{"cpu":{"fqbn":"esp8266:esp8266:nodemcu","name":"","network":false,"type":""}}

Просмотреть файл

@ -0,0 +1,495 @@
/*
PubSubClient.cpp - A simple client for MQTT.
Nick O'Leary
http://knolleary.net
*/
#ifdef ARDUINO
#include "PubSubClient.h"
#include "Arduino.h"
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client* client) {
this->_state = MQTT_DISCONNECTED;
setServer(domain, port);
setClient(client);
this->stream = NULL;
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
return connect(id,user,pass,0,0,0,0,1);
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
if (!connected()) {
int result = 0;
if (domain != NULL) {
result = _client->connect(this->domain, this->port);
} else {
result = _client->connect(this->ip, this->port);
}
if (result == 1) {
nextMsgId = 1;
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
unsigned int j;
#if MQTT_VERSION == MQTT_VERSION_3_1
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
#define MQTT_HEADER_VERSION_LENGTH 9
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
#define MQTT_HEADER_VERSION_LENGTH 7
#endif
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
buffer[length++] = d[j];
}
uint8_t v;
if (willTopic) {
v = 0x04|(willQos<<3)|(willRetain<<5);
} else {
v = 0x00;
}
if (cleanSession) {
v = v|0x02;
}
if(user != NULL) {
v = v|0x80;
if(pass != NULL) {
v = v|(0x80>>1);
}
}
buffer[length++] = v;
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
CHECK_STRING_LENGTH(length,id)
length = writeString(id,buffer,length);
if (willTopic) {
CHECK_STRING_LENGTH(length,willTopic)
length = writeString(willTopic,buffer,length);
CHECK_STRING_LENGTH(length,willMessage)
length = writeString(willMessage,buffer,length);
}
if(user != NULL) {
CHECK_STRING_LENGTH(length,user)
length = writeString(user,buffer,length);
if(pass != NULL) {
CHECK_STRING_LENGTH(length,pass)
length = writeString(pass,buffer,length);
}
}
write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
lastInActivity = lastOutActivity = millis();
while (!_client->available()) {
unsigned long t = millis();
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
_state = MQTT_CONNECTION_TIMEOUT;
_client->stop();
return false;
}
}
uint8_t llen;
uint16_t len = readPacket(&llen);
if (len == 4) {
if (buffer[3] == 0) {
lastInActivity = millis();
pingOutstanding = false;
_state = MQTT_CONNECTED;
return true;
} else {
_state = buffer[3];
}
}
_client->stop();
} else {
_state = MQTT_CONNECT_FAILED;
}
return false;
}
return true;
}
// reads a byte into result
boolean PubSubClient::readByte(uint8_t * result) {
uint32_t previousMillis = millis();
while(!_client->available()) {
yield();
uint32_t currentMillis = millis();
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
return false;
}
}
*result = _client->read();
return true;
}
// reads a byte into result[*index] and increments index
boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
uint16_t current_index = *index;
uint8_t * write_address = &(result[current_index]);
if(readByte(write_address)){
*index = current_index + 1;
return true;
}
return false;
}
uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
uint16_t len = 0;
if(!readByte(buffer, &len)) return 0;
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
uint32_t multiplier = 1;
uint16_t length = 0;
uint8_t digit = 0;
uint16_t skip = 0;
uint8_t start = 0;
do {
if (len == 5) {
// Invalid remaining length encoding - kill the connection
_state = MQTT_DISCONNECTED;
_client->stop();
return 0;
}
if(!readByte(&digit)) return 0;
buffer[len++] = digit;
length += (digit & 127) * multiplier;
multiplier *= 128;
} while ((digit & 128) != 0);
*lengthLength = len-1;
if (isPublish) {
// Read in topic length to calculate bytes to skip over for Stream writing
if(!readByte(buffer, &len)) return 0;
if(!readByte(buffer, &len)) return 0;
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
start = 2;
if (buffer[0]&MQTTQOS1) {
// skip message id
skip += 2;
}
}
for (uint16_t i = start;i<length;i++) {
if(!readByte(&digit)) return 0;
if (this->stream) {
if (isPublish && len-*lengthLength-2>skip) {
this->stream->write(digit);
}
}
if (len < MQTT_MAX_PACKET_SIZE) {
buffer[len] = digit;
}
len++;
}
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
len = 0; // This will cause the packet to be ignored.
}
return len;
}
boolean PubSubClient::loop() {
if (connected()) {
unsigned long t = millis();
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
if (pingOutstanding) {
this->_state = MQTT_CONNECTION_TIMEOUT;
_client->stop();
return false;
} else {
buffer[0] = MQTTPINGREQ;
buffer[1] = 0;
_client->write(buffer,2);
lastOutActivity = t;
lastInActivity = t;
pingOutstanding = true;
}
}
if (_client->available()) {
uint8_t llen;
uint16_t len = readPacket(&llen);
uint16_t msgId = 0;
uint8_t *payload;
if (len > 0) {
lastInActivity = t;
uint8_t type = buffer[0]&0xF0;
if (type == MQTTPUBLISH) {
if (callback) {
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
char *topic = (char*) buffer+llen+2;
// msgId only present for QOS>0
if ((buffer[0]&0x06) == MQTTQOS1) {
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
payload = buffer+llen+3+tl+2;
callback(topic,payload,len-llen-3-tl-2);
buffer[0] = MQTTPUBACK;
buffer[1] = 2;
buffer[2] = (msgId >> 8);
buffer[3] = (msgId & 0xFF);
_client->write(buffer,4);
lastOutActivity = t;
} else {
payload = buffer+llen+3+tl;
callback(topic,payload,len-llen-3-tl);
}
}
} else if (type == MQTTPINGREQ) {
buffer[0] = MQTTPINGRESP;
buffer[1] = 0;
_client->write(buffer,2);
} else if (type == MQTTPINGRESP) {
pingOutstanding = false;
}
} else if (!connected()) {
// readPacket has closed the connection
return false;
}
}
return true;
}
return false;
}
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
if (connected()) {
if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2 + strlen(topic) + plength) {
// Too long
return false;
}
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic, buffer, length);
uint16_t i;
for (i = 0; i < plength; i++) {
buffer[length++] = payload[i];
}
uint8_t header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
return write(header, buffer, length - MQTT_MAX_HEADER_SIZE);
}
return false;
}
boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
if (connected()) {
// Send the header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic,buffer,length);
uint16_t i;
uint8_t header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
lastOutActivity = millis();
return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
}
return false;
}
int PubSubClient::endPublish() {
return 1;
}
size_t PubSubClient::write(uint8_t data) {
lastOutActivity = millis();
return _client->write(data);
}
size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
lastOutActivity = millis();
return _client->write(buffer,size);
}
size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
uint8_t lenBuf[4];
uint8_t llen = 0;
uint8_t digit;
uint8_t pos = 0;
uint16_t len = length;
do {
digit = len % 128;
len = len / 128;
if (len > 0) {
digit |= 0x80;
}
lenBuf[pos++] = digit;
llen++;
} while(len>0);
buf[4-llen] = header;
for (int i=0;i<llen;i++) {
buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
}
return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
}
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
uint16_t rc;
uint8_t hlen = buildHeader(header, buf, length);
#ifdef MQTT_MAX_TRANSFER_SIZE
uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
uint16_t bytesRemaining = length+hlen; //Match the length type
uint8_t bytesToWrite;
boolean result = true;
while((bytesRemaining > 0) && result) {
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
rc = _client->write(writeBuf,bytesToWrite);
result = (rc == bytesToWrite);
bytesRemaining -= rc;
writeBuf += rc;
}
return result;
#else
rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
lastOutActivity = millis();
return (rc == hlen+length);
#endif
}
boolean PubSubClient::subscribe(const char* topic) {
return subscribe(topic, 0);
}
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
if (qos > 1) {
return false;
}
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
// Too long
return false;
}
if (connected()) {
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++;
if (nextMsgId == 0) {
nextMsgId = 1;
}
buffer[length++] = (nextMsgId >> 8);
buffer[length++] = (nextMsgId & 0xFF);
length = writeString((char*)topic, buffer,length);
buffer[length++] = qos;
return write(MQTTSUBSCRIBE|MQTTQOS1, buffer, length - MQTT_MAX_HEADER_SIZE);
}
return false;
}
boolean PubSubClient::unsubscribe(const char* topic) {
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
// Too long
return false;
}
if (connected()) {
uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++;
if (nextMsgId == 0) {
nextMsgId = 1;
}
buffer[length++] = (nextMsgId >> 8);
buffer[length++] = (nextMsgId & 0xFF);
length = writeString(topic, buffer,length);
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
}
return false;
}
void PubSubClient::disconnect() {
buffer[0] = MQTTDISCONNECT;
buffer[1] = 0;
_client->write(buffer,2);
_state = MQTT_DISCONNECTED;
_client->flush();
_client->stop();
lastInActivity = lastOutActivity = millis();
}
uint16_t PubSubClient::writeString(const char* str, uint8_t* buf, uint16_t pos) {
const char* idp = str;
uint16_t i = 0;
pos += 2;
while (*idp) {
buf[pos++] = *idp++;
i++;
}
buf[pos-i-2] = (i >> 8);
buf[pos-i-1] = (i & 0xFF);
return pos;
}
boolean PubSubClient::connected() {
boolean rc;
if (_client == NULL ) {
rc = false;
} else {
rc = (int)_client->connected();
if (!rc) {
if (this->_state == MQTT_CONNECTED) {
this->_state = MQTT_CONNECTION_LOST;
_client->flush();
_client->stop();
}
}
}
return rc;
}
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
return setServer(addr,port);
}
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
this->ip = ip;
this->port = port;
this->domain = NULL;
return *this;
}
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
this->domain = domain;
this->port = port;
return *this;
}
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
this->callback = callback;
return *this;
}
PubSubClient& PubSubClient::setClient(Client* client){
this->_client = client;
return *this;
}
PubSubClient& PubSubClient::setStream(Stream& stream){
this->stream = &stream;
return *this;
}
int PubSubClient::state() {
return this->_state;
}
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,156 @@
/*
PubSubClient.h - A simple client for MQTT.
Nick O'Leary
http://knolleary.net
*/
#ifdef ARDUINO
#ifndef PubSubClient_h
#define PubSubClient_h
#include <Arduino.h>
#include "IPAddress.h"
#include "Client.h"
#include "Stream.h"
#define MQTT_VERSION_3_1 3
#define MQTT_VERSION_3_1_1 4
// MQTT_VERSION : Pick the version
//#define MQTT_VERSION MQTT_VERSION_3_1
#ifndef MQTT_VERSION
#define MQTT_VERSION MQTT_VERSION_3_1_1
#endif
// MQTT_MAX_PACKET_SIZE : Maximum packet size
#ifndef MQTT_MAX_PACKET_SIZE
#define MQTT_MAX_PACKET_SIZE 512
#endif
// MQTT_KEEPALIVE : keepAlive interval in Seconds
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 15
#endif
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
#ifndef MQTT_SOCKET_TIMEOUT
#define MQTT_SOCKET_TIMEOUT 15
#endif
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
// pass the entire MQTT packet in each write call.
// #define MQTT_MAX_TRANSFER_SIZE 80
// Possible values for client.state()
#define MQTT_CONNECTION_TIMEOUT -4
#define MQTT_CONNECTION_LOST -3
#define MQTT_CONNECT_FAILED -2
#define MQTT_DISCONNECTED -1
#define MQTT_CONNECTED 0
#define MQTT_CONNECT_BAD_PROTOCOL 1
#define MQTT_CONNECT_BAD_CLIENT_ID 2
#define MQTT_CONNECT_UNAVAILABLE 3
#define MQTT_CONNECT_BAD_CREDENTIALS 4
#define MQTT_CONNECT_UNAUTHORIZED 5
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
#define MQTTPUBLISH 3 << 4 // Publish message
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
#define MQTTPINGREQ 12 << 4 // PING Request
#define MQTTPINGRESP 13 << 4 // PING Response
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
#define MQTTReserved 15 << 4 // Reserved
#define MQTTQOS0 (0 << 1)
#define MQTTQOS1 (1 << 1)
#define MQTTQOS2 (2 << 1)
// Maximum size of fixed header and variable length size header
#define MQTT_MAX_HEADER_SIZE 5
#if defined(ESP8266) || defined(ESP32)
#include <functional>
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
#else
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
#endif
#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
class PubSubClient : public Print {
private:
Client* _client;
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
uint16_t nextMsgId;
unsigned long lastOutActivity;
unsigned long lastInActivity;
bool pingOutstanding;
MQTT_CALLBACK_SIGNATURE;
uint16_t readPacket(uint8_t*);
boolean readByte(uint8_t * result);
boolean readByte(uint8_t * result, uint16_t * index);
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
// Build up the header ready to send
// Returns the size of the header
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
IPAddress ip;
const char* domain;
uint16_t port;
Stream* stream;
int _state;
public:
PubSubClient(const char*, uint16_t, Client* client);
PubSubClient& setServer(IPAddress ip, uint16_t port);
PubSubClient& setServer(uint8_t * ip, uint16_t port);
PubSubClient& setServer(const char * domain, uint16_t port);
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
PubSubClient& setClient(Client* client);
PubSubClient& setStream(Stream& stream);
boolean connect(const char* id, const char* user, const char* pass);
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
void disconnect();
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
// Start to publish a message.
// This API:
// beginPublish(...)
// one or more calls to write(...)
// endPublish()
// Allows for arbitrarily large payloads to be sent without them having to be copied into
// a new buffer and held in memory at one time
// Returns 1 if the message was started successfully, 0 if there was an error
boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
// Finish off this publish message (started with beginPublish)
// Returns 1 if the packet was sent successfully, 0 if there was an error
int endPublish();
// Write a single byte of payload (only to be used with beginPublish/endPublish)
virtual size_t write(uint8_t);
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
// Returns the number of bytes written
virtual size_t write(const uint8_t *buffer, size_t size);
boolean subscribe(const char* topic);
boolean subscribe(const char* topic, uint8_t qos);
boolean unsubscribe(const char* topic);
boolean loop();
boolean connected();
int state();
};
#endif
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "../common/iotc_platform.h"
#if defined(ARDUINO) && defined(USE_LIGHT_CLIENT)
#include "../common/json.h"
#include "../common/iotc_internal.h"
#include <SPI.h>
#include <stdarg.h>
int mqtt_publish(IOTContextInternal *internal, const char* topic, unsigned long topic_length,
const char* msg, unsigned long msg_length) {
if (!internal->mqttClient->publish(topic, (const uint8_t*)msg, msg_length, false)) {
return 1;
}
return 0;
}
void IOTC_LOG(const __FlashStringHelper *format, ...) {
if (getLogLevel() > IOTC_LOGGING_DISABLED)
va_list ap;
va_start(ap, format);
AzureIOT::StringBuffer buffer(STRING_BUFFER_1024);
buffer.setLength(vsnprintf(*buffer, STRING_BUFFER_1024, (const char *)format, ap));
Serial.println(*buffer);
va_end(ap);
}
}
#endif // defined(ARDUINO) && defined(USE_LIGHT_CLIENT)

Просмотреть файл

@ -0,0 +1,351 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "../common/iotc_platform.h"
#if defined(ARDUINO) && defined(USE_LIGHT_CLIENT)
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "../common/json.h"
#include "../common/iotc_internal.h"
unsigned long _getNow()
{
int retryCount = 0;
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
unsigned long retVal = 0;
retry_getNow:
IPAddress address(129, 6, 15, retryCount % 2 == 1 ? 28 : 29); // time.nist.gov NTP server
WiFiUDP Udp;
if (Udp.begin(2390) == 0) {
if (retryCount < 5) {
retryCount++;
goto retry_getNow;
}
IOTC_LOG(F("ERROR: couldn't fetch the time from NTP."));
return retVal;
}
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
Udp.beginPacket(address, 123);
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
// wait to see if a reply is available
WAITMS(1000);
if (Udp.parsePacket() ) {
Udp.read(packetBuffer, NTP_PACKET_SIZE);
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
unsigned long secsSince1900 = highWord << 16 | lowWord;
retVal = (secsSince1900 - 2208988800UL);
} else {
if (retryCount < 5) {
retryCount++;
goto retry_getNow;
}
IOTC_LOG(F("ERROR: couldn't fetch the time from NTP."));
}
Udp.stop();
return retVal;
}
static unsigned long g_udpTime = 0, g_lastRead = 0;
unsigned long getNow() {
unsigned long ms = millis();
if (g_udpTime == 0 || ms - g_lastRead > NTP_SYNC_PERIOD) {
g_udpTime = _getNow();
g_lastRead = ms;
}
return g_udpTime + ((ms - g_lastRead) / 1000);
}
int _getOperationId(const char* dpsEndpoint, const char* scopeId, const char* deviceId,
const char* authHeader, char *operationId, char *hostName) {
ARDUINO_WIFI_SSL_CLIENT client;
int exitCode = 0;
#ifndef USES_WIFI101
client.setCACert((const uint8_t*)SSL_CA_PEM_DEF, strlen((const char*)SSL_CA_PEM_DEF));
#endif
int retry = 0;
while (retry < 5 && !client.connect(dpsEndpoint, AZURE_HTTPS_SERVER_PORT)) retry++;
if (!client.connected()) {
IOTC_LOG(F("ERROR: DPS endpoint %s call has failed."), hostName == NULL ? "PUT" : "GET");
return 1;
}
AzureIOT::StringBuffer tmpBuffer(STRING_BUFFER_1024);
AzureIOT::StringBuffer deviceIdEncoded(deviceId, strlen(deviceId));
deviceIdEncoded.urlEncode();
size_t size = 0;
if (hostName == NULL) {
size = strlen("{\"registrationId\":\"%s\"}") + strlen(deviceId) - 2;
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, "\
PUT /%s/registrations/%s/register?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
content-length: %d\r\n\
%s\r\n\
connection: close\r\n\
\r\n\
{\"registrationId\":\"%s\"}\r\n\
",
scopeId, *deviceIdEncoded, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, size, authHeader, deviceId);
} else {
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, "\
GET /%s/registrations/%s/operations/%s?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
%s\r\n\
connection: close\r\n\
\r\n",
scopeId, *deviceIdEncoded, operationId, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, authHeader);
}
assert(size != 0 && size < STRING_BUFFER_1024);
tmpBuffer.setLength(size);
client.println(*tmpBuffer);
int index = 0;
while(!client.available()) {
WAITMS(100);
if (index++ > IOTC_SERVER_RESPONSE_TIMEOUT * 10) {
// 20 secs..
client.stop();
IOTC_LOG(F("ERROR: DPS (%s) request has failed. (Server didn't answer within 20 secs.)"), hostName == NULL ? "PUT" : "GET");
return 1;
}
}
index = 0;
bool enableSaving = false;
while (client.available() && index < STRING_BUFFER_1024 - 1) {
char ch = (char) client.read();
if (ch == '{') {
enableSaving = true; // don't use memory for headers
}
if (enableSaving) {
(*tmpBuffer)[index++] = ch;
}
}
tmpBuffer.setLength(index);
const char* lookFor = hostName == NULL ? "{\"operationId\":\"" : "\"assignedHub\":\"";
index = tmpBuffer.indexOf(lookFor, strlen(lookFor), 0);
if (index == -1) {
error_exit:
IOTC_LOG(F("ERROR: DPS (%s) request has failed.\r\n%s"), hostName == NULL ? "PUT" : "GET", *tmpBuffer);
exitCode = 1;
goto exit_operationId;
} else {
index += strlen(lookFor);
int index2 = tmpBuffer.indexOf("\"", 1, index + 1);
if (index2 == -1) goto error_exit;
tmpBuffer.setLength(index2);
strcpy(hostName == NULL ? operationId : hostName, (*tmpBuffer) + index);
}
exit_operationId:
client.stop();
return exitCode;
}
int getHubHostName(const char* dpsEndpoint, const char *scopeId, const char* deviceId, const char* key, char *hostName) {
AzureIOT::StringBuffer authHeader(STRING_BUFFER_256);
size_t size = 0;
IOTC_LOG(F("- iotc.dps : getting auth..."));
if (getDPSAuthString(scopeId, deviceId, key, *authHeader, STRING_BUFFER_256, size)) {
IOTC_LOG(F("ERROR: getDPSAuthString has failed"));
return 1;
}
IOTC_LOG(F("- iotc.dps : getting operation id..."));
AzureIOT::StringBuffer operationId(STRING_BUFFER_64);
int retval = 0;
if ((retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, NULL)) == 0) {
WAITMS(4000);
IOTC_LOG(F("- iotc.dps : getting host name..."));
for (int i = 0; i < 5; i++) {
retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, hostName);
if (retval == 0) break;
WAITMS(3000);
}
}
return retval;
}
static void messageArrived(char* topic, byte* data, unsigned int length) {
handlePayload((char*)data, length, topic, topic ? strlen(topic) : 0);
}
/* extern */
int iotc_free_context(IOTContext ctx) {
MUST_CALL_AFTER_INIT(ctx);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
iotc_disconnect(ctx);
free(internal);
setSingletonContext(NULL);
return 0;
}
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL(keyORcert, 512);
AzureIOT::StringBuffer hostName;
AzureIOT::StringBuffer username;
AzureIOT::StringBuffer password;
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (type == IOTC_CONNECT_CONNECTION_STRING) {
getUsernameAndPasswordFromConnectionString(keyORcert,
strlen(keyORcert), hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_SYMM_KEY) {
assert(scope != NULL && deviceId != NULL);
AzureIOT::StringBuffer tmpHostname(STRING_BUFFER_128);
if (getHubHostName(internal->endpoint == NULL ?
DEFAULT_ENDPOINT : internal->endpoint, scope, deviceId, keyORcert, *tmpHostname)) {
return 1;
}
AzureIOT::StringBuffer cstr(STRING_BUFFER_256);
int rc = snprintf(*cstr, STRING_BUFFER_256,
"HostName=%s;DeviceId=%s;SharedAccessKey=%s", *tmpHostname, deviceId, keyORcert);
assert(rc > 0 && rc < STRING_BUFFER_256);
cstr.setLength(rc);
// TODO: move into iotc_dps and do not re-parse from connection string
getUsernameAndPasswordFromConnectionString(*cstr, rc, hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_X509_CERT) {
IOTC_LOG(F("ERROR: IOTC_CONNECT_X509_CERT NOT IMPLEMENTED"));
connectionStatusCallback(IOTC_CONNECTION_DEVICE_DISABLED, (IOTContextInternal*)ctx);
return 1;
}
internal->tlsClient = new ARDUINO_WIFI_SSL_CLIENT();
#ifndef USES_WIFI101
internal->tlsClient->setCACert((const uint8_t*)SSL_CA_PEM_DEF, strlen((const char*)SSL_CA_PEM_DEF));
#endif
internal->mqttClient = new PubSubClient(*hostName, AZURE_MQTT_SERVER_PORT, internal->tlsClient);
internal->mqttClient->setCallback(messageArrived);
int retry = 0;
while(retry < 10 && !internal->mqttClient->connected()) {
if (internal->mqttClient->connect(*internal->deviceId, *username, *password)) {
break;
} else {
WAITMS(2000);
retry++;
}
}
if (!internal->mqttClient->connected()) {
IOTC_LOG(F("ERROR: MQTT client connect attempt failed. Check host, deviceId, username and password. (state %d)"),
internal->mqttClient->state());
connectionStatusCallback(IOTC_CONNECTION_BAD_CREDENTIAL, (IOTContextInternal*)ctx);
delete internal->tlsClient;
delete internal->mqttClient;
internal->tlsClient = NULL;
internal->mqttClient = NULL;
return 1;
}
AzureIOT::StringBuffer buffer(STRING_BUFFER_64);
buffer.setLength(snprintf(*buffer, 63, "devices/%s/messages/events/#", *internal->deviceId));
int errorCode = 0;
if ( (errorCode = internal->mqttClient->subscribe(*buffer)) == 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), *buffer, errorCode);
buffer.setLength(snprintf(*buffer, 63, "devices/%s/messages/devicebound/#", *internal->deviceId));
if ( (errorCode = internal->mqttClient->subscribe(*buffer)) == 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), *buffer, errorCode);
errorCode = internal->mqttClient->subscribe("$iothub/twin/PATCH/properties/desired/#"); // twin desired property changes
errorCode += internal->mqttClient->subscribe("$iothub/twin/res/#"); // twin properties response
errorCode += internal->mqttClient->subscribe("$iothub/methods/POST/#");
if (errorCode < 3)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to twin/methods etc. error code sum => %d"), errorCode);
connectionStatusCallback(IOTC_CONNECTION_OK, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_disconnect(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
if (internal->mqttClient) {
if(internal->mqttClient->connected()) {
internal->mqttClient->disconnect();
}
delete internal->mqttClient;
internal->mqttClient = NULL;
}
connectionStatusCallback(IOTC_CONNECTION_DISCONNECTED, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_do_work(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
if (!internal->mqttClient->loop()) {
if (!internal->mqttClient->connected()) {
connectionStatusCallback(IOTC_CONNECTION_DISCONNECTED, (IOTContextInternal*)ctx);
delete internal->mqttClient;
internal->mqttClient = NULL;
}
return 1;
}
return 0;
}
/* extern */
int iotc_set_network_interface(void* networkInterface) {
// NO-OP
return 0;
}
#endif // __MBED__

Просмотреть файл

@ -0,0 +1,142 @@
/*Copyright (C) 2013 Adam Rudd
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.
*/
#ifdef ARDUINO
#include "Base64.h"
#include <avr/pgmspace.h>
const char PROGMEM b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* 'Private' declarations */
inline void a3_to_a4(unsigned char * a4, unsigned char * a3);
inline void a4_to_a3(unsigned char * a3, unsigned char * a4);
inline unsigned char b64_lookup(char c);
int base64_encode(char *output, char *input, int inputLen) {
int i = 0, j = 0;
int encLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while(inputLen--) {
a3[i++] = *(input++);
if(i == 3) {
a3_to_a4(a4, a3);
for(i = 0; i < 4; i++) {
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[i]]);
}
i = 0;
}
}
if(i) {
for(j = i; j < 3; j++) {
a3[j] = '\0';
}
a3_to_a4(a4, a3);
for(j = 0; j < i + 1; j++) {
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[j]]);
}
while((i++ < 3)) {
output[encLen++] = '=';
}
}
output[encLen] = '\0';
return encLen;
}
int base64_decode(char * output, char * input, int inputLen) {
int i = 0, j = 0;
int decLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while (inputLen--) {
if(*input == '=') {
break;
}
a4[i++] = *(input++);
if (i == 4) {
for (i = 0; i <4; i++) {
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3,a4);
for (i = 0; i < 3; i++) {
output[decLen++] = a3[i];
}
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) {
a4[j] = '\0';
}
for (j = 0; j <4; j++) {
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3,a4);
for (j = 0; j < i - 1; j++) {
output[decLen++] = a3[j];
}
}
output[decLen] = '\0';
return decLen;
}
int base64_enc_len(int plainLen) {
int n = plainLen;
return (n + 2 - ((n + 2) % 3)) / 3 * 4;
}
int base64_dec_len(char * input, int inputLen) {
int i = 0;
int numEq = 0;
for(i = inputLen - 1; input[i] == '='; i--) {
numEq++;
}
return ((6 * inputLen) / 8) - numEq;
}
inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
a4[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f);
}
inline void a4_to_a3(unsigned char * a3, unsigned char * a4) {
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
}
inline unsigned char b64_lookup(char c) {
if(c >='A' && c <='Z') return c - 'A';
if(c >='a' && c <='z') return c - 71;
if(c >='0' && c <='9') return c + 4;
if(c == '+') return 62;
if(c == '/') return 63;
return -1;
}
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,81 @@
/*
* Copyright (c) 2013 Adam Rudd.
* See LICENSE for more information
*/
#ifdef ARDUINO
#ifndef _BASE64_H
#define _BASE64_H
/* b64_alphabet:
* Description: Base64 alphabet table, a mapping between integers
* and base64 digits
* Notes: This is an extern here but is defined in Base64.c
*/
extern const char b64_alphabet[];
/* base64_encode:
* Description:
* Encode a string of characters as base64
* Parameters:
* output: the output buffer for the encoding, stores the encoded string
* input: the input buffer for the encoding, stores the binary to be encoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the encoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_encode(char *output, char *input, int inputLen);
/* base64_decode:
* Description:
* Decode a base64 encoded string into bytes
* Parameters:
* output: the output buffer for the decoding,
* stores the decoded binary
* input: the input buffer for the decoding,
* stores the base64 string to be decoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the decoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_decode(char *output, char *input, int inputLen);
/* base64_enc_len:
* Description:
* Returns the length of a base64 encoded string whose decoded
* form is inputLen bytes long
* Parameters:
* inputLen: the length of the decoded string
* Return value:
* The length of a base64 encoded string whose decoded form
* is inputLen bytes long
* Requirements:
* None
*/
int base64_enc_len(int inputLen);
/* base64_dec_len:
* Description:
* Returns the length of the decoded form of a
* base64 encoded string
* Parameters:
* input: the base64 encoded string to be measured
* inputLen: the length of the base64 encoded string
* Return value:
* Returns the length of the decoded form of a
* base64 encoded string
* Requirements:
* 1. input must not be null
* 2. input must be greater than or equal to zero
*/
int base64_dec_len(char *input, int inputLen);
#endif // _BASE64_H
#endif

Просмотреть файл

@ -0,0 +1,107 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "iotc_internal.h"
/* extern */
int iotc_on(IOTContext ctx, const char* eventName, IOTCallback callback, void* appContext) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL_NOT_EMPTY(eventName, 64);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
#define SETCB_(x, a, b) x.callback=a;x.appContext=b;
if (strcmp(eventName, "ConnectionStatus") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus], callback, appContext);
} else if (strcmp(eventName, "MessageSent") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::MessageSent], callback, appContext);
} else if (strcmp(eventName, "Error") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::Error], callback, appContext);
} else if (strcmp(eventName, "SettingsUpdated") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated], callback, appContext);
} else if (strcmp(eventName, "Command") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::Command], callback, appContext);
} else {
IOTC_LOG(F("ERROR: (iotc_on) Unknown event definition. (%s)"), eventName);
return 1;
}
#undef SETCB_
return 0;
}
/* extern */
int iotc_send_state (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
return iotc_send_telemetry((IOTContext)internal, payload, length);
}
/* extern */
int iotc_send_event (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
return iotc_send_telemetry((IOTContext)internal, payload, length);
}
/* extern */
int iotc_set_global_endpoint(IOTContext ctx, const char* endpoint_uri) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL_NOT_EMPTY(endpoint_uri, 1024);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
// todo: do not fragment the memory
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
internal->endpoint = (char*) malloc(endpoint_uri_len + 1);
CHECK_NOT_NULL(internal->endpoint);
strcpy(internal->endpoint, endpoint_uri);
*(internal->endpoint + endpoint_uri_len) = 0;
return 0;
}
/* extern */
int iotc_set_trusted_certs(IOTContext ctx, const char* certs) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL_NOT_EMPTY(certs, 4096);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
IOTC_LOG(F("ERROR: (iotc_set_trusted_certs) Not implemented."));
return 0;
}
/* extern */
int iotc_set_proxy(IOTContext ctx, IOTC_HTTP_PROXY_OPTIONS proxy) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
IOTC_LOG(F("ERROR: (iotc_set_proxy) Not implemented."));
return 1;
}
/* extern */
int iotc_set_logging(IOTLogLevel level) {
if (level < IOTC_LOGGING_DISABLED || level > IOTC_LOGGING_ALL) {
IOTC_LOG(F("ERROR: (iotc_set_logging) invalid argument. ERROR:0x0001"));
return 1;
}
setLogLevel(level);
return 0;
}

Просмотреть файл

@ -0,0 +1,76 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#ifndef AZURE_IOTC_LITE_DEFINITIONS_H
#define AZURE_IOTC_LITE_DEFINITIONS_H
#define AZURE_MQTT_SERVER_PORT 8883
#define AZURE_HTTPS_SERVER_PORT 443
#define IOTC_SERVER_RESPONSE_TIMEOUT 20 // seconds
#define SSL_CLIENT_CERT_PEM NULL
#define SSL_CLIENT_PRIVATE_KEY_PEM NULL
/* Baltimore */
#define SSL_CA_PEM_DEF \
"-----BEGIN CERTIFICATE-----\r\n" \
"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\r\n" \
"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n" \
"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\r\n" \
"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\r\n" \
"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\r\n" \
"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\r\n" \
"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\r\n" \
"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\r\n" \
"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\r\n" \
"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\r\n" \
"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\r\n" \
"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\r\n" \
"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\r\n" \
"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\r\n" \
"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\r\n" \
"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\r\n" \
"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\r\n" \
"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\r\n" \
"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\r\n" \
"-----END CERTIFICATE-----\r\n"
// GENERAL
#define TO_STRING_(s) #s
#define TO_STRING(s) TO_STRING_(s)
#define iotc_max(a,b) (a > b ? a : b)
#define iotc_min(a,b) (a < b ? a : b)
#define STRING_BUFFER_16 16
#define STRING_BUFFER_32 32
#define STRING_BUFFER_64 64
#define STRING_BUFFER_128 128
#define STRING_BUFFER_256 256
#define STRING_BUFFER_512 512
#define STRING_BUFFER_1024 1024
#define STRING_BUFFER_4096 4096
#define NTP_SYNC_PERIOD (6 * 60 * 60 * 1000)
#define AZIOTC_API_MAJOR_VERSION 0.
#define AZIOTC_API_MINOR_VERSION 2.
#define AZIOTC_API_PATCH_VERSION 0
#define AZIOTC_API_VERSION TO_STRING(AZIOTC_API_MAJOR_VERSION \
AZIOTC_API_MINOR_VERSION AZIOTC_API_PATCH_VERSION) "-msiotc"
#define AZURE_IOT_CENTRAL_CLIENT_SIGNATURE "user-agent: iot-central-client/" AZIOTC_API_VERSION
#if defined(_DEBUG) || defined(DEBUG)
#define ASSERT_OR_FAIL_FAST(x) assert(x)
#else // defined(_DEBUG) || defined(DEBUG)
#define ASSERT_OR_FAIL_FAST(x) if (!(x)) { LOG_ERROR(TO_STRING(x) "condition has failed"); }
#endif // defined(_DEBUG) || defined(DEBUG)
#ifdef MBED_STATIC_ASSERT
#define ASSERT_STATIC MBED_STATIC_ASSERT
#else
#define ASSERT_STATIC static_assert
#endif
#endif // AZURE_IOTC_LITE_DEFINITIONS_H

Просмотреть файл

@ -0,0 +1,409 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "iotc_internal.h"
#include "../common/json.h"
IOTLogLevel gLogLevel = IOTC_LOGGING_DISABLED;
void setLogLevel(IOTLogLevel l) { gLogLevel = l; }
IOTLogLevel getLogLevel() { return gLogLevel; }
unsigned strlen_s_(const char* str, int max_expected) {
int ret_val = 0;
while(*(str++) != 0) {
ret_val++;
if (ret_val >= max_expected) return max_expected;
}
return ret_val;
}
#if defined(USE_LIGHT_CLIENT)
unsigned long getNow();
// Self MQTT option supports singletonContext only.
static IOTContextInternal *singletonContext = NULL;
IOTContextInternal* getSingletonContext() { return singletonContext; }
void setSingletonContext(IOTContextInternal* ctx) { singletonContext = ctx; }
int getUsernameAndPasswordFromConnectionString(const char* connectionString, size_t connectionStringLength,
AzureIOT::StringBuffer &hostName, AzureIOT::StringBuffer &deviceId,
AzureIOT::StringBuffer &username, AzureIOT::StringBuffer &password) {
// TODO: improve this so we don't depend on a particular order in connection string
AzureIOT::StringBuffer connStr(connectionString, connectionStringLength);
int32_t hostIndex = connStr.indexOf(HOSTNAME_STRING, HOSTNAME_LENGTH);
size_t length = connStr.getLength();
if (hostIndex != 0) {
IOTC_LOG(F("ERROR: connectionString doesn't start with HostName= RESULT:%d"), hostIndex);
return 1;
}
int32_t deviceIndex = connStr.indexOf(DEVICEID_STRING, DEVICEID_LENGTH);
if (deviceIndex == -1) {
IOTC_LOG(F("ERROR: ;DeviceId= not found in the connectionString"));
return 1;
}
int32_t keyIndex = connStr.indexOf(KEY_STRING, KEY_LENGTH);
if (keyIndex == -1) {
IOTC_LOG(F("ERROR: ;SharedAccessKey= not found in the connectionString"));
return 1;
}
hostName.initialize(connectionString + HOSTNAME_LENGTH, deviceIndex - HOSTNAME_LENGTH);
deviceId.initialize(connectionString + (deviceIndex + DEVICEID_LENGTH), keyIndex - (deviceIndex + DEVICEID_LENGTH));
AzureIOT::StringBuffer keyBuffer(length - (keyIndex + KEY_LENGTH));
memcpy(*keyBuffer, connectionString + (keyIndex + KEY_LENGTH), keyBuffer.getLength());
AzureIOT::StringBuffer hostURLEncoded(hostName);
hostURLEncoded.urlEncode();
size_t expires = getNow() + EXPIRES;
AzureIOT::StringBuffer stringToSign(hostURLEncoded.getLength() + STRING_BUFFER_128);
AzureIOT::StringBuffer deviceIdEncoded(deviceId);
deviceIdEncoded.urlEncode();
size_t keyLength = snprintf(*stringToSign, stringToSign.getLength(), "%s%s%s\n%lu000",
*hostURLEncoded, "%2Fdevices%2F", *deviceIdEncoded, expires);
stringToSign.setLength(keyLength);
keyBuffer.base64Decode();
stringToSign.hash(*keyBuffer, keyBuffer.getLength());
if (!stringToSign.base64Encode() || !stringToSign.urlEncode()) {
IOTC_LOG(F("ERROR: stringToSign base64Encode / urlEncode has failed."));
return 1;
}
AzureIOT::StringBuffer passwordBuffer(STRING_BUFFER_512);
size_t passLength = snprintf(*passwordBuffer, STRING_BUFFER_512,
"SharedAccessSignature sr=%s%s%s&sig=%s&se=%lu000",
*hostURLEncoded, "%2Fdevices%2F", *deviceIdEncoded, *stringToSign, expires);
assert(passLength && passLength < STRING_BUFFER_512);
passwordBuffer.setLength(passLength);
password.initialize(*passwordBuffer, passwordBuffer.getLength());
const char * usernameTemplate = "%s/%s/api-version=2016-11-14";
AzureIOT::StringBuffer usernameBuffer((strlen(usernameTemplate) - 3 /* %s twice */)
+ hostName.getLength() + deviceId.getLength());
size_t expLength = snprintf(*usernameBuffer, usernameBuffer.getLength(),
usernameTemplate, *hostName, *deviceId);
assert(expLength <= usernameBuffer.getLength());
username.initialize(*usernameBuffer, usernameBuffer.getLength());
IOTC_LOG(F(
"\r\n"\
"hostname: %s\r\n"\
"deviceId: %s\r\n"\
"username: %s\r\n"\
"password: %s\r\n"),
*hostName, *deviceId, *username, *password);
return 0;
}
int getDPSAuthString(const char* scopeId, const char* deviceId, const char* key,
char *buffer, int bufferSize, size_t &outLength) {
size_t expires = getNow() + EXPIRES;
AzureIOT::StringBuffer deviceIdEncoded(deviceId, strlen(deviceId));
deviceIdEncoded.urlEncode();
AzureIOT::StringBuffer stringToSign(STRING_BUFFER_256);
size_t size = snprintf(*stringToSign, STRING_BUFFER_256, "%s%%2Fregistrations%%2F%s", scopeId, *deviceIdEncoded);
assert(size < STRING_BUFFER_256);
stringToSign.setLength(size);
AzureIOT::StringBuffer sr(stringToSign);
size = snprintf(*stringToSign, STRING_BUFFER_256, "%s\n%lu000", *sr, expires);
assert(size < STRING_BUFFER_256);
stringToSign.setLength(size);
size = 0;
AzureIOT::StringBuffer keyDecoded(key, strlen(key));
keyDecoded.base64Decode();
stringToSign.hash(*keyDecoded, keyDecoded.getLength());
if (!stringToSign.base64Encode() || !stringToSign.urlEncode()) {
IOTC_LOG(F("ERROR: stringToSign base64Encode / urlEncode has failed."));
return 1;
}
outLength = snprintf(buffer, bufferSize, "authorization: SharedAccessSignature sr=%s&sig=%s&se=%lu000&skn=registration",
*sr, *stringToSign, expires);
assert(outLength > 0 && outLength < bufferSize);
buffer[outLength] = 0;
return 0;
}
// send telemetry etc. confirmation callback
/* MessageSent */
void sendConfirmationCallback(const char* buffer, size_t size) {
IOTContextInternal *internal = (IOTContextInternal*)singletonContext;
int result = 0;
if (internal->callbacks[/*IOTCallbacks::*/MessageSent].callback) {
IOTCallbackInfo info;
info.eventName = "MessageSent";
info.tag = NULL;
info.payload = (const char*) buffer;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::MessageSent].appContext;
info.statusCode = (int)result;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::MessageSent].callback(internal, &info);
}
}
/* Command */
static int onCommand(const char* method_name, const char* payload,
size_t size, char** response, size_t* resp_size, void* userContextCallback) {
assert(response != NULL && resp_size != NULL);
*response = NULL;
*resp_size = 0;
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
if (internal->callbacks[/*IOTCallbacks::*/::Command].callback) {
IOTCallbackInfo info;
info.eventName = "Command";
info.tag = method_name;
info.payload = (const char*) payload;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Command].appContext;
info.statusCode = 0;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Command].callback(internal, &info);
if (info.callbackResponse != NULL) {
*response = (char*) info.callbackResponse;
*resp_size = strlen((char*) info.callbackResponse);
}
return 200;
}
return 500;
}
/* ConnectionStatus */
void connectionStatusCallback(IOTConnectionState status, IOTContextInternal *internal) {
if (internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback) {
IOTCallbackInfo info;
info.eventName = "ConnectionStatus";
info.tag = NULL;
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].appContext;
info.statusCode = status;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback(internal, &info);
}
}
void sendOnError(IOTContextInternal *internal, const char* message) {
if (internal->callbacks[/*IOTCallbacks::*/::Error].callback) {
IOTCallbackInfo info;
info.eventName = "Error";
info.tag = message; // message lifetime should be managed by us
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Error].appContext;
info.statusCode = 1;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Error].callback(internal, &info);
}
}
void echoDesired(IOTContextInternal *internal, const char *propertyName,
AzureIOT::StringBuffer &message, const char *status, int statusCode) {
AzureIOT::JSObject rootObject(*message);
AzureIOT::JSObject propertyNameObject;
rootObject.getObjectByName(propertyName, &propertyNameObject);
double value = 0, desiredVersion = 0;
propertyName = rootObject.getNameAt(0);
value = propertyNameObject.getNumberByName("value");
desiredVersion = rootObject.getNumberByName("$version");
const char* echoTemplate = "{\"%s\":{\"value\":%d,\"statusCode\":%d,\
\"status\":\"%s\",\"desiredVersion\":%d}}";
uint32_t buffer_size = strlen(echoTemplate) + 23 /* x64 value */ + 3 /* statusCode */
+ 32 /* status */ + 23 /* version max */;
buffer_size = iotc_min(buffer_size, 512);
AzureIOT::StringBuffer buffer(buffer_size);
size_t size = snprintf(*buffer, buffer_size, echoTemplate, propertyName,
(int) value, statusCode, status, (int) desiredVersion);
buffer.setLength(size);
const char* topicName = "$iothub/twin/PATCH/properties/reported/?$rid=%d";
AzureIOT::StringBuffer topic(strlen(topicName) + 21); // + 2 for %d == +23 in case requestId++ overflows
topic.setLength(snprintf(*topic, topic.getLength(), topicName, internal->messageId++));
if (mqtt_publish(internal, *topic, topic.getLength(), *buffer, size) != 0) {
IOTC_LOG(F("ERROR: (echoDesired) MQTTClient publish has failed."));
}
}
void callDesiredCallback(IOTContextInternal *internal, const char *propertyName, AzureIOT::StringBuffer &payload) {
const char* response = "completed";
if (internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback) {
IOTCallbackInfo info;
info.eventName = "SettingsUpdated";
info.tag = propertyName;
info.payload = *payload;
info.payloadLength = payload.getLength();
info.appContext = internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].appContext;
info.statusCode = 200;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback(internal, &info);
if (info.callbackResponse) {
response = (const char*)info.callbackResponse;
}
echoDesired(internal, propertyName, payload, response, info.statusCode);
}
}
static void deviceTwinGetStateCallback(DEVICE_TWIN_UPDATE_STATE update_state,
AzureIOT::StringBuffer &payload, void* userContextCallback) {
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
AzureIOT::JSObject desired(*payload);
for (unsigned i = 0, count = desired.getCount(); i < count; i++) {
const char * itemName = desired.getNameAt(i);
if (itemName != NULL && itemName[0] != '$') {
callDesiredCallback(internal, itemName, payload);
}
}
}
void handlePayload(char *msg, unsigned long msg_length, char *topic, unsigned long topic_length) {
if (topic_length) {
assert(topic != NULL);
AzureIOT::StringBuffer topicName(topic, topic_length);
if (topicName.startsWith("$iothub/twin/res", strlen("$iothub/twin/res"))) return;
AzureIOT::StringBuffer payload;
if (msg_length) {
payload.initialize(msg, msg_length);
}
if (topicName.startsWith("$iothub/twin/PATCH/properties/desired/", strlen("$iothub/twin/PATCH/properties/desired/"))) {
deviceTwinGetStateCallback(DEVICE_TWIN_UPDATE_ALL, payload, singletonContext);
} else if (topicName.startsWith("$iothub/methods", strlen("$iothub/methods"))) {
int index = topicName.indexOf("$rid=", 5, 0);
if (index == -1) {
IOTC_LOG(F("ERROR: corrupt C2D message topic => %s"), *topicName);
return;
}
const char* topicId = (*topicName + index + 5);
const char* topicTemplate = "$iothub/methods/POST/";
const int topicTemplateLength = strlen(topicTemplate);
index = topicName.indexOf("/", 1, topicTemplateLength + 1);
if (index == -1) {
IOTC_LOG(F("ERROR: corrupt C2D message topic (methodName) => %s"), *topicName);
return;
}
AzureIOT::StringBuffer methodName(topic + topicTemplateLength, index - topicTemplateLength);
const char* constResponse = "{}";
char* response = NULL;
size_t respSize = 0;
int rc = onCommand(*methodName, msg, msg_length, &response, &respSize, getSingletonContext());
if (respSize == 0) {
respSize = 2;
} else {
constResponse = response;
}
AzureIOT::StringBuffer respTopic(STRING_BUFFER_128);
respTopic.setLength(snprintf(*respTopic, STRING_BUFFER_128, "$iothub/methods/res/%d/?$rid=%s", rc, topicId));
if (mqtt_publish(getSingletonContext(), *respTopic, respTopic.getLength(), constResponse, respSize) != 0) {
IOTC_LOG(F("ERROR: mqtt_publish has failed during C2D with response topic '%s' and response '%s'"), *respTopic, constResponse);
}
if (response != constResponse) {
free(response);
}
} else {
IOTC_LOG(F("ERROR: unknown twin topic: %s, msg: %s"), topic, msg_length ? msg : "NULL");
}
}
}
/* extern */
int iotc_send_telemetry(IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
AzureIOT::StringBuffer topic(internal->deviceId.getLength() + strlen("devices/ /messages/events/"));
topic.setLength(snprintf(*topic, topic.getLength(), "devices/%s/messages/events/", *internal->deviceId));
if (mqtt_publish(internal, *topic, topic.getLength(), payload, length) != 0) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) MQTTClient publish has failed."));
return 1;
}
sendConfirmationCallback(payload, length);
return 0;
}
/* extern */
int iotc_send_property (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
const char* topicName = "$iothub/twin/PATCH/properties/reported/?$rid=%d";
AzureIOT::StringBuffer topic(strlen(topicName) + 21); // + 2 for %d == +23 in case requestId++ overflows
topic.setLength(snprintf(*topic, topic.getLength(), topicName, internal->messageId++));
if (mqtt_publish(internal, *topic, topic.getLength(), payload, length) != 0) {
IOTC_LOG(F("ERROR: (iotc_send_property) MQTTClient publish has failed."));
return 1;
}
sendConfirmationCallback(payload, length);
return 0;
}
/* extern */
int iotc_init_context(IOTContext *ctx) {
CHECK_NOT_NULL(ctx)
if (getSingletonContext() != NULL) {
IOTC_LOG(F("ERROR: (iotc_init_context) Self MQTT version supports singleton context only."));
return 1;
}
MUST_CALL_BEFORE_INIT((*ctx));
IOTContextInternal *internal = (IOTContextInternal*)malloc(sizeof(IOTContextInternal));
CHECK_NOT_NULL(internal);
memset(internal, 0, sizeof(IOTContextInternal));
*ctx = (void*)internal;
setSingletonContext(internal);
return 0;
}
#endif // USE_LIGHT_CLIENT

Просмотреть файл

@ -0,0 +1,165 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef AZURE_IOTC_INTERNAL_H
#define AZURE_IOTC_INTERNAL_H
#include "iotc_platform.h"
#include "iotc_definitions.h"
#include <assert.h>
#include <stddef.h> // size_t etc.
#include <limits.h>
#include "../iotc.h"
#include "string_buffer.h"
#define AZ_IOT_HUB_MAX_LEN 1024
#define DEFAULT_ENDPOINT "global.azure-devices-provisioning.net"
#define TO_STR_(s) #s
#define TO_STR(s) TO_STR_(s)
typedef enum IOTCallbacks_TAG {
ConnectionStatus = 0x01,
MessageSent,
Command,
Error,
SettingsUpdated
} IOTCallbacks;
typedef struct CallbackBase_TAG {
IOTCallback callback;
void *appContext;
CallbackBase_TAG() { callback = NULL; appContext = NULL; }
} CallbackBase;
typedef struct IOTContextInternal_TAG {
char *endpoint;
IOTProtocol protocol;
CallbackBase callbacks[8];
#if defined(USE_LIGHT_CLIENT)
int messageId;
AzureIOT::StringBuffer deviceId;
#else // use azure iot client
IOTHUB_CLIENT_LL_HANDLE clientHandle;
#endif // USE_LIGHT_CLIENT
#ifdef USE_LIGHT_CLIENT
#if defined(__MBED__)
AzureIOT::TLSClient *tlsClient;
MQTT::Client<AzureIOT::TLSClient, Countdown, STRING_BUFFER_1024, 5>* mqttClient;
#elif defined(ARDUINO)
ARDUINO_WIFI_SSL_CLIENT *tlsClient;
PubSubClient *mqttClient;
#endif // __MBED__
#endif // USE_LIGHT_CLIENT
} IOTContextInternal;
#define CHECK_NOT_NULL(x) if (x == NULL) { IOTC_LOG(F(TO_STR(x) "is NULL")); return 1; }
#define GET_LENGTH_NOT_NULL_NOT_EMPTY(x, maxlen) \
unsigned x ## _len = 0; \
do { \
CHECK_NOT_NULL(x) \
x ## _len = strlen_s_(x, INT_MAX); \
if (x ## _len == 0 || x ## _len > maxlen) { \
IOTC_LOG(F("ERROR: " TO_STR(x) "has length %d"), x ## _len); return 1; \
} \
} while(0)
#define GET_LENGTH_NOT_NULL(x, maxlen) \
unsigned x ## _len = 0; \
do { \
CHECK_NOT_NULL(x) \
x ## _len = strlen_s_(x, INT_MAX); \
if (x ## _len > maxlen) { \
IOTC_LOG(F("ERROR: " TO_STR(x) " has length %d"), x ## _len); return 1; \
} \
} while(0)
#define MUST_CALL_BEFORE_INIT(x) \
if (x != NULL) { \
IOTC_LOG(F("ERROR: Client was already initialized. ERR:0x0006")); \
return 6; \
}
#define MUST_CALL_AFTER_INIT(x) \
if (x == NULL) { \
IOTC_LOG(F("ERROR: Client was not initialized. ERR:0x0007")); \
return 7; \
}
#ifdef USE_LIGHT_CLIENT
#define MUST_CALL_AFTER_CONNECT(x) \
if (x == NULL || x->mqttClient == NULL) { \
IOTC_LOG(F("ERROR: Client was not connected.")); \
return 1; \
}
#else // USE_LIGHT_CLIENT
#define MUST_CALL_AFTER_CONNECT(x) \
if (x == NULL || x->clientHandle == NULL) { \
IOTC_LOG(F("ERROR: Client was not connected.")); \
return 1; \
}
#endif // USE_LIGHT_CLIENT
// when the auth token expires
#define EXPIRES 21600 // 6 hours
typedef enum IOTHUBMESSAGE_DISPOSITION_RESULT_TAG {
IOTHUBMESSAGE_ACCEPTED = 0x01,
IOTHUBMESSAGE_ABANDONED
} IOTHUBMESSAGE_DISPOSITION_RESULT;
typedef enum DEVICE_TWIN_UPDATE_STATE_TAG {
DEVICE_TWIN_UPDATE_PARTIAL = 0,
DEVICE_TWIN_UPDATE_ALL = 1
} DEVICE_TWIN_UPDATE_STATE;
#define HOSTNAME_STRING "HostName="
#define DEVICEID_STRING ";DeviceId="
#define KEY_STRING ";SharedAccessKey="
#define HOSTNAME_LENGTH (sizeof(HOSTNAME_STRING) - 1)
#define DEVICEID_LENGTH (sizeof(DEVICEID_STRING) - 1)
#define KEY_LENGTH (sizeof(KEY_STRING) - 1)
#ifdef __cplusplus
extern "C" {
#endif
unsigned strlen_s_(const char* str, int max_expected);
int getUsernameAndPasswordFromConnectionString(const char* connectionString, size_t connectionStringLength,
AzureIOT::StringBuffer &hostName, AzureIOT::StringBuffer &deviceId,
AzureIOT::StringBuffer &username, AzureIOT::StringBuffer &password);
int getDPSAuthString(const char* scopeId, const char* deviceId, const char* key,
char *buffer, int bufferSize, size_t &outLength);
void setLogLevel(IOTLogLevel l);
IOTLogLevel getLogLevel();
void handlePayload(char *msg, unsigned long msg_length, char *topic, unsigned long topic_length);
int getHubHostName(const char* dpsEndpoint, const char *scopeId, const char* deviceId, const char* key, char *hostName);
void connectionStatusCallback(IOTConnectionState status, IOTContextInternal *internal);
IOTContextInternal* getSingletonContext();
void setSingletonContext(IOTContextInternal* ctx);
void sendConfirmationCallback(const char* buffer, size_t size);
int mqtt_publish(IOTContextInternal *internal, const char* topic, unsigned long topic_length,
const char* msg, unsigned long msg_length);
#ifdef ARDUINO
void IOTC_LOG(const __FlashStringHelper *format, ...);
#else
#define IOTC_LOG(...) \
if (getLogLevel() > IOTC_LOGGING_DISABLED) { \
printf(" - "); \
printf(__VA_ARGS__); \
printf("\r\n"); \
}
#endif
#ifdef __cplusplus
}
#endif
#endif // AZURE_IOTC_INTERNAL_H

Просмотреть файл

@ -0,0 +1,69 @@
#ifndef IOTC_COMMON_PLATFORM_H
#define IOTC_COMMON_PLATFORM_H
#ifdef ARDUINO_SAMD_FEATHER_M0
#include <Adafruit_WINC1500.h>
#include <Adafruit_WINC1500SSLClient.h>
#define ARDUINO_WIFI_SSL_CLIENT Adafruit_WINC1500SSLClient
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#define ARDUINO_WIFI_SSL_CLIENT WiFiClientSecure
#elif defined(ARDUINO_SAMD_MKR1010)
#include <WiFi101.h>
#include <WiFiNINA.h>
#define ARDUINO_WIFI_SSL_CLIENT WiFiSSLClient
#define USES_WIFI101
#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000)
#include <WiFi101.h>
#include <WifiSSLClient.h>
#define ARDUINO_WIFI_SSL_CLIENT WiFiSSLClient
#define USES_WIFI101
#endif // ARDUINO_SAMD_FEATHER_M0
#if defined(ARDUINO_WIFI_SSL_CLIENT)
#define USE_LIGHT_CLIENT 1
#endif // ARDUINO_WIFI_SSL_CLIENT
#if defined(__MBED__)
#if defined(ARDUINO)
#error "Both __MBED__ and ARDUINO were defined"
#endif // defined(ARDUINO)
#define USE_LIGHT_CLIENT 1
#include <mbed.h>
#include "MQTTmbed.h"
#include "MQTTClient.h"
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include <mbedtls/base64.h>
#include "../mbed_os/mbed_tls_client.h"
#define WAITMS wait_ms
#define SERIAL_PRINT printf
#define F(x) x
#elif defined (ARDUINO)
#include <arduino.h>
#include <avr/pgmspace.h>
#include "WiFiUdp.h"
#include "base64.h"
#include "sha256.h"
#include "../arduino/PubSubClient.h"
#define WAITMS delay
#define SERIAL_PRINT Serial.print
#else
#error "NOT SUPPORTED"
#endif // defined(__MBED__)
#ifndef PROGMEM
#define PROGMEM
#endif
#endif // IOTC_COMMON_PLATFORM_H

Просмотреть файл

@ -0,0 +1,123 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef AZURE_IOTC_API_JSON
#define AZURE_IOTC_API_JSON
#if defined(__MBED__) || defined(ESP_PLATFORM) || defined(ARDUINO)
#include "parson.h"
#else
#include <parson/parson.h>
#endif
#include "../iotc.h"
namespace AzureIOT
{
class JSObject
{
private:
JSON_Value* value;
JSON_Object* object;
bool isSubObject;
JSON_Object* toObject() {
object = json_value_get_object(value);
if (object == NULL) {
LOG_ERROR("JSON value is not an object");
return NULL;
}
return object;
}
public:
JSObject(): value(NULL), object(NULL), isSubObject(false) { }
JSObject(const char * json_string) : isSubObject(false) {
value = json_parse_string(json_string);
if (value == NULL) {
LOG_ERROR("parsing JSON failed");
}
object = toObject();
if (object == NULL) {
LOG_ERROR("json data: %s", json_string);
}
}
const char * getNameAt(unsigned index) {
if (object == NULL) { LOG_ERROR("(getNameAt) object == NULL!"); return NULL; }
return json_object_get_name(object, index);
}
unsigned getCount() {
return object ? (unsigned)json_object_get_count(object) : 0;
}
bool hasProperty(const char * name) {
return object ? json_object_has_value(object, name) == 1 : false;
}
const char * toString() {
if (object == NULL) { LOG_ERROR("(toString) object == NULL!"); return NULL; }
JSON_Value * val = json_object_get_wrapping_value(object);
if (val == NULL) return NULL;
return json_value_get_string(val);
}
~JSObject() {
if (value != NULL && isSubObject == false) {
json_value_free(value);
value = NULL;
}
}
bool getObjectAt(unsigned index, JSObject * outJSObject) {
if (index >= getCount()) return false;
JSON_Value * subValue = json_object_get_value_at(object, index);
if (subValue == NULL) return false;
outJSObject->isSubObject = true;
outJSObject->value = subValue;
outJSObject->toObject();
if (outJSObject->object == NULL) return false;
return true;
}
bool getObjectByName(const char * name, JSObject * outJSObject) {
JSON_Object* subObject = json_object_get_object(object, name);
if (subObject == NULL) {
// outJSObject->value memory freed by it's own de-constructor.
return false; // let consumer file the log
}
outJSObject->value = json_object_get_wrapping_value(object);
outJSObject->object = subObject;
outJSObject->isSubObject = true;
return true;
}
const char * getStringByName(const char * name) {
if (object == NULL) { LOG_ERROR("(getStringByName) object == NULL!"); return NULL; }
const char * text = json_object_get_string(object, name);
if (text == NULL) {
return NULL; // let consumer file the log
}
return text;
}
double getNumberByName(const char * name) {
if (object == NULL) { LOG_ERROR("(getNumberByName) object == NULL!"); return 0; }
// API returns 0.0 on fail hence it doesn't actually have a good
// fail discovery strategy
return json_object_get_number(object, name);
}
};
} // namespace AzureIOT
#endif // AZURE_IOTC_API_JSON

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,237 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 - 2017 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef parson_parson_h
#define parson_parson_h
#if defined(__MBED__) || defined(ESP_PLATFORM) || defined(ARDUINO)
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h> /* size_t */
/* Types and enums */
typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array;
typedef struct json_value_t JSON_Value;
enum json_value_type {
JSONError = -1,
JSONNull = 1,
JSONString = 2,
JSONNumber = 3,
JSONObject = 4,
JSONArray = 5,
JSONBoolean = 6
};
typedef int JSON_Value_Type;
enum json_result_t {
JSONSuccess = 0,
JSONFailure = -1
};
typedef int JSON_Status;
typedef void * (*JSON_Malloc_Function)(size_t);
typedef void (*JSON_Free_Function)(void *);
/* Call only once, before calling any other function from parson API. If not called, malloc and free
from stdlib will be used for all allocations */
void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
/* Parses first JSON value in a file, returns NULL in case of error */
JSON_Value * json_parse_file(const char *filename);
/* Parses first JSON value in a file and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_file_with_comments(const char *filename);
/* Parses first JSON value in a string, returns NULL in case of error */
JSON_Value * json_parse_string(const char *string);
/* Parses first JSON value in a string and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_string_with_comments(const char *string);
/* Serialization */
size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
char * json_serialize_to_string(const JSON_Value *value);
/* Pretty serialization */
size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);
char * json_serialize_to_string_pretty(const JSON_Value *value);
void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
/* Comparing */
int json_value_equals(const JSON_Value *a, const JSON_Value *b);
/* Validation
This is *NOT* JSON Schema. It validates json by checking if object have identically
named fields with matching types.
For example schema {"name":"", "age":0} will validate
{"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
In case of arrays, only first value in schema is checked against all values in tested array.
Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
null validates values of every type.
*/
JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
/*
* JSON Object
*/
JSON_Value * json_object_get_value (const JSON_Object *object, const char *name);
const char * json_object_get_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_get_array (const JSON_Object *object, const char *name);
double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* dotget functions enable addressing values with dot notation in nested objects,
just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
Because valid names in JSON can contain dots, some values may be inaccessible
this way. */
JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name);
const char * json_object_dotget_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name);
double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* Functions to get available names */
size_t json_object_get_count (const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index);
JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index);
JSON_Value * json_object_get_wrapping_value(const JSON_Object *object);
/* Functions to check if object has a value with a specific name. Returned value is 1 if object has
* a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */
int json_object_has_value (const JSON_Object *object, const char *name);
int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
int json_object_dothas_value (const JSON_Object *object, const char *name);
int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
/* Creates new name-value pair or frees and replaces old value with a new one.
* json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_set_null(JSON_Object *object, const char *name);
/* Works like dotget functions, but creates whole hierarchy if necessary.
* json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
/* Frees and removes name-value pair */
JSON_Status json_object_remove(JSON_Object *object, const char *name);
/* Works like dotget function, but removes name-value pair only on exact match. */
JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
/* Removes all name-value pairs in object */
JSON_Status json_object_clear(JSON_Object *object);
/*
*JSON Array
*/
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
const char * json_array_get_string (const JSON_Array *array, size_t index);
JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
JSON_Array * json_array_get_array (const JSON_Array *array, size_t index);
double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */
int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
size_t json_array_get_count (const JSON_Array *array);
JSON_Value * json_array_get_wrapping_value(const JSON_Array *array);
/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
* Order of values in array may change during execution. */
JSON_Status json_array_remove(JSON_Array *array, size_t i);
/* Frees and removes from array value at given index and replaces it with given one.
* Does nothing and returns JSONFailure if index doesn't exist.
* json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
/* Frees and removes all values from array */
JSON_Status json_array_clear(JSON_Array *array);
/* Appends new value at the end of array.
* json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
JSON_Status json_array_append_string(JSON_Array *array, const char *string);
JSON_Status json_array_append_number(JSON_Array *array, double number);
JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
JSON_Status json_array_append_null(JSON_Array *array);
/*
*JSON Value
*/
JSON_Value * json_value_init_object (void);
JSON_Value * json_value_init_array (void);
JSON_Value * json_value_init_string (const char *string); /* copies passed string */
JSON_Value * json_value_init_number (double number);
JSON_Value * json_value_init_boolean(int boolean);
JSON_Value * json_value_init_null (void);
JSON_Value * json_value_deep_copy (const JSON_Value *value);
void json_value_free (JSON_Value *value);
JSON_Value_Type json_value_get_type (const JSON_Value *value);
JSON_Object * json_value_get_object (const JSON_Value *value);
JSON_Array * json_value_get_array (const JSON_Value *value);
const char * json_value_get_string (const JSON_Value *value);
double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(const JSON_Value *value);
JSON_Value * json_value_get_parent (const JSON_Value *value);
/* Same as above, but shorter */
JSON_Value_Type json_type (const JSON_Value *value);
JSON_Object * json_object (const JSON_Value *value);
JSON_Array * json_array (const JSON_Value *value);
const char * json_string (const JSON_Value *value);
double json_number (const JSON_Value *value);
int json_boolean(const JSON_Value *value);
#ifdef __cplusplus
}
#endif
#endif // defined(__MBED__) || defined(ESP_PLATFORM)
#endif // parson_parson_h

Просмотреть файл

@ -0,0 +1,174 @@
#ifdef ARDUINO
#include <string.h>
#include <avr/pgmspace.h>
#include "sha256.h"
const uint32_t SHA256_K[] PROGMEM = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
const uint8_t SHA256_INIT_STATE[] PROGMEM = {
0x67,0xe6,0x09,0x6a, // H0
0x85,0xae,0x67,0xbb, // H1
0x72,0xf3,0x6e,0x3c, // H2
0x3a,0xf5,0x4f,0xa5, // H3
0x7f,0x52,0x0e,0x51, // H4
0x8c,0x68,0x05,0x9b, // H5
0xab,0xd9,0x83,0x1f, // H6
0x19,0xcd,0xe0,0x5b // H7
};
#define ror32(num, bits) ((num << (32 - bits)) | (num >> bits))
void Sha256::init(void) {
memcpy_P(state.b, SHA256_INIT_STATE, 32);
byteCount = 0;
bufferOffset = 0;
}
void Sha256::hashBlock() {
uint8_t i;
uint32_t a,b,c,d,e,f,g,h,t1,t2;
a=state.w[0];
b=state.w[1];
c=state.w[2];
d=state.w[3];
e=state.w[4];
f=state.w[5];
g=state.w[6];
h=state.w[7];
for (i=0; i<64; i++) {
if (i>=16) {
t1 = buffer.w[i&15] + buffer.w[(i-7)&15];
t2 = buffer.w[(i-2)&15];
t1 += ror32(t2,17) ^ ror32(t2,19) ^ (t2>>10);
t2 = buffer.w[(i-15)&15];
t1 += ror32(t2,7) ^ ror32(t2,18) ^ (t2>>3);
buffer.w[i&15] = t1;
}
t1 = h;
t1 += ror32(e,6) ^ ror32(e,11) ^ ror32(e,25); // ∑1(e)
t1 += g ^ (e & (g ^ f)); // Ch(e,f,g)
t1 += pgm_read_dword(SHA256_K + i); // Ki
t1 += buffer.w[i&15]; // Wi
t2 = ror32(a,2) ^ ror32(a,13) ^ ror32(a,22); // ∑0(a)
t2 += ((b & c) | (a & (b | c))); // Maj(a,b,c)
h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2;
}
state.w[0] += a;
state.w[1] += b;
state.w[2] += c;
state.w[3] += d;
state.w[4] += e;
state.w[5] += f;
state.w[6] += g;
state.w[7] += h;
}
void Sha256::push(uint8_t data) {
buffer.b[bufferOffset ^ 3] = data;
bufferOffset++;
if (bufferOffset == BLOCK_LENGTH) {
hashBlock();
bufferOffset = 0;
}
}
#if defined(ARDUINO) && ARDUINO >= 100
size_t Sha256::write(uint8_t data) {
#else
void Sha256::write(uint8_t data) {
#endif
++byteCount;
push(data);
#if defined(ARDUINO) && ARDUINO >= 100
return 1;
#endif
}
void Sha256::padBlock() {
// Implement SHA-256 padding (fips180-2 §5.1.1)
// Pad with 0x80 followed by 0x00 until the end of the block
push(0x80);
while (bufferOffset != 56) push(0x00);
// Append length in the last 8 bytes. We're only using 32 bit lengths, but
// SHA-2 supports 64 bit lengths so zero pad the top bits
push(0);
push(0);
push(0);
push(byteCount >> 29);
push(byteCount >> 21);
push(byteCount >> 13);
push(byteCount >> 5);
push(byteCount << 3);
}
uint8_t* Sha256::result(void) {
// Pad to complete the last block
padBlock();
// Swap byte order back
for (uint8_t i = 0; i < 8; i++) {
uint32_t a,b;
a=state.w[i];
b=a<<24;
b|=(a<<8) & 0x00ff0000;
b|=(a>>8) & 0x0000ff00;
b|=a>>24;
state.w[i]=b;
}
// Return pointer to hash
return state.b;
}
#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c
void Sha256::initHmac(const uint8_t *key, size_t keyLength) {
memset(keyBuffer, 0, BLOCK_LENGTH);
if (keyLength > BLOCK_LENGTH) {
// Hash long keys
init();
for (;keyLength--;) write(*key++);
memcpy(keyBuffer, result(), HASH_LENGTH);
} else {
// Block length keys are used as is
memcpy(keyBuffer, key, keyLength);
}
reset();
}
uint8_t* Sha256::resultHmac(void) {
uint8_t i;
// Complete inner hash
memcpy(innerHash, result(), HASH_LENGTH);
// Calculate outer hash
init();
for (i = 0; i < BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD);
for (i = 0; i < HASH_LENGTH; i++) write(innerHash[i]);
return result();
}
void Sha256::reset(void) {
// Start inner hash
init();
for (uint8_t i = 0; i < BLOCK_LENGTH; i++) {
write(keyBuffer[i] ^ HMAC_IPAD);
}
}
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,57 @@
#ifdef ARDUINO
#ifndef Sha256_h
#define Sha256_h
#include <inttypes.h>
#include "Print.h"
#define HASH_LENGTH 32
#define BLOCK_LENGTH 64
class Sha256 : public Print {
union Buffer {
uint8_t b[BLOCK_LENGTH];
uint32_t w[BLOCK_LENGTH / 4];
};
union State {
uint8_t b[HASH_LENGTH];
uint32_t w[HASH_LENGTH / 4];
};
public:
void init(void);
void initHmac(const uint8_t *key, size_t keyLength);
// Reset to initial state, but preserve key material.
void reset(void);
uint8_t* result(void);
uint8_t* resultHmac(void);
#if defined(ARDUINO) && ARDUINO >= 100
virtual size_t write(uint8_t);
#else
virtual void write(uint8_t);
#endif
using Print::write;
private:
void hashBlock();
void padBlock();
void push(uint8_t data);
uint32_t byteCount;
uint8_t keyBuffer[BLOCK_LENGTH];
uint8_t innerHash[HASH_LENGTH];
State state;
Buffer buffer;
uint8_t bufferOffset;
};
#endif
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,304 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "iotc_internal.h"
namespace AzureIOT {
static char convertToHex(char ch) {
static const char * lst = "0123456789ABCDEF";
return *(lst + (ch & 15));
}
static char convertFromHex(char ch) {
assert(isdigit(ch) || isalpha(ch)); // isalnum ?
if (ch <= '9') {
return ch - '0';
} else {
if (ch <= 'Z') {
ch = ch - 'A';
} else {
ch = ch - 'a';
}
return ch + 10;
}
}
bool StringBuffer::startsWith(const char* str, size_t len) {
if (len > length) return false;
const char *buffer = data == NULL ? immutable : data;
assert(buffer != NULL);
for (size_t i = 0; i < len; i++) {
if (str[i] != buffer[i]) return false;
}
return true;
}
int32_t StringBuffer::indexOf(const char* look_for, size_t look_for_length,
int32_t start_index) {
const char *buffer = data == NULL ? immutable : data;
assert(buffer != NULL);
if (look_for_length > length) {
return -1;
}
for (size_t pos = start_index; pos < length; pos++) {
if (length - pos < look_for_length) {
return -1;
}
if (buffer[pos] == *look_for) {
size_t sub = 1;
for (; sub < look_for_length; sub++) {
if (buffer[pos + sub] != look_for[sub]) break;
}
if (sub == look_for_length) {
return pos;
}
}
}
return -1;
}
bool StringBuffer::urlEncode() {
assert(data != NULL);
size_t buffer_length = (length * 3) + 1;
char *buffer = (char*) malloc(buffer_length);
if (buffer == NULL) {
return false;
}
char *tmp = buffer;
assert(buffer != NULL);
for (size_t i = 0; i < length; i++) {
char ch = data[i];
if (isalnum(ch) ||
ch == '_' || ch == '-' || ch == '~' || ch == '.') {
*tmp = ch;
} else if (ch == ' ') {
*tmp = '+';
} else {
*tmp++ = '%';
*tmp++ = convertToHex(ch >> 4);
*tmp = convertToHex(ch & 15);
}
tmp++;
}
*tmp = 0;
clear(); // free prev memory
data = buffer;
length = (size_t)tmp - (size_t)buffer;
return true;
}
bool StringBuffer::urlDecode() { // in-memory
assert(data != NULL);
char *tmp = data; // fast
for (size_t i = 0; i < length; i++) {
char ch = data[i];
if (ch == '%') {
if (i + 2 < length) {
*tmp = convertFromHex(data[i + 1]) << 4 |
convertFromHex(data[i + 2]);
i += 2;
}
} else if (ch == '+') {
*tmp = ' ';
} else {
*tmp = ch;
}
tmp++;
}
*tmp = 0;
length = (size_t)tmp - (size_t)data;
return true;
}
#ifdef __MBED__
bool StringBuffer::hash(const char *key, unsigned key_length)
{
assert(data != NULL);
mbedtls_md_type_t md = MBEDTLS_MD_SHA256;
const mbedtls_md_info_t *md_info;
mbedtls_md_context_t ctx;
md_info = mbedtls_md_info_from_type(md);
unsigned hash_size = (unsigned) mbedtls_md_get_size(md_info);
unsigned char *hmac_hash = (unsigned char*) malloc(hash_size + 1);
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, md_info, 1);
mbedtls_md_hmac_starts(&ctx, (const unsigned char*) key, key_length);
mbedtls_md_hmac_update(&ctx, (const unsigned char*) data, length);
mbedtls_md_hmac_finish(&ctx, hmac_hash);
free(data);
data = (char*)hmac_hash;
setLength(hash_size);
mbedtls_md_free(&ctx);
return true;
}
bool StringBuffer::base64Decode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length + 1); assert(decoded != NULL);
size_t keyLength = 0;
mbedtls_base64_decode((unsigned char*)decoded, length, &keyLength,
(const unsigned char*)data, getLength());
assert(keyLength > 0);
free(data);
data = (char*) malloc(keyLength + 1); assert(data != NULL);
memcpy(data, decoded, keyLength);
setLength(keyLength);
free(decoded);
return true;
}
bool StringBuffer::base64Encode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length * 3); assert(decoded != NULL);
size_t keyLength = 0;
mbedtls_base64_encode((unsigned char*)decoded, length * 3, &keyLength,
(const unsigned char*)data, getLength());
assert(keyLength > 0);
free(data);
data = (char*) malloc(keyLength + 1); assert(data != NULL);
memcpy(data, decoded, keyLength);
setLength(keyLength);
free(decoded);
return true;
}
#elif defined(ARDUINO)
bool StringBuffer::hash(const char *key, unsigned key_length)
{
assert(data != NULL);
Sha256 *sha256 = new Sha256();
sha256->initHmac((const uint8_t*)key, (size_t)key_length);
sha256->print(data);
char* sign = (char*) sha256->resultHmac();
if (length < HASH_LENGTH) {
free(data);
data = (char*) malloc(HASH_LENGTH + 1);
}
memcpy(data, sign, HASH_LENGTH);
setLength(HASH_LENGTH);
delete sha256;
return true;
}
bool StringBuffer::base64Decode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length + 1); assert(decoded != NULL);
size_t size = base64_decode(decoded, data, length);
assert (size <= length + 1);
free(data);
data = (char*) malloc(size + 1); assert(data != NULL);
memcpy(data, decoded, size);
setLength(size);
free(decoded);
return true;
}
bool StringBuffer::base64Encode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length * 3); assert(decoded != NULL);
size_t size = base64_encode(decoded, data, length);
assert (size < length * 3);
free(data);
data = (char*) malloc(size + 1); assert(data != NULL);
memcpy(data, decoded, size);
setLength(size);
free(decoded);
return true;
}
#endif // __MBED__
StringBuffer::StringBuffer(StringBuffer &buffer): data(NULL), immutable(NULL) {
length = 0;
initialize(buffer.data, buffer.length);
}
StringBuffer::StringBuffer(const char * str, unsigned int lengthStr, bool isCopy):
data(NULL), immutable(NULL) {
if (isCopy) {
length = 0;
if (str != NULL) {
initialize(str, lengthStr);
}
} else {
assert(str != NULL);
immutable = str;
length = lengthStr;
}
}
StringBuffer::StringBuffer(unsigned lengthStr): data(NULL), immutable(NULL) {
length = 0;
alloc(lengthStr + 1);
assert(data != NULL); // out of memory?
length = lengthStr;
}
void StringBuffer::initialize(const char * str, unsigned lengthStr) {
if (str != NULL) {
alloc(lengthStr + 1); // +1 for \0
assert(data != NULL && immutable == NULL); // out of memory?
memcpy(data, str, lengthStr);
data[lengthStr] = char(0);
length = lengthStr;
}
}
void StringBuffer::alloc(unsigned lengthStr) {
ASSERT_OR_FAIL_FAST(lengthStr != 0 && data == NULL && immutable == NULL);
data = (char*) malloc(lengthStr);
ASSERT_OR_FAIL_FAST(data != NULL);
memset(data, 0, lengthStr);
}
void StringBuffer::set(unsigned index, char c) {
assert(index < length && data != NULL);
data[index] = c;
}
void StringBuffer::clear() {
if (data != NULL) {
free(data);
data = NULL;
}
}
StringBuffer::~StringBuffer() {
this->clear();
}
void StringBuffer::setLength(unsigned l) {
ASSERT_OR_FAIL_FAST(data != NULL);
length = l;
data[l] = char(0);
}
} // namespace AzureIOT

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#ifndef AZURE_IOTC_LITE_STRING_BUFFER_H
#define AZURE_IOTC_LITE_STRING_BUFFER_H
namespace AzureIOT {
class StringBuffer {
char * data;
const char * immutable;
unsigned length;
public:
StringBuffer(): data(NULL), immutable(NULL), length(0) { }
StringBuffer(StringBuffer &buffer);
StringBuffer(const char * str, unsigned int lengthStr, bool isCopy = true);
StringBuffer(unsigned lengthStr);
void initialize(const char * str, unsigned lengthStr);
void alloc(unsigned lengthStr);
void set(unsigned index, char c);
void clear();
~StringBuffer();
char* operator*() { return data; }
unsigned getLength() { return length; }
void setLength(unsigned l);
bool startsWith(const char* str, size_t len);
int32_t indexOf(const char* look_for, size_t look_for_length, int32_t start_index = 0);
#if defined(__MBED__) || defined(ARDUINO)
bool hash(const char* key, unsigned key_length);
#endif
bool urlDecode();
bool urlEncode();
bool base64Decode();
bool base64Encode();
};
} // namespace AzureIOTCLite
#endif // AZURE_IOTC_LITE_STRING_BUFFER_H

196
ESP8266/src/iotc/iotc.h Normal file
Просмотреть файл

@ -0,0 +1,196 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef AZURE_IOTC_API
#define AZURE_IOTC_API
#if !defined(ESP_PLATFORM) && !defined(__MBED__)
// MXCHIP
#define TARGET_MXCHIP_AZ3166
#endif
#ifdef TARGET_MXCHIP_AZ3166
#include <Arduino.h>
#endif
#define AZIOTC_MAJOR_VERSION 0
#define AZIOTC_MINOR_VERSION 1
#define AZIOTC_PATCH_VERSION 0
#define AZIOTC_VERSION \
TO_STRING(AZIOTC_MAJOR_VERSION) "." TO_STRING(AZIOTC_MINOR_VERSION) "." TO_STRING(AZIOTC_PATCH_VERSION)
#ifdef __cplusplus
extern "C" {
#endif
// ***** Type definitions *****
typedef struct IOTC_HTTP_PROXY_OPTIONS_TAG
{
const char* host_address;
int port;
const char* username;
const char* password;
} IOTC_HTTP_PROXY_OPTIONS;
typedef struct IOTCallbackInfo_TAG {
const char* eventName;
const char* tag;
const char* payload;
unsigned payloadLength;
void *appContext;
int statusCode;
void *callbackResponse;
} IOTCallbackInfo;
typedef void* IOTContext;
// ***** Macro definitions *****
#define IOTC_PROTOCOL_MQTT 0x01
#define IOTC_PROTOCOL_AMQP 0x02
#define IOTC_PROTOCOL_HTTP 0x04
typedef short IOTProtocol;
#define IOTC_LOGGING_DISABLED 0x01
#define IOTC_LOGGING_API_ONLY 0x02
#define IOTC_LOGGING_ALL 0x10
typedef short IOTLogLevel;
#define IOTC_CONNECT_SYMM_KEY 0x01
#define IOTC_CONNECT_X509_CERT 0x02
#define IOTC_CONNECT_CONNECTION_STRING 0x04
typedef short IOTConnectType;
#define IOTC_CONNECTION_EXPIRED_SAS_TOKEN 0x01
#define IOTC_CONNECTION_DEVICE_DISABLED 0x02
#define IOTC_CONNECTION_BAD_CREDENTIAL 0x04
#define IOTC_CONNECTION_RETRY_EXPIRED 0x08
#define IOTC_CONNECTION_NO_NETWORK 0x10
#define IOTC_CONNECTION_COMMUNICATION_ERROR 0x20
#define IOTC_CONNECTION_OK 0x40
#define IOTC_CONNECTION_DISCONNECTED 0x80
typedef short IOTConnectionState;
#define IOTC_MESSAGE_ACCEPTED 0x01
#define IOTC_MESSAGE_REJECTED 0x02
#define IOTC_MESSAGE_ABANDONED 0x04
typedef short IOTMessageStatus;
// ***** API *****
// Set the level of logging (see the options above)
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_logging(IOTLogLevel level);
// Initialize the device context. The context variable will be used by rest of the API
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_init_context(IOTContext *ctx);
// Free device context.
// Call this after `init_context`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_free_context(IOTContext ctx);
// Connect to Azure IoT Central
// Call this after `init_context`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type);
// Disconnect
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_disconnect(IOTContext ctx);
// If your endpoint is different than the default AzureIoTCentral endpoint, set it
// using this API.
// Call this before `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_global_endpoint(IOTContext ctx, const char* endpoint_uri);
// Set the custom certificates for custom endpoints
// Call this before `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_trusted_certs(IOTContext ctx, const char* certs);
// Set the proxy settings
// Call this before `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_proxy(IOTContext ctx, IOTC_HTTP_PROXY_OPTIONS proxy);
// Sends a telemetry payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_telemetry(IOTContext ctx, const char* payload, unsigned length);
// Sends a state payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_state (IOTContext ctx, const char* payload, unsigned length);
// Sends an event payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_event (IOTContext ctx, const char* payload, unsigned length);
// Sends a property payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_property (IOTContext ctx, const char* payload, unsigned length);
/*
eventName:
ConnectionStatus
MessageSent
Command
SettingsUpdated
Error
*/
typedef void(*IOTCallback)(IOTContext, IOTCallbackInfo*);
// Register to one of the events listed above
// Call this after `init_context`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_on(IOTContext ctx, const char* eventName, IOTCallback callback, void* appContext);
// Lets SDK to do background work
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_do_work(IOTContext ctx);
// Provide platform dependent NetworkInterface
int iotc_set_network_interface(void* networkInterface);
#ifdef ARDUINO
#define SERIAL_PRINT Serial.printf
#else
#define SERIAL_PRINT printf
#endif
#define SERIAL_VERBOSE_LOGGING_ENABLED 1
#ifndef LOG_VERBOSE
#if SERIAL_VERBOSE_LOGGING_ENABLED != 1
#define LOG_VERBOSE(...)
#else
#define LOG_VERBOSE(...) \
do { \
SERIAL_PRINT(" - "); \
SERIAL_PRINT(__VA_ARGS__); \
SERIAL_PRINT("\r\n"); \
} while(0)
#endif // SERIAL_VERBOSE_LOGGING_ENABLED != 1
// Log Errors no matter what
#define LOG_ERROR(...) \
do { \
SERIAL_PRINT("X - Error at %s:%d\r\n\t", __FILE__, __LINE__); \
SERIAL_PRINT(__VA_ARGS__); \
SERIAL_PRINT("\r\n"); \
} while(0)
#endif // !LOG_VERBOSE
#ifdef __cplusplus
}
#endif
#endif // AZURE_IOTC_API

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if defined(__MBED__)
#include "../common/iotc_platform.h"
#if defined(USE_LIGHT_CLIENT)
#include "../common/json.h"
#include "../common/iotc_internal.h"
int mqtt_publish(IOTContextInternal *internal, const char* topic, unsigned long topic_length,
const char* msg, unsigned long msg_length) {
MQTT::Message message;
message.retained = false;
message.dup = false;
message.payload = (void*)msg;
message.payloadlen = msg_length;
message.qos = MQTT::QOS0;
message.id = ++internal->messageId;
int rc = internal->mqttClient->publish(topic, message);
if(rc != MQTT::SUCCESS) {
return rc;
}
return iotc_do_work(internal);
}
#endif // defined(USE_LIGHT_CLIENT)
#endif // defined(__MBED__)

Просмотреть файл

@ -0,0 +1,331 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if defined(__MBED__)
#include "../common/iotc_platform.h"
#if defined(USE_LIGHT_CLIENT)
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include "../common/json.h"
#include "../common/iotc_internal.h"
unsigned long getNow() {
return AzureIOT::TLSClient::nowTime();
}
int socketReadThemAll(AzureIOT::TLSClient *client, AzureIOT::StringBuffer &readBuffer) {
int retryCount = 0;
retry_getOperationId_read:
char *readBufferCSTR = *readBuffer;
WAITMS(3000); // give 3 secs to server to process
int index = 0;
while (index < STRING_BUFFER_1024 - 1) {
unsigned char buff[8];
int rc = client->read(buff, 8, 2);
if (rc <= 0) {
if (rc == NSAPI_ERROR_WOULD_BLOCK)
continue;
else
break;
}
const int inc = iotc_min(rc, STRING_BUFFER_1024 - index);
memcpy(readBufferCSTR, buff, inc);
index += inc;
readBufferCSTR += rc;
}
assert(index < STRING_BUFFER_1024 - 1);
if (index >= 0) {
readBuffer.setLength(index);
if (index == 0) {
if (retryCount > 2) return 1;
retryCount++;
goto retry_getOperationId_read;
}
} else {
readBuffer.setLength(0);
IOTC_LOG(F("ERROR: (socketReadThemAll) client->read returned %d"), index);
return 1;
}
return 0;
}
int _getOperationId(const char* dpsEndpoint, const char* scopeId, const char* deviceId,
const char* authHeader, char *operationId, char *hostName) {
AzureIOT::TLSClient *client = new AzureIOT::TLSClient();
int exitCode = 0;
if (client->connect(dpsEndpoint, AZURE_HTTPS_SERVER_PORT) != 0) {
IOTC_LOG(F("ERROR: %s endpoint PUT call has failed."), hostName == NULL ? "PUT" : "GET");
delete client;
return 1;
}
AzureIOT::StringBuffer tmpBuffer(STRING_BUFFER_1024);
AzureIOT::StringBuffer deviceIdEncoded(deviceId, strlen(deviceId));
deviceIdEncoded.urlEncode();
size_t size = 0;
if (hostName == NULL) {
size = sprintf(NULL, "{\"registrationId\":\"%s\"}", deviceId);
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, F("\
PUT /%s/registrations/%s/register?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
content-length: %d\r\n\
%s\r\n\
connection: close\r\n\
\r\n\
{\"registrationId\":\"%s\"}\r\n\
"),
scopeId, *deviceIdEncoded, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, size, authHeader, deviceId);
} else {
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, F("\
GET /%s/registrations/%s/operations/%s?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
%s\r\n\
connection: close\r\n\
\r\n"),
scopeId, *deviceIdEncoded, operationId, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, authHeader);
}
assert(size != 0 && size < STRING_BUFFER_1024);
tmpBuffer.setLength(size);
const char* lookFor= hostName == NULL ? "{\"operationId\":\"" : "\"assignedHub\":\"";
int index = 0;
size_t total = 0;
while (size > total) {
int sent = client->write((const unsigned char*)*tmpBuffer + total, strlen(*tmpBuffer + total), 100);
if (sent < 0) {
IOTC_LOG(F("ERROR: tlsSocket send has failed with error code %d. Remaining buffer size was %lu"), sent, size);
exitCode = 1;
goto exit_operationId;
}
total += sent;
}
WAITMS(2000);
memset(*tmpBuffer, 0, STRING_BUFFER_1024);
if (socketReadThemAll(client, tmpBuffer) != 0) goto error_exit;
index = tmpBuffer.indexOf(lookFor, strlen(lookFor), 0);
if (index == -1) {
error_exit:
IOTC_LOG(F("ERROR: DPS (%s) request has failed.\r\n%s"), hostName == NULL ? "PUT" : "GET", *tmpBuffer);
exitCode = 1;
goto exit_operationId;
} else {
index += strlen(lookFor);
int index2 = tmpBuffer.indexOf("\"", 1, index + 1);
if (index2 == -1) goto error_exit;
tmpBuffer.setLength(index2);
strcpy(hostName == NULL ? operationId : hostName, (*tmpBuffer) + index);
client->disconnect();
}
exit_operationId:
delete client;
return exitCode;
}
int getHubHostName(const char* dpsEndpoint, const char *scopeId, const char* deviceId, const char* key, char *hostName) {
AzureIOT::StringBuffer authHeader(STRING_BUFFER_256);
size_t size = 0;
IOTC_LOG(F("- iotc.dps : getting auth..."));
if (getDPSAuthString(scopeId, deviceId, key, *authHeader, STRING_BUFFER_256, size)) {
IOTC_LOG(F("ERROR: getDPSAuthString has failed"));
return 1;
}
IOTC_LOG(F("- iotc.dps : getting operation id..."));
AzureIOT::StringBuffer operationId(STRING_BUFFER_64);
int retval = 0;
if ((retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, NULL)) == 0) {
WAITMS(4000);
IOTC_LOG(F("- iotc.dps : getting host name..."));
for (int i = 0; i < 5; i++) {
retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, hostName);
if (retval == 0) break;
WAITMS(3000);
}
}
return retval;
}
static void messageArrived(MQTT::MessageData& md)
{
MQTT::Message &message = md.message;
unsigned long lenTopic = 0;
char *dataTopic = NULL;
if (md.topicName.cstring) {
lenTopic = strlen(md.topicName.cstring);
dataTopic = md.topicName.cstring;
} else if (md.topicName.lenstring.len) {
lenTopic = md.topicName.lenstring.len;
dataTopic = md.topicName.lenstring.data;
}
handlePayload((char*)message.payload, message.payloadlen, dataTopic, lenTopic);
}
/* extern */
int iotc_free_context(IOTContext ctx) {
MUST_CALL_AFTER_INIT(ctx);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
if (internal->tlsClient != NULL) {
iotc_disconnect(ctx);
}
free(internal);
setSingletonContext(NULL);
return 0;
}
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL(keyORcert, 512);
AzureIOT::StringBuffer hostName;
AzureIOT::StringBuffer username;
AzureIOT::StringBuffer password;
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (type == IOTC_CONNECT_CONNECTION_STRING) {
getUsernameAndPasswordFromConnectionString(keyORcert,
strlen(keyORcert), hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_SYMM_KEY) {
assert(scope != NULL && deviceId != NULL);
AzureIOT::StringBuffer tmpHostname(STRING_BUFFER_128);
if (getHubHostName(internal->endpoint == NULL ?
DEFAULT_ENDPOINT : internal->endpoint, scope, deviceId, keyORcert, *tmpHostname)) {
return 1;
}
AzureIOT::StringBuffer cstr(STRING_BUFFER_256);
int rc = snprintf(*cstr, STRING_BUFFER_256,
F("HostName=%s;DeviceId=%s;SharedAccessKey=%s"), *tmpHostname, deviceId, keyORcert);
assert(rc > 0 && rc < STRING_BUFFER_256);
cstr.setLength(rc);
// TODO: move into iotc_dps and do not re-parse from connection string
getUsernameAndPasswordFromConnectionString(*cstr, rc, hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_X509_CERT) {
IOTC_LOG(F("ERROR: IOTC_CONNECT_X509_CERT NOT IMPLEMENTED"));
connectionStatusCallback(IOTC_CONNECTION_DEVICE_DISABLED, (IOTContextInternal*)ctx);
return 1;
}
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
data.MQTTVersion = 4;
data.clientID.cstring = *internal->deviceId;
data.username.cstring = *username;
data.password.cstring = *password;
data.keepAliveInterval = 2;
data.cleansession = 1;
internal->tlsClient = new AzureIOT::TLSClient();
internal->mqttClient = new MQTT::Client<AzureIOT::TLSClient, Countdown, STRING_BUFFER_1024, 5>(*(internal->tlsClient));
if (internal->tlsClient->connect(*hostName, AZURE_MQTT_SERVER_PORT) != 0) {
IOTC_LOG(F("ERROR: TLSClient connect attempt failed to %s\r\nMake sure both certificate and host are correct."), *hostName);
connectionStatusCallback(IOTC_CONNECTION_BAD_CREDENTIAL, (IOTContextInternal*)ctx);
return 1;
}
if (internal->mqttClient->connect(data) != MQTT::SUCCESS) {
IOTC_LOG(F("ERROR: TLSClient connect attempt failed. Check host, deviceId, username and password."));
connectionStatusCallback(IOTC_CONNECTION_BAD_CREDENTIAL, (IOTContextInternal*)ctx);
return 1;
}
AzureIOT::StringBuffer buffer(STRING_BUFFER_64);
size_t size = snprintf(*buffer, 63, "devices/%s/messages/events/#", *internal->deviceId);
buffer.setLength(size);
int errorCode = 0;
if ( (errorCode = internal->mqttClient->subscribe(*buffer, MQTT::QOS1, messageArrived)) != 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), buffer, errorCode);
size = snprintf(*buffer, 63, "devices/%s/messages/devicebound/#", *internal->deviceId);
buffer.setLength(size);
if ( (errorCode = internal->mqttClient->subscribe(*buffer, MQTT::QOS1, messageArrived)) != 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), buffer, errorCode);
errorCode = internal->mqttClient->subscribe("$iothub/twin/PATCH/properties/desired/#", MQTT::QOS1, messageArrived); // twin desired property changes
errorCode += internal->mqttClient->subscribe("$iothub/twin/res/#", MQTT::QOS1, messageArrived); // twin properties response
errorCode += internal->mqttClient->subscribe("$iothub/methods/POST/#", MQTT::QOS1, messageArrived);
if (errorCode != 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to twin/methods etc. error code sum => %d"), errorCode);
internal->mqttClient->setDefaultMessageHandler(messageArrived);
connectionStatusCallback(IOTC_CONNECTION_OK, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_disconnect(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
if (internal->mqttClient) {
if(internal->mqttClient->isConnected()) {
internal->mqttClient->disconnect();
}
delete internal->mqttClient;
internal->mqttClient = NULL;
}
if(internal->tlsClient) {
internal->tlsClient->disconnect();
delete internal->tlsClient;
internal->tlsClient = NULL;
}
connectionStatusCallback(IOTC_CONNECTION_DISCONNECTED, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_do_work(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
return internal->mqttClient->yield();
}
/* extern */
int iotc_set_network_interface(void* networkInterface) {
AzureIOT::TLSClient::setNetworkInterface((NetworkInterface*)networkInterface);
return 0;
}
#endif // defined(USE_LIGHT_CLIENT)
#endif // __MBED__

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#if defined(__MBED__)
#include "../common/iotc_internal.h"
#if defined(USE_LIGHT_CLIENT)
namespace AzureIOT {
time_t TLSClient::timestamp = 0;
time_t TLSClient::timeStart = 0;
NetworkInterface* TLSClient::networkInterface = NULL;
int TLSClient::read(unsigned char* buffer, int len, int timeout) {
tlsSocket->set_timeout(iotc_max(timeout, 10));
return tlsSocket->recv(buffer, len);
}
int TLSClient::write(const unsigned char* buffer, int len, int timeout) {
tlsSocket->set_timeout(iotc_max(timeout, 10));
return tlsSocket->send(buffer, len);
}
bool TLSClient::connect(const char* host, int port) {
IOTC_LOG(F("TLSClient::connect host(%s)"), host);
assert(getNetworkInterface() != NULL);
if (tlsSocket->open(getNetworkInterface()) != 0) {
IOTC_LOG(F("ERROR: TLSClient::connect failed"));
return false;
}
tlsSocket->set_root_ca_cert((const char*)SSL_CA_PEM_DEF);
tlsSocket->set_client_cert_key(SSL_CLIENT_CERT_PEM, SSL_CLIENT_PRIVATE_KEY_PEM);
tlsSocket->set_blocking(true);
return tlsSocket->connect(host, port);
}
bool TLSClient::disconnect() {
IOTC_LOG(F("TLSClient::disconnect"));
return tlsSocket->close() == 0;
}
} // namespace AzureIOT
#endif // defined(USE_LIGHT_CLIENT)
#endif // __MBED__

Просмотреть файл

@ -0,0 +1,90 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#ifndef AZURE_IOTC_MBED_TLS_CLIENT_H
#define AZURE_IOTC_MBED_TLS_CLIENT_H
#if defined(__MBED__)
#include "NetworkInterface.h"
#include "TLSSocket.h"
#include "NTPClient.h"
#include <assert.h>
#include "iotc_definitions.h"
namespace AzureIOT {
class TLSClient {
static NetworkInterface* networkInterface;
TLSSocket* tlsSocket;
static time_t timestamp;
static time_t timeStart;
public:
TLSClient() {
tlsSocket = new TLSSocket();
assert(tlsSocket);
}
int read(unsigned char* buffer, int len, int timeout);
int write(const unsigned char* buffer, int len, int timeout);
int print(const char* buffer);
int println() { print("\r\n"); }
int println(const char* buffer) { print(buffer); println(); }
bool connect(const char* host, int port);
bool disconnect();
~TLSClient() {
delete tlsSocket;
}
static void setNetworkInterface(NetworkInterface* interface) {
networkInterface = interface;
}
static NetworkInterface* getNetworkInterface() {
return networkInterface;
}
static time_t nowTime() {
assert(TLSClient::networkInterface != NULL);
time_t timeDiff;
if (timestamp == 0) {
sync_ntp:
NTPClient ntp(TLSClient::networkInterface);
int retry_count = 0;
retry_time:
{
timeStart = us_ticker_read(); // terrible hack
timestamp = ntp.get_timestamp(15000 /* timeout */);
if (timestamp < 0) // timeout ?
{
if (retry_count++ < 5) {
goto retry_time;
}
printf("- ERROR: can't sync to NTP server\r\n");
return 0;
}
}
}
timeDiff = us_ticker_read() - timeStart;
time_t time_now = timestamp + (10 /* lag */ + (timeDiff / 1000));
if (time_now - timestamp > NTP_SYNC_PERIOD) {
goto sync_ntp;
}
return time_now;
}
};
} // namespace AzureIOT
#endif // __MBED__
#endif // AZURE_IOTC_MBED_TLS_CLIENT_H

Просмотреть файл

@ -0,0 +1,643 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "../common/iotc_internal.h"
#if !defined(USE_LIGHT_CLIENT)
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>
#include "../common/json.h"
#ifdef TARGET_MXCHIP_AZ3166
#include "azure_prov_client/prov_device_ll_client.h"
#include "azure_prov_client/prov_security_factory.h"
#include "azure_prov_client/prov_transport_mqtt_client.h"
#include <AzureIotHub.h>
#include "provisioning_client/adapters/hsm_client_key.h"
int prov_dev_set_symmetric_key_info(const char* registration_name, const char* symmetric_key) {
hsm_client_set_registration_name_and_key(registration_name, symmetric_key);
return 0;
}
#elif defined(ESP_PLATFORM)
#include "iothub_client.h"
#include "iothub_message.h"
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/platform.h"
#include "iothubtransportmqtt.h"
#include "iothub_client_version.h"
#include "iothub_device_client_ll.h"
#include "iothub_client_options.h"
#include "azure_prov_client/prov_device_ll_client.h"
#include "azure_prov_client/prov_security_factory.h"
#include "azure_prov_client/prov_transport_mqtt_client.h"
#else
#error "NOT IMPLEMENTED"
#endif // TARGET_MXCHIP_AZ3166?
typedef struct EVENT_INSTANCE_TAG {
IOTHUB_MESSAGE_HANDLE messageHandle;
IOTContextInternal *internal;
void *appContext;
} EVENT_INSTANCE;
static EVENT_INSTANCE *createEventInstance(IOTContextInternal *internal,
const char* payload, unsigned length, void *applicationContext, int *errorCode) {
*errorCode = 0;
EVENT_INSTANCE *currentMessage = (EVENT_INSTANCE*) malloc(sizeof(EVENT_INSTANCE));
if(currentMessage == NULL) {
IOTC_LOG(F("ERROR: (createEventInstance) currentMessage is NULL."));
*errorCode = 1;
return NULL;
}
memset(currentMessage, 0, sizeof(EVENT_INSTANCE));
currentMessage->messageHandle =
IoTHubMessage_CreateFromByteArray((const unsigned char*)payload, length);
if (currentMessage->messageHandle == NULL) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) IoTHubMessage_CreateFromByteArray has failed."));
free(currentMessage);
*errorCode = 9;
return NULL;
}
currentMessage->internal = internal;
currentMessage->appContext = applicationContext;
return currentMessage;
}
static void freeEventInstance(EVENT_INSTANCE *eventInstance) {
if (eventInstance != NULL) {
if (eventInstance->messageHandle != NULL) {
IoTHubMessage_Destroy(eventInstance->messageHandle);
}
free(eventInstance);
}
}
// send telemetry etc. confirmation callback
/* MessageSent */
static void sendConfirmationCallback(int statusCode, void *userContextCallback) {
EVENT_INSTANCE *eventInstance = (EVENT_INSTANCE *)userContextCallback;
assert(eventInstance != NULL);
IOTContextInternal *internal = (IOTContextInternal*)eventInstance->internal;
const unsigned char* buffer = NULL;
size_t size = 0;
if (internal->callbacks[/*IOTCallbacks::*/MessageSent].callback) {
if (IOTHUB_CLIENT_RESULT::IOTHUB_CLIENT_OK !=
IoTHubMessage_GetByteArray(eventInstance->messageHandle, &buffer, &size)) {
IOTC_LOG(F("ERROR: (sendConfirmationCallback) IoTHubMessage_GetByteArray has failed."));
}
IOTCallbackInfo info;
info.eventName = "MessageSent";
info.tag = NULL;
info.payload = (const char*) buffer;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::MessageSent].appContext;
info.statusCode = (int)result;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::MessageSent].callback(internal, &info);
}
freeEventInstance(eventInstance);
}
#define CONVERT_TO_IOTHUB_MESSAGE(x) \
(x == IOTC_MESSAGE_ACCEPTED ? IOTHUBMESSAGE_ACCEPTED : \
(x == IOTC_MESSAGE_REJECTED ? IOTHUBMESSAGE_REJECTED : IOTHUBMESSAGE_ABANDONED))
/* Command */
static const char* emptyResponse = "{}";
static int onCommand(const char* method_name, const unsigned char* payload,
size_t size, unsigned char** response, size_t* resp_size, void* userContextCallback) {
assert(response != NULL && resp_size != NULL);
*response = NULL;
*resp_size = 0;
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
if (internal->callbacks[/*IOTCallbacks::*/::Command].callback) {
IOTCallbackInfo info;
info.eventName = "Command";
info.tag = method_name;
info.payload = (const char*) payload;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Command].appContext;
info.statusCode = 0;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Command].callback(internal, &info);
if (info.callbackResponse != NULL) {
*response = (unsigned char*) info.callbackResponse;
*resp_size = strlen((char*) info.callbackResponse);
} else {
char *resp = (char*) malloc(3);
resp[2] = 0;
memcpy(resp, emptyResponse, 2);
*response = (unsigned char*)resp;
*resp_size = 2;
// resp should be freed by SDK
}
return 200;
}
return 500;
}
/* MessageSent */
static void deviceTwinConfirmationCallback(int status_code, void* userContextCallback) {
// TODO: use status code
sendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_OK, userContextCallback);
}
#define CONVERT_TO_IOTC_CONNECT_ENUM(x) \
( x == IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN ? IOTC_CONNECTION_EXPIRED_SAS_TOKEN : ( \
x == IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED ? IOTC_CONNECTION_RETRY_EXPIRED : ( \
x == IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED ? IOTC_CONNECTION_DEVICE_DISABLED : ( \
x == IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL ? IOTC_CONNECTION_BAD_CREDENTIAL : ( \
x == IOTHUB_CLIENT_CONNECTION_NO_NETWORK ? IOTC_CONNECTION_NO_NETWORK : ( \
x == IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR ? IOTC_CONNECTION_COMMUNICATION_ERROR : IOTC_CONNECTION_OK \
))))))
/* ConnectionStatus */
static void connectionStatusCallback(IOTHUB_CLIENT_CONNECTION_STATUS result,
IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* userContextCallback) {
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
if (internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback) {
IOTCallbackInfo info;
info.eventName = "ConnectionStatus";
info.tag = NULL;
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].appContext;
info.statusCode = CONVERT_TO_IOTC_CONNECT_ENUM(reason);
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback(internal, &info);
}
}
void sendOnError(IOTContextInternal *internal, const char* message) {
if (internal->callbacks[/*IOTCallbacks::*/::Error].callback) {
IOTCallbackInfo info;
info.eventName = "Error";
info.tag = message; // message lifetime should be managed by us
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Error].appContext;
info.statusCode = 1;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Error].callback(internal, &info);
}
}
void echoDesired(IOTContextInternal *internal, const char *propertyName,
const char *message, const char *status, int statusCode) {
AzureIOT::JSObject rootObject(message);
AzureIOT::JSObject propertyNameObject, desiredObject, desiredObjectPropertyName;
const char* methodName = rootObject.getStringByName("methodName");
rootObject.getObjectByName(propertyName, &propertyNameObject);
double value = 0, desiredVersion = 0;
if (rootObject.getObjectByName("desired", &desiredObject) &&
desiredObject.getObjectByName(propertyName, &desiredObjectPropertyName)) {
value = desiredObjectPropertyName.getNumberByName("value");
desiredVersion = desiredObject.getNumberByName("$version");
} else {
propertyName = rootObject.getNameAt(0);
value = propertyNameObject.getNumberByName("value");
desiredVersion = rootObject.getNumberByName("$version");
}
const char* echoTemplate = "{\"%s\":{\"value\":%d,\"statusCode\":%d,\
\"status\":\"%s\",\"desiredVersion\":%d}}";
uint32_t buffer_size = snprintf(NULL, 0, echoTemplate, propertyName,
(int) value, // BAD HACK
statusCode, status, (int) desiredVersion);
AzureIOT::StringBuffer buffer(buffer_size);
size_t size = snprintf(*buffer, buffer_size, echoTemplate, propertyName,
(int) value, statusCode,
status, (int) desiredVersion);
buffer.setLength(size);
iotc_send_property(internal, *buffer, size, NULL);
}
void callDesiredCallback(IOTContextInternal *internal, const char *propertyName, const char *payLoad, size_t size) {
const char* response = "completed";
if (internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback) {
IOTCallbackInfo info;
info.eventName = "SettingsUpdated";
info.tag = propertyName;
info.payload = payLoad;
info.payloadLength = size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].appContext;
info.statusCode = 200;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback(internal, &info);
if (info.callbackResponse) {
response = (const char*)info.callbackResponse;
}
echoDesired(internal, propertyName, payLoad, response, info.statusCode);
}
}
static void deviceTwinGetStateCallback(DEVICE_TWIN_UPDATE_STATE update_state,
const unsigned char* payLoad, size_t size, void* userContextCallback) {
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
((char*)payLoad)[size] = 0x00;
AzureIOT::JSObject payloadObject((const char *)payLoad);
if (update_state == DEVICE_TWIN_UPDATE_PARTIAL && payloadObject.getNameAt(0) != NULL) {
callDesiredCallback(internal, payloadObject.getNameAt(0), reinterpret_cast<const char*>(payLoad), size);
} else {
AzureIOT::JSObject desired, reported;
// loop through all the desired properties
// look to see if the desired property has an associated reported property
// if so look if the versions match, if they match do nothing
// if they don't match then call the associated callback for the desired property
payloadObject.getObjectByName("desired", &desired);
payloadObject.getObjectByName("reported", &reported);
for (unsigned i = 0, count = desired.getCount(); i < count; i++) {
const char * itemName = desired.getNameAt(i);
if (itemName != NULL && itemName[0] != '$') {
AzureIOT::JSObject keyObject;
const char * version = NULL, * desiredVersion = NULL,
* value = NULL, * desiredValue = NULL;
bool containsKey = reported.getObjectByName(itemName, &keyObject);
if (containsKey) {
desiredVersion = reported.getStringByName("desiredVersion");
version = desired.getStringByName("$version");
desiredValue = reported.getStringByName("desiredValue");
value = desired.getStringByName("value");
}
if (containsKey && strcmp(desiredVersion, version) == 0) {
IOTC_LOG(F("key: %s found in reported and versions match"), itemName);
} else if (containsKey && strcmp(desiredValue, value) != 0){
IOTC_LOG(F("key: %s either not found in reported or versions do not match\r\n"), itemName);
AzureIOT::JSObject itemValue;
if (desired.getObjectAt(i, &itemValue) && itemValue.toString() != NULL) {
IOTC_LOG(F("itemValue: %s"), itemValue.toString());
} else {
IOTC_LOG(F("ERROR: desired doesn't have value at index"));
}
callDesiredCallback(internal, itemName, (const char*)payLoad, size);
} else {
echoDesired(internal, itemName, (const char*)payLoad, "completed", 200);
}
}
}
}
}
/* extern */
int iotc_init_context(IOTContext *ctx) {
CHECK_NOT_NULL(ctx)
MUST_CALL_BEFORE_INIT((*ctx));
IOTContextInternal *internal = (IOTContextInternal*)malloc(sizeof(IOTContextInternal));
CHECK_NOT_NULL(internal);
memset(internal, 0, sizeof(IOTContextInternal));
*ctx = (void*)internal;
return 0;
}
/* extern */
int iotc_free_context(IOTContext ctx) {
MUST_CALL_AFTER_INIT(ctx);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
if (internal->clientHandle != NULL) {
IoTHubClient_LL_Destroy(internal->clientHandle);
}
free(internal);
return 0;
}
typedef struct REGISTRATION_CONTEXT_TAG
{
char* iothub_uri;
int registration_complete;
} REGISTRATION_CONTEXT;
static void registation_status_callback(PROV_DEVICE_REG_STATUS reg_status, void* user_context)
{
if (user_context == NULL)
{
IOTC_LOG(F("ERROR: (registation_status_callback) user_context is NULL"));
}
else
{
if (reg_status == PROV_DEVICE_REG_STATUS_CONNECTED)
{
IOTC_LOG(F("IOTC: Registration status: CONNECTED"));
}
else if (reg_status == PROV_DEVICE_REG_STATUS_REGISTERING)
{
IOTC_LOG(F("IOTC: Registration status: REGISTERING"));
}
else if (reg_status == PROV_DEVICE_REG_STATUS_ASSIGNING)
{
IOTC_LOG(F("IOTC: Registration status: ASSIGNING"));
}
}
}
static void register_device_callback(PROV_DEVICE_RESULT register_result, const char* iothub_uri, const char* deviceId, void* user_context)
{
if (user_context == NULL)
{
IOTC_LOG(F("ERROR: (register_device_callback) user_context is NULL"));
}
else
{
REGISTRATION_CONTEXT* user_ctx = (REGISTRATION_CONTEXT*)user_context;
if (register_result == PROV_DEVICE_RESULT_OK)
{
user_ctx->iothub_uri = strdup(iothub_uri);
user_ctx->registration_complete = 1;
}
else
{
IOTC_LOG(F("ERROR: (register_device_callback) Failure encountered on registration!"));
user_ctx->registration_complete = 2;
}
}
}
/* extern */
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL(scope, 256);
GET_LENGTH_NOT_NULL(keyORcert, 512);
GET_LENGTH_NOT_NULL(deviceId, 256);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
char stringBuffer[AZ_IOT_HUB_MAX_LEN] = {0};
int errorCode = 0;
size_t pos = 0;
bool traceOn = getLogLevel() > IOTC_LOGGING_API_ONLY;
if (type == IOTC_CONNECT_CONNECTION_STRING) {
strcpy(stringBuffer, keyORcert);
pos = strlen(stringBuffer);
} else {
if (type == IOTC_CONNECT_SYMM_KEY) {
prov_dev_set_symmetric_key_info(deviceId, keyORcert);
prov_dev_security_init(SECURE_DEVICE_TYPE_SYMMETRIC_KEY);
} else {
prov_dev_security_init(SECURE_DEVICE_TYPE_X509);
}
PROV_DEVICE_LL_HANDLE handle;
if ((handle = Prov_Device_LL_Create(internal->endpoint == NULL ?
DEFAULT_ENDPOINT : internal->endpoint, scope, Prov_Device_MQTT_Protocol)) == NULL)
{
IOTC_LOG(F("ERROR: (iotc_connect) device registration step has failed."));
errorCode = 1;
goto fnc_exit;
}
REGISTRATION_CONTEXT user_ctx = { 0 };
Prov_Device_LL_SetOption(handle, "logtrace", &traceOn);
#if defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
if (Prov_Device_LL_SetOption(handle, "TrustedCerts", certificates) != PROV_DEVICE_RESULT_OK)
{
IOTC_LOG(F("ERROR: (iotc_connect) Failed to set option \"TrustedCerts\"."));
errorCode = 1;
goto fnc_exit;
} else
#endif // defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
if (Prov_Device_LL_Register_Device(handle, register_device_callback, &user_ctx, registation_status_callback, &user_ctx) != PROV_DEVICE_RESULT_OK)
{
IOTC_LOG(F("ERROR: (iotc_connect) Failed calling Prov_Device_LL_Register_Device."));
errorCode = 1;
goto fnc_exit;
}
else
{
// Waiting the register to be completed
do
{
Prov_Device_LL_DoWork(handle);
ThreadAPI_Sleep(5);
} while (user_ctx.registration_complete == 0);
}
// Free DPS client
Prov_Device_LL_Destroy(handle);
if (user_ctx.registration_complete == 2) {
IOTC_LOG(F("ERROR: (iotc_connect) device registration step has failed."));
errorCode = 1;
goto fnc_exit;
}
if (type == IOTC_CONNECT_SYMM_KEY) {
pos = snprintf(stringBuffer, AZ_IOT_HUB_MAX_LEN,
"HostName=%s;DeviceId=%s;SharedAccessKey=%s",
user_ctx.iothub_uri,
deviceId,
keyORcert);
} else if (type == IOTC_CONNECT_X509_CERT) {
pos = snprintf(stringBuffer, AZ_IOT_HUB_MAX_LEN,
"HostName=%s;DeviceId=%s;UseProvisioning=true",
user_ctx.iothub_uri,
deviceId);
}
}
IOTC_LOG(F("ConnectionString: %s", stringBuffer);
if (pos == 0 || pos >= AZ_IOT_HUB_MAX_LEN) {
IOTC_LOG(F("ERROR: (iotc_connect) connection information is out of buffer."));
errorCode = 15;
goto fnc_exit;
}
if ((internal->clientHandle = IoTHubClient_LL_CreateFromConnectionString(
stringBuffer, MQTT_Protocol)) == NULL) {
IOTC_LOG(F("ERROR: (iotc_connect) Couldn't create hub from connection string."));
errorCode = 4;
goto fnc_exit;
}
IoTHubClient_LL_SetRetryPolicy(internal->clientHandle, IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF, 1200);
#if defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
if (IoTHubClient_LL_SetOption(internal->clientHandle, "TrustedCerts",
certificates /* src/cores/arduino/az_iot/azureiotcerts.h */) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: Failed to set option \"TrustedCerts\" IoTHubClient_LL_SetOption failed."));
return 5;
}
#endif // defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
traceOn = getLogLevel() > IOTC_LOGGING_API_ONLY;
IoTHubClient_LL_SetOption(internal->clientHandle, "logtrace", &traceOn);
if (IoTHubClient_LL_SetMessageCallback(internal->clientHandle, receiveMessageCallback,
internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed."));
errorCode = 5;
goto fnc_exit;
}
if (IoTHubClient_LL_SetDeviceTwinCallback(internal->clientHandle, deviceTwinGetStateCallback,
internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed."));
errorCode = 5;
goto fnc_exit;
}
if (IoTHubClient_LL_SetDeviceMethodCallback(internal->clientHandle, onCommand,
internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed. ERR:0x0005"));
errorCode = 5;
goto fnc_exit;
}
if (IoTHubClient_LL_SetConnectionStatusCallback(internal->clientHandle,
connectionStatusCallback, internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed. ERR:0x0005"));
errorCode = 5;
goto fnc_exit;
}
fnc_exit:
return errorCode;
}
/* extern */
int iotc_disconnect(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
IOTC_LOG(F("ERROR: (iotc_disconnect) Not implemented."));
return 1;
}
/* extern */
int iotc_send_telemetry(IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
int errorCode = 0;
IOTHUB_CLIENT_RESULT hubResult = IOTHUB_CLIENT_RESULT::IOTHUB_CLIENT_OK;
EVENT_INSTANCE *currentMessage = createEventInstance(internal, payload, length, NULL, &errorCode);
if (currentMessage == NULL) return errorCode;
MAP_HANDLE propMap = IoTHubMessage_Properties(currentMessage->messageHandle);
time_t now_utc = time(NULL); // utc time
char timeBuffer[128] = {0};
unsigned outputLength = snprintf(timeBuffer, 128, "%s", ctime(&now_utc));
assert(outputLength && outputLength < 128 && timeBuffer[outputLength - 1] == '\n');
timeBuffer[outputLength - 1] = char(0); // replace `\n` with `\0`
if (Map_AddOrUpdate(propMap, "timestamp", timeBuffer) != MAP_OK)
{
IOTC_LOG(F("ERROR: (iotc_send_telemetry) Map_AddOrUpdate has failed."));
freeEventInstance(currentMessage);
return 1;
}
// submit the message to the Azure IoT hub
hubResult = IoTHubClient_LL_SendEventAsync(internal->clientHandle,
currentMessage->messageHandle, sendConfirmationCallback, currentMessage);
if (hubResult != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) IoTHubClient_LL_SendEventAsync has "
"failed => hubResult is (%d)."), hubResult);
freeEventInstance(currentMessage);
return 1;
}
iotc_do_work(ctx);
return 0;
}
/* extern */
int iotc_send_property (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
int errorCode = 0;
EVENT_INSTANCE *currentMessage = createEventInstance(internal, payload, length, NULL, &errorCode);
if (currentMessage == NULL) return errorCode;
IOTHUB_CLIENT_RESULT hubResult = IoTHubClient_LL_SendReportedState(internal->clientHandle,
(const unsigned char*)payload, length, deviceTwinConfirmationCallback, currentMessage);
if (hubResult != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) IoTHubClient_LL_SendReportedState has "
"failed => hubResult is (%d). ERROR:0x000B"), hubResult);
freeEventInstance(currentMessage);
return 11;
}
iotc_do_work(ctx);
return 0;
}
/* extern */
int iotc_do_work(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
IoTHubClient_LL_DoWork(internal->clientHandle);
return 0;
}
/* extern */
int iotc_set_network_interface(void* networkInterface) {
IOTC_LOG(F("ERROR: (iotc_set_network_interface) is not needed for this platform."));
return 1;
}
#endif // defined(TARGET_MXCHIP_AZ3166) || defined(ESP_PLATFORM)

1
MBED_OS/MQTT.lib Normal file
Просмотреть файл

@ -0,0 +1 @@
https://mbed.org/teams/mqtt/code/MQTT/#9cff7b6bbd01

54
MBED_OS/README.md Normal file
Просмотреть файл

@ -0,0 +1,54 @@
# MBED OS 5.X+ basic example for Azure IoT Central
- Visit [AzureIoTCentral](https://apps.azureiotcentral.com) and create a `new application`.
- Select `Sample Devkits`
- Add a new `mxchip` device. (real device) (Under the `Device Explorer`)
- Browse into device window and click/open `Connect` on the top-right of the device screen
- Grab `scopeId`, `device Id` and `primary key` and fill the necessary parts under `app_main.cpp`
```
// #define WIFI_SSID "<ENTER WIFI SSID HERE>"
// #define WIFI_PASSWORD "<ENTER WIFI PASSWORD HERE>"
// const char* scopeId = "<ENTER SCOPE ID HERE>";
// const char* deviceId = "<ENTER DEVICE ID HERE>";
// const char* deviceKey = "<ENTER DEVICE primary/secondary KEY HERE>";
```
Create a `src/` folder and copy `../iotc` folder into it.
Compile it! and deploy to your device.
- Use MBED online compiler
OR
- Install iotz from [this link](https://github.com/Azure/iotz)
- - (update board name under `iotz.json` i.e. currently it's `"target": "nucleo_l476rg"`)
Setup the environment; (under the project folder)
```
iotz init
iotz export
```
Compile!
```
iotz make -j2
```
Upload
```
Copy the `.bin` file under the `BUILD` folder into your device (possibly an attached disk drive)
```
Monitoring?
```
npm install -g nodemcu-tool
```
Assuming the port/dev for the board is `/dev/tty.usbxxxxxxx`
```
nodemcu-tool -p /dev/tty.usbxxxxx -b 9600 terminal
```

1
MBED_OS/TLSSocket.lib Normal file
Просмотреть файл

@ -0,0 +1 @@
https://github.com/coisme/TLSSocket/#dd86c396c58a12f049b2bc0d62f4bd97aa2cd5d6

135
MBED_OS/app_main.cpp Normal file
Просмотреть файл

@ -0,0 +1,135 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
#include <stdlib.h>
#include "easy-connect.h"
#include "mbed.h"
#include "NetworkInterface.h"
#include <string.h>
#include "src/iotc/iotc.h"
#include "src/iotc/common/string_buffer.h"
// #define WIFI_SSID "<ENTER WIFI SSID HERE>"
// #define WIFI_PASSWORD "<ENTER WIFI PASSWORD HERE>"
// const char* scopeId = "<ENTER SCOPE ID HERE>";
// const char* deviceId = "<ENTER DEVICE ID HERE>";
// const char* deviceKey = "<ENTER DEVICE primary/secondary KEY HERE>";
static IOTContext context = NULL;
static bool isConnected = false;
void onEvent(IOTContext ctx, IOTCallbackInfo *callbackInfo) {
if (strcmp(callbackInfo->eventName, "ConnectionStatus") == 0) {
LOG_VERBOSE("Is connected ? %s (%d)", callbackInfo->statusCode == IOTC_CONNECTION_OK ? "YES" : "NO", callbackInfo->statusCode);
isConnected = callbackInfo->statusCode == IOTC_CONNECTION_OK;
}
AzureIOT::StringBuffer buffer;
if (callbackInfo->payloadLength > 0) {
buffer.initialize(callbackInfo->payload, callbackInfo->payloadLength);
}
LOG_VERBOSE("- [%s] event was received. Payload => %s", callbackInfo->eventName, buffer.getLength() ? *buffer : "EMPTY");
if (strcmp(callbackInfo->eventName, "Command") == 0) {
LOG_VERBOSE("- Command name was => %s\r\n", callbackInfo->tag);
}
}
static unsigned prevMillis = 0, loopId = 0;
void loop()
{
if (isConnected) {
unsigned long ms = us_ticker_read() / 1000;
if (ms - prevMillis > 15000) { // send telemetry every 15 seconds
char msg[64] = {0};
int pos = 0, errorCode = 0;
prevMillis = ms;
if (loopId++ % 2 == 0) {
pos = snprintf(msg, sizeof(msg) - 1, "{\"accelerometerX\": %d}", 10 + (rand() % 20));
errorCode = iotc_send_telemetry(context, msg, pos);
} else {
pos = snprintf(msg, sizeof(msg) - 1, "{\"dieNumber\":%d}", 1 + (rand() % 5));
errorCode = iotc_send_property(context, msg, pos);
}
msg[pos] = 0;
if (errorCode != 0) {
LOG_ERROR("Sending message has failed with error code %d", errorCode);
}
}
iotc_do_work(context); // do background work for iotc
}
}
int main(int argc, char* argv[])
{
NetworkInterface* network = easy_connect(true, WIFI_SSID, WIFI_PASSWORD);
if (!network) {
LOG_VERBOSE("Unable to open network interface.");
return -1;
}
// if this sample is unable to pass NTP phase. enable the logic below to fix DNS issue
// it is a silly hack to set dns server as a router assuming router is sitting at .1
LOG_VERBOSE("Network interface opened successfully.");
AzureIOT::StringBuffer routerAddress(network->get_ip_address(), strlen(network->get_ip_address()));
{
int dotIndex = 0, foundCount = 0;
for (int i = 0; i < 3; i++) {
int pre = dotIndex;
dotIndex = routerAddress.indexOf(".", 1, dotIndex + 1);
if (dotIndex > pre) {
foundCount++;
}
}
if (foundCount != 3 || dotIndex + 1 > routerAddress.getLength()) {
LOG_ERROR("IPv4 address is expected." \
"(retval: %s foundCount:%d dotIndex:%d)",
*routerAddress, foundCount, dotIndex);
return 1;
}
routerAddress.set(dotIndex + 1, '1');
if (routerAddress.getLength() > dotIndex + 1) {
routerAddress.setLength(dotIndex + 2);
}
LOG_VERBOSE("routerAddress is assumed at %s", *routerAddress);
}
SocketAddress socketAddress(*routerAddress);
network->add_dns_server(socketAddress);
iotc_set_network_interface(network);
int errorCode = iotc_init_context(&context);
if (errorCode != 0) {
LOG_ERROR("Error initializing IOTC. Code %d", errorCode);
return 1;
}
iotc_set_logging(IOTC_LOGGING_API_ONLY);
// for the simplicity of this sample, used same callback for all the events below
iotc_on(context, "MessageSent", onEvent, NULL);
iotc_on(context, "Command", onEvent, NULL);
iotc_on(context, "ConnectionStatus", onEvent, NULL);
iotc_on(context, "SettingsUpdated", onEvent, NULL);
iotc_on(context, "Error", onEvent, NULL);
errorCode = iotc_connect(context, scopeId, deviceKey, deviceId, IOTC_CONNECT_SYMM_KEY);
if (errorCode != 0) {
LOG_ERROR("Error @ iotc_connect. Code %d", errorCode);
return 1;
}
prevMillis = us_ticker_read() / 1000;
while(true) loop();
return 1;
}

1
MBED_OS/easy-connect.lib Normal file
Просмотреть файл

@ -0,0 +1 @@
https://github.com/ARMmbed/easy-connect/#48fe1f874292fd843a40984f064a7eed5bbf985c

49
MBED_OS/iotz.json Normal file
Просмотреть файл

@ -0,0 +1,49 @@
{
"toolchain": "mbed",
"target": "nucleo_l476rg",
"deps":
[
{
"name": "MQTT",
"url" : "https://mbed.org/teams/mqtt/code/MQTT/#9cff7b6bbd01"
},
{
"name": "TLSSocket",
"url" : "https://github.com/coisme/TLSSocket/#dd86c396c58a12f049b2bc0d62f4bd97aa2cd5d6"
},
{
"name": "easy-connect",
"url" : "https://github.com/ARMmbed/easy-connect/#48fe1f874292fd843a40984f064a7eed5bbf985c"
},
{
"name": "ntp-client",
"url" : "https://github.com/ARMmbed/ntp-client/#a0805e2d30c4e92e48365f8e9647bb1ac8650793"
},
{
"name": "mbed-os",
"url" : "https://github.com/ARMmbed/mbed-os/#fd6f3cd4a4d879aeb2c45563f66605062a091091"
},
{
"name": "mbed-http",
"url": "https://developer.mbed.org/teams/sandbox/code/mbed-http/#6daf67a96a91"
}
],
"mbed_app.json":
{
"macros": [
"MBEDTLS_SHA1_C"
],
"config": {
"network-interface":{
"help": "options are ETHERNET, WIFI_ESP8266, WIFI_IDW0XX1, WIFI_ODIN, WIFI_RTW, WIFI_WIZFI310, WIFI_ISM43362, MESH_LOWPAN_ND, MESH_THREAD, CELLULAR_ONBOARD",
"value": "WIFI_IDW0XX1"
},
"wifi-ssid": {
"value": "\"\""
},
"wifi-password": {
"value": "\"\""
}
}
}
}

1
MBED_OS/mbed-http.lib Normal file
Просмотреть файл

@ -0,0 +1 @@
https://developer.mbed.org/teams/sandbox/code/mbed-http/#6daf67a96a91

1
MBED_OS/mbed-os.lib Normal file
Просмотреть файл

@ -0,0 +1 @@
https://github.com/ARMmbed/mbed-os/#fd6f3cd4a4d879aeb2c45563f66605062a091091

17
MBED_OS/mbed_app.json Normal file
Просмотреть файл

@ -0,0 +1,17 @@
{
"macros": [
"MBEDTLS_SHA1_C"
],
"config": {
"network-interface": {
"help": "options are ETHERNET, WIFI_ESP8266, WIFI_IDW0XX1, WIFI_ODIN, WIFI_RTW, WIFI_WIZFI310, WIFI_ISM43362, MESH_LOWPAN_ND, MESH_THREAD, CELLULAR_ONBOARD",
"value": "WIFI_IDW0XX1"
},
"wifi-ssid": {
"value": "\"\""
},
"wifi-password": {
"value": "\"\""
}
}
}

1
MBED_OS/ntp-client.lib Normal file
Просмотреть файл

@ -0,0 +1 @@
https://github.com/ARMmbed/ntp-client/#a0805e2d30c4e92e48365f8e9647bb1ac8650793

Просмотреть файл

@ -0,0 +1,495 @@
/*
PubSubClient.cpp - A simple client for MQTT.
Nick O'Leary
http://knolleary.net
*/
#ifdef ARDUINO
#include "PubSubClient.h"
#include "Arduino.h"
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client* client) {
this->_state = MQTT_DISCONNECTED;
setServer(domain, port);
setClient(client);
this->stream = NULL;
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
return connect(id,user,pass,0,0,0,0,1);
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
if (!connected()) {
int result = 0;
if (domain != NULL) {
result = _client->connect(this->domain, this->port);
} else {
result = _client->connect(this->ip, this->port);
}
if (result == 1) {
nextMsgId = 1;
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
unsigned int j;
#if MQTT_VERSION == MQTT_VERSION_3_1
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
#define MQTT_HEADER_VERSION_LENGTH 9
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
#define MQTT_HEADER_VERSION_LENGTH 7
#endif
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
buffer[length++] = d[j];
}
uint8_t v;
if (willTopic) {
v = 0x04|(willQos<<3)|(willRetain<<5);
} else {
v = 0x00;
}
if (cleanSession) {
v = v|0x02;
}
if(user != NULL) {
v = v|0x80;
if(pass != NULL) {
v = v|(0x80>>1);
}
}
buffer[length++] = v;
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
CHECK_STRING_LENGTH(length,id)
length = writeString(id,buffer,length);
if (willTopic) {
CHECK_STRING_LENGTH(length,willTopic)
length = writeString(willTopic,buffer,length);
CHECK_STRING_LENGTH(length,willMessage)
length = writeString(willMessage,buffer,length);
}
if(user != NULL) {
CHECK_STRING_LENGTH(length,user)
length = writeString(user,buffer,length);
if(pass != NULL) {
CHECK_STRING_LENGTH(length,pass)
length = writeString(pass,buffer,length);
}
}
write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
lastInActivity = lastOutActivity = millis();
while (!_client->available()) {
unsigned long t = millis();
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
_state = MQTT_CONNECTION_TIMEOUT;
_client->stop();
return false;
}
}
uint8_t llen;
uint16_t len = readPacket(&llen);
if (len == 4) {
if (buffer[3] == 0) {
lastInActivity = millis();
pingOutstanding = false;
_state = MQTT_CONNECTED;
return true;
} else {
_state = buffer[3];
}
}
_client->stop();
} else {
_state = MQTT_CONNECT_FAILED;
}
return false;
}
return true;
}
// reads a byte into result
boolean PubSubClient::readByte(uint8_t * result) {
uint32_t previousMillis = millis();
while(!_client->available()) {
yield();
uint32_t currentMillis = millis();
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
return false;
}
}
*result = _client->read();
return true;
}
// reads a byte into result[*index] and increments index
boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
uint16_t current_index = *index;
uint8_t * write_address = &(result[current_index]);
if(readByte(write_address)){
*index = current_index + 1;
return true;
}
return false;
}
uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
uint16_t len = 0;
if(!readByte(buffer, &len)) return 0;
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
uint32_t multiplier = 1;
uint16_t length = 0;
uint8_t digit = 0;
uint16_t skip = 0;
uint8_t start = 0;
do {
if (len == 5) {
// Invalid remaining length encoding - kill the connection
_state = MQTT_DISCONNECTED;
_client->stop();
return 0;
}
if(!readByte(&digit)) return 0;
buffer[len++] = digit;
length += (digit & 127) * multiplier;
multiplier *= 128;
} while ((digit & 128) != 0);
*lengthLength = len-1;
if (isPublish) {
// Read in topic length to calculate bytes to skip over for Stream writing
if(!readByte(buffer, &len)) return 0;
if(!readByte(buffer, &len)) return 0;
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
start = 2;
if (buffer[0]&MQTTQOS1) {
// skip message id
skip += 2;
}
}
for (uint16_t i = start;i<length;i++) {
if(!readByte(&digit)) return 0;
if (this->stream) {
if (isPublish && len-*lengthLength-2>skip) {
this->stream->write(digit);
}
}
if (len < MQTT_MAX_PACKET_SIZE) {
buffer[len] = digit;
}
len++;
}
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
len = 0; // This will cause the packet to be ignored.
}
return len;
}
boolean PubSubClient::loop() {
if (connected()) {
unsigned long t = millis();
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
if (pingOutstanding) {
this->_state = MQTT_CONNECTION_TIMEOUT;
_client->stop();
return false;
} else {
buffer[0] = MQTTPINGREQ;
buffer[1] = 0;
_client->write(buffer,2);
lastOutActivity = t;
lastInActivity = t;
pingOutstanding = true;
}
}
if (_client->available()) {
uint8_t llen;
uint16_t len = readPacket(&llen);
uint16_t msgId = 0;
uint8_t *payload;
if (len > 0) {
lastInActivity = t;
uint8_t type = buffer[0]&0xF0;
if (type == MQTTPUBLISH) {
if (callback) {
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
char *topic = (char*) buffer+llen+2;
// msgId only present for QOS>0
if ((buffer[0]&0x06) == MQTTQOS1) {
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
payload = buffer+llen+3+tl+2;
callback(topic,payload,len-llen-3-tl-2);
buffer[0] = MQTTPUBACK;
buffer[1] = 2;
buffer[2] = (msgId >> 8);
buffer[3] = (msgId & 0xFF);
_client->write(buffer,4);
lastOutActivity = t;
} else {
payload = buffer+llen+3+tl;
callback(topic,payload,len-llen-3-tl);
}
}
} else if (type == MQTTPINGREQ) {
buffer[0] = MQTTPINGRESP;
buffer[1] = 0;
_client->write(buffer,2);
} else if (type == MQTTPINGRESP) {
pingOutstanding = false;
}
} else if (!connected()) {
// readPacket has closed the connection
return false;
}
}
return true;
}
return false;
}
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
if (connected()) {
if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2 + strlen(topic) + plength) {
// Too long
return false;
}
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic, buffer, length);
uint16_t i;
for (i = 0; i < plength; i++) {
buffer[length++] = payload[i];
}
uint8_t header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
return write(header, buffer, length - MQTT_MAX_HEADER_SIZE);
}
return false;
}
boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
if (connected()) {
// Send the header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic,buffer,length);
uint16_t i;
uint8_t header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
lastOutActivity = millis();
return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
}
return false;
}
int PubSubClient::endPublish() {
return 1;
}
size_t PubSubClient::write(uint8_t data) {
lastOutActivity = millis();
return _client->write(data);
}
size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
lastOutActivity = millis();
return _client->write(buffer,size);
}
size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
uint8_t lenBuf[4];
uint8_t llen = 0;
uint8_t digit;
uint8_t pos = 0;
uint16_t len = length;
do {
digit = len % 128;
len = len / 128;
if (len > 0) {
digit |= 0x80;
}
lenBuf[pos++] = digit;
llen++;
} while(len>0);
buf[4-llen] = header;
for (int i=0;i<llen;i++) {
buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
}
return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
}
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
uint16_t rc;
uint8_t hlen = buildHeader(header, buf, length);
#ifdef MQTT_MAX_TRANSFER_SIZE
uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
uint16_t bytesRemaining = length+hlen; //Match the length type
uint8_t bytesToWrite;
boolean result = true;
while((bytesRemaining > 0) && result) {
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
rc = _client->write(writeBuf,bytesToWrite);
result = (rc == bytesToWrite);
bytesRemaining -= rc;
writeBuf += rc;
}
return result;
#else
rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
lastOutActivity = millis();
return (rc == hlen+length);
#endif
}
boolean PubSubClient::subscribe(const char* topic) {
return subscribe(topic, 0);
}
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
if (qos > 1) {
return false;
}
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
// Too long
return false;
}
if (connected()) {
// Leave room in the buffer for header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++;
if (nextMsgId == 0) {
nextMsgId = 1;
}
buffer[length++] = (nextMsgId >> 8);
buffer[length++] = (nextMsgId & 0xFF);
length = writeString((char*)topic, buffer,length);
buffer[length++] = qos;
return write(MQTTSUBSCRIBE|MQTTQOS1, buffer, length - MQTT_MAX_HEADER_SIZE);
}
return false;
}
boolean PubSubClient::unsubscribe(const char* topic) {
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
// Too long
return false;
}
if (connected()) {
uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++;
if (nextMsgId == 0) {
nextMsgId = 1;
}
buffer[length++] = (nextMsgId >> 8);
buffer[length++] = (nextMsgId & 0xFF);
length = writeString(topic, buffer,length);
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
}
return false;
}
void PubSubClient::disconnect() {
buffer[0] = MQTTDISCONNECT;
buffer[1] = 0;
_client->write(buffer,2);
_state = MQTT_DISCONNECTED;
_client->flush();
_client->stop();
lastInActivity = lastOutActivity = millis();
}
uint16_t PubSubClient::writeString(const char* str, uint8_t* buf, uint16_t pos) {
const char* idp = str;
uint16_t i = 0;
pos += 2;
while (*idp) {
buf[pos++] = *idp++;
i++;
}
buf[pos-i-2] = (i >> 8);
buf[pos-i-1] = (i & 0xFF);
return pos;
}
boolean PubSubClient::connected() {
boolean rc;
if (_client == NULL ) {
rc = false;
} else {
rc = (int)_client->connected();
if (!rc) {
if (this->_state == MQTT_CONNECTED) {
this->_state = MQTT_CONNECTION_LOST;
_client->flush();
_client->stop();
}
}
}
return rc;
}
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
return setServer(addr,port);
}
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
this->ip = ip;
this->port = port;
this->domain = NULL;
return *this;
}
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
this->domain = domain;
this->port = port;
return *this;
}
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
this->callback = callback;
return *this;
}
PubSubClient& PubSubClient::setClient(Client* client){
this->_client = client;
return *this;
}
PubSubClient& PubSubClient::setStream(Stream& stream){
this->stream = &stream;
return *this;
}
int PubSubClient::state() {
return this->_state;
}
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,156 @@
/*
PubSubClient.h - A simple client for MQTT.
Nick O'Leary
http://knolleary.net
*/
#ifdef ARDUINO
#ifndef PubSubClient_h
#define PubSubClient_h
#include <Arduino.h>
#include "IPAddress.h"
#include "Client.h"
#include "Stream.h"
#define MQTT_VERSION_3_1 3
#define MQTT_VERSION_3_1_1 4
// MQTT_VERSION : Pick the version
//#define MQTT_VERSION MQTT_VERSION_3_1
#ifndef MQTT_VERSION
#define MQTT_VERSION MQTT_VERSION_3_1_1
#endif
// MQTT_MAX_PACKET_SIZE : Maximum packet size
#ifndef MQTT_MAX_PACKET_SIZE
#define MQTT_MAX_PACKET_SIZE 512
#endif
// MQTT_KEEPALIVE : keepAlive interval in Seconds
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 15
#endif
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
#ifndef MQTT_SOCKET_TIMEOUT
#define MQTT_SOCKET_TIMEOUT 15
#endif
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
// pass the entire MQTT packet in each write call.
// #define MQTT_MAX_TRANSFER_SIZE 80
// Possible values for client.state()
#define MQTT_CONNECTION_TIMEOUT -4
#define MQTT_CONNECTION_LOST -3
#define MQTT_CONNECT_FAILED -2
#define MQTT_DISCONNECTED -1
#define MQTT_CONNECTED 0
#define MQTT_CONNECT_BAD_PROTOCOL 1
#define MQTT_CONNECT_BAD_CLIENT_ID 2
#define MQTT_CONNECT_UNAVAILABLE 3
#define MQTT_CONNECT_BAD_CREDENTIALS 4
#define MQTT_CONNECT_UNAUTHORIZED 5
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
#define MQTTPUBLISH 3 << 4 // Publish message
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
#define MQTTPINGREQ 12 << 4 // PING Request
#define MQTTPINGRESP 13 << 4 // PING Response
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
#define MQTTReserved 15 << 4 // Reserved
#define MQTTQOS0 (0 << 1)
#define MQTTQOS1 (1 << 1)
#define MQTTQOS2 (2 << 1)
// Maximum size of fixed header and variable length size header
#define MQTT_MAX_HEADER_SIZE 5
#if defined(ESP8266) || defined(ESP32)
#include <functional>
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
#else
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
#endif
#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
class PubSubClient : public Print {
private:
Client* _client;
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
uint16_t nextMsgId;
unsigned long lastOutActivity;
unsigned long lastInActivity;
bool pingOutstanding;
MQTT_CALLBACK_SIGNATURE;
uint16_t readPacket(uint8_t*);
boolean readByte(uint8_t * result);
boolean readByte(uint8_t * result, uint16_t * index);
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
// Build up the header ready to send
// Returns the size of the header
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
IPAddress ip;
const char* domain;
uint16_t port;
Stream* stream;
int _state;
public:
PubSubClient(const char*, uint16_t, Client* client);
PubSubClient& setServer(IPAddress ip, uint16_t port);
PubSubClient& setServer(uint8_t * ip, uint16_t port);
PubSubClient& setServer(const char * domain, uint16_t port);
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
PubSubClient& setClient(Client* client);
PubSubClient& setStream(Stream& stream);
boolean connect(const char* id, const char* user, const char* pass);
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
void disconnect();
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
// Start to publish a message.
// This API:
// beginPublish(...)
// one or more calls to write(...)
// endPublish()
// Allows for arbitrarily large payloads to be sent without them having to be copied into
// a new buffer and held in memory at one time
// Returns 1 if the message was started successfully, 0 if there was an error
boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
// Finish off this publish message (started with beginPublish)
// Returns 1 if the packet was sent successfully, 0 if there was an error
int endPublish();
// Write a single byte of payload (only to be used with beginPublish/endPublish)
virtual size_t write(uint8_t);
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
// Returns the number of bytes written
virtual size_t write(const uint8_t *buffer, size_t size);
boolean subscribe(const char* topic);
boolean subscribe(const char* topic, uint8_t qos);
boolean unsubscribe(const char* topic);
boolean loop();
boolean connected();
int state();
};
#endif
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "../common/iotc_platform.h"
#if defined(ARDUINO) && defined(USE_LIGHT_CLIENT)
#include "../common/json.h"
#include "../common/iotc_internal.h"
#include <SPI.h>
#include <stdarg.h>
int mqtt_publish(IOTContextInternal *internal, const char* topic, unsigned long topic_length,
const char* msg, unsigned long msg_length) {
if (!internal->mqttClient->publish(topic, (const uint8_t*)msg, msg_length, false)) {
return 1;
}
return 0;
}
void IOTC_LOG(const __FlashStringHelper *format, ...) {
if (getLogLevel() > IOTC_LOGGING_DISABLED)
va_list ap;
va_start(ap, format);
AzureIOT::StringBuffer buffer(STRING_BUFFER_1024);
buffer.setLength(vsnprintf(*buffer, STRING_BUFFER_1024, (const char *)format, ap));
Serial.println(*buffer);
va_end(ap);
}
}
#endif // defined(ARDUINO) && defined(USE_LIGHT_CLIENT)

Просмотреть файл

@ -0,0 +1,351 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "../common/iotc_platform.h"
#if defined(ARDUINO) && defined(USE_LIGHT_CLIENT)
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "../common/json.h"
#include "../common/iotc_internal.h"
unsigned long _getNow()
{
int retryCount = 0;
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
unsigned long retVal = 0;
retry_getNow:
IPAddress address(129, 6, 15, retryCount % 2 == 1 ? 28 : 29); // time.nist.gov NTP server
WiFiUDP Udp;
if (Udp.begin(2390) == 0) {
if (retryCount < 5) {
retryCount++;
goto retry_getNow;
}
IOTC_LOG(F("ERROR: couldn't fetch the time from NTP."));
return retVal;
}
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
Udp.beginPacket(address, 123);
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
// wait to see if a reply is available
WAITMS(1000);
if (Udp.parsePacket() ) {
Udp.read(packetBuffer, NTP_PACKET_SIZE);
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
unsigned long secsSince1900 = highWord << 16 | lowWord;
retVal = (secsSince1900 - 2208988800UL);
} else {
if (retryCount < 5) {
retryCount++;
goto retry_getNow;
}
IOTC_LOG(F("ERROR: couldn't fetch the time from NTP."));
}
Udp.stop();
return retVal;
}
static unsigned long g_udpTime = 0, g_lastRead = 0;
unsigned long getNow() {
unsigned long ms = millis();
if (g_udpTime == 0 || ms - g_lastRead > NTP_SYNC_PERIOD) {
g_udpTime = _getNow();
g_lastRead = ms;
}
return g_udpTime + ((ms - g_lastRead) / 1000);
}
int _getOperationId(const char* dpsEndpoint, const char* scopeId, const char* deviceId,
const char* authHeader, char *operationId, char *hostName) {
ARDUINO_WIFI_SSL_CLIENT client;
int exitCode = 0;
#ifndef USES_WIFI101
client.setCACert((const uint8_t*)SSL_CA_PEM_DEF, strlen((const char*)SSL_CA_PEM_DEF));
#endif
int retry = 0;
while (retry < 5 && !client.connect(dpsEndpoint, AZURE_HTTPS_SERVER_PORT)) retry++;
if (!client.connected()) {
IOTC_LOG(F("ERROR: DPS endpoint %s call has failed."), hostName == NULL ? "PUT" : "GET");
return 1;
}
AzureIOT::StringBuffer tmpBuffer(STRING_BUFFER_1024);
AzureIOT::StringBuffer deviceIdEncoded(deviceId, strlen(deviceId));
deviceIdEncoded.urlEncode();
size_t size = 0;
if (hostName == NULL) {
size = strlen("{\"registrationId\":\"%s\"}") + strlen(deviceId) - 2;
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, "\
PUT /%s/registrations/%s/register?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
content-length: %d\r\n\
%s\r\n\
connection: close\r\n\
\r\n\
{\"registrationId\":\"%s\"}\r\n\
",
scopeId, *deviceIdEncoded, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, size, authHeader, deviceId);
} else {
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, "\
GET /%s/registrations/%s/operations/%s?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
%s\r\n\
connection: close\r\n\
\r\n",
scopeId, *deviceIdEncoded, operationId, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, authHeader);
}
assert(size != 0 && size < STRING_BUFFER_1024);
tmpBuffer.setLength(size);
client.println(*tmpBuffer);
int index = 0;
while(!client.available()) {
WAITMS(100);
if (index++ > IOTC_SERVER_RESPONSE_TIMEOUT * 10) {
// 20 secs..
client.stop();
IOTC_LOG(F("ERROR: DPS (%s) request has failed. (Server didn't answer within 20 secs.)"), hostName == NULL ? "PUT" : "GET");
return 1;
}
}
index = 0;
bool enableSaving = false;
while (client.available() && index < STRING_BUFFER_1024 - 1) {
char ch = (char) client.read();
if (ch == '{') {
enableSaving = true; // don't use memory for headers
}
if (enableSaving) {
(*tmpBuffer)[index++] = ch;
}
}
tmpBuffer.setLength(index);
const char* lookFor = hostName == NULL ? "{\"operationId\":\"" : "\"assignedHub\":\"";
index = tmpBuffer.indexOf(lookFor, strlen(lookFor), 0);
if (index == -1) {
error_exit:
IOTC_LOG(F("ERROR: DPS (%s) request has failed.\r\n%s"), hostName == NULL ? "PUT" : "GET", *tmpBuffer);
exitCode = 1;
goto exit_operationId;
} else {
index += strlen(lookFor);
int index2 = tmpBuffer.indexOf("\"", 1, index + 1);
if (index2 == -1) goto error_exit;
tmpBuffer.setLength(index2);
strcpy(hostName == NULL ? operationId : hostName, (*tmpBuffer) + index);
}
exit_operationId:
client.stop();
return exitCode;
}
int getHubHostName(const char* dpsEndpoint, const char *scopeId, const char* deviceId, const char* key, char *hostName) {
AzureIOT::StringBuffer authHeader(STRING_BUFFER_256);
size_t size = 0;
IOTC_LOG(F("- iotc.dps : getting auth..."));
if (getDPSAuthString(scopeId, deviceId, key, *authHeader, STRING_BUFFER_256, size)) {
IOTC_LOG(F("ERROR: getDPSAuthString has failed"));
return 1;
}
IOTC_LOG(F("- iotc.dps : getting operation id..."));
AzureIOT::StringBuffer operationId(STRING_BUFFER_64);
int retval = 0;
if ((retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, NULL)) == 0) {
WAITMS(4000);
IOTC_LOG(F("- iotc.dps : getting host name..."));
for (int i = 0; i < 5; i++) {
retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, hostName);
if (retval == 0) break;
WAITMS(3000);
}
}
return retval;
}
static void messageArrived(char* topic, byte* data, unsigned int length) {
handlePayload((char*)data, length, topic, topic ? strlen(topic) : 0);
}
/* extern */
int iotc_free_context(IOTContext ctx) {
MUST_CALL_AFTER_INIT(ctx);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
iotc_disconnect(ctx);
free(internal);
setSingletonContext(NULL);
return 0;
}
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL(keyORcert, 512);
AzureIOT::StringBuffer hostName;
AzureIOT::StringBuffer username;
AzureIOT::StringBuffer password;
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (type == IOTC_CONNECT_CONNECTION_STRING) {
getUsernameAndPasswordFromConnectionString(keyORcert,
strlen(keyORcert), hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_SYMM_KEY) {
assert(scope != NULL && deviceId != NULL);
AzureIOT::StringBuffer tmpHostname(STRING_BUFFER_128);
if (getHubHostName(internal->endpoint == NULL ?
DEFAULT_ENDPOINT : internal->endpoint, scope, deviceId, keyORcert, *tmpHostname)) {
return 1;
}
AzureIOT::StringBuffer cstr(STRING_BUFFER_256);
int rc = snprintf(*cstr, STRING_BUFFER_256,
"HostName=%s;DeviceId=%s;SharedAccessKey=%s", *tmpHostname, deviceId, keyORcert);
assert(rc > 0 && rc < STRING_BUFFER_256);
cstr.setLength(rc);
// TODO: move into iotc_dps and do not re-parse from connection string
getUsernameAndPasswordFromConnectionString(*cstr, rc, hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_X509_CERT) {
IOTC_LOG(F("ERROR: IOTC_CONNECT_X509_CERT NOT IMPLEMENTED"));
connectionStatusCallback(IOTC_CONNECTION_DEVICE_DISABLED, (IOTContextInternal*)ctx);
return 1;
}
internal->tlsClient = new ARDUINO_WIFI_SSL_CLIENT();
#ifndef USES_WIFI101
internal->tlsClient->setCACert((const uint8_t*)SSL_CA_PEM_DEF, strlen((const char*)SSL_CA_PEM_DEF));
#endif
internal->mqttClient = new PubSubClient(*hostName, AZURE_MQTT_SERVER_PORT, internal->tlsClient);
internal->mqttClient->setCallback(messageArrived);
int retry = 0;
while(retry < 10 && !internal->mqttClient->connected()) {
if (internal->mqttClient->connect(*internal->deviceId, *username, *password)) {
break;
} else {
WAITMS(2000);
retry++;
}
}
if (!internal->mqttClient->connected()) {
IOTC_LOG(F("ERROR: MQTT client connect attempt failed. Check host, deviceId, username and password. (state %d)"),
internal->mqttClient->state());
connectionStatusCallback(IOTC_CONNECTION_BAD_CREDENTIAL, (IOTContextInternal*)ctx);
delete internal->tlsClient;
delete internal->mqttClient;
internal->tlsClient = NULL;
internal->mqttClient = NULL;
return 1;
}
AzureIOT::StringBuffer buffer(STRING_BUFFER_64);
buffer.setLength(snprintf(*buffer, 63, "devices/%s/messages/events/#", *internal->deviceId));
int errorCode = 0;
if ( (errorCode = internal->mqttClient->subscribe(*buffer)) == 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), *buffer, errorCode);
buffer.setLength(snprintf(*buffer, 63, "devices/%s/messages/devicebound/#", *internal->deviceId));
if ( (errorCode = internal->mqttClient->subscribe(*buffer)) == 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), *buffer, errorCode);
errorCode = internal->mqttClient->subscribe("$iothub/twin/PATCH/properties/desired/#"); // twin desired property changes
errorCode += internal->mqttClient->subscribe("$iothub/twin/res/#"); // twin properties response
errorCode += internal->mqttClient->subscribe("$iothub/methods/POST/#");
if (errorCode < 3)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to twin/methods etc. error code sum => %d"), errorCode);
connectionStatusCallback(IOTC_CONNECTION_OK, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_disconnect(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
if (internal->mqttClient) {
if(internal->mqttClient->connected()) {
internal->mqttClient->disconnect();
}
delete internal->mqttClient;
internal->mqttClient = NULL;
}
connectionStatusCallback(IOTC_CONNECTION_DISCONNECTED, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_do_work(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
if (!internal->mqttClient->loop()) {
if (!internal->mqttClient->connected()) {
connectionStatusCallback(IOTC_CONNECTION_DISCONNECTED, (IOTContextInternal*)ctx);
delete internal->mqttClient;
internal->mqttClient = NULL;
}
return 1;
}
return 0;
}
/* extern */
int iotc_set_network_interface(void* networkInterface) {
// NO-OP
return 0;
}
#endif // __MBED__

Просмотреть файл

@ -0,0 +1,142 @@
/*Copyright (C) 2013 Adam Rudd
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.
*/
#ifdef ARDUINO
#include "Base64.h"
#include <avr/pgmspace.h>
const char PROGMEM b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* 'Private' declarations */
inline void a3_to_a4(unsigned char * a4, unsigned char * a3);
inline void a4_to_a3(unsigned char * a3, unsigned char * a4);
inline unsigned char b64_lookup(char c);
int base64_encode(char *output, char *input, int inputLen) {
int i = 0, j = 0;
int encLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while(inputLen--) {
a3[i++] = *(input++);
if(i == 3) {
a3_to_a4(a4, a3);
for(i = 0; i < 4; i++) {
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[i]]);
}
i = 0;
}
}
if(i) {
for(j = i; j < 3; j++) {
a3[j] = '\0';
}
a3_to_a4(a4, a3);
for(j = 0; j < i + 1; j++) {
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[j]]);
}
while((i++ < 3)) {
output[encLen++] = '=';
}
}
output[encLen] = '\0';
return encLen;
}
int base64_decode(char * output, char * input, int inputLen) {
int i = 0, j = 0;
int decLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while (inputLen--) {
if(*input == '=') {
break;
}
a4[i++] = *(input++);
if (i == 4) {
for (i = 0; i <4; i++) {
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3,a4);
for (i = 0; i < 3; i++) {
output[decLen++] = a3[i];
}
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) {
a4[j] = '\0';
}
for (j = 0; j <4; j++) {
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3,a4);
for (j = 0; j < i - 1; j++) {
output[decLen++] = a3[j];
}
}
output[decLen] = '\0';
return decLen;
}
int base64_enc_len(int plainLen) {
int n = plainLen;
return (n + 2 - ((n + 2) % 3)) / 3 * 4;
}
int base64_dec_len(char * input, int inputLen) {
int i = 0;
int numEq = 0;
for(i = inputLen - 1; input[i] == '='; i--) {
numEq++;
}
return ((6 * inputLen) / 8) - numEq;
}
inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
a4[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f);
}
inline void a4_to_a3(unsigned char * a3, unsigned char * a4) {
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
}
inline unsigned char b64_lookup(char c) {
if(c >='A' && c <='Z') return c - 'A';
if(c >='a' && c <='z') return c - 71;
if(c >='0' && c <='9') return c + 4;
if(c == '+') return 62;
if(c == '/') return 63;
return -1;
}
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,81 @@
/*
* Copyright (c) 2013 Adam Rudd.
* See LICENSE for more information
*/
#ifdef ARDUINO
#ifndef _BASE64_H
#define _BASE64_H
/* b64_alphabet:
* Description: Base64 alphabet table, a mapping between integers
* and base64 digits
* Notes: This is an extern here but is defined in Base64.c
*/
extern const char b64_alphabet[];
/* base64_encode:
* Description:
* Encode a string of characters as base64
* Parameters:
* output: the output buffer for the encoding, stores the encoded string
* input: the input buffer for the encoding, stores the binary to be encoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the encoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_encode(char *output, char *input, int inputLen);
/* base64_decode:
* Description:
* Decode a base64 encoded string into bytes
* Parameters:
* output: the output buffer for the decoding,
* stores the decoded binary
* input: the input buffer for the decoding,
* stores the base64 string to be decoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the decoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_decode(char *output, char *input, int inputLen);
/* base64_enc_len:
* Description:
* Returns the length of a base64 encoded string whose decoded
* form is inputLen bytes long
* Parameters:
* inputLen: the length of the decoded string
* Return value:
* The length of a base64 encoded string whose decoded form
* is inputLen bytes long
* Requirements:
* None
*/
int base64_enc_len(int inputLen);
/* base64_dec_len:
* Description:
* Returns the length of the decoded form of a
* base64 encoded string
* Parameters:
* input: the base64 encoded string to be measured
* inputLen: the length of the base64 encoded string
* Return value:
* Returns the length of the decoded form of a
* base64 encoded string
* Requirements:
* 1. input must not be null
* 2. input must be greater than or equal to zero
*/
int base64_dec_len(char *input, int inputLen);
#endif // _BASE64_H
#endif

Просмотреть файл

@ -0,0 +1,107 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "iotc_internal.h"
/* extern */
int iotc_on(IOTContext ctx, const char* eventName, IOTCallback callback, void* appContext) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL_NOT_EMPTY(eventName, 64);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
#define SETCB_(x, a, b) x.callback=a;x.appContext=b;
if (strcmp(eventName, "ConnectionStatus") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus], callback, appContext);
} else if (strcmp(eventName, "MessageSent") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::MessageSent], callback, appContext);
} else if (strcmp(eventName, "Error") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::Error], callback, appContext);
} else if (strcmp(eventName, "SettingsUpdated") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated], callback, appContext);
} else if (strcmp(eventName, "Command") == 0) {
SETCB_(internal->callbacks[/*IOTCallbacks::*/::Command], callback, appContext);
} else {
IOTC_LOG(F("ERROR: (iotc_on) Unknown event definition. (%s)"), eventName);
return 1;
}
#undef SETCB_
return 0;
}
/* extern */
int iotc_send_state (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
return iotc_send_telemetry((IOTContext)internal, payload, length);
}
/* extern */
int iotc_send_event (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
return iotc_send_telemetry((IOTContext)internal, payload, length);
}
/* extern */
int iotc_set_global_endpoint(IOTContext ctx, const char* endpoint_uri) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL_NOT_EMPTY(endpoint_uri, 1024);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
// todo: do not fragment the memory
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
internal->endpoint = (char*) malloc(endpoint_uri_len + 1);
CHECK_NOT_NULL(internal->endpoint);
strcpy(internal->endpoint, endpoint_uri);
*(internal->endpoint + endpoint_uri_len) = 0;
return 0;
}
/* extern */
int iotc_set_trusted_certs(IOTContext ctx, const char* certs) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL_NOT_EMPTY(certs, 4096);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
IOTC_LOG(F("ERROR: (iotc_set_trusted_certs) Not implemented."));
return 0;
}
/* extern */
int iotc_set_proxy(IOTContext ctx, IOTC_HTTP_PROXY_OPTIONS proxy) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
IOTC_LOG(F("ERROR: (iotc_set_proxy) Not implemented."));
return 1;
}
/* extern */
int iotc_set_logging(IOTLogLevel level) {
if (level < IOTC_LOGGING_DISABLED || level > IOTC_LOGGING_ALL) {
IOTC_LOG(F("ERROR: (iotc_set_logging) invalid argument. ERROR:0x0001"));
return 1;
}
setLogLevel(level);
return 0;
}

Просмотреть файл

@ -0,0 +1,76 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#ifndef AZURE_IOTC_LITE_DEFINITIONS_H
#define AZURE_IOTC_LITE_DEFINITIONS_H
#define AZURE_MQTT_SERVER_PORT 8883
#define AZURE_HTTPS_SERVER_PORT 443
#define IOTC_SERVER_RESPONSE_TIMEOUT 20 // seconds
#define SSL_CLIENT_CERT_PEM NULL
#define SSL_CLIENT_PRIVATE_KEY_PEM NULL
/* Baltimore */
#define SSL_CA_PEM_DEF \
"-----BEGIN CERTIFICATE-----\r\n" \
"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\r\n" \
"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n" \
"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\r\n" \
"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\r\n" \
"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\r\n" \
"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\r\n" \
"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\r\n" \
"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\r\n" \
"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\r\n" \
"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\r\n" \
"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\r\n" \
"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\r\n" \
"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\r\n" \
"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\r\n" \
"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\r\n" \
"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\r\n" \
"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\r\n" \
"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\r\n" \
"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\r\n" \
"-----END CERTIFICATE-----\r\n"
// GENERAL
#define TO_STRING_(s) #s
#define TO_STRING(s) TO_STRING_(s)
#define iotc_max(a,b) (a > b ? a : b)
#define iotc_min(a,b) (a < b ? a : b)
#define STRING_BUFFER_16 16
#define STRING_BUFFER_32 32
#define STRING_BUFFER_64 64
#define STRING_BUFFER_128 128
#define STRING_BUFFER_256 256
#define STRING_BUFFER_512 512
#define STRING_BUFFER_1024 1024
#define STRING_BUFFER_4096 4096
#define NTP_SYNC_PERIOD (6 * 60 * 60 * 1000)
#define AZIOTC_API_MAJOR_VERSION 0.
#define AZIOTC_API_MINOR_VERSION 2.
#define AZIOTC_API_PATCH_VERSION 0
#define AZIOTC_API_VERSION TO_STRING(AZIOTC_API_MAJOR_VERSION \
AZIOTC_API_MINOR_VERSION AZIOTC_API_PATCH_VERSION) "-msiotc"
#define AZURE_IOT_CENTRAL_CLIENT_SIGNATURE "user-agent: iot-central-client/" AZIOTC_API_VERSION
#if defined(_DEBUG) || defined(DEBUG)
#define ASSERT_OR_FAIL_FAST(x) assert(x)
#else // defined(_DEBUG) || defined(DEBUG)
#define ASSERT_OR_FAIL_FAST(x) if (!(x)) { LOG_ERROR(TO_STRING(x) "condition has failed"); }
#endif // defined(_DEBUG) || defined(DEBUG)
#ifdef MBED_STATIC_ASSERT
#define ASSERT_STATIC MBED_STATIC_ASSERT
#else
#define ASSERT_STATIC static_assert
#endif
#endif // AZURE_IOTC_LITE_DEFINITIONS_H

Просмотреть файл

@ -0,0 +1,409 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "iotc_internal.h"
#include "../common/json.h"
IOTLogLevel gLogLevel = IOTC_LOGGING_DISABLED;
void setLogLevel(IOTLogLevel l) { gLogLevel = l; }
IOTLogLevel getLogLevel() { return gLogLevel; }
unsigned strlen_s_(const char* str, int max_expected) {
int ret_val = 0;
while(*(str++) != 0) {
ret_val++;
if (ret_val >= max_expected) return max_expected;
}
return ret_val;
}
#if defined(USE_LIGHT_CLIENT)
unsigned long getNow();
// Self MQTT option supports singletonContext only.
static IOTContextInternal *singletonContext = NULL;
IOTContextInternal* getSingletonContext() { return singletonContext; }
void setSingletonContext(IOTContextInternal* ctx) { singletonContext = ctx; }
int getUsernameAndPasswordFromConnectionString(const char* connectionString, size_t connectionStringLength,
AzureIOT::StringBuffer &hostName, AzureIOT::StringBuffer &deviceId,
AzureIOT::StringBuffer &username, AzureIOT::StringBuffer &password) {
// TODO: improve this so we don't depend on a particular order in connection string
AzureIOT::StringBuffer connStr(connectionString, connectionStringLength);
int32_t hostIndex = connStr.indexOf(HOSTNAME_STRING, HOSTNAME_LENGTH);
size_t length = connStr.getLength();
if (hostIndex != 0) {
IOTC_LOG(F("ERROR: connectionString doesn't start with HostName= RESULT:%d"), hostIndex);
return 1;
}
int32_t deviceIndex = connStr.indexOf(DEVICEID_STRING, DEVICEID_LENGTH);
if (deviceIndex == -1) {
IOTC_LOG(F("ERROR: ;DeviceId= not found in the connectionString"));
return 1;
}
int32_t keyIndex = connStr.indexOf(KEY_STRING, KEY_LENGTH);
if (keyIndex == -1) {
IOTC_LOG(F("ERROR: ;SharedAccessKey= not found in the connectionString"));
return 1;
}
hostName.initialize(connectionString + HOSTNAME_LENGTH, deviceIndex - HOSTNAME_LENGTH);
deviceId.initialize(connectionString + (deviceIndex + DEVICEID_LENGTH), keyIndex - (deviceIndex + DEVICEID_LENGTH));
AzureIOT::StringBuffer keyBuffer(length - (keyIndex + KEY_LENGTH));
memcpy(*keyBuffer, connectionString + (keyIndex + KEY_LENGTH), keyBuffer.getLength());
AzureIOT::StringBuffer hostURLEncoded(hostName);
hostURLEncoded.urlEncode();
size_t expires = getNow() + EXPIRES;
AzureIOT::StringBuffer stringToSign(hostURLEncoded.getLength() + STRING_BUFFER_128);
AzureIOT::StringBuffer deviceIdEncoded(deviceId);
deviceIdEncoded.urlEncode();
size_t keyLength = snprintf(*stringToSign, stringToSign.getLength(), "%s%s%s\n%lu000",
*hostURLEncoded, "%2Fdevices%2F", *deviceIdEncoded, expires);
stringToSign.setLength(keyLength);
keyBuffer.base64Decode();
stringToSign.hash(*keyBuffer, keyBuffer.getLength());
if (!stringToSign.base64Encode() || !stringToSign.urlEncode()) {
IOTC_LOG(F("ERROR: stringToSign base64Encode / urlEncode has failed."));
return 1;
}
AzureIOT::StringBuffer passwordBuffer(STRING_BUFFER_512);
size_t passLength = snprintf(*passwordBuffer, STRING_BUFFER_512,
"SharedAccessSignature sr=%s%s%s&sig=%s&se=%lu000",
*hostURLEncoded, "%2Fdevices%2F", *deviceIdEncoded, *stringToSign, expires);
assert(passLength && passLength < STRING_BUFFER_512);
passwordBuffer.setLength(passLength);
password.initialize(*passwordBuffer, passwordBuffer.getLength());
const char * usernameTemplate = "%s/%s/api-version=2016-11-14";
AzureIOT::StringBuffer usernameBuffer((strlen(usernameTemplate) - 3 /* %s twice */)
+ hostName.getLength() + deviceId.getLength());
size_t expLength = snprintf(*usernameBuffer, usernameBuffer.getLength(),
usernameTemplate, *hostName, *deviceId);
assert(expLength <= usernameBuffer.getLength());
username.initialize(*usernameBuffer, usernameBuffer.getLength());
IOTC_LOG(F(
"\r\n"\
"hostname: %s\r\n"\
"deviceId: %s\r\n"\
"username: %s\r\n"\
"password: %s\r\n"),
*hostName, *deviceId, *username, *password);
return 0;
}
int getDPSAuthString(const char* scopeId, const char* deviceId, const char* key,
char *buffer, int bufferSize, size_t &outLength) {
size_t expires = getNow() + EXPIRES;
AzureIOT::StringBuffer deviceIdEncoded(deviceId, strlen(deviceId));
deviceIdEncoded.urlEncode();
AzureIOT::StringBuffer stringToSign(STRING_BUFFER_256);
size_t size = snprintf(*stringToSign, STRING_BUFFER_256, "%s%%2Fregistrations%%2F%s", scopeId, *deviceIdEncoded);
assert(size < STRING_BUFFER_256);
stringToSign.setLength(size);
AzureIOT::StringBuffer sr(stringToSign);
size = snprintf(*stringToSign, STRING_BUFFER_256, "%s\n%lu000", *sr, expires);
assert(size < STRING_BUFFER_256);
stringToSign.setLength(size);
size = 0;
AzureIOT::StringBuffer keyDecoded(key, strlen(key));
keyDecoded.base64Decode();
stringToSign.hash(*keyDecoded, keyDecoded.getLength());
if (!stringToSign.base64Encode() || !stringToSign.urlEncode()) {
IOTC_LOG(F("ERROR: stringToSign base64Encode / urlEncode has failed."));
return 1;
}
outLength = snprintf(buffer, bufferSize, "authorization: SharedAccessSignature sr=%s&sig=%s&se=%lu000&skn=registration",
*sr, *stringToSign, expires);
assert(outLength > 0 && outLength < bufferSize);
buffer[outLength] = 0;
return 0;
}
// send telemetry etc. confirmation callback
/* MessageSent */
void sendConfirmationCallback(const char* buffer, size_t size) {
IOTContextInternal *internal = (IOTContextInternal*)singletonContext;
int result = 0;
if (internal->callbacks[/*IOTCallbacks::*/MessageSent].callback) {
IOTCallbackInfo info;
info.eventName = "MessageSent";
info.tag = NULL;
info.payload = (const char*) buffer;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::MessageSent].appContext;
info.statusCode = (int)result;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::MessageSent].callback(internal, &info);
}
}
/* Command */
static int onCommand(const char* method_name, const char* payload,
size_t size, char** response, size_t* resp_size, void* userContextCallback) {
assert(response != NULL && resp_size != NULL);
*response = NULL;
*resp_size = 0;
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
if (internal->callbacks[/*IOTCallbacks::*/::Command].callback) {
IOTCallbackInfo info;
info.eventName = "Command";
info.tag = method_name;
info.payload = (const char*) payload;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Command].appContext;
info.statusCode = 0;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Command].callback(internal, &info);
if (info.callbackResponse != NULL) {
*response = (char*) info.callbackResponse;
*resp_size = strlen((char*) info.callbackResponse);
}
return 200;
}
return 500;
}
/* ConnectionStatus */
void connectionStatusCallback(IOTConnectionState status, IOTContextInternal *internal) {
if (internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback) {
IOTCallbackInfo info;
info.eventName = "ConnectionStatus";
info.tag = NULL;
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].appContext;
info.statusCode = status;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback(internal, &info);
}
}
void sendOnError(IOTContextInternal *internal, const char* message) {
if (internal->callbacks[/*IOTCallbacks::*/::Error].callback) {
IOTCallbackInfo info;
info.eventName = "Error";
info.tag = message; // message lifetime should be managed by us
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Error].appContext;
info.statusCode = 1;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Error].callback(internal, &info);
}
}
void echoDesired(IOTContextInternal *internal, const char *propertyName,
AzureIOT::StringBuffer &message, const char *status, int statusCode) {
AzureIOT::JSObject rootObject(*message);
AzureIOT::JSObject propertyNameObject;
rootObject.getObjectByName(propertyName, &propertyNameObject);
double value = 0, desiredVersion = 0;
propertyName = rootObject.getNameAt(0);
value = propertyNameObject.getNumberByName("value");
desiredVersion = rootObject.getNumberByName("$version");
const char* echoTemplate = "{\"%s\":{\"value\":%d,\"statusCode\":%d,\
\"status\":\"%s\",\"desiredVersion\":%d}}";
uint32_t buffer_size = strlen(echoTemplate) + 23 /* x64 value */ + 3 /* statusCode */
+ 32 /* status */ + 23 /* version max */;
buffer_size = iotc_min(buffer_size, 512);
AzureIOT::StringBuffer buffer(buffer_size);
size_t size = snprintf(*buffer, buffer_size, echoTemplate, propertyName,
(int) value, statusCode, status, (int) desiredVersion);
buffer.setLength(size);
const char* topicName = "$iothub/twin/PATCH/properties/reported/?$rid=%d";
AzureIOT::StringBuffer topic(strlen(topicName) + 21); // + 2 for %d == +23 in case requestId++ overflows
topic.setLength(snprintf(*topic, topic.getLength(), topicName, internal->messageId++));
if (mqtt_publish(internal, *topic, topic.getLength(), *buffer, size) != 0) {
IOTC_LOG(F("ERROR: (echoDesired) MQTTClient publish has failed."));
}
}
void callDesiredCallback(IOTContextInternal *internal, const char *propertyName, AzureIOT::StringBuffer &payload) {
const char* response = "completed";
if (internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback) {
IOTCallbackInfo info;
info.eventName = "SettingsUpdated";
info.tag = propertyName;
info.payload = *payload;
info.payloadLength = payload.getLength();
info.appContext = internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].appContext;
info.statusCode = 200;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback(internal, &info);
if (info.callbackResponse) {
response = (const char*)info.callbackResponse;
}
echoDesired(internal, propertyName, payload, response, info.statusCode);
}
}
static void deviceTwinGetStateCallback(DEVICE_TWIN_UPDATE_STATE update_state,
AzureIOT::StringBuffer &payload, void* userContextCallback) {
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
AzureIOT::JSObject desired(*payload);
for (unsigned i = 0, count = desired.getCount(); i < count; i++) {
const char * itemName = desired.getNameAt(i);
if (itemName != NULL && itemName[0] != '$') {
callDesiredCallback(internal, itemName, payload);
}
}
}
void handlePayload(char *msg, unsigned long msg_length, char *topic, unsigned long topic_length) {
if (topic_length) {
assert(topic != NULL);
AzureIOT::StringBuffer topicName(topic, topic_length);
if (topicName.startsWith("$iothub/twin/res", strlen("$iothub/twin/res"))) return;
AzureIOT::StringBuffer payload;
if (msg_length) {
payload.initialize(msg, msg_length);
}
if (topicName.startsWith("$iothub/twin/PATCH/properties/desired/", strlen("$iothub/twin/PATCH/properties/desired/"))) {
deviceTwinGetStateCallback(DEVICE_TWIN_UPDATE_ALL, payload, singletonContext);
} else if (topicName.startsWith("$iothub/methods", strlen("$iothub/methods"))) {
int index = topicName.indexOf("$rid=", 5, 0);
if (index == -1) {
IOTC_LOG(F("ERROR: corrupt C2D message topic => %s"), *topicName);
return;
}
const char* topicId = (*topicName + index + 5);
const char* topicTemplate = "$iothub/methods/POST/";
const int topicTemplateLength = strlen(topicTemplate);
index = topicName.indexOf("/", 1, topicTemplateLength + 1);
if (index == -1) {
IOTC_LOG(F("ERROR: corrupt C2D message topic (methodName) => %s"), *topicName);
return;
}
AzureIOT::StringBuffer methodName(topic + topicTemplateLength, index - topicTemplateLength);
const char* constResponse = "{}";
char* response = NULL;
size_t respSize = 0;
int rc = onCommand(*methodName, msg, msg_length, &response, &respSize, getSingletonContext());
if (respSize == 0) {
respSize = 2;
} else {
constResponse = response;
}
AzureIOT::StringBuffer respTopic(STRING_BUFFER_128);
respTopic.setLength(snprintf(*respTopic, STRING_BUFFER_128, "$iothub/methods/res/%d/?$rid=%s", rc, topicId));
if (mqtt_publish(getSingletonContext(), *respTopic, respTopic.getLength(), constResponse, respSize) != 0) {
IOTC_LOG(F("ERROR: mqtt_publish has failed during C2D with response topic '%s' and response '%s'"), *respTopic, constResponse);
}
if (response != constResponse) {
free(response);
}
} else {
IOTC_LOG(F("ERROR: unknown twin topic: %s, msg: %s"), topic, msg_length ? msg : "NULL");
}
}
}
/* extern */
int iotc_send_telemetry(IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
AzureIOT::StringBuffer topic(internal->deviceId.getLength() + strlen("devices/ /messages/events/"));
topic.setLength(snprintf(*topic, topic.getLength(), "devices/%s/messages/events/", *internal->deviceId));
if (mqtt_publish(internal, *topic, topic.getLength(), payload, length) != 0) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) MQTTClient publish has failed."));
return 1;
}
sendConfirmationCallback(payload, length);
return 0;
}
/* extern */
int iotc_send_property (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
const char* topicName = "$iothub/twin/PATCH/properties/reported/?$rid=%d";
AzureIOT::StringBuffer topic(strlen(topicName) + 21); // + 2 for %d == +23 in case requestId++ overflows
topic.setLength(snprintf(*topic, topic.getLength(), topicName, internal->messageId++));
if (mqtt_publish(internal, *topic, topic.getLength(), payload, length) != 0) {
IOTC_LOG(F("ERROR: (iotc_send_property) MQTTClient publish has failed."));
return 1;
}
sendConfirmationCallback(payload, length);
return 0;
}
/* extern */
int iotc_init_context(IOTContext *ctx) {
CHECK_NOT_NULL(ctx)
if (getSingletonContext() != NULL) {
IOTC_LOG(F("ERROR: (iotc_init_context) Self MQTT version supports singleton context only."));
return 1;
}
MUST_CALL_BEFORE_INIT((*ctx));
IOTContextInternal *internal = (IOTContextInternal*)malloc(sizeof(IOTContextInternal));
CHECK_NOT_NULL(internal);
memset(internal, 0, sizeof(IOTContextInternal));
*ctx = (void*)internal;
setSingletonContext(internal);
return 0;
}
#endif // USE_LIGHT_CLIENT

Просмотреть файл

@ -0,0 +1,165 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef AZURE_IOTC_INTERNAL_H
#define AZURE_IOTC_INTERNAL_H
#include "iotc_platform.h"
#include "iotc_definitions.h"
#include <assert.h>
#include <stddef.h> // size_t etc.
#include <limits.h>
#include "../iotc.h"
#include "string_buffer.h"
#define AZ_IOT_HUB_MAX_LEN 1024
#define DEFAULT_ENDPOINT "global.azure-devices-provisioning.net"
#define TO_STR_(s) #s
#define TO_STR(s) TO_STR_(s)
typedef enum IOTCallbacks_TAG {
ConnectionStatus = 0x01,
MessageSent,
Command,
Error,
SettingsUpdated
} IOTCallbacks;
typedef struct CallbackBase_TAG {
IOTCallback callback;
void *appContext;
CallbackBase_TAG() { callback = NULL; appContext = NULL; }
} CallbackBase;
typedef struct IOTContextInternal_TAG {
char *endpoint;
IOTProtocol protocol;
CallbackBase callbacks[8];
#if defined(USE_LIGHT_CLIENT)
int messageId;
AzureIOT::StringBuffer deviceId;
#else // use azure iot client
IOTHUB_CLIENT_LL_HANDLE clientHandle;
#endif // USE_LIGHT_CLIENT
#ifdef USE_LIGHT_CLIENT
#if defined(__MBED__)
AzureIOT::TLSClient *tlsClient;
MQTT::Client<AzureIOT::TLSClient, Countdown, STRING_BUFFER_1024, 5>* mqttClient;
#elif defined(ARDUINO)
ARDUINO_WIFI_SSL_CLIENT *tlsClient;
PubSubClient *mqttClient;
#endif // __MBED__
#endif // USE_LIGHT_CLIENT
} IOTContextInternal;
#define CHECK_NOT_NULL(x) if (x == NULL) { IOTC_LOG(F(TO_STR(x) "is NULL")); return 1; }
#define GET_LENGTH_NOT_NULL_NOT_EMPTY(x, maxlen) \
unsigned x ## _len = 0; \
do { \
CHECK_NOT_NULL(x) \
x ## _len = strlen_s_(x, INT_MAX); \
if (x ## _len == 0 || x ## _len > maxlen) { \
IOTC_LOG(F("ERROR: " TO_STR(x) "has length %d"), x ## _len); return 1; \
} \
} while(0)
#define GET_LENGTH_NOT_NULL(x, maxlen) \
unsigned x ## _len = 0; \
do { \
CHECK_NOT_NULL(x) \
x ## _len = strlen_s_(x, INT_MAX); \
if (x ## _len > maxlen) { \
IOTC_LOG(F("ERROR: " TO_STR(x) " has length %d"), x ## _len); return 1; \
} \
} while(0)
#define MUST_CALL_BEFORE_INIT(x) \
if (x != NULL) { \
IOTC_LOG(F("ERROR: Client was already initialized. ERR:0x0006")); \
return 6; \
}
#define MUST_CALL_AFTER_INIT(x) \
if (x == NULL) { \
IOTC_LOG(F("ERROR: Client was not initialized. ERR:0x0007")); \
return 7; \
}
#ifdef USE_LIGHT_CLIENT
#define MUST_CALL_AFTER_CONNECT(x) \
if (x == NULL || x->mqttClient == NULL) { \
IOTC_LOG(F("ERROR: Client was not connected.")); \
return 1; \
}
#else // USE_LIGHT_CLIENT
#define MUST_CALL_AFTER_CONNECT(x) \
if (x == NULL || x->clientHandle == NULL) { \
IOTC_LOG(F("ERROR: Client was not connected.")); \
return 1; \
}
#endif // USE_LIGHT_CLIENT
// when the auth token expires
#define EXPIRES 21600 // 6 hours
typedef enum IOTHUBMESSAGE_DISPOSITION_RESULT_TAG {
IOTHUBMESSAGE_ACCEPTED = 0x01,
IOTHUBMESSAGE_ABANDONED
} IOTHUBMESSAGE_DISPOSITION_RESULT;
typedef enum DEVICE_TWIN_UPDATE_STATE_TAG {
DEVICE_TWIN_UPDATE_PARTIAL = 0,
DEVICE_TWIN_UPDATE_ALL = 1
} DEVICE_TWIN_UPDATE_STATE;
#define HOSTNAME_STRING "HostName="
#define DEVICEID_STRING ";DeviceId="
#define KEY_STRING ";SharedAccessKey="
#define HOSTNAME_LENGTH (sizeof(HOSTNAME_STRING) - 1)
#define DEVICEID_LENGTH (sizeof(DEVICEID_STRING) - 1)
#define KEY_LENGTH (sizeof(KEY_STRING) - 1)
#ifdef __cplusplus
extern "C" {
#endif
unsigned strlen_s_(const char* str, int max_expected);
int getUsernameAndPasswordFromConnectionString(const char* connectionString, size_t connectionStringLength,
AzureIOT::StringBuffer &hostName, AzureIOT::StringBuffer &deviceId,
AzureIOT::StringBuffer &username, AzureIOT::StringBuffer &password);
int getDPSAuthString(const char* scopeId, const char* deviceId, const char* key,
char *buffer, int bufferSize, size_t &outLength);
void setLogLevel(IOTLogLevel l);
IOTLogLevel getLogLevel();
void handlePayload(char *msg, unsigned long msg_length, char *topic, unsigned long topic_length);
int getHubHostName(const char* dpsEndpoint, const char *scopeId, const char* deviceId, const char* key, char *hostName);
void connectionStatusCallback(IOTConnectionState status, IOTContextInternal *internal);
IOTContextInternal* getSingletonContext();
void setSingletonContext(IOTContextInternal* ctx);
void sendConfirmationCallback(const char* buffer, size_t size);
int mqtt_publish(IOTContextInternal *internal, const char* topic, unsigned long topic_length,
const char* msg, unsigned long msg_length);
#ifdef ARDUINO
void IOTC_LOG(const __FlashStringHelper *format, ...);
#else
#define IOTC_LOG(...) \
if (getLogLevel() > IOTC_LOGGING_DISABLED) { \
printf(" - "); \
printf(__VA_ARGS__); \
printf("\r\n"); \
}
#endif
#ifdef __cplusplus
}
#endif
#endif // AZURE_IOTC_INTERNAL_H

Просмотреть файл

@ -0,0 +1,69 @@
#ifndef IOTC_COMMON_PLATFORM_H
#define IOTC_COMMON_PLATFORM_H
#ifdef ARDUINO_SAMD_FEATHER_M0
#include <Adafruit_WINC1500.h>
#include <Adafruit_WINC1500SSLClient.h>
#define ARDUINO_WIFI_SSL_CLIENT Adafruit_WINC1500SSLClient
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#define ARDUINO_WIFI_SSL_CLIENT WiFiClientSecure
#elif defined(ARDUINO_SAMD_MKR1010)
#include <WiFi101.h>
#include <WiFiNINA.h>
#define ARDUINO_WIFI_SSL_CLIENT WiFiSSLClient
#define USES_WIFI101
#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000)
#include <WiFi101.h>
#include <WifiSSLClient.h>
#define ARDUINO_WIFI_SSL_CLIENT WiFiSSLClient
#define USES_WIFI101
#endif // ARDUINO_SAMD_FEATHER_M0
#if defined(ARDUINO_WIFI_SSL_CLIENT)
#define USE_LIGHT_CLIENT 1
#endif // ARDUINO_WIFI_SSL_CLIENT
#if defined(__MBED__)
#if defined(ARDUINO)
#error "Both __MBED__ and ARDUINO were defined"
#endif // defined(ARDUINO)
#define USE_LIGHT_CLIENT 1
#include <mbed.h>
#include "MQTTmbed.h"
#include "MQTTClient.h"
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include <mbedtls/base64.h>
#include "../mbed_os/mbed_tls_client.h"
#define WAITMS wait_ms
#define SERIAL_PRINT printf
#define F(x) x
#elif defined (ARDUINO)
#include <arduino.h>
#include <avr/pgmspace.h>
#include "WiFiUdp.h"
#include "base64.h"
#include "sha256.h"
#include "../arduino/PubSubClient.h"
#define WAITMS delay
#define SERIAL_PRINT Serial.print
#else
#error "NOT SUPPORTED"
#endif // defined(__MBED__)
#ifndef PROGMEM
#define PROGMEM
#endif
#endif // IOTC_COMMON_PLATFORM_H

Просмотреть файл

@ -0,0 +1,123 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef AZURE_IOTC_API_JSON
#define AZURE_IOTC_API_JSON
#if defined(__MBED__) || defined(ESP_PLATFORM) || defined(ARDUINO)
#include "parson.h"
#else
#include <parson/parson.h>
#endif
#include "../iotc.h"
namespace AzureIOT
{
class JSObject
{
private:
JSON_Value* value;
JSON_Object* object;
bool isSubObject;
JSON_Object* toObject() {
object = json_value_get_object(value);
if (object == NULL) {
LOG_ERROR("JSON value is not an object");
return NULL;
}
return object;
}
public:
JSObject(): value(NULL), object(NULL), isSubObject(false) { }
JSObject(const char * json_string) : isSubObject(false) {
value = json_parse_string(json_string);
if (value == NULL) {
LOG_ERROR("parsing JSON failed");
}
object = toObject();
if (object == NULL) {
LOG_ERROR("json data: %s", json_string);
}
}
const char * getNameAt(unsigned index) {
if (object == NULL) { LOG_ERROR("(getNameAt) object == NULL!"); return NULL; }
return json_object_get_name(object, index);
}
unsigned getCount() {
return object ? (unsigned)json_object_get_count(object) : 0;
}
bool hasProperty(const char * name) {
return object ? json_object_has_value(object, name) == 1 : false;
}
const char * toString() {
if (object == NULL) { LOG_ERROR("(toString) object == NULL!"); return NULL; }
JSON_Value * val = json_object_get_wrapping_value(object);
if (val == NULL) return NULL;
return json_value_get_string(val);
}
~JSObject() {
if (value != NULL && isSubObject == false) {
json_value_free(value);
value = NULL;
}
}
bool getObjectAt(unsigned index, JSObject * outJSObject) {
if (index >= getCount()) return false;
JSON_Value * subValue = json_object_get_value_at(object, index);
if (subValue == NULL) return false;
outJSObject->isSubObject = true;
outJSObject->value = subValue;
outJSObject->toObject();
if (outJSObject->object == NULL) return false;
return true;
}
bool getObjectByName(const char * name, JSObject * outJSObject) {
JSON_Object* subObject = json_object_get_object(object, name);
if (subObject == NULL) {
// outJSObject->value memory freed by it's own de-constructor.
return false; // let consumer file the log
}
outJSObject->value = json_object_get_wrapping_value(object);
outJSObject->object = subObject;
outJSObject->isSubObject = true;
return true;
}
const char * getStringByName(const char * name) {
if (object == NULL) { LOG_ERROR("(getStringByName) object == NULL!"); return NULL; }
const char * text = json_object_get_string(object, name);
if (text == NULL) {
return NULL; // let consumer file the log
}
return text;
}
double getNumberByName(const char * name) {
if (object == NULL) { LOG_ERROR("(getNumberByName) object == NULL!"); return 0; }
// API returns 0.0 on fail hence it doesn't actually have a good
// fail discovery strategy
return json_object_get_number(object, name);
}
};
} // namespace AzureIOT
#endif // AZURE_IOTC_API_JSON

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,237 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 - 2017 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef parson_parson_h
#define parson_parson_h
#if defined(__MBED__) || defined(ESP_PLATFORM) || defined(ARDUINO)
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h> /* size_t */
/* Types and enums */
typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array;
typedef struct json_value_t JSON_Value;
enum json_value_type {
JSONError = -1,
JSONNull = 1,
JSONString = 2,
JSONNumber = 3,
JSONObject = 4,
JSONArray = 5,
JSONBoolean = 6
};
typedef int JSON_Value_Type;
enum json_result_t {
JSONSuccess = 0,
JSONFailure = -1
};
typedef int JSON_Status;
typedef void * (*JSON_Malloc_Function)(size_t);
typedef void (*JSON_Free_Function)(void *);
/* Call only once, before calling any other function from parson API. If not called, malloc and free
from stdlib will be used for all allocations */
void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
/* Parses first JSON value in a file, returns NULL in case of error */
JSON_Value * json_parse_file(const char *filename);
/* Parses first JSON value in a file and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_file_with_comments(const char *filename);
/* Parses first JSON value in a string, returns NULL in case of error */
JSON_Value * json_parse_string(const char *string);
/* Parses first JSON value in a string and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_string_with_comments(const char *string);
/* Serialization */
size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
char * json_serialize_to_string(const JSON_Value *value);
/* Pretty serialization */
size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);
char * json_serialize_to_string_pretty(const JSON_Value *value);
void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
/* Comparing */
int json_value_equals(const JSON_Value *a, const JSON_Value *b);
/* Validation
This is *NOT* JSON Schema. It validates json by checking if object have identically
named fields with matching types.
For example schema {"name":"", "age":0} will validate
{"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
In case of arrays, only first value in schema is checked against all values in tested array.
Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
null validates values of every type.
*/
JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
/*
* JSON Object
*/
JSON_Value * json_object_get_value (const JSON_Object *object, const char *name);
const char * json_object_get_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_get_array (const JSON_Object *object, const char *name);
double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* dotget functions enable addressing values with dot notation in nested objects,
just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
Because valid names in JSON can contain dots, some values may be inaccessible
this way. */
JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name);
const char * json_object_dotget_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name);
double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* Functions to get available names */
size_t json_object_get_count (const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index);
JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index);
JSON_Value * json_object_get_wrapping_value(const JSON_Object *object);
/* Functions to check if object has a value with a specific name. Returned value is 1 if object has
* a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */
int json_object_has_value (const JSON_Object *object, const char *name);
int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
int json_object_dothas_value (const JSON_Object *object, const char *name);
int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
/* Creates new name-value pair or frees and replaces old value with a new one.
* json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_set_null(JSON_Object *object, const char *name);
/* Works like dotget functions, but creates whole hierarchy if necessary.
* json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
/* Frees and removes name-value pair */
JSON_Status json_object_remove(JSON_Object *object, const char *name);
/* Works like dotget function, but removes name-value pair only on exact match. */
JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
/* Removes all name-value pairs in object */
JSON_Status json_object_clear(JSON_Object *object);
/*
*JSON Array
*/
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
const char * json_array_get_string (const JSON_Array *array, size_t index);
JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
JSON_Array * json_array_get_array (const JSON_Array *array, size_t index);
double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */
int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
size_t json_array_get_count (const JSON_Array *array);
JSON_Value * json_array_get_wrapping_value(const JSON_Array *array);
/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
* Order of values in array may change during execution. */
JSON_Status json_array_remove(JSON_Array *array, size_t i);
/* Frees and removes from array value at given index and replaces it with given one.
* Does nothing and returns JSONFailure if index doesn't exist.
* json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
/* Frees and removes all values from array */
JSON_Status json_array_clear(JSON_Array *array);
/* Appends new value at the end of array.
* json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
JSON_Status json_array_append_string(JSON_Array *array, const char *string);
JSON_Status json_array_append_number(JSON_Array *array, double number);
JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
JSON_Status json_array_append_null(JSON_Array *array);
/*
*JSON Value
*/
JSON_Value * json_value_init_object (void);
JSON_Value * json_value_init_array (void);
JSON_Value * json_value_init_string (const char *string); /* copies passed string */
JSON_Value * json_value_init_number (double number);
JSON_Value * json_value_init_boolean(int boolean);
JSON_Value * json_value_init_null (void);
JSON_Value * json_value_deep_copy (const JSON_Value *value);
void json_value_free (JSON_Value *value);
JSON_Value_Type json_value_get_type (const JSON_Value *value);
JSON_Object * json_value_get_object (const JSON_Value *value);
JSON_Array * json_value_get_array (const JSON_Value *value);
const char * json_value_get_string (const JSON_Value *value);
double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(const JSON_Value *value);
JSON_Value * json_value_get_parent (const JSON_Value *value);
/* Same as above, but shorter */
JSON_Value_Type json_type (const JSON_Value *value);
JSON_Object * json_object (const JSON_Value *value);
JSON_Array * json_array (const JSON_Value *value);
const char * json_string (const JSON_Value *value);
double json_number (const JSON_Value *value);
int json_boolean(const JSON_Value *value);
#ifdef __cplusplus
}
#endif
#endif // defined(__MBED__) || defined(ESP_PLATFORM)
#endif // parson_parson_h

Просмотреть файл

@ -0,0 +1,174 @@
#ifdef ARDUINO
#include <string.h>
#include <avr/pgmspace.h>
#include "sha256.h"
const uint32_t SHA256_K[] PROGMEM = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
const uint8_t SHA256_INIT_STATE[] PROGMEM = {
0x67,0xe6,0x09,0x6a, // H0
0x85,0xae,0x67,0xbb, // H1
0x72,0xf3,0x6e,0x3c, // H2
0x3a,0xf5,0x4f,0xa5, // H3
0x7f,0x52,0x0e,0x51, // H4
0x8c,0x68,0x05,0x9b, // H5
0xab,0xd9,0x83,0x1f, // H6
0x19,0xcd,0xe0,0x5b // H7
};
#define ror32(num, bits) ((num << (32 - bits)) | (num >> bits))
void Sha256::init(void) {
memcpy_P(state.b, SHA256_INIT_STATE, 32);
byteCount = 0;
bufferOffset = 0;
}
void Sha256::hashBlock() {
uint8_t i;
uint32_t a,b,c,d,e,f,g,h,t1,t2;
a=state.w[0];
b=state.w[1];
c=state.w[2];
d=state.w[3];
e=state.w[4];
f=state.w[5];
g=state.w[6];
h=state.w[7];
for (i=0; i<64; i++) {
if (i>=16) {
t1 = buffer.w[i&15] + buffer.w[(i-7)&15];
t2 = buffer.w[(i-2)&15];
t1 += ror32(t2,17) ^ ror32(t2,19) ^ (t2>>10);
t2 = buffer.w[(i-15)&15];
t1 += ror32(t2,7) ^ ror32(t2,18) ^ (t2>>3);
buffer.w[i&15] = t1;
}
t1 = h;
t1 += ror32(e,6) ^ ror32(e,11) ^ ror32(e,25); // ∑1(e)
t1 += g ^ (e & (g ^ f)); // Ch(e,f,g)
t1 += pgm_read_dword(SHA256_K + i); // Ki
t1 += buffer.w[i&15]; // Wi
t2 = ror32(a,2) ^ ror32(a,13) ^ ror32(a,22); // ∑0(a)
t2 += ((b & c) | (a & (b | c))); // Maj(a,b,c)
h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2;
}
state.w[0] += a;
state.w[1] += b;
state.w[2] += c;
state.w[3] += d;
state.w[4] += e;
state.w[5] += f;
state.w[6] += g;
state.w[7] += h;
}
void Sha256::push(uint8_t data) {
buffer.b[bufferOffset ^ 3] = data;
bufferOffset++;
if (bufferOffset == BLOCK_LENGTH) {
hashBlock();
bufferOffset = 0;
}
}
#if defined(ARDUINO) && ARDUINO >= 100
size_t Sha256::write(uint8_t data) {
#else
void Sha256::write(uint8_t data) {
#endif
++byteCount;
push(data);
#if defined(ARDUINO) && ARDUINO >= 100
return 1;
#endif
}
void Sha256::padBlock() {
// Implement SHA-256 padding (fips180-2 §5.1.1)
// Pad with 0x80 followed by 0x00 until the end of the block
push(0x80);
while (bufferOffset != 56) push(0x00);
// Append length in the last 8 bytes. We're only using 32 bit lengths, but
// SHA-2 supports 64 bit lengths so zero pad the top bits
push(0);
push(0);
push(0);
push(byteCount >> 29);
push(byteCount >> 21);
push(byteCount >> 13);
push(byteCount >> 5);
push(byteCount << 3);
}
uint8_t* Sha256::result(void) {
// Pad to complete the last block
padBlock();
// Swap byte order back
for (uint8_t i = 0; i < 8; i++) {
uint32_t a,b;
a=state.w[i];
b=a<<24;
b|=(a<<8) & 0x00ff0000;
b|=(a>>8) & 0x0000ff00;
b|=a>>24;
state.w[i]=b;
}
// Return pointer to hash
return state.b;
}
#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c
void Sha256::initHmac(const uint8_t *key, size_t keyLength) {
memset(keyBuffer, 0, BLOCK_LENGTH);
if (keyLength > BLOCK_LENGTH) {
// Hash long keys
init();
for (;keyLength--;) write(*key++);
memcpy(keyBuffer, result(), HASH_LENGTH);
} else {
// Block length keys are used as is
memcpy(keyBuffer, key, keyLength);
}
reset();
}
uint8_t* Sha256::resultHmac(void) {
uint8_t i;
// Complete inner hash
memcpy(innerHash, result(), HASH_LENGTH);
// Calculate outer hash
init();
for (i = 0; i < BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD);
for (i = 0; i < HASH_LENGTH; i++) write(innerHash[i]);
return result();
}
void Sha256::reset(void) {
// Start inner hash
init();
for (uint8_t i = 0; i < BLOCK_LENGTH; i++) {
write(keyBuffer[i] ^ HMAC_IPAD);
}
}
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,57 @@
#ifdef ARDUINO
#ifndef Sha256_h
#define Sha256_h
#include <inttypes.h>
#include "Print.h"
#define HASH_LENGTH 32
#define BLOCK_LENGTH 64
class Sha256 : public Print {
union Buffer {
uint8_t b[BLOCK_LENGTH];
uint32_t w[BLOCK_LENGTH / 4];
};
union State {
uint8_t b[HASH_LENGTH];
uint32_t w[HASH_LENGTH / 4];
};
public:
void init(void);
void initHmac(const uint8_t *key, size_t keyLength);
// Reset to initial state, but preserve key material.
void reset(void);
uint8_t* result(void);
uint8_t* resultHmac(void);
#if defined(ARDUINO) && ARDUINO >= 100
virtual size_t write(uint8_t);
#else
virtual void write(uint8_t);
#endif
using Print::write;
private:
void hashBlock();
void padBlock();
void push(uint8_t data);
uint32_t byteCount;
uint8_t keyBuffer[BLOCK_LENGTH];
uint8_t innerHash[HASH_LENGTH];
State state;
Buffer buffer;
uint8_t bufferOffset;
};
#endif
#endif // ARDUINO

Просмотреть файл

@ -0,0 +1,304 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "iotc_internal.h"
namespace AzureIOT {
static char convertToHex(char ch) {
static const char * lst = "0123456789ABCDEF";
return *(lst + (ch & 15));
}
static char convertFromHex(char ch) {
assert(isdigit(ch) || isalpha(ch)); // isalnum ?
if (ch <= '9') {
return ch - '0';
} else {
if (ch <= 'Z') {
ch = ch - 'A';
} else {
ch = ch - 'a';
}
return ch + 10;
}
}
bool StringBuffer::startsWith(const char* str, size_t len) {
if (len > length) return false;
const char *buffer = data == NULL ? immutable : data;
assert(buffer != NULL);
for (size_t i = 0; i < len; i++) {
if (str[i] != buffer[i]) return false;
}
return true;
}
int32_t StringBuffer::indexOf(const char* look_for, size_t look_for_length,
int32_t start_index) {
const char *buffer = data == NULL ? immutable : data;
assert(buffer != NULL);
if (look_for_length > length) {
return -1;
}
for (size_t pos = start_index; pos < length; pos++) {
if (length - pos < look_for_length) {
return -1;
}
if (buffer[pos] == *look_for) {
size_t sub = 1;
for (; sub < look_for_length; sub++) {
if (buffer[pos + sub] != look_for[sub]) break;
}
if (sub == look_for_length) {
return pos;
}
}
}
return -1;
}
bool StringBuffer::urlEncode() {
assert(data != NULL);
size_t buffer_length = (length * 3) + 1;
char *buffer = (char*) malloc(buffer_length);
if (buffer == NULL) {
return false;
}
char *tmp = buffer;
assert(buffer != NULL);
for (size_t i = 0; i < length; i++) {
char ch = data[i];
if (isalnum(ch) ||
ch == '_' || ch == '-' || ch == '~' || ch == '.') {
*tmp = ch;
} else if (ch == ' ') {
*tmp = '+';
} else {
*tmp++ = '%';
*tmp++ = convertToHex(ch >> 4);
*tmp = convertToHex(ch & 15);
}
tmp++;
}
*tmp = 0;
clear(); // free prev memory
data = buffer;
length = (size_t)tmp - (size_t)buffer;
return true;
}
bool StringBuffer::urlDecode() { // in-memory
assert(data != NULL);
char *tmp = data; // fast
for (size_t i = 0; i < length; i++) {
char ch = data[i];
if (ch == '%') {
if (i + 2 < length) {
*tmp = convertFromHex(data[i + 1]) << 4 |
convertFromHex(data[i + 2]);
i += 2;
}
} else if (ch == '+') {
*tmp = ' ';
} else {
*tmp = ch;
}
tmp++;
}
*tmp = 0;
length = (size_t)tmp - (size_t)data;
return true;
}
#ifdef __MBED__
bool StringBuffer::hash(const char *key, unsigned key_length)
{
assert(data != NULL);
mbedtls_md_type_t md = MBEDTLS_MD_SHA256;
const mbedtls_md_info_t *md_info;
mbedtls_md_context_t ctx;
md_info = mbedtls_md_info_from_type(md);
unsigned hash_size = (unsigned) mbedtls_md_get_size(md_info);
unsigned char *hmac_hash = (unsigned char*) malloc(hash_size + 1);
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, md_info, 1);
mbedtls_md_hmac_starts(&ctx, (const unsigned char*) key, key_length);
mbedtls_md_hmac_update(&ctx, (const unsigned char*) data, length);
mbedtls_md_hmac_finish(&ctx, hmac_hash);
free(data);
data = (char*)hmac_hash;
setLength(hash_size);
mbedtls_md_free(&ctx);
return true;
}
bool StringBuffer::base64Decode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length + 1); assert(decoded != NULL);
size_t keyLength = 0;
mbedtls_base64_decode((unsigned char*)decoded, length, &keyLength,
(const unsigned char*)data, getLength());
assert(keyLength > 0);
free(data);
data = (char*) malloc(keyLength + 1); assert(data != NULL);
memcpy(data, decoded, keyLength);
setLength(keyLength);
free(decoded);
return true;
}
bool StringBuffer::base64Encode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length * 3); assert(decoded != NULL);
size_t keyLength = 0;
mbedtls_base64_encode((unsigned char*)decoded, length * 3, &keyLength,
(const unsigned char*)data, getLength());
assert(keyLength > 0);
free(data);
data = (char*) malloc(keyLength + 1); assert(data != NULL);
memcpy(data, decoded, keyLength);
setLength(keyLength);
free(decoded);
return true;
}
#elif defined(ARDUINO)
bool StringBuffer::hash(const char *key, unsigned key_length)
{
assert(data != NULL);
Sha256 *sha256 = new Sha256();
sha256->initHmac((const uint8_t*)key, (size_t)key_length);
sha256->print(data);
char* sign = (char*) sha256->resultHmac();
if (length < HASH_LENGTH) {
free(data);
data = (char*) malloc(HASH_LENGTH + 1);
}
memcpy(data, sign, HASH_LENGTH);
setLength(HASH_LENGTH);
delete sha256;
return true;
}
bool StringBuffer::base64Decode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length + 1); assert(decoded != NULL);
size_t size = base64_decode(decoded, data, length);
assert (size <= length + 1);
free(data);
data = (char*) malloc(size + 1); assert(data != NULL);
memcpy(data, decoded, size);
setLength(size);
free(decoded);
return true;
}
bool StringBuffer::base64Encode() {
assert(data != NULL && length > 0);
char *decoded = (char*) malloc(length * 3); assert(decoded != NULL);
size_t size = base64_encode(decoded, data, length);
assert (size < length * 3);
free(data);
data = (char*) malloc(size + 1); assert(data != NULL);
memcpy(data, decoded, size);
setLength(size);
free(decoded);
return true;
}
#endif // __MBED__
StringBuffer::StringBuffer(StringBuffer &buffer): data(NULL), immutable(NULL) {
length = 0;
initialize(buffer.data, buffer.length);
}
StringBuffer::StringBuffer(const char * str, unsigned int lengthStr, bool isCopy):
data(NULL), immutable(NULL) {
if (isCopy) {
length = 0;
if (str != NULL) {
initialize(str, lengthStr);
}
} else {
assert(str != NULL);
immutable = str;
length = lengthStr;
}
}
StringBuffer::StringBuffer(unsigned lengthStr): data(NULL), immutable(NULL) {
length = 0;
alloc(lengthStr + 1);
assert(data != NULL); // out of memory?
length = lengthStr;
}
void StringBuffer::initialize(const char * str, unsigned lengthStr) {
if (str != NULL) {
alloc(lengthStr + 1); // +1 for \0
assert(data != NULL && immutable == NULL); // out of memory?
memcpy(data, str, lengthStr);
data[lengthStr] = char(0);
length = lengthStr;
}
}
void StringBuffer::alloc(unsigned lengthStr) {
ASSERT_OR_FAIL_FAST(lengthStr != 0 && data == NULL && immutable == NULL);
data = (char*) malloc(lengthStr);
ASSERT_OR_FAIL_FAST(data != NULL);
memset(data, 0, lengthStr);
}
void StringBuffer::set(unsigned index, char c) {
assert(index < length && data != NULL);
data[index] = c;
}
void StringBuffer::clear() {
if (data != NULL) {
free(data);
data = NULL;
}
}
StringBuffer::~StringBuffer() {
this->clear();
}
void StringBuffer::setLength(unsigned l) {
ASSERT_OR_FAIL_FAST(data != NULL);
length = l;
data[l] = char(0);
}
} // namespace AzureIOT

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#ifndef AZURE_IOTC_LITE_STRING_BUFFER_H
#define AZURE_IOTC_LITE_STRING_BUFFER_H
namespace AzureIOT {
class StringBuffer {
char * data;
const char * immutable;
unsigned length;
public:
StringBuffer(): data(NULL), immutable(NULL), length(0) { }
StringBuffer(StringBuffer &buffer);
StringBuffer(const char * str, unsigned int lengthStr, bool isCopy = true);
StringBuffer(unsigned lengthStr);
void initialize(const char * str, unsigned lengthStr);
void alloc(unsigned lengthStr);
void set(unsigned index, char c);
void clear();
~StringBuffer();
char* operator*() { return data; }
unsigned getLength() { return length; }
void setLength(unsigned l);
bool startsWith(const char* str, size_t len);
int32_t indexOf(const char* look_for, size_t look_for_length, int32_t start_index = 0);
#if defined(__MBED__) || defined(ARDUINO)
bool hash(const char* key, unsigned key_length);
#endif
bool urlDecode();
bool urlEncode();
bool base64Decode();
bool base64Encode();
};
} // namespace AzureIOTCLite
#endif // AZURE_IOTC_LITE_STRING_BUFFER_H

196
MBED_OS/src/iotc/iotc.h Normal file
Просмотреть файл

@ -0,0 +1,196 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef AZURE_IOTC_API
#define AZURE_IOTC_API
#if !defined(ESP_PLATFORM) && !defined(__MBED__)
// MXCHIP
#define TARGET_MXCHIP_AZ3166
#endif
#ifdef TARGET_MXCHIP_AZ3166
#include <Arduino.h>
#endif
#define AZIOTC_MAJOR_VERSION 0
#define AZIOTC_MINOR_VERSION 1
#define AZIOTC_PATCH_VERSION 0
#define AZIOTC_VERSION \
TO_STRING(AZIOTC_MAJOR_VERSION) "." TO_STRING(AZIOTC_MINOR_VERSION) "." TO_STRING(AZIOTC_PATCH_VERSION)
#ifdef __cplusplus
extern "C" {
#endif
// ***** Type definitions *****
typedef struct IOTC_HTTP_PROXY_OPTIONS_TAG
{
const char* host_address;
int port;
const char* username;
const char* password;
} IOTC_HTTP_PROXY_OPTIONS;
typedef struct IOTCallbackInfo_TAG {
const char* eventName;
const char* tag;
const char* payload;
unsigned payloadLength;
void *appContext;
int statusCode;
void *callbackResponse;
} IOTCallbackInfo;
typedef void* IOTContext;
// ***** Macro definitions *****
#define IOTC_PROTOCOL_MQTT 0x01
#define IOTC_PROTOCOL_AMQP 0x02
#define IOTC_PROTOCOL_HTTP 0x04
typedef short IOTProtocol;
#define IOTC_LOGGING_DISABLED 0x01
#define IOTC_LOGGING_API_ONLY 0x02
#define IOTC_LOGGING_ALL 0x10
typedef short IOTLogLevel;
#define IOTC_CONNECT_SYMM_KEY 0x01
#define IOTC_CONNECT_X509_CERT 0x02
#define IOTC_CONNECT_CONNECTION_STRING 0x04
typedef short IOTConnectType;
#define IOTC_CONNECTION_EXPIRED_SAS_TOKEN 0x01
#define IOTC_CONNECTION_DEVICE_DISABLED 0x02
#define IOTC_CONNECTION_BAD_CREDENTIAL 0x04
#define IOTC_CONNECTION_RETRY_EXPIRED 0x08
#define IOTC_CONNECTION_NO_NETWORK 0x10
#define IOTC_CONNECTION_COMMUNICATION_ERROR 0x20
#define IOTC_CONNECTION_OK 0x40
#define IOTC_CONNECTION_DISCONNECTED 0x80
typedef short IOTConnectionState;
#define IOTC_MESSAGE_ACCEPTED 0x01
#define IOTC_MESSAGE_REJECTED 0x02
#define IOTC_MESSAGE_ABANDONED 0x04
typedef short IOTMessageStatus;
// ***** API *****
// Set the level of logging (see the options above)
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_logging(IOTLogLevel level);
// Initialize the device context. The context variable will be used by rest of the API
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_init_context(IOTContext *ctx);
// Free device context.
// Call this after `init_context`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_free_context(IOTContext ctx);
// Connect to Azure IoT Central
// Call this after `init_context`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type);
// Disconnect
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_disconnect(IOTContext ctx);
// If your endpoint is different than the default AzureIoTCentral endpoint, set it
// using this API.
// Call this before `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_global_endpoint(IOTContext ctx, const char* endpoint_uri);
// Set the custom certificates for custom endpoints
// Call this before `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_trusted_certs(IOTContext ctx, const char* certs);
// Set the proxy settings
// Call this before `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_set_proxy(IOTContext ctx, IOTC_HTTP_PROXY_OPTIONS proxy);
// Sends a telemetry payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_telemetry(IOTContext ctx, const char* payload, unsigned length);
// Sends a state payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_state (IOTContext ctx, const char* payload, unsigned length);
// Sends an event payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_event (IOTContext ctx, const char* payload, unsigned length);
// Sends a property payload (JSON)
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_send_property (IOTContext ctx, const char* payload, unsigned length);
/*
eventName:
ConnectionStatus
MessageSent
Command
SettingsUpdated
Error
*/
typedef void(*IOTCallback)(IOTContext, IOTCallbackInfo*);
// Register to one of the events listed above
// Call this after `init_context`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_on(IOTContext ctx, const char* eventName, IOTCallback callback, void* appContext);
// Lets SDK to do background work
// Call this after `connect`
// returns 0 if there is no error. Otherwise, error code will be returned.
int iotc_do_work(IOTContext ctx);
// Provide platform dependent NetworkInterface
int iotc_set_network_interface(void* networkInterface);
#ifdef ARDUINO
#define SERIAL_PRINT Serial.printf
#else
#define SERIAL_PRINT printf
#endif
#define SERIAL_VERBOSE_LOGGING_ENABLED 1
#ifndef LOG_VERBOSE
#if SERIAL_VERBOSE_LOGGING_ENABLED != 1
#define LOG_VERBOSE(...)
#else
#define LOG_VERBOSE(...) \
do { \
SERIAL_PRINT(" - "); \
SERIAL_PRINT(__VA_ARGS__); \
SERIAL_PRINT("\r\n"); \
} while(0)
#endif // SERIAL_VERBOSE_LOGGING_ENABLED != 1
// Log Errors no matter what
#define LOG_ERROR(...) \
do { \
SERIAL_PRINT("X - Error at %s:%d\r\n\t", __FILE__, __LINE__); \
SERIAL_PRINT(__VA_ARGS__); \
SERIAL_PRINT("\r\n"); \
} while(0)
#endif // !LOG_VERBOSE
#ifdef __cplusplus
}
#endif
#endif // AZURE_IOTC_API

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if defined(__MBED__)
#include "../common/iotc_platform.h"
#if defined(USE_LIGHT_CLIENT)
#include "../common/json.h"
#include "../common/iotc_internal.h"
int mqtt_publish(IOTContextInternal *internal, const char* topic, unsigned long topic_length,
const char* msg, unsigned long msg_length) {
MQTT::Message message;
message.retained = false;
message.dup = false;
message.payload = (void*)msg;
message.payloadlen = msg_length;
message.qos = MQTT::QOS0;
message.id = ++internal->messageId;
int rc = internal->mqttClient->publish(topic, message);
if(rc != MQTT::SUCCESS) {
return rc;
}
return iotc_do_work(internal);
}
#endif // defined(USE_LIGHT_CLIENT)
#endif // defined(__MBED__)

Просмотреть файл

@ -0,0 +1,331 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if defined(__MBED__)
#include "../common/iotc_platform.h"
#if defined(USE_LIGHT_CLIENT)
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include "../common/json.h"
#include "../common/iotc_internal.h"
unsigned long getNow() {
return AzureIOT::TLSClient::nowTime();
}
int socketReadThemAll(AzureIOT::TLSClient *client, AzureIOT::StringBuffer &readBuffer) {
int retryCount = 0;
retry_getOperationId_read:
char *readBufferCSTR = *readBuffer;
WAITMS(3000); // give 3 secs to server to process
int index = 0;
while (index < STRING_BUFFER_1024 - 1) {
unsigned char buff[8];
int rc = client->read(buff, 8, 2);
if (rc <= 0) {
if (rc == NSAPI_ERROR_WOULD_BLOCK)
continue;
else
break;
}
const int inc = iotc_min(rc, STRING_BUFFER_1024 - index);
memcpy(readBufferCSTR, buff, inc);
index += inc;
readBufferCSTR += rc;
}
assert(index < STRING_BUFFER_1024 - 1);
if (index >= 0) {
readBuffer.setLength(index);
if (index == 0) {
if (retryCount > 2) return 1;
retryCount++;
goto retry_getOperationId_read;
}
} else {
readBuffer.setLength(0);
IOTC_LOG(F("ERROR: (socketReadThemAll) client->read returned %d"), index);
return 1;
}
return 0;
}
int _getOperationId(const char* dpsEndpoint, const char* scopeId, const char* deviceId,
const char* authHeader, char *operationId, char *hostName) {
AzureIOT::TLSClient *client = new AzureIOT::TLSClient();
int exitCode = 0;
if (client->connect(dpsEndpoint, AZURE_HTTPS_SERVER_PORT) != 0) {
IOTC_LOG(F("ERROR: %s endpoint PUT call has failed."), hostName == NULL ? "PUT" : "GET");
delete client;
return 1;
}
AzureIOT::StringBuffer tmpBuffer(STRING_BUFFER_1024);
AzureIOT::StringBuffer deviceIdEncoded(deviceId, strlen(deviceId));
deviceIdEncoded.urlEncode();
size_t size = 0;
if (hostName == NULL) {
size = sprintf(NULL, "{\"registrationId\":\"%s\"}", deviceId);
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, F("\
PUT /%s/registrations/%s/register?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
content-length: %d\r\n\
%s\r\n\
connection: close\r\n\
\r\n\
{\"registrationId\":\"%s\"}\r\n\
"),
scopeId, *deviceIdEncoded, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, size, authHeader, deviceId);
} else {
size = snprintf(*tmpBuffer, STRING_BUFFER_1024, F("\
GET /%s/registrations/%s/operations/%s?api-version=2018-11-01 HTTP/1.1\r\n\
Host: %s\r\n\
content-type: application/json; charset=utf-8\r\n\
%s\r\n\
accept: */*\r\n\
%s\r\n\
connection: close\r\n\
\r\n"),
scopeId, *deviceIdEncoded, operationId, dpsEndpoint,
AZURE_IOT_CENTRAL_CLIENT_SIGNATURE, authHeader);
}
assert(size != 0 && size < STRING_BUFFER_1024);
tmpBuffer.setLength(size);
const char* lookFor= hostName == NULL ? "{\"operationId\":\"" : "\"assignedHub\":\"";
int index = 0;
size_t total = 0;
while (size > total) {
int sent = client->write((const unsigned char*)*tmpBuffer + total, strlen(*tmpBuffer + total), 100);
if (sent < 0) {
IOTC_LOG(F("ERROR: tlsSocket send has failed with error code %d. Remaining buffer size was %lu"), sent, size);
exitCode = 1;
goto exit_operationId;
}
total += sent;
}
WAITMS(2000);
memset(*tmpBuffer, 0, STRING_BUFFER_1024);
if (socketReadThemAll(client, tmpBuffer) != 0) goto error_exit;
index = tmpBuffer.indexOf(lookFor, strlen(lookFor), 0);
if (index == -1) {
error_exit:
IOTC_LOG(F("ERROR: DPS (%s) request has failed.\r\n%s"), hostName == NULL ? "PUT" : "GET", *tmpBuffer);
exitCode = 1;
goto exit_operationId;
} else {
index += strlen(lookFor);
int index2 = tmpBuffer.indexOf("\"", 1, index + 1);
if (index2 == -1) goto error_exit;
tmpBuffer.setLength(index2);
strcpy(hostName == NULL ? operationId : hostName, (*tmpBuffer) + index);
client->disconnect();
}
exit_operationId:
delete client;
return exitCode;
}
int getHubHostName(const char* dpsEndpoint, const char *scopeId, const char* deviceId, const char* key, char *hostName) {
AzureIOT::StringBuffer authHeader(STRING_BUFFER_256);
size_t size = 0;
IOTC_LOG(F("- iotc.dps : getting auth..."));
if (getDPSAuthString(scopeId, deviceId, key, *authHeader, STRING_BUFFER_256, size)) {
IOTC_LOG(F("ERROR: getDPSAuthString has failed"));
return 1;
}
IOTC_LOG(F("- iotc.dps : getting operation id..."));
AzureIOT::StringBuffer operationId(STRING_BUFFER_64);
int retval = 0;
if ((retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, NULL)) == 0) {
WAITMS(4000);
IOTC_LOG(F("- iotc.dps : getting host name..."));
for (int i = 0; i < 5; i++) {
retval = _getOperationId(dpsEndpoint, scopeId, deviceId, *authHeader, *operationId, hostName);
if (retval == 0) break;
WAITMS(3000);
}
}
return retval;
}
static void messageArrived(MQTT::MessageData& md)
{
MQTT::Message &message = md.message;
unsigned long lenTopic = 0;
char *dataTopic = NULL;
if (md.topicName.cstring) {
lenTopic = strlen(md.topicName.cstring);
dataTopic = md.topicName.cstring;
} else if (md.topicName.lenstring.len) {
lenTopic = md.topicName.lenstring.len;
dataTopic = md.topicName.lenstring.data;
}
handlePayload((char*)message.payload, message.payloadlen, dataTopic, lenTopic);
}
/* extern */
int iotc_free_context(IOTContext ctx) {
MUST_CALL_AFTER_INIT(ctx);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
if (internal->tlsClient != NULL) {
iotc_disconnect(ctx);
}
free(internal);
setSingletonContext(NULL);
return 0;
}
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL(keyORcert, 512);
AzureIOT::StringBuffer hostName;
AzureIOT::StringBuffer username;
AzureIOT::StringBuffer password;
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (type == IOTC_CONNECT_CONNECTION_STRING) {
getUsernameAndPasswordFromConnectionString(keyORcert,
strlen(keyORcert), hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_SYMM_KEY) {
assert(scope != NULL && deviceId != NULL);
AzureIOT::StringBuffer tmpHostname(STRING_BUFFER_128);
if (getHubHostName(internal->endpoint == NULL ?
DEFAULT_ENDPOINT : internal->endpoint, scope, deviceId, keyORcert, *tmpHostname)) {
return 1;
}
AzureIOT::StringBuffer cstr(STRING_BUFFER_256);
int rc = snprintf(*cstr, STRING_BUFFER_256,
F("HostName=%s;DeviceId=%s;SharedAccessKey=%s"), *tmpHostname, deviceId, keyORcert);
assert(rc > 0 && rc < STRING_BUFFER_256);
cstr.setLength(rc);
// TODO: move into iotc_dps and do not re-parse from connection string
getUsernameAndPasswordFromConnectionString(*cstr, rc, hostName, internal->deviceId, username, password);
} else if (type == IOTC_CONNECT_X509_CERT) {
IOTC_LOG(F("ERROR: IOTC_CONNECT_X509_CERT NOT IMPLEMENTED"));
connectionStatusCallback(IOTC_CONNECTION_DEVICE_DISABLED, (IOTContextInternal*)ctx);
return 1;
}
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
data.MQTTVersion = 4;
data.clientID.cstring = *internal->deviceId;
data.username.cstring = *username;
data.password.cstring = *password;
data.keepAliveInterval = 2;
data.cleansession = 1;
internal->tlsClient = new AzureIOT::TLSClient();
internal->mqttClient = new MQTT::Client<AzureIOT::TLSClient, Countdown, STRING_BUFFER_1024, 5>(*(internal->tlsClient));
if (internal->tlsClient->connect(*hostName, AZURE_MQTT_SERVER_PORT) != 0) {
IOTC_LOG(F("ERROR: TLSClient connect attempt failed to %s\r\nMake sure both certificate and host are correct."), *hostName);
connectionStatusCallback(IOTC_CONNECTION_BAD_CREDENTIAL, (IOTContextInternal*)ctx);
return 1;
}
if (internal->mqttClient->connect(data) != MQTT::SUCCESS) {
IOTC_LOG(F("ERROR: TLSClient connect attempt failed. Check host, deviceId, username and password."));
connectionStatusCallback(IOTC_CONNECTION_BAD_CREDENTIAL, (IOTContextInternal*)ctx);
return 1;
}
AzureIOT::StringBuffer buffer(STRING_BUFFER_64);
size_t size = snprintf(*buffer, 63, "devices/%s/messages/events/#", *internal->deviceId);
buffer.setLength(size);
int errorCode = 0;
if ( (errorCode = internal->mqttClient->subscribe(*buffer, MQTT::QOS1, messageArrived)) != 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), buffer, errorCode);
size = snprintf(*buffer, 63, "devices/%s/messages/devicebound/#", *internal->deviceId);
buffer.setLength(size);
if ( (errorCode = internal->mqttClient->subscribe(*buffer, MQTT::QOS1, messageArrived)) != 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to %s. error code => %d"), buffer, errorCode);
errorCode = internal->mqttClient->subscribe("$iothub/twin/PATCH/properties/desired/#", MQTT::QOS1, messageArrived); // twin desired property changes
errorCode += internal->mqttClient->subscribe("$iothub/twin/res/#", MQTT::QOS1, messageArrived); // twin properties response
errorCode += internal->mqttClient->subscribe("$iothub/methods/POST/#", MQTT::QOS1, messageArrived);
if (errorCode != 0)
IOTC_LOG(F("ERROR: mqttClient couldn't subscribe to twin/methods etc. error code sum => %d"), errorCode);
internal->mqttClient->setDefaultMessageHandler(messageArrived);
connectionStatusCallback(IOTC_CONNECTION_OK, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_disconnect(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
if (internal->mqttClient) {
if(internal->mqttClient->isConnected()) {
internal->mqttClient->disconnect();
}
delete internal->mqttClient;
internal->mqttClient = NULL;
}
if(internal->tlsClient) {
internal->tlsClient->disconnect();
delete internal->tlsClient;
internal->tlsClient = NULL;
}
connectionStatusCallback(IOTC_CONNECTION_DISCONNECTED, (IOTContextInternal*)ctx);
return 0;
}
/* extern */
int iotc_do_work(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
return internal->mqttClient->yield();
}
/* extern */
int iotc_set_network_interface(void* networkInterface) {
AzureIOT::TLSClient::setNetworkInterface((NetworkInterface*)networkInterface);
return 0;
}
#endif // defined(USE_LIGHT_CLIENT)
#endif // __MBED__

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#if defined(__MBED__)
#include "../common/iotc_internal.h"
#if defined(USE_LIGHT_CLIENT)
namespace AzureIOT {
time_t TLSClient::timestamp = 0;
time_t TLSClient::timeStart = 0;
NetworkInterface* TLSClient::networkInterface = NULL;
int TLSClient::read(unsigned char* buffer, int len, int timeout) {
tlsSocket->set_timeout(iotc_max(timeout, 10));
return tlsSocket->recv(buffer, len);
}
int TLSClient::write(const unsigned char* buffer, int len, int timeout) {
tlsSocket->set_timeout(iotc_max(timeout, 10));
return tlsSocket->send(buffer, len);
}
bool TLSClient::connect(const char* host, int port) {
IOTC_LOG(F("TLSClient::connect host(%s)"), host);
assert(getNetworkInterface() != NULL);
if (tlsSocket->open(getNetworkInterface()) != 0) {
IOTC_LOG(F("ERROR: TLSClient::connect failed"));
return false;
}
tlsSocket->set_root_ca_cert((const char*)SSL_CA_PEM_DEF);
tlsSocket->set_client_cert_key(SSL_CLIENT_CERT_PEM, SSL_CLIENT_PRIVATE_KEY_PEM);
tlsSocket->set_blocking(true);
return tlsSocket->connect(host, port);
}
bool TLSClient::disconnect() {
IOTC_LOG(F("TLSClient::disconnect"));
return tlsSocket->close() == 0;
}
} // namespace AzureIOT
#endif // defined(USE_LIGHT_CLIENT)
#endif // __MBED__

Просмотреть файл

@ -0,0 +1,90 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license.
#ifndef AZURE_IOTC_MBED_TLS_CLIENT_H
#define AZURE_IOTC_MBED_TLS_CLIENT_H
#if defined(__MBED__)
#include "NetworkInterface.h"
#include "TLSSocket.h"
#include "NTPClient.h"
#include <assert.h>
#include "iotc_definitions.h"
namespace AzureIOT {
class TLSClient {
static NetworkInterface* networkInterface;
TLSSocket* tlsSocket;
static time_t timestamp;
static time_t timeStart;
public:
TLSClient() {
tlsSocket = new TLSSocket();
assert(tlsSocket);
}
int read(unsigned char* buffer, int len, int timeout);
int write(const unsigned char* buffer, int len, int timeout);
int print(const char* buffer);
int println() { print("\r\n"); }
int println(const char* buffer) { print(buffer); println(); }
bool connect(const char* host, int port);
bool disconnect();
~TLSClient() {
delete tlsSocket;
}
static void setNetworkInterface(NetworkInterface* interface) {
networkInterface = interface;
}
static NetworkInterface* getNetworkInterface() {
return networkInterface;
}
static time_t nowTime() {
assert(TLSClient::networkInterface != NULL);
time_t timeDiff;
if (timestamp == 0) {
sync_ntp:
NTPClient ntp(TLSClient::networkInterface);
int retry_count = 0;
retry_time:
{
timeStart = us_ticker_read(); // terrible hack
timestamp = ntp.get_timestamp(15000 /* timeout */);
if (timestamp < 0) // timeout ?
{
if (retry_count++ < 5) {
goto retry_time;
}
printf("- ERROR: can't sync to NTP server\r\n");
return 0;
}
}
}
timeDiff = us_ticker_read() - timeStart;
time_t time_now = timestamp + (10 /* lag */ + (timeDiff / 1000));
if (time_now - timestamp > NTP_SYNC_PERIOD) {
goto sync_ntp;
}
return time_now;
}
};
} // namespace AzureIOT
#endif // __MBED__
#endif // AZURE_IOTC_MBED_TLS_CLIENT_H

Просмотреть файл

@ -0,0 +1,643 @@
// Copyright (c) Oguz Bastemur. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "../common/iotc_internal.h"
#if !defined(USE_LIGHT_CLIENT)
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>
#include "../common/json.h"
#ifdef TARGET_MXCHIP_AZ3166
#include "azure_prov_client/prov_device_ll_client.h"
#include "azure_prov_client/prov_security_factory.h"
#include "azure_prov_client/prov_transport_mqtt_client.h"
#include <AzureIotHub.h>
#include "provisioning_client/adapters/hsm_client_key.h"
int prov_dev_set_symmetric_key_info(const char* registration_name, const char* symmetric_key) {
hsm_client_set_registration_name_and_key(registration_name, symmetric_key);
return 0;
}
#elif defined(ESP_PLATFORM)
#include "iothub_client.h"
#include "iothub_message.h"
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/platform.h"
#include "iothubtransportmqtt.h"
#include "iothub_client_version.h"
#include "iothub_device_client_ll.h"
#include "iothub_client_options.h"
#include "azure_prov_client/prov_device_ll_client.h"
#include "azure_prov_client/prov_security_factory.h"
#include "azure_prov_client/prov_transport_mqtt_client.h"
#else
#error "NOT IMPLEMENTED"
#endif // TARGET_MXCHIP_AZ3166?
typedef struct EVENT_INSTANCE_TAG {
IOTHUB_MESSAGE_HANDLE messageHandle;
IOTContextInternal *internal;
void *appContext;
} EVENT_INSTANCE;
static EVENT_INSTANCE *createEventInstance(IOTContextInternal *internal,
const char* payload, unsigned length, void *applicationContext, int *errorCode) {
*errorCode = 0;
EVENT_INSTANCE *currentMessage = (EVENT_INSTANCE*) malloc(sizeof(EVENT_INSTANCE));
if(currentMessage == NULL) {
IOTC_LOG(F("ERROR: (createEventInstance) currentMessage is NULL."));
*errorCode = 1;
return NULL;
}
memset(currentMessage, 0, sizeof(EVENT_INSTANCE));
currentMessage->messageHandle =
IoTHubMessage_CreateFromByteArray((const unsigned char*)payload, length);
if (currentMessage->messageHandle == NULL) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) IoTHubMessage_CreateFromByteArray has failed."));
free(currentMessage);
*errorCode = 9;
return NULL;
}
currentMessage->internal = internal;
currentMessage->appContext = applicationContext;
return currentMessage;
}
static void freeEventInstance(EVENT_INSTANCE *eventInstance) {
if (eventInstance != NULL) {
if (eventInstance->messageHandle != NULL) {
IoTHubMessage_Destroy(eventInstance->messageHandle);
}
free(eventInstance);
}
}
// send telemetry etc. confirmation callback
/* MessageSent */
static void sendConfirmationCallback(int statusCode, void *userContextCallback) {
EVENT_INSTANCE *eventInstance = (EVENT_INSTANCE *)userContextCallback;
assert(eventInstance != NULL);
IOTContextInternal *internal = (IOTContextInternal*)eventInstance->internal;
const unsigned char* buffer = NULL;
size_t size = 0;
if (internal->callbacks[/*IOTCallbacks::*/MessageSent].callback) {
if (IOTHUB_CLIENT_RESULT::IOTHUB_CLIENT_OK !=
IoTHubMessage_GetByteArray(eventInstance->messageHandle, &buffer, &size)) {
IOTC_LOG(F("ERROR: (sendConfirmationCallback) IoTHubMessage_GetByteArray has failed."));
}
IOTCallbackInfo info;
info.eventName = "MessageSent";
info.tag = NULL;
info.payload = (const char*) buffer;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::MessageSent].appContext;
info.statusCode = (int)result;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::MessageSent].callback(internal, &info);
}
freeEventInstance(eventInstance);
}
#define CONVERT_TO_IOTHUB_MESSAGE(x) \
(x == IOTC_MESSAGE_ACCEPTED ? IOTHUBMESSAGE_ACCEPTED : \
(x == IOTC_MESSAGE_REJECTED ? IOTHUBMESSAGE_REJECTED : IOTHUBMESSAGE_ABANDONED))
/* Command */
static const char* emptyResponse = "{}";
static int onCommand(const char* method_name, const unsigned char* payload,
size_t size, unsigned char** response, size_t* resp_size, void* userContextCallback) {
assert(response != NULL && resp_size != NULL);
*response = NULL;
*resp_size = 0;
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
if (internal->callbacks[/*IOTCallbacks::*/::Command].callback) {
IOTCallbackInfo info;
info.eventName = "Command";
info.tag = method_name;
info.payload = (const char*) payload;
info.payloadLength = (unsigned) size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Command].appContext;
info.statusCode = 0;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Command].callback(internal, &info);
if (info.callbackResponse != NULL) {
*response = (unsigned char*) info.callbackResponse;
*resp_size = strlen((char*) info.callbackResponse);
} else {
char *resp = (char*) malloc(3);
resp[2] = 0;
memcpy(resp, emptyResponse, 2);
*response = (unsigned char*)resp;
*resp_size = 2;
// resp should be freed by SDK
}
return 200;
}
return 500;
}
/* MessageSent */
static void deviceTwinConfirmationCallback(int status_code, void* userContextCallback) {
// TODO: use status code
sendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_OK, userContextCallback);
}
#define CONVERT_TO_IOTC_CONNECT_ENUM(x) \
( x == IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN ? IOTC_CONNECTION_EXPIRED_SAS_TOKEN : ( \
x == IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED ? IOTC_CONNECTION_RETRY_EXPIRED : ( \
x == IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED ? IOTC_CONNECTION_DEVICE_DISABLED : ( \
x == IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL ? IOTC_CONNECTION_BAD_CREDENTIAL : ( \
x == IOTHUB_CLIENT_CONNECTION_NO_NETWORK ? IOTC_CONNECTION_NO_NETWORK : ( \
x == IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR ? IOTC_CONNECTION_COMMUNICATION_ERROR : IOTC_CONNECTION_OK \
))))))
/* ConnectionStatus */
static void connectionStatusCallback(IOTHUB_CLIENT_CONNECTION_STATUS result,
IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* userContextCallback) {
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
if (internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback) {
IOTCallbackInfo info;
info.eventName = "ConnectionStatus";
info.tag = NULL;
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].appContext;
info.statusCode = CONVERT_TO_IOTC_CONNECT_ENUM(reason);
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::ConnectionStatus].callback(internal, &info);
}
}
void sendOnError(IOTContextInternal *internal, const char* message) {
if (internal->callbacks[/*IOTCallbacks::*/::Error].callback) {
IOTCallbackInfo info;
info.eventName = "Error";
info.tag = message; // message lifetime should be managed by us
info.payload = NULL;
info.payloadLength = 0;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::Error].appContext;
info.statusCode = 1;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::Error].callback(internal, &info);
}
}
void echoDesired(IOTContextInternal *internal, const char *propertyName,
const char *message, const char *status, int statusCode) {
AzureIOT::JSObject rootObject(message);
AzureIOT::JSObject propertyNameObject, desiredObject, desiredObjectPropertyName;
const char* methodName = rootObject.getStringByName("methodName");
rootObject.getObjectByName(propertyName, &propertyNameObject);
double value = 0, desiredVersion = 0;
if (rootObject.getObjectByName("desired", &desiredObject) &&
desiredObject.getObjectByName(propertyName, &desiredObjectPropertyName)) {
value = desiredObjectPropertyName.getNumberByName("value");
desiredVersion = desiredObject.getNumberByName("$version");
} else {
propertyName = rootObject.getNameAt(0);
value = propertyNameObject.getNumberByName("value");
desiredVersion = rootObject.getNumberByName("$version");
}
const char* echoTemplate = "{\"%s\":{\"value\":%d,\"statusCode\":%d,\
\"status\":\"%s\",\"desiredVersion\":%d}}";
uint32_t buffer_size = snprintf(NULL, 0, echoTemplate, propertyName,
(int) value, // BAD HACK
statusCode, status, (int) desiredVersion);
AzureIOT::StringBuffer buffer(buffer_size);
size_t size = snprintf(*buffer, buffer_size, echoTemplate, propertyName,
(int) value, statusCode,
status, (int) desiredVersion);
buffer.setLength(size);
iotc_send_property(internal, *buffer, size, NULL);
}
void callDesiredCallback(IOTContextInternal *internal, const char *propertyName, const char *payLoad, size_t size) {
const char* response = "completed";
if (internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback) {
IOTCallbackInfo info;
info.eventName = "SettingsUpdated";
info.tag = propertyName;
info.payload = payLoad;
info.payloadLength = size;
info.appContext = internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].appContext;
info.statusCode = 200;
info.callbackResponse = NULL;
internal->callbacks[/*IOTCallbacks::*/::SettingsUpdated].callback(internal, &info);
if (info.callbackResponse) {
response = (const char*)info.callbackResponse;
}
echoDesired(internal, propertyName, payLoad, response, info.statusCode);
}
}
static void deviceTwinGetStateCallback(DEVICE_TWIN_UPDATE_STATE update_state,
const unsigned char* payLoad, size_t size, void* userContextCallback) {
IOTContextInternal *internal = (IOTContextInternal*)userContextCallback;
assert(internal != NULL);
((char*)payLoad)[size] = 0x00;
AzureIOT::JSObject payloadObject((const char *)payLoad);
if (update_state == DEVICE_TWIN_UPDATE_PARTIAL && payloadObject.getNameAt(0) != NULL) {
callDesiredCallback(internal, payloadObject.getNameAt(0), reinterpret_cast<const char*>(payLoad), size);
} else {
AzureIOT::JSObject desired, reported;
// loop through all the desired properties
// look to see if the desired property has an associated reported property
// if so look if the versions match, if they match do nothing
// if they don't match then call the associated callback for the desired property
payloadObject.getObjectByName("desired", &desired);
payloadObject.getObjectByName("reported", &reported);
for (unsigned i = 0, count = desired.getCount(); i < count; i++) {
const char * itemName = desired.getNameAt(i);
if (itemName != NULL && itemName[0] != '$') {
AzureIOT::JSObject keyObject;
const char * version = NULL, * desiredVersion = NULL,
* value = NULL, * desiredValue = NULL;
bool containsKey = reported.getObjectByName(itemName, &keyObject);
if (containsKey) {
desiredVersion = reported.getStringByName("desiredVersion");
version = desired.getStringByName("$version");
desiredValue = reported.getStringByName("desiredValue");
value = desired.getStringByName("value");
}
if (containsKey && strcmp(desiredVersion, version) == 0) {
IOTC_LOG(F("key: %s found in reported and versions match"), itemName);
} else if (containsKey && strcmp(desiredValue, value) != 0){
IOTC_LOG(F("key: %s either not found in reported or versions do not match\r\n"), itemName);
AzureIOT::JSObject itemValue;
if (desired.getObjectAt(i, &itemValue) && itemValue.toString() != NULL) {
IOTC_LOG(F("itemValue: %s"), itemValue.toString());
} else {
IOTC_LOG(F("ERROR: desired doesn't have value at index"));
}
callDesiredCallback(internal, itemName, (const char*)payLoad, size);
} else {
echoDesired(internal, itemName, (const char*)payLoad, "completed", 200);
}
}
}
}
}
/* extern */
int iotc_init_context(IOTContext *ctx) {
CHECK_NOT_NULL(ctx)
MUST_CALL_BEFORE_INIT((*ctx));
IOTContextInternal *internal = (IOTContextInternal*)malloc(sizeof(IOTContextInternal));
CHECK_NOT_NULL(internal);
memset(internal, 0, sizeof(IOTContextInternal));
*ctx = (void*)internal;
return 0;
}
/* extern */
int iotc_free_context(IOTContext ctx) {
MUST_CALL_AFTER_INIT(ctx);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
if (internal->endpoint != NULL) {
free(internal->endpoint);
}
if (internal->clientHandle != NULL) {
IoTHubClient_LL_Destroy(internal->clientHandle);
}
free(internal);
return 0;
}
typedef struct REGISTRATION_CONTEXT_TAG
{
char* iothub_uri;
int registration_complete;
} REGISTRATION_CONTEXT;
static void registation_status_callback(PROV_DEVICE_REG_STATUS reg_status, void* user_context)
{
if (user_context == NULL)
{
IOTC_LOG(F("ERROR: (registation_status_callback) user_context is NULL"));
}
else
{
if (reg_status == PROV_DEVICE_REG_STATUS_CONNECTED)
{
IOTC_LOG(F("IOTC: Registration status: CONNECTED"));
}
else if (reg_status == PROV_DEVICE_REG_STATUS_REGISTERING)
{
IOTC_LOG(F("IOTC: Registration status: REGISTERING"));
}
else if (reg_status == PROV_DEVICE_REG_STATUS_ASSIGNING)
{
IOTC_LOG(F("IOTC: Registration status: ASSIGNING"));
}
}
}
static void register_device_callback(PROV_DEVICE_RESULT register_result, const char* iothub_uri, const char* deviceId, void* user_context)
{
if (user_context == NULL)
{
IOTC_LOG(F("ERROR: (register_device_callback) user_context is NULL"));
}
else
{
REGISTRATION_CONTEXT* user_ctx = (REGISTRATION_CONTEXT*)user_context;
if (register_result == PROV_DEVICE_RESULT_OK)
{
user_ctx->iothub_uri = strdup(iothub_uri);
user_ctx->registration_complete = 1;
}
else
{
IOTC_LOG(F("ERROR: (register_device_callback) Failure encountered on registration!"));
user_ctx->registration_complete = 2;
}
}
}
/* extern */
int iotc_connect(IOTContext ctx, const char* scope, const char* keyORcert,
const char* deviceId, IOTConnectType type) {
CHECK_NOT_NULL(ctx)
GET_LENGTH_NOT_NULL(scope, 256);
GET_LENGTH_NOT_NULL(keyORcert, 512);
GET_LENGTH_NOT_NULL(deviceId, 256);
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_INIT(internal);
char stringBuffer[AZ_IOT_HUB_MAX_LEN] = {0};
int errorCode = 0;
size_t pos = 0;
bool traceOn = getLogLevel() > IOTC_LOGGING_API_ONLY;
if (type == IOTC_CONNECT_CONNECTION_STRING) {
strcpy(stringBuffer, keyORcert);
pos = strlen(stringBuffer);
} else {
if (type == IOTC_CONNECT_SYMM_KEY) {
prov_dev_set_symmetric_key_info(deviceId, keyORcert);
prov_dev_security_init(SECURE_DEVICE_TYPE_SYMMETRIC_KEY);
} else {
prov_dev_security_init(SECURE_DEVICE_TYPE_X509);
}
PROV_DEVICE_LL_HANDLE handle;
if ((handle = Prov_Device_LL_Create(internal->endpoint == NULL ?
DEFAULT_ENDPOINT : internal->endpoint, scope, Prov_Device_MQTT_Protocol)) == NULL)
{
IOTC_LOG(F("ERROR: (iotc_connect) device registration step has failed."));
errorCode = 1;
goto fnc_exit;
}
REGISTRATION_CONTEXT user_ctx = { 0 };
Prov_Device_LL_SetOption(handle, "logtrace", &traceOn);
#if defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
if (Prov_Device_LL_SetOption(handle, "TrustedCerts", certificates) != PROV_DEVICE_RESULT_OK)
{
IOTC_LOG(F("ERROR: (iotc_connect) Failed to set option \"TrustedCerts\"."));
errorCode = 1;
goto fnc_exit;
} else
#endif // defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
if (Prov_Device_LL_Register_Device(handle, register_device_callback, &user_ctx, registation_status_callback, &user_ctx) != PROV_DEVICE_RESULT_OK)
{
IOTC_LOG(F("ERROR: (iotc_connect) Failed calling Prov_Device_LL_Register_Device."));
errorCode = 1;
goto fnc_exit;
}
else
{
// Waiting the register to be completed
do
{
Prov_Device_LL_DoWork(handle);
ThreadAPI_Sleep(5);
} while (user_ctx.registration_complete == 0);
}
// Free DPS client
Prov_Device_LL_Destroy(handle);
if (user_ctx.registration_complete == 2) {
IOTC_LOG(F("ERROR: (iotc_connect) device registration step has failed."));
errorCode = 1;
goto fnc_exit;
}
if (type == IOTC_CONNECT_SYMM_KEY) {
pos = snprintf(stringBuffer, AZ_IOT_HUB_MAX_LEN,
"HostName=%s;DeviceId=%s;SharedAccessKey=%s",
user_ctx.iothub_uri,
deviceId,
keyORcert);
} else if (type == IOTC_CONNECT_X509_CERT) {
pos = snprintf(stringBuffer, AZ_IOT_HUB_MAX_LEN,
"HostName=%s;DeviceId=%s;UseProvisioning=true",
user_ctx.iothub_uri,
deviceId);
}
}
IOTC_LOG(F("ConnectionString: %s", stringBuffer);
if (pos == 0 || pos >= AZ_IOT_HUB_MAX_LEN) {
IOTC_LOG(F("ERROR: (iotc_connect) connection information is out of buffer."));
errorCode = 15;
goto fnc_exit;
}
if ((internal->clientHandle = IoTHubClient_LL_CreateFromConnectionString(
stringBuffer, MQTT_Protocol)) == NULL) {
IOTC_LOG(F("ERROR: (iotc_connect) Couldn't create hub from connection string."));
errorCode = 4;
goto fnc_exit;
}
IoTHubClient_LL_SetRetryPolicy(internal->clientHandle, IOTHUB_CLIENT_RETRY_EXPONENTIAL_BACKOFF, 1200);
#if defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
if (IoTHubClient_LL_SetOption(internal->clientHandle, "TrustedCerts",
certificates /* src/cores/arduino/az_iot/azureiotcerts.h */) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: Failed to set option \"TrustedCerts\" IoTHubClient_LL_SetOption failed."));
return 5;
}
#endif // defined(MBED_BUILD_TIMESTAMP) || defined(TARGET_MXCHIP_AZ3166)
traceOn = getLogLevel() > IOTC_LOGGING_API_ONLY;
IoTHubClient_LL_SetOption(internal->clientHandle, "logtrace", &traceOn);
if (IoTHubClient_LL_SetMessageCallback(internal->clientHandle, receiveMessageCallback,
internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed."));
errorCode = 5;
goto fnc_exit;
}
if (IoTHubClient_LL_SetDeviceTwinCallback(internal->clientHandle, deviceTwinGetStateCallback,
internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed."));
errorCode = 5;
goto fnc_exit;
}
if (IoTHubClient_LL_SetDeviceMethodCallback(internal->clientHandle, onCommand,
internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed. ERR:0x0005"));
errorCode = 5;
goto fnc_exit;
}
if (IoTHubClient_LL_SetConnectionStatusCallback(internal->clientHandle,
connectionStatusCallback, internal) != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_connect) IoTHubClient_LL_SetXXXXCallback failed. ERR:0x0005"));
errorCode = 5;
goto fnc_exit;
}
fnc_exit:
return errorCode;
}
/* extern */
int iotc_disconnect(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
IOTC_LOG(F("ERROR: (iotc_disconnect) Not implemented."));
return 1;
}
/* extern */
int iotc_send_telemetry(IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
int errorCode = 0;
IOTHUB_CLIENT_RESULT hubResult = IOTHUB_CLIENT_RESULT::IOTHUB_CLIENT_OK;
EVENT_INSTANCE *currentMessage = createEventInstance(internal, payload, length, NULL, &errorCode);
if (currentMessage == NULL) return errorCode;
MAP_HANDLE propMap = IoTHubMessage_Properties(currentMessage->messageHandle);
time_t now_utc = time(NULL); // utc time
char timeBuffer[128] = {0};
unsigned outputLength = snprintf(timeBuffer, 128, "%s", ctime(&now_utc));
assert(outputLength && outputLength < 128 && timeBuffer[outputLength - 1] == '\n');
timeBuffer[outputLength - 1] = char(0); // replace `\n` with `\0`
if (Map_AddOrUpdate(propMap, "timestamp", timeBuffer) != MAP_OK)
{
IOTC_LOG(F("ERROR: (iotc_send_telemetry) Map_AddOrUpdate has failed."));
freeEventInstance(currentMessage);
return 1;
}
// submit the message to the Azure IoT hub
hubResult = IoTHubClient_LL_SendEventAsync(internal->clientHandle,
currentMessage->messageHandle, sendConfirmationCallback, currentMessage);
if (hubResult != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) IoTHubClient_LL_SendEventAsync has "
"failed => hubResult is (%d)."), hubResult);
freeEventInstance(currentMessage);
return 1;
}
iotc_do_work(ctx);
return 0;
}
/* extern */
int iotc_send_property (IOTContext ctx, const char* payload, unsigned length) {
CHECK_NOT_NULL(ctx)
CHECK_NOT_NULL(payload)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
int errorCode = 0;
EVENT_INSTANCE *currentMessage = createEventInstance(internal, payload, length, NULL, &errorCode);
if (currentMessage == NULL) return errorCode;
IOTHUB_CLIENT_RESULT hubResult = IoTHubClient_LL_SendReportedState(internal->clientHandle,
(const unsigned char*)payload, length, deviceTwinConfirmationCallback, currentMessage);
if (hubResult != IOTHUB_CLIENT_OK) {
IOTC_LOG(F("ERROR: (iotc_send_telemetry) IoTHubClient_LL_SendReportedState has "
"failed => hubResult is (%d). ERROR:0x000B"), hubResult);
freeEventInstance(currentMessage);
return 11;
}
iotc_do_work(ctx);
return 0;
}
/* extern */
int iotc_do_work(IOTContext ctx) {
CHECK_NOT_NULL(ctx)
IOTContextInternal *internal = (IOTContextInternal*)ctx;
MUST_CALL_AFTER_CONNECT(internal);
IoTHubClient_LL_DoWork(internal->clientHandle);
return 0;
}
/* extern */
int iotc_set_network_interface(void* networkInterface) {
IOTC_LOG(F("ERROR: (iotc_set_network_interface) is not needed for this platform."));
return 1;
}
#endif // defined(TARGET_MXCHIP_AZ3166) || defined(ESP_PLATFORM)

Просмотреть файл

@ -8,7 +8,9 @@ Create an issue on this repository and let us know.
### Contents
- [Azure MXChip IoT DevKit (AZ3166)](./MXCHIP)
- [ESP32/ESP8266 Espressif](./ESP32)
- [ESP32 Espressif](./ESP32)
- [ESP8266/ESP8285](./ESP8266)
- [MBED OS](./MBED_OS)
- [HTTP Only C#](./HttpOnly/CSharp)
- [HTTP Only Bash/Node.js](./HttpOnly/Bash)
- [Raspberry Pi - Python](./RaspberryPi)