ESP8266 - Extracted MQTT and HTTP code into specific classes

This commit is contained in:
Jan Gromeš 2018-07-11 13:43:54 +02:00
parent 89fd8fb279
commit b8f93c5077
9 changed files with 737 additions and 677 deletions

View file

@ -1,5 +1,5 @@
#######################################
# Syntax Coloring Map For LoRaLib
# Syntax Coloring Map For KiteLib
#######################################
#######################################
@ -21,9 +21,8 @@ SX1278 KEYWORD1
SX1279 KEYWORD1
XBee KEYWORD1
Bandwidth KEYWORD1
SpreadingFactor KEYWORD1
CodingRate KEYWORD1
MQTTClient KEYWORD1
HTTPClient KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -66,26 +65,31 @@ setFrequencyDeviation KEYWORD2
# ESP8266
join KEYWORD2
reset KEYWORD2
HttpGet KEYWORD2
HttpPost KEYWORD2
MqttConnect KEYWORD2
MqttDisconnect KEYWORD2
MqttPublish KEYWORD2
MqttSubscribe KEYWORD2
MqttUnsubscribe KEYWORD2
MqttPing KEYWORD2
MqttCheck KEYWORD2
startTcp KEYWORD2
closeTcp KEYWORD2
startUdp KEYWORD2
closeUdp KEYWORD2
send KEYWORD2
receive KEYWORD2
# XBee
setDestinationAddress KEYWORD2
setPanId KEYWORD2
# HTTP
get KEYWORD2
post KEYWORD2
# MQTT
connect KEYWORD2
disconnect KEYWORD2
publish KEYWORD2
subscribe KEYWORD2
unsubscribe KEYWORD2
ping KEYWORD2
check KEYWORD2
# TransportLayer
openTransportConnection KEYWORD2
closeTransportConnection KEYWORD2
send KEYWORD2
receive KEYWORD2
getNumBytes KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -15,6 +15,10 @@
#include "modules/SX1279.h"
#include "modules/XBee.h"
#include "protocols/TransportLayer.h"
#include "protocols/HTTP.h"
#include "protocols/MQTT.h"
#define KITE_CS_A 10
#define KITE_TX_A 9
#define KITE_RX_A 8

View file

