mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
update to 1.7.0
This commit is contained in:
@@ -3,50 +3,16 @@
|
|||||||
MQTT client library for the Espressif devices ESP8266 and ESP32 on the Arduino framework.
|
MQTT client library for the Espressif devices ESP8266 and ESP32 on the Arduino framework.
|
||||||
Aims to be a non-blocking, fully compliant MQTT 3.1.1 client.
|
Aims to be a non-blocking, fully compliant MQTT 3.1.1 client.
|
||||||
|
|
||||||

|
Copy of <https://github.com/bertmelis/espMqttClient>
|
||||||

|
|
||||||

|
|
||||||
[](https://registry.platformio.org/libraries/bertmelis/espMqttClient)
|
|
||||||
|
|
||||||
# Features
|
Based on Version 1.7.0 - <https://github.com/bertmelis/espMqttClient/tree/v1.7.0>
|
||||||
|
|
||||||
- MQTT 3.1.1 compliant library
|
with additional changes to support EMS-ESP such as compiling with Tasmota and not using `SecureWifiClient` in these two files:
|
||||||
- Sending and receiving at all QoS levels
|
|
||||||
- TCP and TCP/TLS using standard WiFiClient and WiFiClientSecure connections
|
|
||||||
- Virtually unlimited incoming and outgoing payload sizes
|
|
||||||
- Readable and understandable code
|
|
||||||
- Fully async clients available via [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) or [ESPAsnycTCP](https://github.com/me-no-dev/ESPAsyncTCP) (no TLS supported)
|
|
||||||
- Supported platforms:
|
|
||||||
- Espressif ESP8266 and ESP32 using the Arduino framework
|
|
||||||
- Basic Linux compatibility*. This includes WSL on Windows
|
|
||||||
|
|
||||||
> Linux compatibility is mainly for automatic testing. It relies on a quick and dirty Arduino-style `Client` with a POSIX TCP client underneath and Arduino-style `IPAddress` class. These are lacking many features needed for proper Linux support.
|
```
|
||||||
|
src/espMqttClient.cpp
|
||||||
# Documentation
|
src/Transport/ClientSecureSync.h
|
||||||
|
```
|
||||||
See [documentation](https://www.emelis.net/espMqttClient/) and the [examples](examples/).
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
### MQTT 3.1.1 Compliancy
|
|
||||||
|
|
||||||
Outgoing messages and session data are not stored in non-volatile memory. Any events like loss of power or sudden resets result in loss of data. Despite this limitation, one could still consider this library as fully complaint based on the non normative remark in point 4.1.1 of the specification.
|
|
||||||
|
|
||||||
### Non-blocking
|
|
||||||
|
|
||||||
This library aims to be fully non-blocking. It is however limited by the underlying `WiFiClient` library which is part of the Arduino framework and has a blocking `connect` method. This is not an issue on ESP32 because the call is offloaded to a separate task. On ESP8266 however, connecting will block until succesful or until the connection timeouts.
|
|
||||||
|
|
||||||
If you need a fully asynchronous MQTT client, you can use `espMqttClientAsync` which uses AsyncTCP/ESPAsyncTCP under the hood. These underlying libraries do not support TLS (anymore). I will not provide support TLS for the async client.
|
|
||||||
|
|
||||||
# Bugs and feature requests
|
|
||||||
|
|
||||||
Please use Github's facilities to get in touch.
|
|
||||||
|
|
||||||
# About this library
|
|
||||||
|
|
||||||
This client wouldn't exist without [Async-mqtt-client](https://github.com/marvinroger/async-mqtt-client). It has been my go-to MQTT client for many years. It was fast, reliable and had features that were non-existing in alternative libraries. However, the underlying async TCP libraries are lacking updates, especially updates related to secure connections. Adapting this library to use up-to-date TCP clients would not be trivial. I eventually decided to write my own MQTT library, from scratch.
|
|
||||||
|
|
||||||
The result is an almost non-blocking library with no external dependencies. The library is almost a drop-in replacement for the async-mqtt-client except a few parameter type changes (eg. `uint8_t*` instead of `char*` for payloads).
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,8 @@ the LICENSE file.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef TASMOTA_SDK
|
|
||||||
#define EMC_CLIENT_SECURE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef EMC_TX_TIMEOUT
|
#ifndef EMC_TX_TIMEOUT
|
||||||
#define EMC_TX_TIMEOUT 2000
|
#define EMC_TX_TIMEOUT 10000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef EMC_RX_BUFFER_SIZE
|
#ifndef EMC_RX_BUFFER_SIZE
|
||||||
@@ -64,3 +60,16 @@ the LICENSE file.
|
|||||||
#ifndef EMC_USE_WATCHDOG
|
#ifndef EMC_USE_WATCHDOG
|
||||||
#define EMC_USE_WATCHDOG 0
|
#define EMC_USE_WATCHDOG 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMC_USE_MEMPOOL
|
||||||
|
#define EMC_USE_MEMPOOL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if EMC_USE_MEMPOOL
|
||||||
|
#ifndef EMC_NUM_POOL_ELEMENTS
|
||||||
|
#define EMC_NUM_POOL_ELEMENTS 32
|
||||||
|
#endif
|
||||||
|
#ifndef EMC_SIZE_POOL_ELEMENTS
|
||||||
|
#define EMC_SIZE_POOL_ELEMENTS 128
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|||||||
21
lib/espMqttClient/src/MemoryPool/LICENSE
Normal file
21
lib/espMqttClient/src/MemoryPool/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Bert Melis
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
105
lib/espMqttClient/src/MemoryPool/README.md
Normal file
105
lib/espMqttClient/src/MemoryPool/README.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Memory Pool
|
||||||
|
|
||||||
|
EARLY VERSION. USE AT OWN RISK.
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This is a simple memory pool that doesn't solve the fragmentation problem but contains it. Inside the pool you will still suffer memory fragmentation. The upside is that you're not restricted on memory size. As long as it fits in the pool, you can request any size!
|
||||||
|
|
||||||
|
For applications where the (maximum) size to allocate is known, a simple fixed block size memory pool is available. There is no memory fragmentation happening in this case. The downside is wastage of memory if you need less then the specified blocksize.
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
- pool memory is statically allocated
|
||||||
|
- pool size adjusts on architecture
|
||||||
|
- no size calculation required: input number of blocks and size of block
|
||||||
|
- header-only library
|
||||||
|
- Variable size pool: no restriction on allocated size
|
||||||
|
- Variable size pool: malloc and free are O(n); The number of allocated blocks affects lookup.
|
||||||
|
- Fixed size pool: malloc and free are O(1).
|
||||||
|
|
||||||
|
[](https://github.com/bertmelis/MemoryPool/actions/workflows/test-platformio.yml)
|
||||||
|
[](https://github.com/bertmelis/MemoryPool/actions/workflows/cpplint.yml)
|
||||||
|
<!---[](https://github.com/bertmelis/MemoryPool/actions/workflows/cppcheck.yml)--->
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
#### Variable size pool
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <MemoryPool.h>
|
||||||
|
|
||||||
|
Struct MyStruct {
|
||||||
|
unsigned int id;
|
||||||
|
std::size_t size;
|
||||||
|
unsigned char data[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
// pool will be able to hold 10 blocks the size of MyStruct
|
||||||
|
MemoryPool::Variable<10, sizeof(MyStruct)> pool;
|
||||||
|
|
||||||
|
// you can allocate the specified blocksize
|
||||||
|
// allocation is done in number of 'unsigned char'
|
||||||
|
MyStruct* s = reinterpret_cast<MyStruct*>(pool.malloc(sizeof(MyStruct)));
|
||||||
|
|
||||||
|
// you can allocate less than the specified blocksize
|
||||||
|
int* i = reinterpret_cast<int*>(pool.malloc(sizeof(int)));
|
||||||
|
|
||||||
|
// you can allocate more than the specified blocksize
|
||||||
|
unsigned char* m = reinterpret_cast<unsigned char*>(pool.malloc(400));
|
||||||
|
|
||||||
|
pool.free(s);
|
||||||
|
pool.free(i);
|
||||||
|
pool.free(m);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fixed size pool
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <MemoryPool.h>
|
||||||
|
|
||||||
|
Struct MyStruct {
|
||||||
|
unsigned int id;
|
||||||
|
std::size_t size;
|
||||||
|
unsigned char data[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
// pool will be able to hold 10 blocks the size of MyStruct
|
||||||
|
MemoryPool::Fixed<10, sizeof(MyStruct)> pool;
|
||||||
|
|
||||||
|
// there is no size argument in the malloc function!
|
||||||
|
MyStruct* s = reinterpret_cast<MyStruct*>(pool.malloc());
|
||||||
|
|
||||||
|
// you can allocate less than the specified blocksize
|
||||||
|
int* i = reinterpret_cast<int*>(pool.malloc());
|
||||||
|
|
||||||
|
pool.free(s);
|
||||||
|
pool.free(i);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### How it works
|
||||||
|
|
||||||
|
##### Variable size pool
|
||||||
|
|
||||||
|
Free blocks are organized as a linked list with their header (contains pointer to next and size). An allocated block also has this header with it's pointer set to `nullptr`. Therefore, each allocation wastes memory the size of the header (`sizeof(void*) + sizeof(std::size_t)`). On creation, the pool calculations the needed space to store the number of blocks wich each their header.
|
||||||
|
|
||||||
|
However, memory allocation isn't restricted the the specified blocksize. So in reality, you can allocate more if you allocate larger chunks because less memory blocks means less headers. After all, memory needs to be contiguous.
|
||||||
|
|
||||||
|
If you inspect the pool you'll see that a free pool only has one big block.
|
||||||
|
|
||||||
|
Allocation is linear: the pool is iterated until a suitable spot is found.
|
||||||
|
Freeing is also linear as the pool is traversed to insert the chunk in the linked list of free blocks
|
||||||
|
|
||||||
|
When freeing, free blocks which are adjacent are combined into one.
|
||||||
|
|
||||||
|
##### Fixed size pool
|
||||||
|
|
||||||
|
The fixed size pool is implemented as an array. Free blocks are saved as a linked list in this array.
|
||||||
|
|
||||||
|
### Bugs and feature requests
|
||||||
|
|
||||||
|
Please use Github's facilities to get in touch.
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
This library is released under the MIT Licence. A copy is included in the repo.
|
||||||
16
lib/espMqttClient/src/MemoryPool/keywords.txt
Normal file
16
lib/espMqttClient/src/MemoryPool/keywords.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
Fixed KEYWORD1
|
||||||
|
Variable KEYWORD1
|
||||||
|
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
malloc KEYWORD2
|
||||||
|
free KEYWORD2
|
||||||
|
freeMemory KEYWORD2
|
||||||
|
maxBlockSize KEYWORD2
|
||||||
|
print KEYWORD2
|
||||||
|
|
||||||
|
# Structures (KEYWORD3)
|
||||||
|
# structure KEYWORD3
|
||||||
|
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
MemoryPool LITERAL1
|
||||||
21
lib/espMqttClient/src/MemoryPool/library.json
Normal file
21
lib/espMqttClient/src/MemoryPool/library.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "MemoryPool",
|
||||||
|
"keywords": "memory",
|
||||||
|
"description": "A simple memory pool for fixed and variable sizes",
|
||||||
|
"authors":
|
||||||
|
{
|
||||||
|
"name": "Bert Melis",
|
||||||
|
"url": "https://github.com/bertmelis"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/bertmelis/MemoryPool",
|
||||||
|
"repository":
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/bertmelis/MemoryPool.git"
|
||||||
|
},
|
||||||
|
"version": "0.1.0",
|
||||||
|
"frameworks": "*",
|
||||||
|
"platforms": "*",
|
||||||
|
"headers": ["MemoryPool.h"]
|
||||||
|
}
|
||||||
10
lib/espMqttClient/src/MemoryPool/library.properties
Normal file
10
lib/espMqttClient/src/MemoryPool/library.properties
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
name=MemoryPool
|
||||||
|
version=0.1.0
|
||||||
|
author=Bert Melis
|
||||||
|
maintainer=Bert Melis
|
||||||
|
sentence=A simple memory pool for fixed and variable sizes
|
||||||
|
paragraph=
|
||||||
|
category=Other
|
||||||
|
url=https://github.com/bertmelis/MemoryPool
|
||||||
|
architectures=*
|
||||||
|
includes=MemoryPool.h
|
||||||
119
lib/espMqttClient/src/MemoryPool/src/Fixed.h
Normal file
119
lib/espMqttClient/src/MemoryPool/src/Fixed.h
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2024 Bert Melis. All rights reserved.
|
||||||
|
|
||||||
|
This work is licensed under the terms of the MIT license.
|
||||||
|
For a copy, see <https://opensource.org/licenses/MIT> or
|
||||||
|
the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // std::size_t
|
||||||
|
#include <cassert> // assert
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
#include <mutex> // NOLINT [build/c++11] std::mutex, std::lock_guard
|
||||||
|
#else
|
||||||
|
#warning "The memory pool is not thread safe"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
#include <iostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace MemoryPool {
|
||||||
|
|
||||||
|
template <std::size_t nrBlocks, std::size_t blocksize>
|
||||||
|
class Fixed {
|
||||||
|
public:
|
||||||
|
Fixed() // cppcheck-suppress uninitMemberVar
|
||||||
|
: _buffer{0}
|
||||||
|
, _head(_buffer) {
|
||||||
|
unsigned char* b = _head;
|
||||||
|
std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize;
|
||||||
|
for (std::size_t i = 0; i < nrBlocks - 1; ++i) {
|
||||||
|
*reinterpret_cast<unsigned char**>(b) = b + adjustedBlocksize;
|
||||||
|
b += adjustedBlocksize;
|
||||||
|
}
|
||||||
|
*reinterpret_cast<unsigned char**>(b) = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no copy nor move
|
||||||
|
Fixed (const Fixed&) = delete;
|
||||||
|
Fixed& operator= (const Fixed&) = delete;
|
||||||
|
|
||||||
|
void* malloc() {
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
const std::lock_guard<std::mutex> lockGuard(_mutex);
|
||||||
|
#endif
|
||||||
|
if (_head) {
|
||||||
|
void* retVal = _head;
|
||||||
|
_head = *reinterpret_cast<unsigned char**>(_head);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* ptr) {
|
||||||
|
if (!ptr) return;
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
const std::lock_guard<std::mutex> lockGuard(_mutex);
|
||||||
|
#endif
|
||||||
|
*reinterpret_cast<unsigned char**>(ptr) = _head;
|
||||||
|
_head = reinterpret_cast<unsigned char*>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t freeMemory() {
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
const std::lock_guard<std::mutex> lockGuard(_mutex);
|
||||||
|
#endif
|
||||||
|
unsigned char* i = _head;
|
||||||
|
std::size_t retVal = 0;
|
||||||
|
while (i) {
|
||||||
|
retVal += blocksize;
|
||||||
|
i = reinterpret_cast<unsigned char**>(i)[0];
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
void print() {
|
||||||
|
std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize;
|
||||||
|
std::cout << "+--------------------" << std::endl;
|
||||||
|
std::cout << "|start:" << reinterpret_cast<void*>(_buffer) << std::endl;
|
||||||
|
std::cout << "|blocks:" << nrBlocks << std::endl;
|
||||||
|
std::cout << "|blocksize:" << adjustedBlocksize << std::endl;
|
||||||
|
std::cout << "|head: " << reinterpret_cast<void*>(_head) << std::endl;
|
||||||
|
unsigned char* currentBlock = _buffer;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < nrBlocks; ++i) {
|
||||||
|
std::cout << "|" << i + 1 << ": " << reinterpret_cast<void*>(currentBlock) << std::endl;
|
||||||
|
if (_isFree(currentBlock)) {
|
||||||
|
std::cout << "| free" << std::endl;
|
||||||
|
std::cout << "| next: " << reinterpret_cast<void*>(*reinterpret_cast<unsigned char**>(currentBlock)) << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "| allocated" << std::endl;
|
||||||
|
}
|
||||||
|
currentBlock += adjustedBlocksize;
|
||||||
|
}
|
||||||
|
std::cout << "+--------------------" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isFree(const unsigned char* ptr) {
|
||||||
|
unsigned char* b = _head;
|
||||||
|
while (b) {
|
||||||
|
if (b == ptr) return true;
|
||||||
|
b = *reinterpret_cast<unsigned char**>(b);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char _buffer[nrBlocks * (sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize)];
|
||||||
|
unsigned char* _head;
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
std::mutex _mutex;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace MemoryPool
|
||||||
12
lib/espMqttClient/src/MemoryPool/src/MemoryPool.h
Normal file
12
lib/espMqttClient/src/MemoryPool/src/MemoryPool.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2024 Bert Melis. All rights reserved.
|
||||||
|
|
||||||
|
This work is licensed under the terms of the MIT license.
|
||||||
|
For a copy, see <https://opensource.org/licenses/MIT> or
|
||||||
|
the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Variable.h"
|
||||||
|
#include "Fixed.h"
|
||||||
242
lib/espMqttClient/src/MemoryPool/src/Variable.h
Normal file
242
lib/espMqttClient/src/MemoryPool/src/Variable.h
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2024 Bert Melis. All rights reserved.
|
||||||
|
|
||||||
|
This work is licensed under the terms of the MIT license.
|
||||||
|
For a copy, see <https://opensource.org/licenses/MIT> or
|
||||||
|
the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // std::size_t
|
||||||
|
#include <cassert> // assert
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
#include <mutex> // NOLINT [build/c++11] std::mutex, std::lock_guard
|
||||||
|
#else
|
||||||
|
#warning "The memory pool is not thread safe"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
#include <iostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace MemoryPool {
|
||||||
|
|
||||||
|
template <std::size_t nrBlocks, std::size_t blocksize>
|
||||||
|
class Variable {
|
||||||
|
public:
|
||||||
|
Variable()
|
||||||
|
: _buffer{0}
|
||||||
|
, _head(nullptr)
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
, _bufferSize(0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
std::size_t _normBlocksize = blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0);
|
||||||
|
size_t nrBlocksToAlloc = nrBlocks * (_normBlocksize + 1);
|
||||||
|
BlockHeader* h = reinterpret_cast<BlockHeader*>(_buffer);
|
||||||
|
h->next = nullptr;
|
||||||
|
h->size = nrBlocksToAlloc;
|
||||||
|
_head = h;
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
_bufferSize = nrBlocksToAlloc;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// no copy nor move
|
||||||
|
Variable (const Variable&) = delete;
|
||||||
|
Variable& operator= (const Variable&) = delete;
|
||||||
|
|
||||||
|
void* malloc(size_t size) {
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
const std::lock_guard<std::mutex> lockGuard(_mutex);
|
||||||
|
#endif
|
||||||
|
if (size == 0) return nullptr;
|
||||||
|
|
||||||
|
size = (size / sizeof(BlockHeader) + (size % sizeof(BlockHeader) != 0)) + 1; // count by BlockHeader size, add 1 for header
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
std::cout << "malloc (raw) " << size << std::endl;
|
||||||
|
std::cout << "malloc (adj) " << size << " - ";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BlockHeader* currentBlock = _head;
|
||||||
|
BlockHeader* previousBlock = nullptr;
|
||||||
|
void* retVal = nullptr;
|
||||||
|
|
||||||
|
// iterate through linked free blocks
|
||||||
|
while (currentBlock) {
|
||||||
|
// consume whole block is size equals required size
|
||||||
|
if (currentBlock->size == size) {
|
||||||
|
if (previousBlock) previousBlock->next = currentBlock->next;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// split block if size is larger and add second part to list of free blocks
|
||||||
|
} else if (currentBlock->size > size) {
|
||||||
|
BlockHeader* newBlock = currentBlock + size;
|
||||||
|
if (previousBlock) previousBlock->next = newBlock;
|
||||||
|
newBlock->next = currentBlock->next;
|
||||||
|
newBlock->size = currentBlock->size - size;
|
||||||
|
currentBlock->next = newBlock;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previousBlock = currentBlock;
|
||||||
|
currentBlock = currentBlock->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentBlock) {
|
||||||
|
if (currentBlock == _head) {
|
||||||
|
_head = currentBlock->next;
|
||||||
|
}
|
||||||
|
currentBlock->size = size;
|
||||||
|
currentBlock->next = nullptr; // used when freeing memory
|
||||||
|
retVal = currentBlock + 1;
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
std::cout << "ok" << std::endl;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
std::cout << "nok" << std::endl;
|
||||||
|
#endif
|
||||||
|
(void)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* ptr) {
|
||||||
|
if (!ptr) return;
|
||||||
|
// check if ptr points to region in _buffer
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
std::cout << "free " << static_cast<void*>(reinterpret_cast<BlockHeader*>(ptr) - 1) << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
const std::lock_guard<std::mutex> lockGuard(_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BlockHeader* toFree = reinterpret_cast<BlockHeader*>(ptr) - 1;
|
||||||
|
BlockHeader* previous = reinterpret_cast<BlockHeader*>(_buffer);
|
||||||
|
BlockHeader* next = _head;
|
||||||
|
|
||||||
|
// toFree is the only free block
|
||||||
|
if (!next) {
|
||||||
|
_head = toFree;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (previous) {
|
||||||
|
if (!next || toFree < next) {
|
||||||
|
// 1. add block to linked list of free blocks
|
||||||
|
if (toFree < _head) {
|
||||||
|
toFree->next = _head;
|
||||||
|
_head = toFree;
|
||||||
|
} else {
|
||||||
|
previous->next = toFree;
|
||||||
|
toFree->next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. merge with previous if adjacent
|
||||||
|
if (toFree > _head && toFree == previous + previous->size) {
|
||||||
|
previous->size += toFree->size;
|
||||||
|
previous->next = toFree->next;
|
||||||
|
toFree = previous; // used in next check
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. merge with next if adjacent
|
||||||
|
if (toFree + toFree->size == next) {
|
||||||
|
toFree->size += next->size;
|
||||||
|
toFree->next = next->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previous = next;
|
||||||
|
next = next->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t freeMemory() {
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
const std::lock_guard<std::mutex> lockGuard(_mutex);
|
||||||
|
#endif
|
||||||
|
size_t retVal = 0;
|
||||||
|
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_head);
|
||||||
|
|
||||||
|
while (currentBlock) {
|
||||||
|
retVal += currentBlock->size - 1;
|
||||||
|
currentBlock = currentBlock->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal * sizeof(BlockHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t maxBlockSize() {
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
const std::lock_guard<std::mutex> lockGuard(_mutex);
|
||||||
|
#endif
|
||||||
|
size_t retVal = 0;
|
||||||
|
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_head);
|
||||||
|
|
||||||
|
while (currentBlock) {
|
||||||
|
retVal = (currentBlock->size - 1 > retVal) ? currentBlock->size - 1 : retVal;
|
||||||
|
currentBlock = currentBlock->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal * sizeof(BlockHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
void print() {
|
||||||
|
std::cout << "+--------------------" << std::endl;
|
||||||
|
std::cout << "|start:" << static_cast<void*>(_buffer) << std::endl;
|
||||||
|
std::cout << "|size:" << _bufferSize << std::endl;
|
||||||
|
std::cout << "|headersize:" << sizeof(BlockHeader) << std::endl;
|
||||||
|
std::cout << "|head: " << static_cast<void*>(_head) << std::endl;
|
||||||
|
BlockHeader* nextFreeBlock = _head;
|
||||||
|
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_buffer);
|
||||||
|
size_t blockNumber = 1;
|
||||||
|
while (currentBlock < reinterpret_cast<BlockHeader*>(_buffer) + _bufferSize) {
|
||||||
|
std::cout << "|" << blockNumber << ": " << static_cast<void*>(currentBlock) << std::endl;
|
||||||
|
std::cout << "| " << static_cast<void*>(currentBlock->next) << std::endl;
|
||||||
|
std::cout << "| " << currentBlock->size << std::endl;
|
||||||
|
if (currentBlock == nextFreeBlock) {
|
||||||
|
std::cout << "| free" << std::endl;
|
||||||
|
nextFreeBlock = nextFreeBlock->next;
|
||||||
|
} else {
|
||||||
|
std::cout << "| allocated" << std::endl;
|
||||||
|
}
|
||||||
|
++blockNumber;
|
||||||
|
currentBlock += currentBlock->size;
|
||||||
|
}
|
||||||
|
std::cout << "+--------------------" << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct BlockHeader {
|
||||||
|
BlockHeader* next;
|
||||||
|
std::size_t size;
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
pool size is aligned to sizeof(BlockHeader).
|
||||||
|
requested blocksize is therefore multiple of blockheader (rounded up)
|
||||||
|
total size = nr requested blocks * multiplier * blockheadersize
|
||||||
|
|
||||||
|
see constructor for calculation
|
||||||
|
*/
|
||||||
|
unsigned char _buffer[(nrBlocks * ((blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0)) + 1)) * sizeof(BlockHeader)];
|
||||||
|
BlockHeader* _head;
|
||||||
|
#if _GLIBCXX_HAS_GTHREADS
|
||||||
|
std::mutex _mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MEMPOL_DEBUG
|
||||||
|
std::size_t _bufferSize;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace MemoryPool
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -24,179 +24,178 @@ the LICENSE file.
|
|||||||
#include "Transport/Transport.h"
|
#include "Transport/Transport.h"
|
||||||
|
|
||||||
class MqttClient {
|
class MqttClient {
|
||||||
public:
|
public:
|
||||||
virtual ~MqttClient();
|
virtual ~MqttClient();
|
||||||
bool connected() const;
|
bool connected() const;
|
||||||
bool disconnected() const;
|
bool disconnected() const;
|
||||||
bool connect();
|
bool connect();
|
||||||
bool disconnect(bool force = false);
|
bool disconnect(bool force = false);
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
uint16_t subscribe(const char * topic, uint8_t qos, Args &&... args) {
|
uint16_t subscribe(const char* topic, uint8_t qos, Args&&... args) {
|
||||||
uint16_t packetId = _getNextPacketId();
|
uint16_t packetId = 0;
|
||||||
if (_state != State::connected) {
|
if (_state != State::connected) {
|
||||||
packetId = 0;
|
return packetId;
|
||||||
} else {
|
} else {
|
||||||
EMC_SEMAPHORE_TAKE();
|
EMC_SEMAPHORE_TAKE();
|
||||||
if (!_addPacket(packetId, topic, qos, std::forward<Args>(args)...)) {
|
packetId = _getNextPacketId();
|
||||||
emc_log_e("Could not create SUBSCRIBE packet");
|
if (!_addPacket(packetId, topic, qos, std::forward<Args>(args) ...)) {
|
||||||
packetId = 0;
|
emc_log_e("Could not create SUBSCRIBE packet");
|
||||||
}
|
packetId = 0;
|
||||||
EMC_SEMAPHORE_GIVE();
|
}
|
||||||
}
|
EMC_SEMAPHORE_GIVE();
|
||||||
return packetId;
|
|
||||||
}
|
}
|
||||||
template <typename... Args>
|
return packetId;
|
||||||
uint16_t unsubscribe(const char * topic, Args &&... args) {
|
}
|
||||||
uint16_t packetId = _getNextPacketId();
|
template <typename... Args>
|
||||||
if (_state != State::connected) {
|
uint16_t unsubscribe(const char* topic, Args&&... args) {
|
||||||
packetId = 0;
|
uint16_t packetId = 0;
|
||||||
} else {
|
if (_state != State::connected) {
|
||||||
EMC_SEMAPHORE_TAKE();
|
return packetId;
|
||||||
if (!_addPacket(packetId, topic, std::forward<Args>(args)...)) {
|
} else {
|
||||||
emc_log_e("Could not create UNSUBSCRIBE packet");
|
EMC_SEMAPHORE_TAKE();
|
||||||
packetId = 0;
|
packetId = _getNextPacketId();
|
||||||
}
|
if (!_addPacket(packetId, topic, std::forward<Args>(args) ...)) {
|
||||||
EMC_SEMAPHORE_GIVE();
|
emc_log_e("Could not create UNSUBSCRIBE packet");
|
||||||
}
|
packetId = 0;
|
||||||
return packetId;
|
}
|
||||||
|
EMC_SEMAPHORE_GIVE();
|
||||||
}
|
}
|
||||||
uint16_t publish(const char * topic, uint8_t qos, bool retain, const uint8_t * payload, size_t length);
|
return packetId;
|
||||||
uint16_t publish(const char * topic, uint8_t qos, bool retain, const char * payload);
|
}
|
||||||
uint16_t publish(const char * topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length);
|
uint16_t publish(const char* topic, uint8_t qos, bool retain, const uint8_t* payload, size_t length);
|
||||||
void clearQueue(bool deleteSessionData = false); // Not MQTT compliant and may cause unpredictable results when `deleteSessionData` = true!
|
uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload);
|
||||||
const char * getClientId() const;
|
uint16_t publish(const char* topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length);
|
||||||
size_t queueSize(); // No const because of mutex
|
void clearQueue(bool deleteSessionData = false); // Not MQTT compliant and may cause unpredictable results when `deleteSessionData` = true!
|
||||||
void loop();
|
const char* getClientId() const;
|
||||||
|
size_t queueSize(); // No const because of mutex
|
||||||
|
void loop();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
|
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
|
||||||
espMqttClientTypes::UseInternalTask _useInternalTask;
|
espMqttClientTypes::UseInternalTask _useInternalTask;
|
||||||
espMqttClientInternals::Transport * _transport;
|
espMqttClientInternals::Transport* _transport;
|
||||||
|
|
||||||
espMqttClientTypes::OnConnectCallback _onConnectCallback;
|
espMqttClientTypes::OnConnectCallback _onConnectCallback;
|
||||||
espMqttClientTypes::OnDisconnectCallback _onDisconnectCallback;
|
espMqttClientTypes::OnDisconnectCallback _onDisconnectCallback;
|
||||||
espMqttClientTypes::OnSubscribeCallback _onSubscribeCallback;
|
espMqttClientTypes::OnSubscribeCallback _onSubscribeCallback;
|
||||||
espMqttClientTypes::OnUnsubscribeCallback _onUnsubscribeCallback;
|
espMqttClientTypes::OnUnsubscribeCallback _onUnsubscribeCallback;
|
||||||
espMqttClientTypes::OnMessageCallback _onMessageCallback;
|
espMqttClientTypes::OnMessageCallback _onMessageCallback;
|
||||||
espMqttClientTypes::OnPublishCallback _onPublishCallback;
|
espMqttClientTypes::OnPublishCallback _onPublishCallback;
|
||||||
espMqttClientTypes::OnErrorCallback _onErrorCallback;
|
espMqttClientTypes::OnErrorCallback _onErrorCallback;
|
||||||
typedef void (*mqttClientHook)(void *);
|
typedef void(*mqttClientHook)(void*);
|
||||||
const char * _clientId;
|
const char* _clientId;
|
||||||
IPAddress _ip;
|
IPAddress _ip;
|
||||||
const char * _host;
|
const char* _host;
|
||||||
uint16_t _port;
|
uint16_t _port;
|
||||||
bool _useIp;
|
bool _useIp;
|
||||||
uint32_t _keepAlive;
|
uint32_t _keepAlive;
|
||||||
bool _cleanSession;
|
bool _cleanSession;
|
||||||
const char * _username;
|
const char* _username;
|
||||||
const char * _password;
|
const char* _password;
|
||||||
const char * _willTopic;
|
const char* _willTopic;
|
||||||
const uint8_t * _willPayload;
|
const uint8_t* _willPayload;
|
||||||
uint16_t _willPayloadLength;
|
uint16_t _willPayloadLength;
|
||||||
uint8_t _willQos;
|
uint8_t _willQos;
|
||||||
bool _willRetain;
|
bool _willRetain;
|
||||||
uint32_t _timeout;
|
uint32_t _timeout;
|
||||||
|
|
||||||
// state is protected to allow state changes by the transport system, defined in child classes
|
// state is protected to allow state changes by the transport system, defined in child classes
|
||||||
// eg. to allow AsyncTCP
|
// eg. to allow AsyncTCP
|
||||||
enum class State {
|
enum class State {
|
||||||
disconnected = 0,
|
disconnected = 0,
|
||||||
connectingTcp1 = 1,
|
connectingTcp1 = 1,
|
||||||
connectingTcp2 = 2,
|
connectingTcp2 = 2,
|
||||||
connectingMqtt = 3,
|
connectingMqtt = 3,
|
||||||
connected = 4,
|
connected = 4,
|
||||||
disconnectingMqtt1 = 5,
|
disconnectingMqtt1 = 5,
|
||||||
disconnectingMqtt2 = 6,
|
disconnectingMqtt2 = 6,
|
||||||
disconnectingTcp1 = 7,
|
disconnectingTcp1 = 7,
|
||||||
disconnectingTcp2 = 8
|
disconnectingTcp2 = 8
|
||||||
};
|
};
|
||||||
std::atomic<State> _state;
|
std::atomic<State> _state;
|
||||||
|
inline void _setState(State newState);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char _generatedClientId[EMC_CLIENTID_LENGTH];
|
char _generatedClientId[EMC_CLIENTID_LENGTH];
|
||||||
uint16_t _packetId;
|
uint16_t _packetId;
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
SemaphoreHandle_t _xSemaphore;
|
SemaphoreHandle_t _xSemaphore;
|
||||||
TaskHandle_t _taskHandle;
|
TaskHandle_t _taskHandle;
|
||||||
static void _loop(MqttClient * c);
|
static void _loop(MqttClient* c);
|
||||||
#elif defined(ARDUINO_ARCH_ESP8266) && EMC_ESP8266_MULTITHREADING
|
#elif defined(ARDUINO_ARCH_ESP8266) && EMC_ESP8266_MULTITHREADING
|
||||||
std::atomic<bool> _xSemaphore = false;
|
std::atomic<bool> _xSemaphore = false;
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
mutable std::mutex mtx; // modified by proddy for EMS-ESP compiling standalone
|
std::mutex mtx;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint8_t _rxBuffer[EMC_RX_BUFFER_SIZE];
|
uint8_t _rxBuffer[EMC_RX_BUFFER_SIZE];
|
||||||
struct OutgoingPacket {
|
struct OutgoingPacket {
|
||||||
uint32_t timeSent;
|
uint32_t timeSent;
|
||||||
espMqttClientInternals::Packet packet;
|
espMqttClientInternals::Packet packet;
|
||||||
template <typename... Args>
|
|
||||||
OutgoingPacket(uint32_t t, espMqttClientTypes::Error & error, Args &&... args)
|
|
||||||
: // NOLINT(runtime/references)
|
|
||||||
timeSent(t)
|
|
||||||
, packet(error, std::forward<Args>(args)...) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
espMqttClientInternals::Outbox<OutgoingPacket> _outbox;
|
|
||||||
size_t _bytesSent;
|
|
||||||
espMqttClientInternals::Parser _parser;
|
|
||||||
uint32_t _lastClientActivity;
|
|
||||||
uint32_t _lastServerActivity;
|
|
||||||
bool _pingSent;
|
|
||||||
espMqttClientTypes::DisconnectReason _disconnectReason;
|
|
||||||
|
|
||||||
uint16_t _getNextPacketId();
|
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
bool _addPacket(Args &&... args) {
|
OutgoingPacket(uint32_t t, espMqttClientTypes::Error& error, Args&&... args) : // NOLINT(runtime/references)
|
||||||
espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS);
|
timeSent(t),
|
||||||
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplace(0, error, std::forward<Args>(args)...);
|
packet(error, std::forward<Args>(args) ...) {}
|
||||||
if (it && error == espMqttClientTypes::Error::SUCCESS) {
|
};
|
||||||
return true;
|
espMqttClientInternals::Outbox<OutgoingPacket> _outbox;
|
||||||
} else {
|
size_t _bytesSent;
|
||||||
if (it)
|
espMqttClientInternals::Parser _parser;
|
||||||
_outbox.remove(it);
|
uint32_t _lastClientActivity;
|
||||||
return false;
|
uint32_t _lastServerActivity;
|
||||||
}
|
bool _pingSent;
|
||||||
|
espMqttClientTypes::DisconnectReason _disconnectReason;
|
||||||
|
|
||||||
|
uint16_t _getNextPacketId();
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
bool _addPacket(Args&&... args) {
|
||||||
|
espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS);
|
||||||
|
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplace(0, error, std::forward<Args>(args) ...);
|
||||||
|
if (it && error == espMqttClientTypes::Error::SUCCESS) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (it) _outbox.remove(it);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
bool _addPacketFront(Args &&... args) {
|
bool _addPacketFront(Args&&... args) {
|
||||||
espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS);
|
espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS);
|
||||||
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplaceFront(0, error, std::forward<Args>(args)...);
|
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplaceFront(0, error, std::forward<Args>(args) ...);
|
||||||
if (it && error == espMqttClientTypes::Error::SUCCESS) {
|
if (it && error == espMqttClientTypes::Error::SUCCESS) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (it)
|
if (it) _outbox.remove(it);
|
||||||
_outbox.remove(it);
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _checkOutbox();
|
void _checkOutbox();
|
||||||
int _sendPacket();
|
int _sendPacket();
|
||||||
bool _advanceOutbox();
|
bool _advanceOutbox();
|
||||||
void _checkIncoming();
|
void _checkIncoming();
|
||||||
void _checkPing();
|
void _checkPing();
|
||||||
void _checkTimeout();
|
void _checkTimeout();
|
||||||
|
|
||||||
void _onConnack();
|
void _onConnack();
|
||||||
void _onPublish();
|
void _onPublish();
|
||||||
void _onPuback();
|
void _onPuback();
|
||||||
void _onPubrec();
|
void _onPubrec();
|
||||||
void _onPubrel();
|
void _onPubrel();
|
||||||
void _onPubcomp();
|
void _onPubcomp();
|
||||||
void _onSuback();
|
void _onSuback();
|
||||||
void _onUnsuback();
|
void _onUnsuback();
|
||||||
|
|
||||||
void _clearQueue(int clearData); // 0: keep session,
|
void _clearQueue(int clearData); // 0: keep session,
|
||||||
// 1: keep only PUBLISH qos > 0
|
// 1: keep only PUBLISH qos > 0
|
||||||
// 2: delete all
|
// 2: delete all
|
||||||
void _onError(uint16_t packetId, espMqttClientTypes::Error error);
|
void _onError(uint16_t packetId, espMqttClientTypes::Error error);
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
|
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
|
||||||
size_t _highWaterMark;
|
size_t _highWaterMark;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,217 +1,255 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (c) 2022 Bert Melis. All rights reserved.
|
Copyright (c) 2022 Bert Melis. All rights reserved.
|
||||||
|
|
||||||
This work is licensed under the terms of the MIT license.
|
This work is licensed under the terms of the MIT license.
|
||||||
For a copy, see <https://opensource.org/licenses/MIT> or
|
For a copy, see <https://opensource.org/licenses/MIT> or
|
||||||
the LICENSE file.
|
the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <new> // new (std::nothrow)
|
#if EMC_USE_MEMPOOL
|
||||||
#include <utility> // std::forward
|
#include "MemoryPool/src/MemoryPool.h"
|
||||||
|
#include "Config.h"
|
||||||
namespace espMqttClientInternals {
|
#else
|
||||||
|
#include <new> // new (std::nothrow)
|
||||||
/**
|
#endif
|
||||||
* @brief Singly linked queue with builtin non-invalidating forward iterator
|
#include <utility> // std::forward
|
||||||
*
|
|
||||||
* Queue items can only be emplaced, at front and back of the queue.
|
namespace espMqttClientInternals {
|
||||||
* Remove items using an iterator or the builtin iterator.
|
|
||||||
*/
|
/**
|
||||||
|
* @brief Singly linked queue with builtin non-invalidating forward iterator
|
||||||
template <typename T>
|
*
|
||||||
class Outbox {
|
* Queue items can only be emplaced, at front and back of the queue.
|
||||||
public:
|
* Remove items using an iterator or the builtin iterator.
|
||||||
Outbox()
|
*/
|
||||||
: _first(nullptr)
|
|
||||||
, _last(nullptr)
|
template <typename T>
|
||||||
, _current(nullptr)
|
class Outbox {
|
||||||
, _prev(nullptr) {}
|
public:
|
||||||
~Outbox() {
|
Outbox()
|
||||||
while (_first) {
|
: _first(nullptr)
|
||||||
Node* n = _first->next;
|
, _last(nullptr)
|
||||||
delete _first;
|
, _current(nullptr)
|
||||||
_first = n;
|
, _prev(nullptr)
|
||||||
}
|
#if EMC_USE_MEMPOOL
|
||||||
}
|
, _memPool()
|
||||||
|
#endif
|
||||||
struct Node {
|
{}
|
||||||
public:
|
~Outbox() {
|
||||||
template <typename... Args>
|
while (_first) {
|
||||||
explicit Node(Args&&... args)
|
Node* n = _first->next;
|
||||||
: data(std::forward<Args>(args) ...)
|
#if EMC_USE_MEMPOOL
|
||||||
, next(nullptr) {
|
_first->~Node();
|
||||||
// empty
|
_memPool.free(_first);
|
||||||
}
|
#else
|
||||||
|
delete _first;
|
||||||
T data;
|
#endif
|
||||||
Node* next;
|
_first = n;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
class Iterator {
|
|
||||||
friend class Outbox;
|
struct Node {
|
||||||
public:
|
public:
|
||||||
void operator++() {
|
template <typename... Args>
|
||||||
if (_node) {
|
explicit Node(Args&&... args)
|
||||||
_prev = _node;
|
: data(std::forward<Args>(args) ...)
|
||||||
_node = _node->next;
|
, next(nullptr) {
|
||||||
}
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() const {
|
T data;
|
||||||
if (_node) return true;
|
Node* next;
|
||||||
return false;
|
};
|
||||||
}
|
|
||||||
|
class Iterator {
|
||||||
T* get() const {
|
friend class Outbox;
|
||||||
if (_node) return &(_node->data);
|
public:
|
||||||
return nullptr;
|
void operator++() {
|
||||||
}
|
if (_node) {
|
||||||
|
_prev = _node;
|
||||||
private:
|
_node = _node->next;
|
||||||
Node* _node = nullptr;
|
}
|
||||||
Node* _prev = nullptr;
|
}
|
||||||
};
|
|
||||||
|
explicit operator bool() const {
|
||||||
// add node to back, advance current to new if applicable
|
if (_node) return true;
|
||||||
template <class... Args>
|
return false;
|
||||||
Iterator emplace(Args&&... args) {
|
}
|
||||||
Iterator it;
|
|
||||||
Node* node = new (std::nothrow) Node(std::forward<Args>(args) ...);
|
T* get() const {
|
||||||
if (node != nullptr) {
|
if (_node) return &(_node->data);
|
||||||
if (!_first) {
|
return nullptr;
|
||||||
// queue is empty
|
}
|
||||||
_first = _current = node;
|
|
||||||
} else {
|
private:
|
||||||
// queue has at least one item
|
Node* _node = nullptr;
|
||||||
_last->next = node;
|
Node* _prev = nullptr;
|
||||||
it._prev = _last;
|
};
|
||||||
}
|
|
||||||
_last = node;
|
// add node to back, advance current to new if applicable
|
||||||
it._node = node;
|
template <class... Args>
|
||||||
// point current to newly created if applicable
|
Iterator emplace(Args&&... args) {
|
||||||
if (!_current) {
|
Iterator it;
|
||||||
_current = _last;
|
#if EMC_USE_MEMPOOL
|
||||||
}
|
void* buf = _memPool.malloc();
|
||||||
}
|
Node* node = nullptr;
|
||||||
return it;
|
if (buf) {
|
||||||
}
|
node = new(buf) Node(std::forward<Args>(args) ...);
|
||||||
|
}
|
||||||
// add item to front, current points to newly created front.
|
#else
|
||||||
template <class... Args>
|
Node* node = new(std::nothrow) Node(std::forward<Args>(args) ...);
|
||||||
Iterator emplaceFront(Args&&... args) {
|
#endif
|
||||||
Iterator it;
|
if (node != nullptr) {
|
||||||
Node* node = new (std::nothrow) Node(std::forward<Args>(args) ...);
|
if (!_first) {
|
||||||
if (node != nullptr) {
|
// queue is empty
|
||||||
if (!_first) {
|
_first = _current = node;
|
||||||
// queue is empty
|
} else {
|
||||||
_last = node;
|
// queue has at least one item
|
||||||
} else {
|
_last->next = node;
|
||||||
// queue has at least one item
|
it._prev = _last;
|
||||||
node->next = _first;
|
}
|
||||||
}
|
_last = node;
|
||||||
_current = _first = node;
|
it._node = node;
|
||||||
_prev = nullptr;
|
// point current to newly created if applicable
|
||||||
it._node = node;
|
if (!_current) {
|
||||||
}
|
_current = _last;
|
||||||
return it;
|
}
|
||||||
}
|
}
|
||||||
|
return it;
|
||||||
// remove node at iterator, iterator points to next
|
}
|
||||||
void remove(Iterator& it) { // NOLINT(runtime/references)
|
|
||||||
if (!it) return;
|
// add item to front, current points to newly created front.
|
||||||
Node* node = it._node;
|
template <class... Args>
|
||||||
Node* prev = it._prev;
|
Iterator emplaceFront(Args&&... args) {
|
||||||
++it;
|
Iterator it;
|
||||||
_remove(prev, node);
|
#if EMC_USE_MEMPOOL
|
||||||
}
|
void* buf = _memPool.malloc();
|
||||||
|
Node* node = nullptr;
|
||||||
// remove current node, current points to next
|
if (buf) {
|
||||||
void removeCurrent() {
|
node = new(buf) Node(std::forward<Args>(args) ...);
|
||||||
_remove(_prev, _current);
|
}
|
||||||
}
|
#else
|
||||||
|
Node* node = new(std::nothrow) Node(std::forward<Args>(args) ...);
|
||||||
// Get current item or return nullptr
|
#endif
|
||||||
T* getCurrent() const {
|
if (node != nullptr) {
|
||||||
if (_current) return &(_current->data);
|
if (!_first) {
|
||||||
return nullptr;
|
// queue is empty
|
||||||
}
|
_last = node;
|
||||||
|
} else {
|
||||||
void resetCurrent() {
|
// queue has at least one item
|
||||||
_current = _first;
|
node->next = _first;
|
||||||
}
|
}
|
||||||
|
_current = _first = node;
|
||||||
Iterator front() const {
|
_prev = nullptr;
|
||||||
Iterator it;
|
it._node = node;
|
||||||
it._node = _first;
|
}
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance current item
|
// remove node at iterator, iterator points to next
|
||||||
void next() {
|
void remove(Iterator& it) { // NOLINT(runtime/references)
|
||||||
if (_current) {
|
if (!it) return;
|
||||||
_prev = _current;
|
Node* node = it._node;
|
||||||
_current = _current->next;
|
Node* prev = it._prev;
|
||||||
}
|
++it;
|
||||||
}
|
_remove(prev, node);
|
||||||
|
}
|
||||||
// Outbox is empty
|
|
||||||
bool empty() {
|
// remove current node, current points to next
|
||||||
if (!_first) return true;
|
void removeCurrent() {
|
||||||
return false;
|
_remove(_prev, _current);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const {
|
// Get current item or return nullptr
|
||||||
Node* n = _first;
|
T* getCurrent() const {
|
||||||
size_t count = 0;
|
if (_current) return &(_current->data);
|
||||||
while (n) {
|
return nullptr;
|
||||||
n = n->next;
|
}
|
||||||
++count;
|
|
||||||
}
|
void resetCurrent() {
|
||||||
return count;
|
_current = _first;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
Iterator front() const {
|
||||||
Node* _first;
|
Iterator it;
|
||||||
Node* _last;
|
it._node = _first;
|
||||||
Node* _current;
|
return it;
|
||||||
Node* _prev; // element just before _current
|
}
|
||||||
|
|
||||||
void _remove(Node* prev, Node* node) {
|
// Advance current item
|
||||||
if (!node) return;
|
void next() {
|
||||||
|
if (_current) {
|
||||||
// set current to next, node->next may be nullptr
|
_prev = _current;
|
||||||
if (_current == node) {
|
_current = _current->next;
|
||||||
_current = node->next;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_prev == node) {
|
// Outbox is empty
|
||||||
_prev = prev;
|
bool empty() {
|
||||||
}
|
if (!_first) return true;
|
||||||
|
return false;
|
||||||
// only one element in outbox
|
}
|
||||||
if (_first == _last) {
|
|
||||||
_first = _last = nullptr;
|
size_t size() const {
|
||||||
|
Node* n = _first;
|
||||||
// delete first el in longer outbox
|
size_t count = 0;
|
||||||
} else if (_first == node) {
|
while (n) {
|
||||||
_first = node->next;
|
n = n->next;
|
||||||
|
++count;
|
||||||
// delete last in longer outbox
|
}
|
||||||
} else if (_last == node) {
|
return count;
|
||||||
_last = prev;
|
}
|
||||||
_last->next = nullptr;
|
|
||||||
|
private:
|
||||||
// delete somewhere in the middle
|
Node* _first;
|
||||||
} else {
|
Node* _last;
|
||||||
prev->next = node->next;
|
Node* _current;
|
||||||
}
|
Node* _prev; // element just before _current
|
||||||
|
#if EMC_USE_MEMPOOL
|
||||||
// finally, delete the node
|
MemoryPool::Fixed<EMC_NUM_POOL_ELEMENTS, sizeof(Node)> _memPool;
|
||||||
delete node;
|
#endif
|
||||||
}
|
|
||||||
};
|
void _remove(Node* prev, Node* node) {
|
||||||
|
if (!node) return;
|
||||||
} // end namespace espMqttClientInternals
|
|
||||||
|
// set current to next, node->next may be nullptr
|
||||||
|
if (_current == node) {
|
||||||
|
_current = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_prev == node) {
|
||||||
|
_prev = prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only one element in outbox
|
||||||
|
if (_first == _last) {
|
||||||
|
_first = _last = nullptr;
|
||||||
|
|
||||||
|
// delete first el in longer outbox
|
||||||
|
} else if (_first == node) {
|
||||||
|
_first = node->next;
|
||||||
|
|
||||||
|
// delete last in longer outbox
|
||||||
|
} else if (_last == node) {
|
||||||
|
_last = prev;
|
||||||
|
_last->next = nullptr;
|
||||||
|
|
||||||
|
// delete somewhere in the middle
|
||||||
|
} else {
|
||||||
|
prev->next = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, delete the node
|
||||||
|
#if EMC_USE_MEMPOOL
|
||||||
|
node->~Node();
|
||||||
|
_memPool.free(node);
|
||||||
|
#else
|
||||||
|
delete node;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace espMqttClientInternals
|
||||||
|
|||||||
@@ -10,427 +10,445 @@ the LICENSE file.
|
|||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
|
#if EMC_USE_MEMPOOL
|
||||||
|
MemoryPool::Variable<EMC_NUM_POOL_ELEMENTS, EMC_SIZE_POOL_ELEMENTS> Packet::_memPool;
|
||||||
|
#endif
|
||||||
|
|
||||||
Packet::~Packet() {
|
Packet::~Packet() {
|
||||||
free(_data);
|
#if EMC_USE_MEMPOOL
|
||||||
|
_memPool.free(_data);
|
||||||
|
#else
|
||||||
|
free(_data);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Packet::available(size_t index) {
|
size_t Packet::available(size_t index) {
|
||||||
if (index >= _size)
|
if (index >= _size) return 0;
|
||||||
return 0;
|
if (!_getPayload) return _size - index;
|
||||||
if (!_getPayload)
|
return _chunkedAvailable(index);
|
||||||
return _size - index;
|
|
||||||
return _chunkedAvailable(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t * Packet::data(size_t index) const {
|
const uint8_t* Packet::data(size_t index) const {
|
||||||
if (!_getPayload) {
|
if (!_getPayload) {
|
||||||
if (!_data)
|
if (!_data) return nullptr;
|
||||||
return nullptr;
|
if (index >= _size) return nullptr;
|
||||||
if (index >= _size)
|
return &_data[index];
|
||||||
return nullptr;
|
}
|
||||||
return &_data[index];
|
return _chunkedData(index);
|
||||||
}
|
|
||||||
return _chunkedData(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Packet::size() const {
|
size_t Packet::size() const {
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Packet::setDup() {
|
void Packet::setDup() {
|
||||||
if (!_data)
|
if (!_data) return;
|
||||||
return;
|
if (packetType() != PacketType.PUBLISH) return;
|
||||||
if (packetType() != PacketType.PUBLISH)
|
if (_packetId == 0) return;
|
||||||
return;
|
_data[0] |= 0x08;
|
||||||
if (_packetId == 0)
|
|
||||||
return;
|
|
||||||
_data[0] |= 0x08;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Packet::packetId() const {
|
uint16_t Packet::packetId() const {
|
||||||
return _packetId;
|
return _packetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
MQTTPacketType Packet::packetType() const {
|
MQTTPacketType Packet::packetType() const {
|
||||||
if (_data)
|
if (_data) return static_cast<MQTTPacketType>(_data[0] & 0xF0);
|
||||||
return static_cast<MQTTPacketType>(_data[0] & 0xF0);
|
return static_cast<MQTTPacketType>(0);
|
||||||
return static_cast<MQTTPacketType>(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Packet::removable() const {
|
bool Packet::removable() const {
|
||||||
if (_packetId == 0)
|
if (_packetId == 0) return true;
|
||||||
return true;
|
if ((packetType() == PacketType.PUBACK) || (packetType() == PacketType.PUBCOMP)) return true;
|
||||||
if ((packetType() == PacketType.PUBACK) || (packetType() == PacketType.PUBCOMP))
|
return false;
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet::Packet(espMqttClientTypes::Error & error,
|
Packet::Packet(espMqttClientTypes::Error& error,
|
||||||
bool cleanSession,
|
bool cleanSession,
|
||||||
const char * username,
|
const char* username,
|
||||||
const char * password,
|
const char* password,
|
||||||
const char * willTopic,
|
const char* willTopic,
|
||||||
bool willRetain,
|
bool willRetain,
|
||||||
uint8_t willQos,
|
uint8_t willQos,
|
||||||
const uint8_t * willPayload,
|
const uint8_t* willPayload,
|
||||||
uint16_t willPayloadLength,
|
uint16_t willPayloadLength,
|
||||||
uint16_t keepAlive,
|
uint16_t keepAlive,
|
||||||
const char * clientId)
|
const char* clientId)
|
||||||
: _packetId(0)
|
: _packetId(0)
|
||||||
, _data(nullptr)
|
, _data(nullptr)
|
||||||
, _size(0)
|
, _size(0)
|
||||||
, _payloadIndex(0)
|
, _payloadIndex(0)
|
||||||
, _payloadStartIndex(0)
|
, _payloadStartIndex(0)
|
||||||
, _payloadEndIndex(0)
|
, _payloadEndIndex(0)
|
||||||
, _getPayload(nullptr) {
|
, _getPayload(nullptr) {
|
||||||
if (willPayload && willPayloadLength == 0) {
|
if (willPayload && willPayloadLength == 0) {
|
||||||
size_t length = strlen(reinterpret_cast<const char *>(willPayload));
|
size_t length = strlen(reinterpret_cast<const char*>(willPayload));
|
||||||
if (length > UINT16_MAX) {
|
if (length > UINT16_MAX) {
|
||||||
emc_log_w("Payload length truncated (l:%zu)", length);
|
emc_log_w("Payload length truncated (l:%zu)", length);
|
||||||
willPayloadLength = UINT16_MAX;
|
willPayloadLength = UINT16_MAX;
|
||||||
} else {
|
|
||||||
willPayloadLength = length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!clientId || strlen(clientId) == 0) {
|
|
||||||
emc_log_w("clientId not set error");
|
|
||||||
error = espMqttClientTypes::Error::MALFORMED_PARAMETER;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate size
|
|
||||||
size_t remainingLength = 6 + // protocol
|
|
||||||
1 + // protocol level
|
|
||||||
1 + // connect flags
|
|
||||||
2 + // keepalive
|
|
||||||
2 + strlen(clientId) + (willTopic ? 2 + strlen(willTopic) + 2 + willPayloadLength : 0) + (username ? 2 + strlen(username) : 0)
|
|
||||||
+ (password ? 2 + strlen(password) : 0);
|
|
||||||
|
|
||||||
// allocate memory
|
|
||||||
if (!_allocate(remainingLength, false)) {
|
|
||||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// serialize
|
|
||||||
size_t pos = 0;
|
|
||||||
|
|
||||||
// FIXED HEADER
|
|
||||||
_data[pos++] = PacketType.CONNECT | HeaderFlag.CONNECT_RESERVED;
|
|
||||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
|
||||||
pos += encodeString(PROTOCOL, &_data[pos]);
|
|
||||||
_data[pos++] = PROTOCOL_LEVEL;
|
|
||||||
uint8_t connectFlags = 0;
|
|
||||||
if (cleanSession)
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.CLEAN_SESSION;
|
|
||||||
if (username != nullptr)
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.USERNAME;
|
|
||||||
if (password != nullptr)
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.PASSWORD;
|
|
||||||
if (willTopic != nullptr) {
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL;
|
|
||||||
if (willRetain)
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_RETAIN;
|
|
||||||
switch (willQos) {
|
|
||||||
case 0:
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS0;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_data[pos++] = connectFlags;
|
|
||||||
_data[pos++] = keepAlive >> 8;
|
|
||||||
_data[pos++] = keepAlive & 0xFF;
|
|
||||||
|
|
||||||
// PAYLOAD
|
|
||||||
// client ID
|
|
||||||
pos += encodeString(clientId, &_data[pos]);
|
|
||||||
// will
|
|
||||||
if (willTopic != nullptr && willPayload != nullptr) {
|
|
||||||
pos += encodeString(willTopic, &_data[pos]);
|
|
||||||
_data[pos++] = willPayloadLength >> 8;
|
|
||||||
_data[pos++] = willPayloadLength & 0xFF;
|
|
||||||
memcpy(&_data[pos], willPayload, willPayloadLength);
|
|
||||||
pos += willPayloadLength;
|
|
||||||
}
|
|
||||||
// credentials
|
|
||||||
if (username != nullptr)
|
|
||||||
pos += encodeString(username, &_data[pos]);
|
|
||||||
if (password != nullptr)
|
|
||||||
encodeString(password, &_data[pos]);
|
|
||||||
|
|
||||||
error = espMqttClientTypes::Error::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic, const uint8_t * payload, size_t payloadLength, uint8_t qos, bool retain)
|
|
||||||
: _packetId(packetId)
|
|
||||||
, _data(nullptr)
|
|
||||||
, _size(0)
|
|
||||||
, _payloadIndex(0)
|
|
||||||
, _payloadStartIndex(0)
|
|
||||||
, _payloadEndIndex(0)
|
|
||||||
, _getPayload(nullptr) {
|
|
||||||
size_t remainingLength = 2 + strlen(topic) + // topic length + topic
|
|
||||||
2 + // packet ID
|
|
||||||
payloadLength;
|
|
||||||
|
|
||||||
if (qos == 0) {
|
|
||||||
remainingLength -= 2;
|
|
||||||
_packetId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_allocate(remainingLength)) {
|
|
||||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
|
||||||
|
|
||||||
// PAYLOAD
|
|
||||||
memcpy(&_data[pos], payload, payloadLength);
|
|
||||||
|
|
||||||
error = espMqttClientTypes::Error::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet::Packet(espMqttClientTypes::Error & error,
|
|
||||||
uint16_t packetId,
|
|
||||||
const char * topic,
|
|
||||||
espMqttClientTypes::PayloadCallback payloadCallback,
|
|
||||||
size_t payloadLength,
|
|
||||||
uint8_t qos,
|
|
||||||
bool retain)
|
|
||||||
: _packetId(packetId)
|
|
||||||
, _data(nullptr)
|
|
||||||
, _size(0)
|
|
||||||
, _payloadIndex(0)
|
|
||||||
, _payloadStartIndex(0)
|
|
||||||
, _payloadEndIndex(0)
|
|
||||||
, _getPayload(payloadCallback) {
|
|
||||||
size_t remainingLength = 2 + strlen(topic) + // topic length + topic
|
|
||||||
2 + // packet ID
|
|
||||||
payloadLength;
|
|
||||||
|
|
||||||
if (qos == 0) {
|
|
||||||
remainingLength -= 2;
|
|
||||||
_packetId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_allocate(remainingLength - payloadLength + std::min(payloadLength, static_cast<size_t>(EMC_RX_BUFFER_SIZE)))) {
|
|
||||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
|
||||||
|
|
||||||
// payload will be added by 'Packet::available'
|
|
||||||
_size = pos + payloadLength;
|
|
||||||
_payloadIndex = pos;
|
|
||||||
_payloadStartIndex = _payloadIndex;
|
|
||||||
_payloadEndIndex = _payloadIndex;
|
|
||||||
|
|
||||||
error = espMqttClientTypes::Error::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic, uint8_t qos)
|
|
||||||
: _packetId(packetId)
|
|
||||||
, _data(nullptr)
|
|
||||||
, _size(0)
|
|
||||||
, _payloadIndex(0)
|
|
||||||
, _payloadStartIndex(0)
|
|
||||||
, _payloadEndIndex(0)
|
|
||||||
, _getPayload(nullptr) {
|
|
||||||
SubscribeItem list[1] = {{topic, qos}};
|
|
||||||
_createSubscribe(error, list, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet::Packet(espMqttClientTypes::Error & error, MQTTPacketType type, uint16_t packetId)
|
|
||||||
: _packetId(packetId)
|
|
||||||
, _data(nullptr)
|
|
||||||
, _size(0)
|
|
||||||
, _payloadIndex(0)
|
|
||||||
, _payloadStartIndex(0)
|
|
||||||
, _payloadEndIndex(0)
|
|
||||||
, _getPayload(nullptr) {
|
|
||||||
if (!_allocate(2)) {
|
|
||||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = 0;
|
|
||||||
_data[pos] = type;
|
|
||||||
if (type == PacketType.PUBREL) {
|
|
||||||
_data[pos++] |= HeaderFlag.PUBREL_RESERVED;
|
|
||||||
} else {
|
} else {
|
||||||
pos++;
|
willPayloadLength = length;
|
||||||
}
|
}
|
||||||
pos += encodeRemainingLength(2, &_data[pos]);
|
}
|
||||||
_data[pos++] = packetId >> 8;
|
if (!clientId || strlen(clientId) == 0) {
|
||||||
_data[pos] = packetId & 0xFF;
|
emc_log_w("clientId not set error");
|
||||||
|
error = espMqttClientTypes::Error::MALFORMED_PARAMETER;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
error = espMqttClientTypes::Error::SUCCESS;
|
// Calculate size
|
||||||
|
size_t remainingLength =
|
||||||
|
6 + // protocol
|
||||||
|
1 + // protocol level
|
||||||
|
1 + // connect flags
|
||||||
|
2 + // keepalive
|
||||||
|
2 + strlen(clientId) +
|
||||||
|
(willTopic ? 2 + strlen(willTopic) + 2 + willPayloadLength : 0) +
|
||||||
|
(username ? 2 + strlen(username) : 0) +
|
||||||
|
(password ? 2 + strlen(password) : 0);
|
||||||
|
|
||||||
|
// allocate memory
|
||||||
|
if (!_allocate(remainingLength, false)) {
|
||||||
|
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialize
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
// FIXED HEADER
|
||||||
|
_data[pos++] = PacketType.CONNECT | HeaderFlag.CONNECT_RESERVED;
|
||||||
|
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||||
|
pos += encodeString(PROTOCOL, &_data[pos]);
|
||||||
|
_data[pos++] = PROTOCOL_LEVEL;
|
||||||
|
uint8_t connectFlags = 0;
|
||||||
|
if (cleanSession) connectFlags |= espMqttClientInternals::ConnectFlag.CLEAN_SESSION;
|
||||||
|
if (username != nullptr) connectFlags |= espMqttClientInternals::ConnectFlag.USERNAME;
|
||||||
|
if (password != nullptr) connectFlags |= espMqttClientInternals::ConnectFlag.PASSWORD;
|
||||||
|
if (willTopic != nullptr) {
|
||||||
|
connectFlags |= espMqttClientInternals::ConnectFlag.WILL;
|
||||||
|
if (willRetain) connectFlags |= espMqttClientInternals::ConnectFlag.WILL_RETAIN;
|
||||||
|
switch (willQos) {
|
||||||
|
case 0:
|
||||||
|
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_data[pos++] = connectFlags;
|
||||||
|
_data[pos++] = keepAlive >> 8;
|
||||||
|
_data[pos++] = keepAlive & 0xFF;
|
||||||
|
|
||||||
|
// PAYLOAD
|
||||||
|
// client ID
|
||||||
|
pos += encodeString(clientId, &_data[pos]);
|
||||||
|
// will
|
||||||
|
if (willTopic != nullptr && willPayload != nullptr) {
|
||||||
|
pos += encodeString(willTopic, &_data[pos]);
|
||||||
|
_data[pos++] = willPayloadLength >> 8;
|
||||||
|
_data[pos++] = willPayloadLength & 0xFF;
|
||||||
|
memcpy(&_data[pos], willPayload, willPayloadLength);
|
||||||
|
pos += willPayloadLength;
|
||||||
|
}
|
||||||
|
// credentials
|
||||||
|
if (username != nullptr) pos += encodeString(username, &_data[pos]);
|
||||||
|
if (password != nullptr) encodeString(password, &_data[pos]);
|
||||||
|
|
||||||
|
error = espMqttClientTypes::Error::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic)
|
Packet::Packet(espMqttClientTypes::Error& error,
|
||||||
: _packetId(packetId)
|
uint16_t packetId,
|
||||||
, _data(nullptr)
|
const char* topic,
|
||||||
, _size(0)
|
const uint8_t* payload,
|
||||||
, _payloadIndex(0)
|
size_t payloadLength,
|
||||||
, _payloadStartIndex(0)
|
uint8_t qos,
|
||||||
, _payloadEndIndex(0)
|
bool retain)
|
||||||
, _getPayload(nullptr) {
|
: _packetId(packetId)
|
||||||
const char * list[1] = {topic};
|
, _data(nullptr)
|
||||||
_createUnsubscribe(error, list, 1);
|
, _size(0)
|
||||||
|
, _payloadIndex(0)
|
||||||
|
, _payloadStartIndex(0)
|
||||||
|
, _payloadEndIndex(0)
|
||||||
|
, _getPayload(nullptr) {
|
||||||
|
size_t remainingLength =
|
||||||
|
2 + strlen(topic) + // topic length + topic
|
||||||
|
2 + // packet ID
|
||||||
|
payloadLength;
|
||||||
|
|
||||||
|
if (qos == 0) {
|
||||||
|
remainingLength -= 2;
|
||||||
|
_packetId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_allocate(remainingLength, true)) {
|
||||||
|
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
||||||
|
|
||||||
|
// PAYLOAD
|
||||||
|
memcpy(&_data[pos], payload, payloadLength);
|
||||||
|
|
||||||
|
error = espMqttClientTypes::Error::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet::Packet(espMqttClientTypes::Error & error, MQTTPacketType type)
|
Packet::Packet(espMqttClientTypes::Error& error,
|
||||||
: _packetId(0)
|
uint16_t packetId,
|
||||||
, _data(nullptr)
|
const char* topic,
|
||||||
, _size(0)
|
espMqttClientTypes::PayloadCallback payloadCallback,
|
||||||
, _payloadIndex(0)
|
size_t payloadLength,
|
||||||
, _payloadStartIndex(0)
|
uint8_t qos,
|
||||||
, _payloadEndIndex(0)
|
bool retain)
|
||||||
, _getPayload(nullptr) {
|
: _packetId(packetId)
|
||||||
if (!_allocate(0)) {
|
, _data(nullptr)
|
||||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
, _size(0)
|
||||||
return;
|
, _payloadIndex(0)
|
||||||
}
|
, _payloadStartIndex(0)
|
||||||
_data[0] |= type;
|
, _payloadEndIndex(0)
|
||||||
|
, _getPayload(payloadCallback) {
|
||||||
|
size_t remainingLength =
|
||||||
|
2 + strlen(topic) + // topic length + topic
|
||||||
|
2 + // packet ID
|
||||||
|
payloadLength;
|
||||||
|
|
||||||
error = espMqttClientTypes::Error::SUCCESS;
|
if (qos == 0) {
|
||||||
|
remainingLength -= 2;
|
||||||
|
_packetId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_allocate(remainingLength - payloadLength + std::min(payloadLength, static_cast<size_t>(EMC_RX_BUFFER_SIZE)), true)) {
|
||||||
|
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
||||||
|
|
||||||
|
// payload will be added by 'Packet::available'
|
||||||
|
_size = pos + payloadLength;
|
||||||
|
_payloadIndex = pos;
|
||||||
|
_payloadStartIndex = _payloadIndex;
|
||||||
|
_payloadEndIndex = _payloadIndex;
|
||||||
|
|
||||||
|
error = espMqttClientTypes::Error::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(espMqttClientTypes::Error& error, uint16_t packetId, const char* topic, uint8_t qos)
|
||||||
|
: _packetId(packetId)
|
||||||
|
, _data(nullptr)
|
||||||
|
, _size(0)
|
||||||
|
, _payloadIndex(0)
|
||||||
|
, _payloadStartIndex(0)
|
||||||
|
, _payloadEndIndex(0)
|
||||||
|
, _getPayload(nullptr) {
|
||||||
|
SubscribeItem list[1] = {topic, qos};
|
||||||
|
_createSubscribe(error, list, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(espMqttClientTypes::Error& error, MQTTPacketType type, uint16_t packetId)
|
||||||
|
: _packetId(packetId)
|
||||||
|
, _data(nullptr)
|
||||||
|
, _size(0)
|
||||||
|
, _payloadIndex(0)
|
||||||
|
, _payloadStartIndex(0)
|
||||||
|
, _payloadEndIndex(0)
|
||||||
|
, _getPayload(nullptr) {
|
||||||
|
if (!_allocate(2, true)) {
|
||||||
|
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
_data[pos] = type;
|
||||||
|
if (type == PacketType.PUBREL) {
|
||||||
|
_data[pos++] |= HeaderFlag.PUBREL_RESERVED;
|
||||||
|
} else {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
pos += encodeRemainingLength(2, &_data[pos]);
|
||||||
|
_data[pos++] = packetId >> 8;
|
||||||
|
_data[pos] = packetId & 0xFF;
|
||||||
|
|
||||||
|
error = espMqttClientTypes::Error::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(espMqttClientTypes::Error& error, uint16_t packetId, const char* topic)
|
||||||
|
: _packetId(packetId)
|
||||||
|
, _data(nullptr)
|
||||||
|
, _size(0)
|
||||||
|
, _payloadIndex(0)
|
||||||
|
, _payloadStartIndex(0)
|
||||||
|
, _payloadEndIndex(0)
|
||||||
|
, _getPayload(nullptr) {
|
||||||
|
const char* list[1] = {topic};
|
||||||
|
_createUnsubscribe(error, list, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(espMqttClientTypes::Error& error, MQTTPacketType type)
|
||||||
|
: _packetId(0)
|
||||||
|
, _data(nullptr)
|
||||||
|
, _size(0)
|
||||||
|
, _payloadIndex(0)
|
||||||
|
, _payloadStartIndex(0)
|
||||||
|
, _payloadEndIndex(0)
|
||||||
|
, _getPayload(nullptr) {
|
||||||
|
if (!_allocate(0, true)) {
|
||||||
|
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_data[0] |= type;
|
||||||
|
|
||||||
|
error = espMqttClientTypes::Error::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Packet::_allocate(size_t remainingLength, bool check) {
|
bool Packet::_allocate(size_t remainingLength, bool check) {
|
||||||
if (check && EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) {
|
#if EMC_USE_MEMPOOL
|
||||||
emc_log_w("Packet buffer not allocated: low memory");
|
(void) check;
|
||||||
return false;
|
#else
|
||||||
}
|
if (check && EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) {
|
||||||
_size = 1 + remainingLengthLength(remainingLength) + remainingLength;
|
emc_log_w("Packet buffer not allocated: low memory");
|
||||||
_data = reinterpret_cast<uint8_t *>(malloc(_size));
|
return false;
|
||||||
if (!_data) {
|
}
|
||||||
_size = 0;
|
#endif
|
||||||
emc_log_w("Alloc failed (l:%zu)", _size);
|
_size = 1 + remainingLengthLength(remainingLength) + remainingLength;
|
||||||
return false;
|
#if EMC_USE_MEMPOOL
|
||||||
}
|
_data = reinterpret_cast<uint8_t*>(_memPool.malloc(_size));
|
||||||
emc_log_i("Alloc (l:%zu)", _size);
|
#else
|
||||||
memset(_data, 0, _size);
|
_data = reinterpret_cast<uint8_t*>(malloc(_size));
|
||||||
return true;
|
#endif
|
||||||
|
if (!_data) {
|
||||||
|
_size = 0;
|
||||||
|
emc_log_w("Alloc failed (l:%zu)", _size);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
emc_log_i("Alloc (l:%zu)", _size);
|
||||||
|
memset(_data, 0, _size);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Packet::_fillPublishHeader(uint16_t packetId, const char * topic, size_t remainingLength, uint8_t qos, bool retain) {
|
size_t Packet::_fillPublishHeader(uint16_t packetId,
|
||||||
size_t index = 0;
|
const char* topic,
|
||||||
|
size_t remainingLength,
|
||||||
|
uint8_t qos,
|
||||||
|
bool retain) {
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
// FIXED HEADER
|
// FIXED HEADER
|
||||||
_data[index] = PacketType.PUBLISH;
|
_data[index] = PacketType.PUBLISH;
|
||||||
if (retain)
|
if (retain) _data[index] |= HeaderFlag.PUBLISH_RETAIN;
|
||||||
_data[index] |= HeaderFlag.PUBLISH_RETAIN;
|
if (qos == 0) {
|
||||||
if (qos == 0) {
|
_data[index++] |= HeaderFlag.PUBLISH_QOS0;
|
||||||
_data[index++] |= HeaderFlag.PUBLISH_QOS0;
|
} else if (qos == 1) {
|
||||||
} else if (qos == 1) {
|
_data[index++] |= HeaderFlag.PUBLISH_QOS1;
|
||||||
_data[index++] |= HeaderFlag.PUBLISH_QOS1;
|
} else if (qos == 2) {
|
||||||
} else if (qos == 2) {
|
_data[index++] |= HeaderFlag.PUBLISH_QOS2;
|
||||||
_data[index++] |= HeaderFlag.PUBLISH_QOS2;
|
}
|
||||||
}
|
index += encodeRemainingLength(remainingLength, &_data[index]);
|
||||||
index += encodeRemainingLength(remainingLength, &_data[index]);
|
|
||||||
|
|
||||||
// VARIABLE HEADER
|
// VARIABLE HEADER
|
||||||
index += encodeString(topic, &_data[index]);
|
index += encodeString(topic, &_data[index]);
|
||||||
if (qos > 0) {
|
if (qos > 0) {
|
||||||
_data[index++] = packetId >> 8;
|
_data[index++] = packetId >> 8;
|
||||||
_data[index++] = packetId & 0xFF;
|
_data[index++] = packetId & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Packet::_createSubscribe(espMqttClientTypes::Error & error, SubscribeItem * list, size_t numberTopics) {
|
void Packet::_createSubscribe(espMqttClientTypes::Error& error,
|
||||||
// Calculate size
|
SubscribeItem* list,
|
||||||
size_t payload = 0;
|
size_t numberTopics) {
|
||||||
for (size_t i = 0; i < numberTopics; ++i) {
|
// Calculate size
|
||||||
payload += 2 + strlen(list[i].topic) + 1; // length bytes, string, qos
|
size_t payload = 0;
|
||||||
}
|
for (size_t i = 0; i < numberTopics; ++i) {
|
||||||
size_t remainingLength = 2 + payload; // packetId + payload
|
payload += 2 + strlen(list[i].topic) + 1; // length bytes, string, qos
|
||||||
|
}
|
||||||
|
size_t remainingLength = 2 + payload; // packetId + payload
|
||||||
|
|
||||||
// allocate memory
|
// allocate memory
|
||||||
if (!_allocate(remainingLength)) {
|
if (!_allocate(remainingLength, true)) {
|
||||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialize
|
// serialize
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
_data[pos++] = PacketType.SUBSCRIBE | HeaderFlag.SUBSCRIBE_RESERVED;
|
_data[pos++] = PacketType.SUBSCRIBE | HeaderFlag.SUBSCRIBE_RESERVED;
|
||||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||||
_data[pos++] = _packetId >> 8;
|
_data[pos++] = _packetId >> 8;
|
||||||
_data[pos++] = _packetId & 0xFF;
|
_data[pos++] = _packetId & 0xFF;
|
||||||
for (size_t i = 0; i < numberTopics; ++i) {
|
for (size_t i = 0; i < numberTopics; ++i) {
|
||||||
pos += encodeString(list[i].topic, &_data[pos]);
|
pos += encodeString(list[i].topic, &_data[pos]);
|
||||||
_data[pos++] = list[i].qos;
|
_data[pos++] = list[i].qos;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = espMqttClientTypes::Error::SUCCESS;
|
error = espMqttClientTypes::Error::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Packet::_createUnsubscribe(espMqttClientTypes::Error & error, const char ** list, size_t numberTopics) {
|
void Packet::_createUnsubscribe(espMqttClientTypes::Error& error,
|
||||||
// Calculate size
|
const char** list,
|
||||||
size_t payload = 0;
|
size_t numberTopics) {
|
||||||
for (size_t i = 0; i < numberTopics; ++i) {
|
// Calculate size
|
||||||
payload += 2 + strlen(list[i]); // length bytes, string
|
size_t payload = 0;
|
||||||
}
|
for (size_t i = 0; i < numberTopics; ++i) {
|
||||||
size_t remainingLength = 2 + payload; // packetId + payload
|
payload += 2 + strlen(list[i]); // length bytes, string
|
||||||
|
}
|
||||||
|
size_t remainingLength = 2 + payload; // packetId + payload
|
||||||
|
|
||||||
// allocate memory
|
// allocate memory
|
||||||
if (!_allocate(remainingLength)) {
|
if (!_allocate(remainingLength, true)) {
|
||||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialize
|
// serialize
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
_data[pos++] = PacketType.UNSUBSCRIBE | HeaderFlag.UNSUBSCRIBE_RESERVED;
|
_data[pos++] = PacketType.UNSUBSCRIBE | HeaderFlag.UNSUBSCRIBE_RESERVED;
|
||||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||||
_data[pos++] = _packetId >> 8;
|
_data[pos++] = _packetId >> 8;
|
||||||
_data[pos++] = _packetId & 0xFF;
|
_data[pos++] = _packetId & 0xFF;
|
||||||
for (size_t i = 0; i < numberTopics; ++i) {
|
for (size_t i = 0; i < numberTopics; ++i) {
|
||||||
pos += encodeString(list[i], &_data[pos]);
|
pos += encodeString(list[i], &_data[pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = espMqttClientTypes::Error::SUCCESS;
|
error = espMqttClientTypes::Error::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Packet::_chunkedAvailable(size_t index) {
|
size_t Packet::_chunkedAvailable(size_t index) {
|
||||||
// index vs size check done in 'available(index)'
|
// index vs size check done in 'available(index)'
|
||||||
|
|
||||||
// index points to header or first payload byte
|
// index points to header or first payload byte
|
||||||
if (index < _payloadIndex) {
|
if (index < _payloadIndex) {
|
||||||
if (_size > _payloadIndex && _payloadEndIndex != 0) {
|
if (_size > _payloadIndex && _payloadEndIndex != 0) {
|
||||||
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
||||||
_payloadStartIndex = _payloadIndex;
|
_payloadStartIndex = _payloadIndex;
|
||||||
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
||||||
}
|
|
||||||
|
|
||||||
// index points to payload unavailable
|
|
||||||
} else if (index > _payloadEndIndex || _payloadStartIndex > index) {
|
|
||||||
_payloadStartIndex = index;
|
|
||||||
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
|
||||||
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// now index points to header or payload available
|
// index points to payload unavailable
|
||||||
return _payloadEndIndex - index + 1;
|
} else if (index > _payloadEndIndex || _payloadStartIndex > index) {
|
||||||
|
_payloadStartIndex = index;
|
||||||
|
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
||||||
|
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now index points to header or payload available
|
||||||
|
return _payloadEndIndex - index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t * Packet::_chunkedData(size_t index) const {
|
const uint8_t* Packet::_chunkedData(size_t index) const {
|
||||||
// CAUTION!! available(index) has to be called first to check available data and possibly fill payloadbuffer
|
// CAUTION!! available(index) has to be called first to check available data and possibly fill payloadbuffer
|
||||||
if (index < _payloadIndex) {
|
if (index < _payloadIndex) {
|
||||||
return &_data[index];
|
return &_data[index];
|
||||||
}
|
}
|
||||||
return &_data[index - _payloadStartIndex + _payloadIndex];
|
return &_data[index - _payloadStartIndex + _payloadIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace espMqttClientInternals
|
} // end namespace espMqttClientInternals
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ the LICENSE file.
|
|||||||
#include "../Helpers.h"
|
#include "../Helpers.h"
|
||||||
#include "../Logging.h"
|
#include "../Logging.h"
|
||||||
#include "RemainingLength.h"
|
#include "RemainingLength.h"
|
||||||
#include "String.h"
|
#include "StringUtil.h"
|
||||||
|
|
||||||
|
#if EMC_USE_MEMPOOL
|
||||||
|
#include "MemoryPool/src/MemoryPool.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
@@ -133,7 +137,7 @@ class Packet {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// pass remainingLength = total size - header - remainingLengthLength!
|
// pass remainingLength = total size - header - remainingLengthLength!
|
||||||
bool _allocate(size_t remainingLength, bool check = true);
|
bool _allocate(size_t remainingLength, bool check);
|
||||||
|
|
||||||
// fills header and returns index of next available byte in buffer
|
// fills header and returns index of next available byte in buffer
|
||||||
size_t _fillPublishHeader(uint16_t packetId,
|
size_t _fillPublishHeader(uint16_t packetId,
|
||||||
@@ -150,6 +154,10 @@ class Packet {
|
|||||||
|
|
||||||
size_t _chunkedAvailable(size_t index);
|
size_t _chunkedAvailable(size_t index);
|
||||||
const uint8_t* _chunkedData(size_t index) const;
|
const uint8_t* _chunkedData(size_t index) const;
|
||||||
|
|
||||||
|
#if EMC_USE_MEMPOOL
|
||||||
|
static MemoryPool::Variable<EMC_NUM_POOL_ELEMENTS, EMC_SIZE_POOL_ELEMENTS> _memPool;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace espMqttClientInternals
|
} // end namespace espMqttClientInternals
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ For a copy, see <https://opensource.org/licenses/MIT> or
|
|||||||
the LICENSE file.
|
the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "String.h"
|
#include "StringUtil.h"
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
@@ -38,9 +38,10 @@ bool ClientPosix::connect(IPAddress ip, uint16_t port) {
|
|||||||
memset(&_host, 0, sizeof(_host));
|
memset(&_host, 0, sizeof(_host));
|
||||||
_host.sin_family = AF_INET;
|
_host.sin_family = AF_INET;
|
||||||
_host.sin_addr.s_addr = htonl(uint32_t(ip));
|
_host.sin_addr.s_addr = htonl(uint32_t(ip));
|
||||||
_host.sin_port = htons(port); // modified by proddy for EMS-ESP compiling standalone
|
_host.sin_port = ::htons(port);
|
||||||
|
|
||||||
int ret = ::connect(_sockfd, reinterpret_cast<sockaddr*>(&_host), sizeof(_host));
|
int ret = ::connect(_sockfd, reinterpret_cast<sockaddr*>(&_host), sizeof(_host));
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
emc_log_e("Error connecting: %d - (%d) %s", ret, errno, strerror(errno));
|
emc_log_e("Error connecting: %d - (%d) %s", ret, errno, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ the LICENSE file.
|
|||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
|
||||||
#include "IPAddress.h"
|
#include "ClientPosixIPAddress.h"
|
||||||
|
|
||||||
IPAddress::IPAddress()
|
IPAddress::IPAddress()
|
||||||
: _address(0) {
|
: _address(0) {
|
||||||
@@ -17,7 +17,6 @@ the LICENSE file.
|
|||||||
#else
|
#else
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Transport.h"
|
#include "Transport.h"
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
@@ -32,6 +31,7 @@ class ClientSecureSync : public Transport {
|
|||||||
void stop() override;
|
void stop() override;
|
||||||
bool connected() override;
|
bool connected() override;
|
||||||
bool disconnected() override;
|
bool disconnected() override;
|
||||||
|
// added for EMS-ESP
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#if defined(EMC_CLIENT_SECURE)
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ the LICENSE file.
|
|||||||
|
|
||||||
#include <stddef.h> // size_t
|
#include <stddef.h> // size_t
|
||||||
|
|
||||||
#include "IPAddress.h"
|
#include "ClientPosixIPAddress.h"
|
||||||
|
|
||||||
namespace espMqttClientInternals {
|
namespace espMqttClientInternals {
|
||||||
|
|
||||||
|
|||||||
@@ -10,114 +10,114 @@ the LICENSE file.
|
|||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
espMqttClient::espMqttClient()
|
espMqttClient::espMqttClient()
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure::espMqttClientSecure()
|
espMqttClientSecure::espMqttClientSecure()
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setInsecure() {
|
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
||||||
_client.client.setInsecure();
|
_client.client.setInsecure();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) {
|
espMqttClientSecure & espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) {
|
||||||
_client.client.setFingerprint(fingerprint);
|
_client.client.setFingerprint(fingerprint);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setTrustAnchors(const X509List *ta) {
|
espMqttClientSecure & espMqttClientSecure::setTrustAnchors(const X509List * ta) {
|
||||||
_client.client.setTrustAnchors(ta);
|
_client.client.setTrustAnchors(ta);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setClientRSACert(const X509List *cert, const PrivateKey *sk) {
|
espMqttClientSecure & espMqttClientSecure::setClientRSACert(const X509List * cert, const PrivateKey * sk) {
|
||||||
_client.client.setClientRSACert(cert, sk);
|
_client.client.setClientRSACert(cert, sk);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setClientECCert(const X509List *cert, const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
espMqttClientSecure & espMqttClientSecure::setClientECCert(const X509List * cert, const PrivateKey * sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
||||||
_client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
|
_client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setCertStore(CertStoreBase *certStore) {
|
espMqttClientSecure & espMqttClientSecure::setCertStore(CertStoreBase * certStore) {
|
||||||
_client.client.setCertStore(certStore);
|
_client.client.setCertStore(certStore);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
|
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
|
||||||
: MqttClientSetup(useInternalTask)
|
: MqttClientSetup(useInternalTask)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClient::espMqttClient(uint8_t priority, uint8_t core)
|
espMqttClient::espMqttClient(uint8_t priority, uint8_t core)
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core)
|
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure::espMqttClientSecure(espMqttClientTypes::UseInternalTask useInternalTask)
|
espMqttClientSecure::espMqttClientSecure(espMqttClientTypes::UseInternalTask useInternalTask)
|
||||||
: MqttClientSetup(useInternalTask)
|
: MqttClientSetup(useInternalTask)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
|
espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core)
|
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setInsecure() {
|
espMqttClientSecure & espMqttClientSecure::setInsecure() {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#if defined(EMC_CLIENT_SECURE)
|
||||||
_client.client.setInsecure();
|
_client.client.setInsecure();
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setCACert(const char* rootCA) {
|
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#if defined(EMC_CLIENT_SECURE)
|
||||||
_client.client.setCACert(rootCA);
|
_client.client.setCACert(rootCA);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setCertificate(const char* clientCa) {
|
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#if defined(EMC_CLIENT_SECURE)
|
||||||
_client.client.setCertificate(clientCa);
|
_client.client.setCertificate(clientCa);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setPrivateKey(const char* privateKey) {
|
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#if defined(EMC_CLIENT_SECURE)
|
||||||
_client.client.setPrivateKey(privateKey);
|
_client.client.setPrivateKey(privateKey);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
espMqttClientSecure& espMqttClientSecure::setPreSharedKey(const char* pskIdent, const char* psKey) {
|
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
|
||||||
#if defined(EMC_CLIENT_SECURE)
|
#if defined(EMC_CLIENT_SECURE)
|
||||||
_client.client.setPreSharedKey(pskIdent, psKey);
|
_client.client.setPreSharedKey(pskIdent, psKey);
|
||||||
#endif
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
espMqttClient::espMqttClient()
|
espMqttClient::espMqttClient()
|
||||||
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
|
||||||
, _client() {
|
, _client() {
|
||||||
_transport = &_client;
|
_transport = &_client;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user