diff --git a/MqttSnClient.h b/MqttSnClient.h index 9d0e7aa..824dc47 100644 --- a/MqttSnClient.h +++ b/MqttSnClient.h @@ -16,7 +16,6 @@ template class MqttSnMessageHandler; - struct topic_registration { char *topic_name; uint16_t topic_id; @@ -158,9 +157,10 @@ class MqttSnClient { return true; } + // Publish using normal topic bool publish(char *payload, char *topic_name, int8_t qos) { - - // check if payload is not too long + // N.B Short topics not handled + // check if payload is not too long uint16_t payload_length = 0; uint8_t msg_publish_without_payload_length = 7; if (strlen(payload) + 1 > socketInterface.getMaximumMessageLength() - msg_publish_without_payload_length) { @@ -185,23 +185,28 @@ class MqttSnClient { } } } - - // check if topic_name is already registered - uint16_t topic_id = 0; - if (this->registration.topic_name == topic_name) { + uint8_t topic_type = REGISTERED; // normal + uint16_t topic_id = 0; + if (strlen(topic_name) != 2) { + // check if topic_name is already registered + if (this->registration.topic_name == topic_name) { topic_id = registration.topic_id; - } else if (this->publish_registration.topic_name == topic_name) { + } else if (this->publish_registration.topic_name == topic_name) { topic_id = publish_registration.topic_id; - } else { + } else { // it is not registered - register it now topic_id = register_topic(topic_name); - } - if (topic_id == 0) { + } + if (topic_id == 0) { // TODO check if compatible or if gateway deliveres 0 too (then change return type of register_topic) return false; - } - publish_registration.topic_id = topic_id; - + } + publish_registration.topic_id = topic_id; + } + else { // short topic + topic_id = (uint16_t)((topic_name[1] << 8) | topic_name[0]); + topic_type = SHORTTOPIC; + } uint16_t msg_id = this->increment_and_get_msg_id_counter(); if (qos == 0) { @@ -211,8 +216,8 @@ class MqttSnClient { this->set_await_message(MQTTSN_PUBACK); } - mqttSnMessageHandler.send_publish(&gw_address, (uint8_t *) payload, payload_length, msg_id, topic_id, true, - false, qos, false); + mqttSnMessageHandler.send_publish(&gw_address, (uint8_t *) payload, payload_length, msg_id, + topic_id, topic_type, false, qos, false); if (qos > 0) { while (this->await_msg_type != MQTTSN_PINGREQ) { // wait until we have no other messages in flight @@ -224,6 +229,65 @@ class MqttSnClient { return true; } + // Publish using pre-defined topic + bool publish(char *payload, uint16_t predef_topic_id, int8_t qos) { + + // check if payload is not too long + uint16_t payload_length = 0; + uint8_t msg_publish_without_payload_length = 7; + if (strlen(payload) + 1 > socketInterface.getMaximumMessageLength() - msg_publish_without_payload_length) { + // payload is too long + return false; + } + payload_length = strlen(payload) + 1; + if (qos == 2) { + // TODO not implemented yet + return false; + } + if (qos < -1 || qos > 2) { + // invalid qos + return false; + } + + if (qos > 0) { + while (this->await_msg_type != MQTTSN_PINGREQ) { + // wait until we have no other messages are in flight + if (!socketInterface.loop()) { + return false; + } + } + } + + // check if topic_name is already registered + uint16_t topic_id = 0; + if (predef_topic_id != 0) { + topic_id = (uint16_t)((predef_topic_id >> 8) | (predef_topic_id << 8)); // swap high/low bytes + } + else { + return false; + } + + uint16_t msg_id = this->increment_and_get_msg_id_counter(); + if (qos == 0) { + msg_id = 0; + } + if (qos == 1) { + this->set_await_message(MQTTSN_PUBACK); + } + + mqttSnMessageHandler.send_publish(&gw_address, (uint8_t *) payload, payload_length, msg_id, + topic_id, PREDEFINED, false, qos, false); // RJM + if (qos > 0) { + while (this->await_msg_type != MQTTSN_PINGREQ) { + // wait until we have no other messages in flight + if (!socketInterface.loop()) { + return false; + } + } + } + return true; + } + uint16_t register_topic(char *topic_name) { while (this->await_msg_type != MQTTSN_PINGREQ) { // wait until we have no other messages in flight diff --git a/MqttSnMessageHandler.h b/MqttSnMessageHandler.h index df2d21c..2eacfa1 100644 --- a/MqttSnMessageHandler.h +++ b/MqttSnMessageHandler.h @@ -247,8 +247,8 @@ class MqttSnMessageHandler { void send_publish(device_address *address, uint8_t *data, uint8_t data_len, uint16_t msg_id, - uint16_t topic_id, bool short_topic, bool retain, uint8_t qos, bool dup) { - msg_publish to_send(dup, qos, retain, short_topic, topic_id, msg_id, data, data_len); + uint16_t topic_id, uint8_t topic_type, bool retain, uint8_t qos, bool dup) { + msg_publish to_send(dup, qos, retain, topic_type, topic_id, msg_id, data, data_len); if (!socketInterface.send(address, (uint8_t *) &to_send, (uint16_t) to_send.length)) { mqttSnClient.notify_socket_disconnected(); } diff --git a/Readme-topics-info.txt b/Readme-topics-info.txt new file mode 100644 index 0000000..da263e6 --- /dev/null +++ b/Readme-topics-info.txt @@ -0,0 +1,60 @@ +Topics and Topic IDs - Excerpts from the MQTT-SN standard. +-------------------- + +TopicIdType:indicates whether the field TopicId or TopicName included in this +message contains a: + + + a normal topic id (set to “0b00”), + + pre-defined topic id (set to “0b01”), + + short topic name (set to “0b10”). + +The value “0b11” is reserved. + +Refer to sections 3 and 6.7 for the definition of the various types of topic ids. + +“Pre-defined” topic ids and “short” topic names are introduced, for which no +registration is required. Pre-defined topic ids are also a two-byte long +replacement of the topic name, their mapping to the topic names is however +known in advance by both the client’s application and the +gateway/server. Therefore both sides can start using pre-defined topic ids; +there is no need for a registration as in the case of “normal” topic ids +mentioned above. + +Short topic names are topic names that have a fixed length of two octets. +They are short enough for being carried together with the data within PUBLISH +messages. As for pre-defined topic ids, there is also no need for a +registration for short topic names + +6.7 Pre-defined topic ids and short topic names. As described in Section 6.5, +a topic id is a two-byte long replacement of the string-based topic name. A +client needs to use the REGISTER procedure to inform the gateway about the +topic name it wants to employ and gets from the gateway the corresponding +topic id. It then will use this topic id in the PUBLISH messages it sends to +the gateway. In the opposite direction, the PUBLISH messages also contain a +2-byte topic id (instead of the string-based topic name). The client is +informed about the relation between topic id and topic name by means of +either a former SUBSCRIBE procedure or a REGISTER procedure started by the +gateway + +A “pre-defined” topic id is a topic id whose mapping to a topic name is known +in advance by both the client’s application and the gateway. This is +indicated in the Flags field of the message. When using pre-defined topic ids +both sides can start immediately with the sending of PUBLISH messages; there +is no need for the REGISTER procedure as in the case of ”normal” topic +ids. When receiving a PUBLISH message with a pre-defined topic id of which +the mapping to a topic name is unknown, the receiver should return a PUBACK +with the ReturnCode = “Rejection: invalid topic Id”. Note that this error +situation cannot be resolved by means of re-registering as in the case of +normal topic id. A client is still required to subscribe to a pre-defined +topic id, if it wants to receive PUBLISH messages relating to that topic id. +To avoid confusion between a pre-defined topic id and a two-byte short topic +name, the SUBSCRIBE message contains a flag indicating whether it is +subscribing for a short topic name or a pre-defined topic id. A “short” topic +name is a topic name that has a fixed length of two octets. It could be +carried together with the data within a PUBLISH message, thus no REGISTER +procedure is needed for a short topic name. Otherwise, all rules that apply +to normal topic names also apply to short topic names. Note however that it +does not make sense to do wildcarding in subscriptions to short topic names, +because it is not possible to define a meaningful name hierarchy with only +two characters. + diff --git a/System.cpp b/System.cpp index d3048dd..172cd7d 100644 --- a/System.cpp +++ b/System.cpp @@ -36,7 +36,7 @@ void System::sleep(uint32_t duration) { } void System::exit() { -#if defined(ESP8266) +#if defined(ESP8266) or defined(ESP32) ESP.restart(); #elif defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_MEGA2560) asm volatile (" jmp 0"); diff --git a/TransmissionProtocolUartBridge.h b/TransmissionProtocolUartBridge.h index 0388653..e7a93be 100644 --- a/TransmissionProtocolUartBridge.h +++ b/TransmissionProtocolUartBridge.h @@ -64,7 +64,7 @@ enum CONFIGURATION_STATUS { // #define PARSERDATADEBUG // #define PARSERADDRESSDEBUG #define SERIAL_BUFFER_SIZE 200 -#define RECEIVE_BUFFER_SIZE 64 +#define RECEIVE_BUFFER_SZ 64 #define SEND_BUFFER_SIZE 64 template @@ -93,7 +93,7 @@ class TransmissionProtocolUartBridge { // ReceiveBuffer device_address receive_address; - uint8_t receive_buffer[RECEIVE_BUFFER_SIZE]; + uint8_t receive_buffer[RECEIVE_BUFFER_SZ]; uint16_t receive_buffer_length = 0; Stream *stream; @@ -178,7 +178,7 @@ class TransmissionProtocolUartBridge { } void resetChip() { -#if defined(ESP8266) +#if defined(ESP8266) or defined(ESP32) // RJM stream->print(F("OK RESET\n")); delay(500); ESP.restart(); diff --git a/WiFiUdpSocket.h b/WiFiUdpSocket.h index 8e75771..abfc11a 100644 --- a/WiFiUdpSocket.h +++ b/WiFiUdpSocket.h @@ -10,7 +10,11 @@ #include "MqttSnMessageHandler.h" #include "SocketInterface.h" +#ifdef ESP32 +#include +#else #include +#endif #include #define RECEIVE_BUFFER_SIZE 255 @@ -40,7 +44,7 @@ class WiFiUdpSocket : SocketInterface { device_address *getAddress() override { IPAddress localIP = WiFi.localIP(); - uint16_t localPort = wiFiUdp.localPort(); + uint16_t localPort = port; convertIPAddressAndPortToDeviceAddress(localIP, localPort, own_address); return &own_address; } diff --git a/mqttsn_messages.h b/mqttsn_messages.h index faeb051..3008026 100644 --- a/mqttsn_messages.h +++ b/mqttsn_messages.h @@ -28,6 +28,11 @@ THE SOFTWARE. #include "global_defines.h" #include +// Types of topic IDs +#define REGISTERED 0 // registered in this session +#define PREDEFINED 1 // known to gateway (no need to register) +#define SHORTTOPIC 2 // only two characters (no need to register) + #define PROTOCOL_ID 0x01 #define FLAG_NO_FLAGS 0x00 @@ -277,7 +282,7 @@ struct msg_publish : public message_header { uint16_t message_id; uint8_t data[UINT8_MAX - 7]; - msg_publish(bool dup, int8_t qos, bool retain, bool short_topic, uint16_t topic_id, uint16_t msg_id, + msg_publish(bool dup, int8_t qos, bool retain, uint8_t topic_type, uint16_t topic_id, uint16_t msg_id, const uint8_t *s_data, uint8_t s_data_len) : topic_id(topic_id), message_id(msg_id) { memset(this, 0, sizeof(this)); this->length = ((uint8_t) 7) + s_data_len; @@ -289,10 +294,14 @@ struct msg_publish : public message_header { if (retain) { this->flags |= FLAG_RETAIN; } - if (short_topic) { - this->flags |= FLAG_TOPIC_SHORT_NAME; - } else { - this->flags |= FLAG_TOPIC_PREDEFINED_ID; + if (topic_type == REGISTERED) { + this->flags |= FLAG_TOPIC_NAME; // Normal + } + else if (topic_type == PREDEFINED) { // Previously known to gateway + this->flags |= FLAG_TOPIC_PREDEFINED_ID; + } + else if (topic_type == SHORTTOPIC) { // Short topic + this->flags |= FLAG_TOPIC_SHORT_NAME; } if (qos == 0) { this->flags |= FLAG_QOS_0;