@ -1,10 +1,7 @@
#include "ESP8266.h"
ESP8266::ESP8266(Module* module) {
portHttp = 80;
portMqtt = 1883;
_mod = module;
_MqttPacketId = 1;
}
uint8_t ESP8266::begin(long speed) {
@ -83,587 +80,6 @@ uint8_t ESP8266::join(const char* ssid, const char* password) {
return(ERR_NONE);
}
uint16_t ESP8266::HttpGet(const char* url, String& response) {
// get the host address and endpoint
char* httpPrefix = strstr(url, "http://");
char* endpoint;
char* host;
if(httpPrefix != NULL) {
// find the host string
char* hostStart = strchr(url, '/');
hostStart = strchr(hostStart + 1, '/');
char* hostEnd = strchr(hostStart + 1, '/');
host = new char[hostEnd - hostStart];
strncpy(host, hostStart + 1, hostEnd - hostStart - 1);
host[hostEnd - hostStart - 1] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
} else {
// find the host string
char* hostEnd = strchr(url, '/');
host = new char[hostEnd - url + 1];
strncpy(host, url, hostEnd - url);
host[hostEnd - url] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
}
// build the GET request
char* request = new char[strlen(endpoint) + strlen(host) + 25];
strcpy(request, "GET ");
strcat(request, endpoint);
strcat(request, " HTTP/1.1\r\nHost: ");
strcat(request, host);
strcat(request, "\r\n\r\n");
delete[] endpoint;
// create TCP connection
uint8_t state = openTransportConnection(host, "TCP", portHttp);
delete[] host;
if(state != ERR_NONE) {
delete[] request;
return(state);
}
// send the GET request
state = send(request);
delete[] request;
if(state != ERR_NONE) {
return(state);
}
delay(1000);
// get the response length
uint16_t numBytes = getNumBytes();
if(numBytes == 0) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
char* raw = new char[numBytes];
size_t rawLength = receive((uint8_t*)raw, numBytes);
if(rawLength == 0) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
// close the TCP connection
state = closeTransportConnection();
if(state != ERR_NONE) {
delete[] raw;
return(state);
}
// get the response body
char* responseStart = strstr(raw, "\r\n");
if(responseStart == NULL) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
char* responseStr = new char[raw + rawLength - responseStart - 1];
strncpy(responseStr, responseStart + 2, raw + rawLength - responseStart - 1);
responseStr[raw + rawLength - responseStart - 2] = 0x00;
response = String(responseStr);
delete[] responseStr;
// return the HTTP status code
char* statusStart = strchr(raw, ' ');
delete[] raw;
if(statusStart == NULL) {
return(ERR_RESPONSE_MALFORMED);
}
char statusStr[4];
strncpy(statusStr, statusStart + 1, 3);
statusStr[3] = 0x00;
return(atoi(statusStr));
}
uint16_t ESP8266::HttpPost(const char* url, const char* content, String& response, const char* contentType) {
// get the host address and endpoint
char* httpPrefix = strstr(url, "http://");
char* endpoint;
char* host;
if(httpPrefix != NULL) {
// find the host string
char* hostStart = strchr(url, '/');
hostStart = strchr(hostStart + 1, '/');
char* hostEnd = strchr(hostStart + 1, '/');
host = new char[hostEnd - hostStart];
strncpy(host, hostStart + 1, hostEnd - hostStart - 1);
host[hostEnd - hostStart - 1] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
} else {
// find the host string
char* hostEnd = strchr(url, '/');
host = new char[hostEnd - url + 1];
strncpy(host, url, hostEnd - url);
host[hostEnd - url] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
}
// build the POST request
char contentLengthStr[8];
itoa(strlen(content), contentLengthStr, 10);
char* request = new char[strlen(endpoint) + strlen(host) + strlen(contentType) + strlen(contentLengthStr) + strlen(content) + 64];
strcpy(request, "POST ");
strcat(request, endpoint);
strcat(request, " HTTP/1.1\r\nHost: ");
strcat(request, host);
strcat(request, "\r\nContent-Type: ");
strcat(request, contentType);
strcat(request, "\r\nContent-length: ");
strcat(request, contentLengthStr);
strcat(request, "\r\n\r\n");
strcat(request, content);
strcat(request, "\r\n\r\n");
delete[] endpoint;
// create TCP connection
uint8_t state = openTransportConnection(host, "TCP", portHttp);
delete[] host;
if(state != ERR_NONE) {
return(state);
}
// send the POST request
state = send(request);
delete[] request;
if(state != ERR_NONE) {
return(state);
}
delay(2000);
// get the response length
uint16_t numBytes = getNumBytes();
if(numBytes == 0) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
char* raw = new char[numBytes];
size_t rawLength = receive((uint8_t*)raw, numBytes);
if(rawLength == 0) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
// close the TCP connection
state = closeTransportConnection();
if(state != ERR_NONE) {
delete[] raw;
return(state);
}
// get the response body
char* responseStart = strstr(raw, "\r\n");
if(responseStart == NULL) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
char* responseStr = new char[raw + rawLength - responseStart - 1];
strncpy(responseStr, responseStart + 2, raw + rawLength - responseStart - 1);
responseStr[raw + rawLength - responseStart - 2] = 0x00;
response = String(responseStr);
delete[] responseStr;
// return the HTTP status code
char* statusStart = strchr(raw, ' ');
delete[] raw;
if(statusStart == NULL) {
return(ERR_RESPONSE_MALFORMED);
}
char statusStr[4];
strncpy(statusStr, statusStart + 1, 3);
statusStr[3] = 0x00;
return(atoi(statusStr));
}
uint8_t ESP8266::MqttConnect(const char* host, const char* clientId, const char* userName, const char* password, uint16_t keepAlive, bool cleanSession, const char* willTopic, const char* willMessage) {
// encode packet length
size_t clientIdLen = strlen(clientId);
size_t userNameLen = strlen(userName);
size_t passwordLen = strlen(password);
size_t willTopicLen = strlen(willTopic);
size_t willMessageLen = strlen(willMessage);
uint32_t remainingLength = 10 + (2 + clientIdLen);
if(userNameLen > 0) {
remainingLength += (2 + userNameLen);
}
if(passwordLen > 0) {
remainingLength += (2 + passwordLen);
}
if((willTopicLen > 0) && (willMessageLen > 0)) {
remainingLength += (2 + willTopicLen) + (2 + willMessageLen);
}
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = MqttEncodeLength(remainingLength, encoded);
// build the CONNECT packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_CONNECT << 4) | 0b0000;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// protocol name
size_t pos = encodedBytes + 1;
packet[pos++] = 0x00;
packet[pos++] = 0x04;
memcpy(packet + pos, "MQTT", 4);
pos += 4;
// protocol level
packet[pos++] = 0x04;
// flags
packet[pos++] = 0x00;
if(cleanSession) {
packet[encodedBytes + 8] |= MQTT_CONNECT_CLEAN_SESSION;
}
// keep alive interval in seconds
packet[pos++] = (keepAlive & 0xFF00) >> 8;
packet[pos++] = keepAlive & 0x00FF;
// payload
// clientId
packet[pos++] = (clientIdLen & 0xFF00) >> 8;
packet[pos++] = clientIdLen & 0x00FF;
memcpy(packet + pos, clientId, clientIdLen);
pos += clientIdLen;
// will topic and message
if((willTopicLen > 0) && (willMessageLen > 0)) {
packet[encodedBytes + 8] |= MQTT_CONNECT_WILL_FLAG;
packet[pos++] = (willTopicLen & 0xFF00) >> 8;
packet[pos++] = willTopicLen & 0x00FF;
memcpy(packet + pos, willTopic, willTopicLen);
pos += willTopicLen;
packet[pos++] = (willMessageLen & 0xFF00) >> 8;
packet[pos++] = willMessageLen & 0x00FF;
memcpy(packet + pos, willMessage, willMessageLen);
pos += willMessageLen;
}
// user name
if(userNameLen > 0) {
packet[encodedBytes + 8] |= MQTT_CONNECT_USER_NAME_FLAG;
packet[pos++] = (userNameLen & 0xFF00) >> 8;
packet[pos++] = userNameLen & 0x00FF;
memcpy(packet + pos, userName, userNameLen);
pos += userNameLen;
}
// password
if(passwordLen > 0) {
packet[encodedBytes + 8] |= MQTT_CONNECT_PASSWORD_FLAG;
packet[pos++] = (passwordLen & 0xFF00) >> 8;;
packet[pos++] = passwordLen & 0x00FF;
memcpy(packet + pos, password, passwordLen);
pos += passwordLen;
}
// create TCP connection
uint8_t state = openTransportConnection(host, "TCP", portMqtt, keepAlive);
if(state != ERR_NONE) {
delete[] packet;
return(state);
}
// send MQTT packet
state = send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT CONNACK response has to be 4 bytes long)
uint16_t numBytes = getNumBytes();
if(numBytes != 4) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
receive(response, numBytes);
if((response[0] == MQTT_CONNACK << 4) && (response[1] == 2)) {
uint8_t returnCode = response[3];
delete[] response;
return(returnCode);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t ESP8266::MqttDisconnect() {
// build the DISCONNECT packet
uint8_t packet[2];
// fixed header
packet[0] = (MQTT_DISCONNECT << 4) | 0b0000;
packet[1] = 0x00;
// send MQTT packet
uint8_t state = send(packet, 2);
if(state != ERR_NONE) {
return(state);
}
// close TCP connection
return(closeTransportConnection());
}
uint8_t ESP8266::MqttPublish(const char* topic, const char* message) {
// encode packet length
size_t topicLen = strlen(topic);
size_t messageLen = strlen(message);
uint32_t remainingLength = (2 + topicLen) + messageLen;
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = MqttEncodeLength(remainingLength, encoded);
// build the PUBLISH packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_PUBLISH << 4) | 0b0000;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// topic name
size_t pos = encodedBytes + 1;
packet[pos++] = (topicLen & 0xFF00) >> 8;
packet[pos++] = topicLen & 0x00FF;
memcpy(packet + pos, topic, topicLen);
pos += topicLen;
// packet ID
// payload
// message
memcpy(packet + pos, message, messageLen);
pos += messageLen;
// send MQTT packet
uint8_t state = send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
return(state);
//TODO: implement QoS > 0 and PUBACK response checking
}
uint8_t ESP8266::MqttSubscribe(const char* topicFilter) {
// encode packet length
size_t topicFilterLen = strlen(topicFilter);
uint32_t remainingLength = 2 + (2 + topicFilterLen + 1);
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = MqttEncodeLength(remainingLength, encoded);
// build the SUBSCRIBE packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_SUBSCRIBE << 4) | 0b0010;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// packet ID
size_t pos = encodedBytes + 1;
uint16_t packetId = _MqttPacketId++;
packet[pos++] = (packetId & 0xFF00) >> 8;
packet[pos++] = packetId & 0x00FF;
// payload
// topic filter
packet[pos++] = (topicFilterLen & 0xFF00) >> 8;;
packet[pos++] = topicFilterLen & 0x00FF;
memcpy(packet + pos, topicFilter, topicFilterLen);
pos += topicFilterLen;
packet[pos++] = 0x00; // QoS 0
// send MQTT packet
uint8_t state = send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT SUBACK response has to be 5 bytes long for single subscription)
uint16_t numBytes = getNumBytes();
if(numBytes != 5) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
receive(response, numBytes);
if((response[0] == MQTT_SUBACK << 4) && (response[1] == 3)) {
// check packet ID
uint16_t receivedId = response[3] | response[2] << 8;
uint8_t returnCode = response[4];
delete[] response;
if(receivedId != packetId) {
return(ERR_MQTT_UNEXPECTED_PACKET_ID);
}
return(returnCode);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t ESP8266::MqttUnsubscribe(const char* topicFilter) {
// encode packet length
size_t topicFilterLen = strlen(topicFilter);
uint32_t remainingLength = 2 + (2 + topicFilterLen);
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = MqttEncodeLength(remainingLength, encoded);
// build the UNSUBSCRIBE packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_UNSUBSCRIBE << 4) | 0b0010;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// packet ID
size_t pos = encodedBytes + 1;
uint16_t packetId = _MqttPacketId++;
packet[pos++] = (packetId & 0xFF00) >> 8;
packet[pos++] = packetId & 0x00FF;
// payload
// topic filter
packet[pos++] = (topicFilterLen & 0xFF00) >> 8;;
packet[pos++] = topicFilterLen & 0x00FF;
memcpy(packet + pos, topicFilter, topicFilterLen);
pos += topicFilterLen;
// send MQTT packet
uint8_t state = send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT UNSUBACK response has to be 4 bytes long)
uint16_t numBytes = getNumBytes();
if(numBytes != 4) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
receive(response, numBytes);
if((response[0] == MQTT_UNSUBACK << 4) && (response[1] == 2)) {
// check packet ID
uint16_t receivedId = response[3] | response[2] << 8;
delete[] response;
if(receivedId != packetId) {
return(ERR_MQTT_UNEXPECTED_PACKET_ID);
}
return(ERR_NONE);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t ESP8266::MqttPing() {
// build the PINGREQ packet
uint8_t packet[2];
// fixed header
packet[0] = (MQTT_PINGREQ << 4) | 0b0000;
packet[1] = 0x00;
// send MQTT packet
uint8_t state = send(packet, 2);
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT PINGRESP response has to be 2 bytes long)
uint16_t numBytes = getNumBytes();
if(numBytes != 2) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
receive(response, numBytes);
if((response[0] == MQTT_PINGRESP << 4) && (response[1] == 0)) {
delete[] response;
return(ERR_NONE);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t ESP8266::MqttCheck(void (*func)(const char*, const char*)) {
// ping the server
uint8_t state = MqttPing();
if(state != ERR_NONE) {
return(state);
}
// check new data
uint16_t numBytes = getNumBytes();
if(numBytes == 0) {
return(ERR_MQTT_NO_NEW_PACKET_AVAILABLE);
}
// read the PUBLISH packet from server
uint8_t* dataIn = new uint8_t[numBytes];
receive(dataIn, numBytes);
if(dataIn[0] == MQTT_PUBLISH << 4) {
// TODO: properly decode remaining length
uint8_t remainingLength = dataIn[1];
// get the topic
size_t topicLength = dataIn[3] | dataIn[2] << 8;
char* topic = new char[topicLength + 1];
memcpy(topic, dataIn + 4, topicLength);
topic[topicLength] = 0x00;
// get the message
size_t messageLength = remainingLength - topicLength - 2;
char* message = new char[messageLength + 1];
memcpy(message, dataIn + 4 + topicLength, messageLength);
message[messageLength] = 0x00;
// execute the callback function provided by user
func(topic, message);
delete[] topic;
delete[] message;
delete[] dataIn;
return(ERR_NONE);
}
delete[] dataIn;
return(ERR_MQTT_NO_NEW_PACKET_AVAILABLE);
}
uint8_t ESP8266::send(const char* data) {
// build AT command
char lenStr[8];
@ -768,34 +184,6 @@ uint8_t ESP8266::closeTransportConnection() {
return(ERR_NONE);
}
size_t ESP8266::MqttEncodeLength(uint32_t len, uint8_t* encoded) {
size_t i = 0;
do {
encoded[i] = len % 128;
len /= 128;
if(len > 0) {
encoded[i] |= 128;
}
i++;
} while(len > 0);
return(i);
}
uint32_t ESP8266::MqttDecodeLength(uint8_t* encoded) {
uint32_t mult = 1;
uint32_t len = 0;
uint8_t i = 0;
do {
len += (encoded[i] & 127) * mult;
mult *= 128;
if(mult > 2097152) {
// malformed remaining length
return(0);
}
} while((encoded[i] & 128) != 0);
return len;
}
uint16_t ESP8266::getNumBytes(uint32_t timeout, size_t minBytes) {
// wait for available data
uint32_t start = millis();

View file

@ -3,70 +3,27 @@
#include "Module.h"
// MQTT packet types
#define MQTT_CONNECT 0x01
#define MQTT_CONNACK 0x02
#define MQTT_PUBLISH 0x03
#define MQTT_PUBACK 0x04
#define MQTT_PUBREC 0x05
#define MQTT_PUBREL 0x06
#define MQTT_PUBCOMP 0x07
#define MQTT_SUBSCRIBE 0x08
#define MQTT_SUBACK 0x09
#define MQTT_UNSUBSCRIBE 0x0A
#define MQTT_UNSUBACK 0x0B
#define MQTT_PINGREQ 0x0C
#define MQTT_PINGRESP 0x0D
#define MQTT_DISCONNECT 0x0E
#include "../protocols/TransportLayer.h"
// MQTT CONNECT flags
#define MQTT_CONNECT_USER_NAME_FLAG 0b10000000
#define MQTT_CONNECT_PASSWORD_FLAG 0b01000000
#define MQTT_CONNECT_WILL_RETAIN 0b00100000
#define MQTT_CONNECT_WILL_FLAG 0b00000100
#define MQTT_CONNECT_CLEAN_SESSION 0b00000010
class ESP8266 {
class ESP8266: public TransportLayer {
public:
ESP8266(Module* module);
// Port numbers
uint16_t portHttp, portMqtt;
// Basic methods
uint8_t begin(long speed);
uint8_t reset();
uint8_t join(const char* ssid, const char* password);
// HTTP methods
uint16_t HttpGet(const char* url, String& response);
uint16_t HttpPost(const char* url, const char* content, String& response, const char* contentType = "text/plain");
// MQTT methods
uint8_t MqttConnect(const char* host, const char* clientId, const char* userName = "", const char* password = "", uint16_t keepAlive = 60, bool cleanSession = true, const char* willTopic = "", const char* willMessage = "");
uint8_t MqttDisconnect();
uint8_t MqttPublish(const char* topic, const char* message);
uint8_t MqttSubscribe(const char* topicFilter);
uint8_t MqttUnsubscribe(const char* topicFilter);
uint8_t MqttPing();
uint8_t MqttCheck(void (*func)(const char*, const char*));
// Transport layer methods
uint8_t openTransportConnection(const char* host, const char* protocol, uint16_t port, uint16_t tcpKeepAlive = 0);
uint8_t closeTransportConnection();
uint8_t send(const char* data);
uint8_t send(uint8_t* data, uint32_t len);
size_t receive(uint8_t* data, size_t len, uint32_t timeout = 10000);
uint16_t getNumBytes(uint32_t timeout = 10000, size_t minBytes = 10);
private:
Module* _mod;
uint16_t _MqttPacketId;
size_t MqttEncodeLength(uint32_t len, uint8_t* encoded);
uint32_t MqttDecodeLength(uint8_t* encoded);
uint16_t getNumBytes(uint32_t timeout = 10000, size_t minBytes = 10);
};
#endif

213
src/protocols/HTTP.cpp Normal file
View file

@ -0,0 +1,213 @@
#include "HTTP.h"
HTTPClient::HTTPClient(TransportLayer* tl, uint16_t port) {
_tl = tl;
_port = port;
}
uint16_t HTTPClient::get(const char* url, String& response) {
// get the host address and endpoint
char* httpPrefix = strstr(url, "http://");
char* endpoint;
char* host;
if(httpPrefix != NULL) {
// find the host string
char* hostStart = strchr(url, '/');
hostStart = strchr(hostStart + 1, '/');
char* hostEnd = strchr(hostStart + 1, '/');
host = new char[hostEnd - hostStart];
strncpy(host, hostStart + 1, hostEnd - hostStart - 1);
host[hostEnd - hostStart - 1] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
} else {
// find the host string
char* hostEnd = strchr(url, '/');
host = new char[hostEnd - url + 1];
strncpy(host, url, hostEnd - url);
host[hostEnd - url] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
}
// build the GET request
char* request = new char[strlen(endpoint) + strlen(host) + 25];
strcpy(request, "GET ");
strcat(request, endpoint);
strcat(request, " HTTP/1.1\r\nHost: ");
strcat(request, host);
strcat(request, "\r\n\r\n");
delete[] endpoint;
// create TCP connection
uint8_t state = _tl->openTransportConnection(host, "TCP", _port);
delete[] host;
if(state != ERR_NONE) {
delete[] request;
return(state);
}
// send the GET request
state = _tl->send(request);
delete[] request;
if(state != ERR_NONE) {
return(state);
}
//delay(1000);
// get the response length
uint16_t numBytes = _tl->getNumBytes();
if(numBytes == 0) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
char* raw = new char[numBytes];
size_t rawLength = _tl->receive((uint8_t*)raw, numBytes);
if(rawLength == 0) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
// close the tl connection
state = _tl->closeTransportConnection();
if(state != ERR_NONE) {
delete[] raw;
return(state);
}
// get the response body
char* responseStart = strstr(raw, "\r\n");
if(responseStart == NULL) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
char* responseStr = new char[raw + rawLength - responseStart - 1];
strncpy(responseStr, responseStart + 2, raw + rawLength - responseStart - 1);
responseStr[raw + rawLength - responseStart - 2] = 0x00;
response = String(responseStr);
delete[] responseStr;
// return the HTTP status code
char* statusStart = strchr(raw, ' ');
delete[] raw;
if(statusStart == NULL) {
return(ERR_RESPONSE_MALFORMED);
}
char statusStr[4];
strncpy(statusStr, statusStart + 1, 3);
statusStr[3] = 0x00;
return(atoi(statusStr));
}
uint16_t HTTPClient::post(const char* url, const char* content, String& response, const char* contentType) {
// get the host address and endpoint
char* httpPrefix = strstr(url, "http://");
char* endpoint;
char* host;
if(httpPrefix != NULL) {
// find the host string
char* hostStart = strchr(url, '/');
hostStart = strchr(hostStart + 1, '/');
char* hostEnd = strchr(hostStart + 1, '/');
host = new char[hostEnd - hostStart];
strncpy(host, hostStart + 1, hostEnd - hostStart - 1);
host[hostEnd - hostStart - 1] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
} else {
// find the host string
char* hostEnd = strchr(url, '/');
host = new char[hostEnd - url + 1];
strncpy(host, url, hostEnd - url);
host[hostEnd - url] = 0x00;
// find the endpoint string
endpoint = new char[url + strlen(url) - hostEnd + 1];
strcpy(endpoint, hostEnd);
}
// build the POST request
char contentLengthStr[8];
itoa(strlen(content), contentLengthStr, 10);
char* request = new char[strlen(endpoint) + strlen(host) + strlen(contentType) + strlen(contentLengthStr) + strlen(content) + 64];
strcpy(request, "POST ");
strcat(request, endpoint);
strcat(request, " HTTP/1.1\r\nHost: ");
strcat(request, host);
strcat(request, "\r\nContent-Type: ");
strcat(request, contentType);
strcat(request, "\r\nContent-length: ");
strcat(request, contentLengthStr);
strcat(request, "\r\n\r\n");
strcat(request, content);
strcat(request, "\r\n\r\n");
delete[] endpoint;
// create TCP connection
uint8_t state = _tl->openTransportConnection(host, "TCP", _port);
delete[] host;
if(state != ERR_NONE) {
return(state);
}
// send the POST request
state = _tl->send(request);
delete[] request;
if(state != ERR_NONE) {
return(state);
}
// get the response length
uint16_t numBytes = _tl->getNumBytes();
if(numBytes == 0) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
char* raw = new char[numBytes];
size_t rawLength = _tl->receive((uint8_t*)raw, numBytes);
if(rawLength == 0) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
// close the tl connection
state = _tl->closeTransportConnection();
if(state != ERR_NONE) {
delete[] raw;
return(state);
}
// get the response body
char* responseStart = strstr(raw, "\r\n");
if(responseStart == NULL) {
delete[] raw;
return(ERR_RESPONSE_MALFORMED);
}
char* responseStr = new char[raw + rawLength - responseStart - 1];
strncpy(responseStr, responseStart + 2, raw + rawLength - responseStart - 1);
responseStr[raw + rawLength - responseStart - 2] = 0x00;
response = String(responseStr);
delete[] responseStr;
// return the HTTP status code
char* statusStart = strchr(raw, ' ');
delete[] raw;
if(statusStart == NULL) {
return(ERR_RESPONSE_MALFORMED);
}
char statusStr[4];
strncpy(statusStr, statusStart + 1, 3);
statusStr[3] = 0x00;
return(atoi(statusStr));
}

19
src/protocols/HTTP.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef _KITELIB_HTTP_H
#define _KITELIB_HTTP_H
#include "TypeDef.h"
#include "TransportLayer.h"
class HTTPClient {
public:
HTTPClient(TransportLayer* tl, uint16_t port = 80);
uint16_t get(const char* url, String& response);
uint16_t post(const char* url, const char* content, String& response, const char* contentType = "text/plain");
private:
TransportLayer* _tl;
uint16_t _port;
};
#endif

407
src/protocols/MQTT.cpp Normal file
View file

@ -0,0 +1,407 @@
#include "MQTT.h"
MQTTClient::MQTTClient(TransportLayer* tl, uint16_t port) {
_tl = tl;
_port = port;
_packetId = 1;
}
uint8_t MQTTClient::connect(const char* host, const char* clientId, const char* userName, const char* password, uint16_t keepAlive, bool cleanSession, const char* willTopic, const char* willMessage) {
// encode packet length
size_t clientIdLen = strlen(clientId);
size_t userNameLen = strlen(userName);
size_t passwordLen = strlen(password);
size_t willTopicLen = strlen(willTopic);
size_t willMessageLen = strlen(willMessage);
uint32_t remainingLength = 10 + (2 + clientIdLen);
if(userNameLen > 0) {
remainingLength += (2 + userNameLen);
}
if(passwordLen > 0) {
remainingLength += (2 + passwordLen);
}
if((willTopicLen > 0) && (willMessageLen > 0)) {
remainingLength += (2 + willTopicLen) + (2 + willMessageLen);
}
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = encodeLength(remainingLength, encoded);
// build the CONNECT packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_CONNECT << 4) | 0b0000;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// protocol name
size_t pos = encodedBytes + 1;
packet[pos++] = 0x00;
packet[pos++] = 0x04;
memcpy(packet + pos, "MQTT", 4);
pos += 4;
// protocol level
packet[pos++] = 0x04;
// flags
packet[pos++] = 0x00;
if(cleanSession) {
packet[encodedBytes + 8] |= MQTT_CONNECT_CLEAN_SESSION;
}
// keep alive interval in seconds
packet[pos++] = (keepAlive & 0xFF00) >> 8;
packet[pos++] = keepAlive & 0x00FF;
// payload
// clientId
packet[pos++] = (clientIdLen & 0xFF00) >> 8;
packet[pos++] = clientIdLen & 0x00FF;
memcpy(packet + pos, clientId, clientIdLen);
pos += clientIdLen;
// will topic and message
if((willTopicLen > 0) && (willMessageLen > 0)) {
packet[encodedBytes + 8] |= MQTT_CONNECT_WILL_FLAG;
packet[pos++] = (willTopicLen & 0xFF00) >> 8;
packet[pos++] = willTopicLen & 0x00FF;
memcpy(packet + pos, willTopic, willTopicLen);
pos += willTopicLen;
packet[pos++] = (willMessageLen & 0xFF00) >> 8;
packet[pos++] = willMessageLen & 0x00FF;
memcpy(packet + pos, willMessage, willMessageLen);
pos += willMessageLen;
}
// user name
if(userNameLen > 0) {
packet[encodedBytes + 8] |= MQTT_CONNECT_USER_NAME_FLAG;
packet[pos++] = (userNameLen & 0xFF00) >> 8;
packet[pos++] = userNameLen & 0x00FF;
memcpy(packet + pos, userName, userNameLen);
pos += userNameLen;
}
// password
if(passwordLen > 0) {
packet[encodedBytes + 8] |= MQTT_CONNECT_PASSWORD_FLAG;
packet[pos++] = (passwordLen & 0xFF00) >> 8;;
packet[pos++] = passwordLen & 0x00FF;
memcpy(packet + pos, password, passwordLen);
pos += passwordLen;
}
// create TCP connection
uint8_t state = _tl->openTransportConnection(host, "TCP", _port, keepAlive);
if(state != ERR_NONE) {
delete[] packet;
return(state);
}
// send MQTT packet
state = _tl->send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT CONNACK response has to be 4 bytes long)
uint16_t numBytes = _tl->getNumBytes();
if(numBytes != 4) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
_tl->receive(response, numBytes);
if((response[0] == MQTT_CONNACK << 4) && (response[1] == 2)) {
uint8_t returnCode = response[3];
delete[] response;
return(returnCode);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t MQTTClient::disconnect() {
// build the DISCONNECT packet
uint8_t packet[2];
// fixed header
packet[0] = (MQTT_DISCONNECT << 4) | 0b0000;
packet[1] = 0x00;
// send MQTT packet
uint8_t state = _tl->send(packet, 2);
if(state != ERR_NONE) {
return(state);
}
// close tl connection
return(_tl->closeTransportConnection());
}
uint8_t MQTTClient::publish(const char* topic, const char* message) {
// encode packet length
size_t topicLen = strlen(topic);
size_t messageLen = strlen(message);
uint32_t remainingLength = (2 + topicLen) + messageLen;
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = encodeLength(remainingLength, encoded);
// build the PUBLISH packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_PUBLISH << 4) | 0b0000;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// topic name
size_t pos = encodedBytes + 1;
packet[pos++] = (topicLen & 0xFF00) >> 8;
packet[pos++] = topicLen & 0x00FF;
memcpy(packet + pos, topic, topicLen);
pos += topicLen;
// packet ID
// payload
// message
memcpy(packet + pos, message, messageLen);
pos += messageLen;
// send MQTT packet
uint8_t state = _tl->send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
return(state);
//TODO: implement QoS > 0 and PUBACK response checking
}
uint8_t MQTTClient::subscribe(const char* topicFilter) {
// encode packet length
size_t topicFilterLen = strlen(topicFilter);
uint32_t remainingLength = 2 + (2 + topicFilterLen + 1);
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = encodeLength(remainingLength, encoded);
// build the SUBSCRIBE packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_SUBSCRIBE << 4) | 0b0010;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// packet ID
size_t pos = encodedBytes + 1;
uint16_t packetId = _packetId++;
packet[pos++] = (packetId & 0xFF00) >> 8;
packet[pos++] = packetId & 0x00FF;
// payload
// topic filter
packet[pos++] = (topicFilterLen & 0xFF00) >> 8;;
packet[pos++] = topicFilterLen & 0x00FF;
memcpy(packet + pos, topicFilter, topicFilterLen);
pos += topicFilterLen;
packet[pos++] = 0x00; // QoS 0
// send MQTT packet
uint8_t state = _tl->send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT SUBACK response has to be 5 bytes long for single subscription)
uint16_t numBytes = _tl->getNumBytes();
if(numBytes != 5) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
_tl->receive(response, numBytes);
if((response[0] == MQTT_SUBACK << 4) && (response[1] == 3)) {
// check packet ID
uint16_t receivedId = response[3] | response[2] << 8;
uint8_t returnCode = response[4];
delete[] response;
if(receivedId != packetId) {
return(ERR_MQTT_UNEXPECTED_PACKET_ID);
}
return(returnCode);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t MQTTClient::unsubscribe(const char* topicFilter) {
// encode packet length
size_t topicFilterLen = strlen(topicFilter);
uint32_t remainingLength = 2 + (2 + topicFilterLen);
uint8_t encoded[] = {0, 0, 0, 0};
size_t encodedBytes = encodeLength(remainingLength, encoded);
// build the UNSUBSCRIBE packet
uint8_t* packet = new uint8_t[1 + encodedBytes + remainingLength];
// fixed header
packet[0] = (MQTT_UNSUBSCRIBE << 4) | 0b0010;
memcpy(packet + 1, encoded, encodedBytes);
// variable header
// packet ID
size_t pos = encodedBytes + 1;
uint16_t packetId = _packetId++;
packet[pos++] = (packetId & 0xFF00) >> 8;
packet[pos++] = packetId & 0x00FF;
// payload
// topic filter
packet[pos++] = (topicFilterLen & 0xFF00) >> 8;;
packet[pos++] = topicFilterLen & 0x00FF;
memcpy(packet + pos, topicFilter, topicFilterLen);
pos += topicFilterLen;
// send MQTT packet
uint8_t state = _tl->send(packet, 1 + encodedBytes + remainingLength);
delete[] packet;
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT UNSUBACK response has to be 4 bytes long)
uint16_t numBytes = _tl->getNumBytes();
if(numBytes != 4) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
_tl->receive(response, numBytes);
if((response[0] == MQTT_UNSUBACK << 4) && (response[1] == 2)) {
// check packet ID
uint16_t receivedId = response[3] | response[2] << 8;
delete[] response;
if(receivedId != packetId) {
return(ERR_MQTT_UNEXPECTED_PACKET_ID);
}
return(ERR_NONE);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t MQTTClient::ping() {
// build the PINGREQ packet
uint8_t packet[2];
// fixed header
packet[0] = (MQTT_PINGREQ << 4) | 0b0000;
packet[1] = 0x00;
// send MQTT packet
uint8_t state = _tl->send(packet, 2);
if(state != ERR_NONE) {
return(state);
}
// get the response length (MQTT PINGRESP response has to be 2 bytes long)
uint16_t numBytes = _tl->getNumBytes();
if(numBytes != 2) {
return(ERR_RESPONSE_MALFORMED_AT);
}
// read the response
uint8_t* response = new uint8_t[numBytes];
_tl->receive(response, numBytes);
if((response[0] == MQTT_PINGRESP << 4) && (response[1] == 0)) {
delete[] response;
return(ERR_NONE);
}
delete[] response;
return(ERR_RESPONSE_MALFORMED);
}
uint8_t MQTTClient::check(void (*func)(const char*, const char*)) {
// ping the server
uint8_t state = ping();
if(state != ERR_NONE) {
return(state);
}
// check new data
uint16_t numBytes = _tl->getNumBytes();
if(numBytes == 0) {
return(ERR_MQTT_NO_NEW_PACKET_AVAILABLE);
}
// read the PUBLISH packet from server
uint8_t* dataIn = new uint8_t[numBytes];
_tl->receive(dataIn, numBytes);
if(dataIn[0] == MQTT_PUBLISH << 4) {
// TODO: properly decode remaining length
uint8_t remainingLength = dataIn[1];
// get the topic
size_t topicLength = dataIn[3] | dataIn[2] << 8;
char* topic = new char[topicLength + 1];
memcpy(topic, dataIn + 4, topicLength);
topic[topicLength] = 0x00;
// get the message
size_t messageLength = remainingLength - topicLength - 2;
char* message = new char[messageLength + 1];
memcpy(message, dataIn + 4 + topicLength, messageLength);
message[messageLength] = 0x00;
// execute the callback function provided by user
func(topic, message);
delete[] topic;
delete[] message;
delete[] dataIn;
return(ERR_NONE);
}
delete[] dataIn;
return(ERR_MQTT_NO_NEW_PACKET_AVAILABLE);
}
size_t MQTTClient::encodeLength(uint32_t len, uint8_t* encoded) {
size_t i = 0;
do {
encoded[i] = len % 128;
len /= 128;
if(len > 0) {
encoded[i] |= 128;
}
i++;
} while(len > 0);
return(i);
}
uint32_t MQTTClient::decodeLength(uint8_t* encoded) {
uint32_t mult = 1;
uint32_t len = 0;
uint8_t i = 0;
do {
len += (encoded[i] & 127) * mult;
mult *= 128;
if(mult > 2097152) {
// malformed remaining length
return(0);
}
} while((encoded[i] & 128) != 0);
return len;
}

52
src/protocols/MQTT.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef _KITELIB_MQTT_H
#define _KITELIB_MQTT_H
#include "TypeDef.h"
#include "TransportLayer.h"
// MQTT packet types
#define MQTT_CONNECT 0x01
#define MQTT_CONNACK 0x02
#define MQTT_PUBLISH 0x03
#define MQTT_PUBACK 0x04
#define MQTT_PUBREC 0x05
#define MQTT_PUBREL 0x06
#define MQTT_PUBCOMP 0x07
#define MQTT_SUBSCRIBE 0x08
#define MQTT_SUBACK 0x09
#define MQTT_UNSUBSCRIBE 0x0A
#define MQTT_UNSUBACK 0x0B
#define MQTT_PINGREQ 0x0C
#define MQTT_PINGRESP 0x0D
#define MQTT_DISCONNECT 0x0E
// MQTT CONNECT flags
#define MQTT_CONNECT_USER_NAME_FLAG 0b10000000
#define MQTT_CONNECT_PASSWORD_FLAG 0b01000000
#define MQTT_CONNECT_WILL_RETAIN 0b00100000
#define MQTT_CONNECT_WILL_FLAG 0b00000100
#define MQTT_CONNECT_CLEAN_SESSION 0b00000010
class MQTTClient {
public:
MQTTClient(TransportLayer* tl, uint16_t port = 1883);
uint8_t connect(const char* host, const char* clientId, const char* userName = "", const char* password = "", uint16_t keepAlive = 60, bool cleanSession = true, const char* willTopic = "", const char* willMessage = "");
uint8_t disconnect();
uint8_t publish(const char* topic, const char* message);
uint8_t subscribe(const char* topicFilter);
uint8_t unsubscribe(const char* topicFilter);
uint8_t ping();
uint8_t check(void (*func)(const char*, const char*));
private:
TransportLayer* _tl;
uint16_t _port;
uint16_t _packetId;
size_t encodeLength(uint32_t len, uint8_t* encoded);
uint32_t decodeLength(uint8_t* encoded);
};
#endif

View file

@ -0,0 +1,16 @@
#ifndef _KITELIB_TRANSPORT_LAYER_H
#define _KITELIB_TRANSPORT_LAYER_H
#include "TypeDef.h"
class TransportLayer {
public:
virtual uint8_t openTransportConnection(const char* host, const char* protocol, uint16_t port, uint16_t tcpKeepAlive = 0) = 0;
virtual uint8_t closeTransportConnection() = 0;
virtual uint8_t send(const char* data) = 0;
virtual uint8_t send(uint8_t* data, uint32_t len) = 0;
virtual size_t receive(uint8_t* data, size_t len, uint32_t timeout = 10000) = 0;
virtual uint16_t getNumBytes(uint32_t timeout = 10000, size_t minBytes = 10) = 0;
};
#endif