ESP8266 - Extracted MQTT and HTTP code into specific classes
This commit is contained in:
parent
89fd8fb279
commit
b8f93c5077
9 changed files with 737 additions and 677 deletions
42
keywords.txt
42
keywords.txt
|
@ -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)
|
||||
#######################################
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
213
src/protocols/HTTP.cpp
Normal 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
19
src/protocols/HTTP.h
Normal 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
407
src/protocols/MQTT.cpp
Normal 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
52
src/protocols/MQTT.h
Normal 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
|
16
src/protocols/TransportLayer.h
Normal file
16
src/protocols/TransportLayer.h
Normal 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
|
Loading…
Add table
Reference in a new issue