初始化提交
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Wrapper file, which is used to test on PC hardware
|
||||
*/
|
||||
#ifndef ARDUINO_WRAP_H
|
||||
#define ARDUINO_WRAP_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define F(string_literal) string_literal
|
||||
#define ARDUINO_ARCH_ESP8266
|
||||
#define PAINLESSMESH_BOOST
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
inline unsigned long millis() {
|
||||
struct timeval te;
|
||||
gettimeofday(&te, NULL); // get current time
|
||||
long long milliseconds =
|
||||
te.tv_sec * 1000LL + te.tv_usec / 1000; // calculate milliseconds
|
||||
// printf("milliseconds: %lld\n", milliseconds);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
inline unsigned long micros() {
|
||||
struct timeval te;
|
||||
gettimeofday(&te, NULL); // get current time
|
||||
long long milliseconds = te.tv_sec * 1000000LL + te.tv_usec;
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
inline void delay(int i) { usleep(i); }
|
||||
|
||||
inline void yield() {}
|
||||
|
||||
/**
|
||||
* Override the configution file.
|
||||
**/
|
||||
|
||||
#ifndef _PAINLESS_MESH_CONFIGURATION_HPP_
|
||||
#define _PAINLESS_MESH_CONFIGURATION_HPP_
|
||||
|
||||
#define _TASK_PRIORITY // Support for layered scheduling priority
|
||||
#define _TASK_STD_FUNCTION
|
||||
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 1
|
||||
#include <ArduinoJson.h>
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
|
||||
#define ICACHE_FLASH_ATTR
|
||||
|
||||
#define PAINLESSMESH_ENABLE_STD_STRING
|
||||
#define PAINLESSMESH_ENABLE_OTA
|
||||
#define NODE_TIMEOUT 5 * TASK_SECOND
|
||||
|
||||
typedef std::string TSTRING;
|
||||
|
||||
#ifdef ESP32
|
||||
#define MAX_CONN 10
|
||||
#else
|
||||
#define MAX_CONN 4
|
||||
#endif // DEBUG
|
||||
|
||||
#include "boost/asynctcp.hpp"
|
||||
#include "fake_serial.hpp"
|
||||
|
||||
typedef enum {
|
||||
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
|
||||
WL_IDLE_STATUS = 0,
|
||||
WL_NO_SSID_AVAIL = 1,
|
||||
WL_SCAN_COMPLETED = 2,
|
||||
WL_CONNECTED = 3,
|
||||
WL_CONNECT_FAILED = 4,
|
||||
WL_CONNECTION_LOST = 5,
|
||||
WL_DISCONNECTED = 6
|
||||
} wl_status_t;
|
||||
|
||||
class WiFiClass {
|
||||
public:
|
||||
void disconnect() {}
|
||||
auto status() { return WL_CONNECTED; }
|
||||
};
|
||||
|
||||
class ESPClass {
|
||||
public:
|
||||
size_t getFreeHeap() { return 1e6; }
|
||||
};
|
||||
|
||||
extern WiFiClass WiFi;
|
||||
extern ESPClass ESP;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,399 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
#include "boost/asynctcp.hpp"
|
||||
|
||||
WiFiClass WiFi;
|
||||
ESPClass ESP;
|
||||
|
||||
#include "painlessMeshConnection.h"
|
||||
|
||||
#include "painlessmesh/mesh.hpp"
|
||||
|
||||
using PMesh = painlessmesh::Mesh<MeshConnection>;
|
||||
|
||||
using namespace painlessmesh;
|
||||
painlessmesh::logger::LogClass Log;
|
||||
|
||||
class MeshTest : public PMesh {
|
||||
public:
|
||||
MeshTest(Scheduler *scheduler, size_t id, boost::asio::io_service &io)
|
||||
: io_service(io) {
|
||||
this->nodeId = id;
|
||||
this->init(scheduler, this->nodeId);
|
||||
timeOffset = runif(0, 1e09);
|
||||
pServer = std::make_shared<AsyncServer>(io_service, this->nodeId);
|
||||
painlessmesh::tcp::initServer<MeshConnection, PMesh>(*pServer, (*this));
|
||||
}
|
||||
|
||||
void connect(MeshTest &mesh) {
|
||||
auto pClient = new AsyncClient(io_service);
|
||||
painlessmesh::tcp::connect<MeshConnection, PMesh>(
|
||||
(*pClient), boost::asio::ip::address::from_string("127.0.0.1"),
|
||||
mesh.nodeId, (*this));
|
||||
}
|
||||
|
||||
std::shared_ptr<AsyncServer> pServer;
|
||||
boost::asio::io_service &io_service;
|
||||
};
|
||||
|
||||
class Nodes {
|
||||
public:
|
||||
Nodes(Scheduler *scheduler, size_t n, boost::asio::io_service &io)
|
||||
: io_service(io) {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto m = std::make_shared<MeshTest>(scheduler, i + baseID, io_service);
|
||||
if (i > 0) m->connect((*nodes[runif(0, i - 1)]));
|
||||
nodes.push_back(m);
|
||||
}
|
||||
}
|
||||
void update() {
|
||||
for (auto &&m : nodes) {
|
||||
m->update();
|
||||
io_service.poll();
|
||||
}
|
||||
}
|
||||
|
||||
void stop() {
|
||||
for (auto &&m : nodes) m->stop();
|
||||
}
|
||||
|
||||
auto size() { return nodes.size(); }
|
||||
|
||||
std::shared_ptr<MeshTest> get(size_t nodeId) {
|
||||
return nodes[nodeId - baseID];
|
||||
}
|
||||
|
||||
size_t baseID = 6481;
|
||||
std::vector<std::shared_ptr<MeshTest>> nodes;
|
||||
boost::asio::io_service &io_service;
|
||||
};
|
||||
|
||||
SCENARIO("We can setup and connect two meshes over localport") {
|
||||
using namespace logger;
|
||||
Scheduler scheduler;
|
||||
Log.setLogLevel(ERROR);
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
PMesh mesh1;
|
||||
mesh1.init(&scheduler, 6841);
|
||||
std::shared_ptr<AsyncServer> pServer;
|
||||
pServer = std::make_shared<AsyncServer>(io_service, 6841);
|
||||
painlessmesh::tcp::initServer<MeshConnection, PMesh>(*pServer, mesh1);
|
||||
|
||||
PMesh mesh2;
|
||||
mesh2.init(&scheduler, 6842);
|
||||
auto pClient = new AsyncClient(io_service);
|
||||
painlessmesh::tcp::connect<MeshConnection, PMesh>(
|
||||
(*pClient), boost::asio::ip::address::from_string("127.0.0.1"), 6841,
|
||||
mesh2);
|
||||
|
||||
for (auto i = 0; i < 100; ++i) {
|
||||
mesh1.update();
|
||||
mesh2.update();
|
||||
io_service.poll();
|
||||
}
|
||||
|
||||
REQUIRE(layout::size(mesh1.asNodeTree()) == 2);
|
||||
REQUIRE(layout::size(mesh2.asNodeTree()) == 2);
|
||||
}
|
||||
|
||||
SCENARIO("The MeshTest class works correctly") {
|
||||
using namespace logger;
|
||||
Scheduler scheduler;
|
||||
Log.setLogLevel(ERROR);
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
MeshTest mesh1(&scheduler, 6841, io_service);
|
||||
MeshTest mesh2(&scheduler, 6842, io_service);
|
||||
mesh2.connect(mesh1);
|
||||
|
||||
for (auto i = 0; i < 100; ++i) {
|
||||
mesh1.update();
|
||||
mesh2.update();
|
||||
io_service.poll();
|
||||
}
|
||||
|
||||
REQUIRE(layout::size(mesh1.asNodeTree()) == 2);
|
||||
REQUIRE(layout::size(mesh2.asNodeTree()) == 2);
|
||||
}
|
||||
|
||||
SCENARIO("We can send a message using our Nodes class") {
|
||||
delay(1000);
|
||||
using namespace logger;
|
||||
Log.setLogLevel(ERROR);
|
||||
|
||||
Scheduler scheduler;
|
||||
boost::asio::io_service io_service;
|
||||
Nodes n(&scheduler, 12, io_service);
|
||||
|
||||
for (auto i = 0; i < 1000; ++i) {
|
||||
n.update();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
REQUIRE(layout::size(n.nodes[0]->asNodeTree()) == 12);
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
std::string z;
|
||||
n.nodes[0]->onReceive([&x, &y, &z](auto id, auto msg) {
|
||||
++x;
|
||||
y = id;
|
||||
z = msg;
|
||||
});
|
||||
n.nodes[10]->sendSingle(n.nodes[2]->getNodeId(), "Blaat");
|
||||
for (auto i = 0; i < 1000; ++i) n.update();
|
||||
REQUIRE(x == 0);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(z == "");
|
||||
|
||||
n.nodes[10]->sendSingle(n.nodes[0]->getNodeId(), "Blaat");
|
||||
for (auto i = 0; i < 1000; ++i) n.update();
|
||||
REQUIRE(z == "Blaat");
|
||||
REQUIRE(x == 1);
|
||||
REQUIRE(y == n.nodes[10]->getNodeId());
|
||||
|
||||
n.nodes[5]->onReceive([&x, &y, &z](auto id, auto msg) {
|
||||
++x;
|
||||
y = id;
|
||||
z = msg;
|
||||
});
|
||||
n.nodes[10]->sendBroadcast("Blargh");
|
||||
for (auto i = 0; i < 10000; ++i) n.update();
|
||||
REQUIRE(z == "Blargh");
|
||||
REQUIRE(x == 3);
|
||||
REQUIRE(y == n.nodes[10]->getNodeId());
|
||||
n.stop();
|
||||
}
|
||||
|
||||
SCENARIO("Time sync works") {
|
||||
using namespace logger;
|
||||
Log.setLogLevel(ERROR);
|
||||
|
||||
Scheduler scheduler;
|
||||
boost::asio::io_service io_service;
|
||||
auto dim = runif(8, 15);
|
||||
Nodes n(&scheduler, dim, io_service);
|
||||
|
||||
int diff = 0;
|
||||
for (size_t i = 0; i < n.size() - 1; ++i) {
|
||||
diff += std::abs((int)n.nodes[0]->getNodeTime() -
|
||||
(int)n.nodes[i + 1]->getNodeTime());
|
||||
}
|
||||
REQUIRE(diff / n.size() > 10000);
|
||||
|
||||
for (auto i = 0; i < 10000; ++i) {
|
||||
n.update();
|
||||
delay(10);
|
||||
}
|
||||
diff = 0;
|
||||
for (size_t i = 0; i < n.size() - 1; ++i) {
|
||||
diff += std::abs((int)n.nodes[0]->getNodeTime() -
|
||||
(int)n.nodes[i + 1]->getNodeTime());
|
||||
}
|
||||
REQUIRE(diff / n.size() < 10000);
|
||||
n.stop();
|
||||
}
|
||||
|
||||
SCENARIO("Rooting works") {
|
||||
using namespace logger;
|
||||
Log.setLogLevel(ERROR);
|
||||
|
||||
Scheduler scheduler;
|
||||
boost::asio::io_service io_service;
|
||||
auto dim = runif(8, 15);
|
||||
Nodes n(&scheduler, dim, io_service);
|
||||
|
||||
n.nodes[5]->setRoot(true);
|
||||
REQUIRE(n.nodes[5]->isRoot());
|
||||
REQUIRE(layout::isRooted(n.nodes[5]->asNodeTree()));
|
||||
|
||||
for (auto i = 0; i < 10000; ++i) {
|
||||
n.update();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
for (auto &&node : n.nodes) {
|
||||
REQUIRE(layout::isRooted(node->asNodeTree()));
|
||||
if (n.nodes[5]->getNodeId() == node->getNodeId()) {
|
||||
REQUIRE(node->isRoot());
|
||||
} else {
|
||||
REQUIRE(!node->isRoot());
|
||||
}
|
||||
}
|
||||
|
||||
n.stop();
|
||||
}
|
||||
|
||||
SCENARIO("Network loops are detected") {
|
||||
using namespace logger;
|
||||
Log.setLogLevel(ERROR);
|
||||
|
||||
Scheduler scheduler;
|
||||
boost::asio::io_service io_service;
|
||||
MeshTest mesh1(&scheduler, 6841, io_service);
|
||||
MeshTest mesh2(&scheduler, 6842, io_service);
|
||||
MeshTest mesh3(&scheduler, 6843, io_service);
|
||||
MeshTest mesh4(&scheduler, 6844, io_service);
|
||||
MeshTest mesh5(&scheduler, 6845, io_service);
|
||||
|
||||
mesh1.connect(mesh5);
|
||||
mesh2.connect(mesh1);
|
||||
mesh3.connect(mesh2);
|
||||
mesh4.connect(mesh3);
|
||||
mesh5.connect(mesh4);
|
||||
|
||||
for (auto i = 0; i < 10000; ++i) {
|
||||
mesh1.update();
|
||||
io_service.poll();
|
||||
mesh2.update();
|
||||
io_service.poll();
|
||||
mesh3.update();
|
||||
io_service.poll();
|
||||
mesh4.update();
|
||||
io_service.poll();
|
||||
mesh5.update();
|
||||
io_service.poll();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
// Looped network, should break up so it can reform
|
||||
REQUIRE(layout::size(mesh1.asNodeTree()) < 5);
|
||||
REQUIRE(layout::size(mesh2.asNodeTree()) < 5);
|
||||
REQUIRE(layout::size(mesh3.asNodeTree()) < 5);
|
||||
REQUIRE(layout::size(mesh4.asNodeTree()) < 5);
|
||||
REQUIRE(layout::size(mesh5.asNodeTree()) < 5);
|
||||
|
||||
mesh1.stop();
|
||||
mesh2.stop();
|
||||
mesh3.stop();
|
||||
mesh4.stop();
|
||||
mesh5.stop();
|
||||
}
|
||||
|
||||
SCENARIO("Disconnects are detected and forwarded") {
|
||||
using namespace logger;
|
||||
Log.setLogLevel(ERROR);
|
||||
|
||||
Scheduler scheduler;
|
||||
boost::asio::io_service io_service;
|
||||
auto dim = runif(10, 15);
|
||||
Nodes n(&scheduler, dim, io_service);
|
||||
|
||||
// Dummy task. This can catch mistaken use of the scheduler
|
||||
Task dummyT;
|
||||
int y = 0;
|
||||
dummyT.set(TASK_MILLISECOND, TASK_FOREVER, [&y]() { ++y; });
|
||||
scheduler.addTask(dummyT);
|
||||
dummyT.enable();
|
||||
|
||||
for (auto i = 0; i < 1000; ++i) {
|
||||
n.update();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
for (auto &&node : n.nodes) {
|
||||
REQUIRE(layout::size(node->asNodeTree()) == dim);
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
n.nodes[5]->onChangedConnections([&x]() { ++x; });
|
||||
|
||||
n.nodes[5]->onDroppedConnection([&x](auto nodeId) { ++x; });
|
||||
|
||||
n.nodes[dim - 1]->onChangedConnections([&x]() { ++x; });
|
||||
|
||||
auto no = n.nodes[5]->subs.size();
|
||||
REQUIRE(no > 0);
|
||||
|
||||
auto ptr = (*n.nodes[5]->subs.begin());
|
||||
|
||||
(*n.nodes[5]->subs.begin())->close();
|
||||
for (auto i = 0; i < 1000; ++i) {
|
||||
n.update();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
REQUIRE(n.nodes[5]->subs.size() == no - 1);
|
||||
REQUIRE(ptr.use_count() == 1);
|
||||
ptr = NULL;
|
||||
|
||||
REQUIRE(x == 3);
|
||||
|
||||
for (auto &&node : n.nodes) {
|
||||
REQUIRE(layout::size(node->asNodeTree()) < dim);
|
||||
}
|
||||
|
||||
n.stop();
|
||||
REQUIRE(y > 0);
|
||||
}
|
||||
|
||||
SCENARIO("Disconnects don't lead to crashes") {
|
||||
using namespace logger;
|
||||
Log.setLogLevel(ERROR);
|
||||
|
||||
Scheduler scheduler;
|
||||
boost::asio::io_service io_service;
|
||||
auto dim = runif(10, 15);
|
||||
Nodes n(&scheduler, dim, io_service);
|
||||
|
||||
// Dummy task. This can catch mistaken use of the scheduler
|
||||
Task dummyT;
|
||||
int y = 0;
|
||||
dummyT.set(TASK_MILLISECOND, TASK_FOREVER, [&y]() { ++y; });
|
||||
scheduler.addTask(dummyT);
|
||||
dummyT.enable();
|
||||
|
||||
for (auto i = 0; i < 1000; ++i) {
|
||||
n.update();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
for (auto &&node : n.nodes) {
|
||||
REQUIRE(layout::size(node->asNodeTree()) == dim);
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
n.nodes[5]->onChangedConnections([&x]() { ++x; });
|
||||
|
||||
n.nodes[5]->onDroppedConnection([&x](auto nodeId) { ++x; });
|
||||
|
||||
n.nodes[dim - 1]->onChangedConnections([&x]() { ++x; });
|
||||
|
||||
auto no = n.nodes[5]->subs.size();
|
||||
REQUIRE(no > 0);
|
||||
|
||||
auto ptr = (*n.nodes[5]->subs.begin());
|
||||
|
||||
(*n.nodes[5]->subs.begin())->close();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
io_service.poll();
|
||||
}
|
||||
n.update();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
io_service.poll();
|
||||
}
|
||||
for (auto i = 0; i < 1000; ++i) {
|
||||
n.update();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
REQUIRE(n.nodes[5]->subs.size() == no - 1);
|
||||
REQUIRE(ptr.use_count() == 1);
|
||||
ptr = NULL;
|
||||
|
||||
REQUIRE(x == 3);
|
||||
|
||||
for (auto &&node : n.nodes) {
|
||||
REQUIRE(layout::size(node->asNodeTree()) < dim);
|
||||
}
|
||||
|
||||
n.stop();
|
||||
REQUIRE(y > 0);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Wrapper file, which is used to test on PC hardware
|
||||
*/
|
||||
#ifndef ARDUINO_WRAP_H
|
||||
#define ARDUINO_WRAP_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define F(string_literal) string_literal
|
||||
#define ARDUINO_ARCH_ESP8266
|
||||
#define PAINLESSMESH_BOOST
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
inline unsigned long millis() {
|
||||
struct timeval te;
|
||||
gettimeofday(&te, NULL); // get current time
|
||||
long long milliseconds =
|
||||
te.tv_sec * 1000LL + te.tv_usec / 1000; // calculate milliseconds
|
||||
// printf("milliseconds: %lld\n", milliseconds);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
inline unsigned long micros() {
|
||||
struct timeval te;
|
||||
gettimeofday(&te, NULL); // get current time
|
||||
long long milliseconds = te.tv_sec * 1000000LL + te.tv_usec;
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
inline void delay(int i) { usleep(i); }
|
||||
|
||||
inline void yield() {}
|
||||
|
||||
struct IPAddress {
|
||||
IPAddress() {}
|
||||
IPAddress(int, int, int, int) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Override the configution file.
|
||||
**/
|
||||
|
||||
#ifndef _PAINLESS_MESH_CONFIGURATION_HPP_
|
||||
#define _PAINLESS_MESH_CONFIGURATION_HPP_
|
||||
|
||||
#define _TASK_PRIORITY // Support for layered scheduling priority
|
||||
#define _TASK_STD_FUNCTION
|
||||
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 1
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
|
||||
#define ICACHE_FLASH_ATTR
|
||||
|
||||
#define PAINLESSMESH_ENABLE_STD_STRING
|
||||
|
||||
// Enable OTA support
|
||||
#define PAINLESSMESH_ENABLE_OTA
|
||||
|
||||
#define NODE_TIMEOUT 5 * TASK_SECOND
|
||||
|
||||
typedef std::string TSTRING;
|
||||
|
||||
#ifdef ESP32
|
||||
#define MAX_CONN 10
|
||||
#else
|
||||
#define MAX_CONN 4
|
||||
#endif // DEBUG
|
||||
|
||||
#include "fake_asynctcp.hpp"
|
||||
#include "fake_serial.hpp"
|
||||
|
||||
extern WiFiClass WiFi;
|
||||
extern ESPClass ESP;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "painlessmesh/configuration.hpp"
|
||||
|
||||
#include "painlessmesh/base64.hpp"
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
SCENARIO("Base64 encoding can succesfully be decoded") {
|
||||
using namespace painlessmesh;
|
||||
auto bindata = randomString(100);
|
||||
auto enc = base64::encode(bindata);
|
||||
auto dec = base64::decode(enc);
|
||||
REQUIRE(dec.length() > 0);
|
||||
REQUIRE(dec == bindata);
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 1
|
||||
#include "ArduinoJson.h"
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
#undef PAINLESSMESH_ENABLE_ARDUINO_STRING
|
||||
#define PAINLESSMESH_ENABLE_STD_STRING
|
||||
typedef std::string TSTRING;
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
#include "painlessmesh/buffer.hpp"
|
||||
|
||||
using namespace painlessmesh::buffer;
|
||||
|
||||
SCENARIO("ReceiveBuffer receives strings and needs to process them") {
|
||||
temp_buffer_t tmp_buffer;
|
||||
char cstring[3 * tmp_buffer.length];
|
||||
ReceiveBuffer<std::string> rBuffer = ReceiveBuffer<std::string>();
|
||||
|
||||
GIVEN("A random string of short length pushed to the received buffer") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
auto length = runif(10, tmp_buffer.length - 10);
|
||||
randomCString(cstring, length);
|
||||
// Note we need to send a \0 to know this is the end
|
||||
rBuffer.push(cstring, length + 1, tmp_buffer);
|
||||
THEN("It gets copied to the front of the buffer") {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
REQUIRE(rBuffer.front() == std::string(cstring));
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A random string of long length pushed to the received buffer") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
auto length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length);
|
||||
randomCString(cstring, length);
|
||||
// Note we need to send a \0 to know this is the end
|
||||
rBuffer.push(cstring, length + 1, tmp_buffer);
|
||||
THEN("It gets copied to the front of the buffer") {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
REQUIRE(rBuffer.front() == std::string(cstring));
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A random string we can push it in multiple parts") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
auto length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length);
|
||||
size_t part_len = length / 2;
|
||||
randomCString(cstring, length);
|
||||
rBuffer.push(cstring, part_len, tmp_buffer);
|
||||
THEN("The first part doesn't get copied to the front of the buffer") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
}
|
||||
|
||||
auto data_ptr = cstring + sizeof(char) * part_len;
|
||||
rBuffer.push(data_ptr, length - part_len + 1, tmp_buffer);
|
||||
THEN(
|
||||
"When getting the second part the whole thing gets copied to the front "
|
||||
"of the buffer") {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
REQUIRE(rBuffer.front() == std::string(cstring));
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("ReceiveBuffer can receive multiple messages and hold them all") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
auto length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length);
|
||||
size_t part_len = length / 2;
|
||||
randomCString(cstring, length);
|
||||
rBuffer.push(cstring, part_len, tmp_buffer);
|
||||
auto data_ptr = cstring + sizeof(char) * part_len;
|
||||
rBuffer.push(data_ptr, length - part_len + 1, tmp_buffer);
|
||||
}
|
||||
THEN(
|
||||
"When getting the second part the whole thing gets copied to the front "
|
||||
"of the buffer") {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
rBuffer.pop_front();
|
||||
}
|
||||
REQUIRE(rBuffer.empty());
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN(
|
||||
"ReceiveBuffer can receive multiple messages in one char string "
|
||||
"(separated by \0") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
auto length = runif(10, tmp_buffer.length - 10);
|
||||
randomCString(cstring, length);
|
||||
auto data_ptr = cstring + sizeof(char) * (length + 1);
|
||||
auto length2 = runif(10, tmp_buffer.length - 10);
|
||||
randomCString(data_ptr, length2);
|
||||
|
||||
// Note we need to send a \0 to know this is the end
|
||||
rBuffer.push(cstring, length + length2 + 2, tmp_buffer);
|
||||
THEN("We have both strings in the buffer") {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
REQUIRE(rBuffer.front() == std::string(cstring));
|
||||
rBuffer.pop_front();
|
||||
REQUIRE(!rBuffer.empty());
|
||||
REQUIRE(rBuffer.front() == std::string(data_ptr));
|
||||
rBuffer.pop_front();
|
||||
REQUIRE(rBuffer.empty());
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN(
|
||||
"ReceiveBuffer has copied the message we can overwrite the previous "
|
||||
"cstring and buffer without affecting the outcome") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
cstring[0] = 'B';
|
||||
cstring[1] = 'l';
|
||||
cstring[2] = 'a';
|
||||
cstring[3] = 'a';
|
||||
cstring[4] = 't';
|
||||
cstring[5] = '\0';
|
||||
rBuffer.push(cstring, 3, tmp_buffer);
|
||||
cstring[0] = 'r';
|
||||
cstring[1] = 'n';
|
||||
cstring[2] = 'd';
|
||||
randomCString(tmp_buffer.buffer, tmp_buffer.length);
|
||||
auto data_ptr = cstring + sizeof(char) * 3;
|
||||
rBuffer.push(data_ptr, 3, tmp_buffer);
|
||||
THEN("We still have the correct result") {
|
||||
REQUIRE(rBuffer.front() == std::string("Blaat"));
|
||||
REQUIRE(std::string(cstring) != std::string("Blaat"));
|
||||
REQUIRE(std::string(tmp_buffer.buffer, 6) != std::string("Blaat"));
|
||||
REQUIRE(std::string(tmp_buffer.buffer, 5) != std::string("Blaat"));
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A buffer with multiple messages") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
auto length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length);
|
||||
size_t part_len = length / 2;
|
||||
randomCString(cstring, length);
|
||||
rBuffer.push(cstring, part_len, tmp_buffer);
|
||||
auto data_ptr = cstring + sizeof(char) * part_len;
|
||||
rBuffer.push(data_ptr, length - part_len + 1, tmp_buffer);
|
||||
}
|
||||
THEN("We can clear it") {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
rBuffer.clear();
|
||||
REQUIRE(rBuffer.empty());
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A buffer with a half written message") {
|
||||
REQUIRE(rBuffer.empty());
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
auto length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length);
|
||||
size_t part_len = length / 2;
|
||||
randomCString(cstring, length);
|
||||
rBuffer.push(cstring, part_len, tmp_buffer);
|
||||
auto data_ptr = cstring + sizeof(char) * part_len;
|
||||
rBuffer.push(data_ptr, length - part_len + 1, tmp_buffer);
|
||||
}
|
||||
auto length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length);
|
||||
size_t part_len = length / 2;
|
||||
randomCString(cstring, length);
|
||||
rBuffer.push(cstring, part_len, tmp_buffer);
|
||||
|
||||
THEN("We can clear removes it") {
|
||||
REQUIRE(!rBuffer.empty());
|
||||
rBuffer.clear();
|
||||
REQUIRE(rBuffer.empty());
|
||||
}
|
||||
rBuffer.clear();
|
||||
randomCString(cstring, length);
|
||||
rBuffer.push(cstring, length + 1, tmp_buffer);
|
||||
THEN("Reusing it works correctly") {
|
||||
REQUIRE(rBuffer.front() == std::string(cstring));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("SentBuffer receives strings and can be read in parts") {
|
||||
temp_buffer_t tmp_buffer;
|
||||
SentBuffer<std::string> sBuffer = SentBuffer<std::string>();
|
||||
GIVEN("A SentBuffer and a string") {
|
||||
auto length = runif(0, tmp_buffer.length - 10);
|
||||
auto msg = randomString(length);
|
||||
THEN("We can pass strings to it") {
|
||||
REQUIRE(sBuffer.empty());
|
||||
sBuffer.push(msg);
|
||||
REQUIRE(!sBuffer.empty());
|
||||
}
|
||||
THEN("We can pass it and read it back") {
|
||||
REQUIRE(sBuffer.empty());
|
||||
sBuffer.push(msg);
|
||||
REQUIRE(!sBuffer.empty());
|
||||
auto rlength = sBuffer.requestLength(2 * length);
|
||||
REQUIRE(rlength <= 2 * length);
|
||||
sBuffer.read(rlength, tmp_buffer);
|
||||
if (rlength == msg.length() + 1)
|
||||
REQUIRE(std::string(tmp_buffer.buffer, msg.length()) == msg);
|
||||
|
||||
// Test free read as well
|
||||
sBuffer.freeRead();
|
||||
if (rlength == msg.length() + 1) REQUIRE(sBuffer.empty());
|
||||
}
|
||||
}
|
||||
|
||||
// We can read in multiple parts
|
||||
GIVEN("A long string passed to the SentBuffer") {
|
||||
size_t length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length - 10);
|
||||
auto msg = randomString(length);
|
||||
sBuffer.push(msg);
|
||||
char cstring[length + 1];
|
||||
auto data_ptr = cstring;
|
||||
THEN("We can read it in multiple parts") {
|
||||
while (!sBuffer.empty()) {
|
||||
auto rlength = sBuffer.requestLength(tmp_buffer.length);
|
||||
sBuffer.read(rlength, tmp_buffer);
|
||||
memcpy(data_ptr, tmp_buffer.buffer, rlength);
|
||||
data_ptr += rlength * sizeof(char);
|
||||
sBuffer.freeRead();
|
||||
}
|
||||
REQUIRE(std::string(cstring) == msg);
|
||||
}
|
||||
|
||||
THEN("We can use direct access to read it in multiple parts") {
|
||||
while (!sBuffer.empty()) {
|
||||
auto rlength = sBuffer.requestLength(tmp_buffer.length);
|
||||
auto ptr = sBuffer.readPtr(rlength);
|
||||
memcpy(data_ptr, ptr, rlength);
|
||||
data_ptr += rlength * sizeof(char);
|
||||
sBuffer.freeRead();
|
||||
}
|
||||
REQUIRE(std::string(cstring) == msg);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN(
|
||||
"We have read a message halfway, priority messages can safely be added") {
|
||||
size_t length = runif(tmp_buffer.length + 10, 2 * tmp_buffer.length - 10);
|
||||
auto msg1 = randomString(length);
|
||||
auto msg2 = randomString(length);
|
||||
auto msgH = randomString(length);
|
||||
sBuffer.push(msg1);
|
||||
char cstring[3 * length + 3];
|
||||
auto data_ptr = cstring;
|
||||
THEN("We can read it in multiple parts") {
|
||||
auto rlength = sBuffer.requestLength(tmp_buffer.length);
|
||||
|
||||
// Read first message halfway
|
||||
sBuffer.read(rlength, tmp_buffer);
|
||||
memcpy(data_ptr, tmp_buffer.buffer, rlength);
|
||||
data_ptr += rlength * sizeof(char);
|
||||
sBuffer.freeRead();
|
||||
sBuffer.push(msg2);
|
||||
sBuffer.push(msgH, true);
|
||||
|
||||
// Read the rest of the messages
|
||||
while (!sBuffer.empty()) {
|
||||
rlength = sBuffer.requestLength(tmp_buffer.length);
|
||||
sBuffer.read(rlength, tmp_buffer);
|
||||
memcpy(data_ptr, tmp_buffer.buffer, rlength);
|
||||
data_ptr += rlength * sizeof(char);
|
||||
sBuffer.freeRead();
|
||||
}
|
||||
REQUIRE(std::string(cstring) == msg1);
|
||||
REQUIRE(std::string(cstring + length + 1) == msgH);
|
||||
REQUIRE(std::string(cstring + 2 * (length + 1)) == msg2);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A SentBuffer with a message in it") {
|
||||
auto length = runif(0, tmp_buffer.length - 10);
|
||||
auto msg = randomString(length);
|
||||
sBuffer.push(msg);
|
||||
|
||||
REQUIRE(!sBuffer.empty());
|
||||
THEN("Clear will empty it") {
|
||||
sBuffer.clear();
|
||||
REQUIRE(sBuffer.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
#include "painlessmesh/callback.hpp"
|
||||
|
||||
using namespace painlessmesh;
|
||||
|
||||
logger::LogClass Log;
|
||||
|
||||
SCENARIO("CallbackMap should hold multiple callbacks by ID") {
|
||||
GIVEN("A callback map with added callbacks") {
|
||||
auto cbl = callback::PackageCallbackList<int>();
|
||||
|
||||
auto i = 0;
|
||||
auto j = 0;
|
||||
|
||||
cbl.onPackage(1, [&i](int z) { ++i; });
|
||||
cbl.onPackage(1, [&j](int z) { ++j; });
|
||||
|
||||
WHEN("We call execute") {
|
||||
auto cnt = cbl.execute(1, 0);
|
||||
REQUIRE(cnt == 2);
|
||||
THEN("The callbacks are called") {
|
||||
REQUIRE(i == 1);
|
||||
REQUIRE(j == 1);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("We call execute on another event") {
|
||||
auto cnt = cbl.execute(2, 0);
|
||||
REQUIRE(cnt == 0);
|
||||
THEN("The callbacks are not called") {
|
||||
REQUIRE(i == 0);
|
||||
REQUIRE(j == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 1
|
||||
#include "ArduinoJson.h"
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
#undef PAINLESSMESH_ENABLE_ARDUINO_STRING
|
||||
#define PAINLESSMESH_ENABLE_STD_STRING
|
||||
typedef std::string TSTRING;
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
#include "painlessmesh/layout.hpp"
|
||||
#include "painlessmesh/protocol.hpp"
|
||||
|
||||
using namespace painlessmesh;
|
||||
|
||||
SCENARIO("isRoot returns true if the top level Node is the root of the mesh") {
|
||||
GIVEN("A nodeTree with root as a top node") {
|
||||
std::string rootJson =
|
||||
"{\"type\":6,\"root\":true,\"dest\":2428398258,\"from\":3907768579,"
|
||||
"\"subs\":[{"
|
||||
"\"nodeId\":3959373838,\"subs\":[{\"nodeId\":416992913},{\"nodeId\":"
|
||||
"1895675348}]}]}";
|
||||
auto variant = protocol::Variant(rootJson);
|
||||
auto tree1 = variant.to<protocol::NodeTree>();
|
||||
THEN("isRoot returns true") { REQUIRE(layout::isRoot(tree1)); }
|
||||
}
|
||||
GIVEN("A nodeTree without a root as a top node") {
|
||||
std::string jsonTree1 =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
auto variant1 = protocol::Variant(jsonTree1);
|
||||
auto tree1 = variant1.to<protocol::NodeTree>();
|
||||
std::string jsonTree2 =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
auto variant2 = protocol::Variant(jsonTree2);
|
||||
auto tree2 = variant2.to<protocol::NodeTree>();
|
||||
THEN("isRoot returns false") {
|
||||
REQUIRE(!layout::isRoot(tree1));
|
||||
REQUIRE(!layout::isRoot(tree2));
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A random tree with a root at top level") {
|
||||
auto tree1 = createNodeTree(runif(1, 255), 0);
|
||||
THEN("isRoot returns true") { REQUIRE(layout::isRoot(tree1)); }
|
||||
}
|
||||
|
||||
GIVEN("A random tree with no root at top level") {
|
||||
auto noNodes = runif(2, 255);
|
||||
auto tree1 = createNodeTree(noNodes, runif(1, noNodes - 1));
|
||||
auto tree2 = createNodeTree(runif(1, 255), -1);
|
||||
THEN("isRoot returns false") {
|
||||
REQUIRE(!layout::isRoot(tree1));
|
||||
REQUIRE(!layout::isRoot(tree2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("isRooted returns true if any node in the mesh is the root node") {
|
||||
GIVEN("A nodeTree with root as a top node") {
|
||||
std::string rootJson =
|
||||
"{\"type\":6,\"root\":true,\"dest\":2428398258,\"from\":3907768579,"
|
||||
"\"subs\":[{"
|
||||
"\"nodeId\":3959373838,\"subs\":[{\"nodeId\":416992913},{\"nodeId\":"
|
||||
"1895675348}]}]}";
|
||||
auto variant = protocol::Variant(rootJson);
|
||||
auto tree1 = variant.to<protocol::NodeTree>();
|
||||
THEN("isRooted returns true") { REQUIRE(layout::isRooted(tree1)); }
|
||||
}
|
||||
GIVEN("A nodeTree with a root some where else") {
|
||||
std::string jsonTree1 =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
auto variant1 = protocol::Variant(jsonTree1);
|
||||
auto tree1 = variant1.to<protocol::NodeTree>();
|
||||
THEN("isRooted returns true") { REQUIRE(layout::isRooted(tree1)); }
|
||||
}
|
||||
|
||||
GIVEN("A nodeTree without a root any where else") {
|
||||
std::string jsonTree2 =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
auto variant2 = protocol::Variant(jsonTree2);
|
||||
auto tree2 = variant2.to<protocol::NodeTree>();
|
||||
THEN("isRooted returns false") { REQUIRE(!layout::isRoot(tree2)); }
|
||||
}
|
||||
|
||||
GIVEN("A random tree with a root") {
|
||||
auto noNodes = runif(1, 255);
|
||||
auto tree1 = createNodeTree(noNodes, runif(0, noNodes - 1));
|
||||
THEN("isRooted returns true") { REQUIRE(layout::isRooted(tree1)); }
|
||||
}
|
||||
|
||||
GIVEN("A random tree without a root") {
|
||||
auto tree1 = createNodeTree(runif(1, 255), -1);
|
||||
THEN("isRooted returns false") { REQUIRE(!layout::isRooted(tree1)); }
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("We can get the size of the mesh") {
|
||||
GIVEN("A tree with a set size") {
|
||||
std::string jsonTree =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
auto variant = protocol::Variant(jsonTree);
|
||||
auto tree = variant.to<protocol::NodeTree>();
|
||||
THEN("Size returns the correct size") { REQUIRE(layout::size(tree) == 4); }
|
||||
}
|
||||
GIVEN("A random tree with a set size") {
|
||||
auto noNodes = runif(1, 255);
|
||||
auto tree = createNodeTree(noNodes, runif(-1, noNodes - 1));
|
||||
THEN("Size returns the correct size") {
|
||||
REQUIRE(layout::size(tree) == noNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("We can confirm whether a mesh contains specific nodes") {
|
||||
GIVEN("A tree with known nodes") {
|
||||
std::string jsonTree =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3107768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
auto variant = protocol::Variant(jsonTree);
|
||||
auto tree = variant.to<protocol::NodeTree>();
|
||||
THEN(
|
||||
"contains should return the true when it contains a node, false "
|
||||
"otherwise") {
|
||||
REQUIRE(layout::contains(tree, 1895675348));
|
||||
REQUIRE(layout::contains(tree, 3907768579));
|
||||
REQUIRE(layout::contains(tree, 3959373838));
|
||||
REQUIRE(!layout::contains(tree, 0));
|
||||
REQUIRE(!layout::contains(tree, 2428398258));
|
||||
REQUIRE(!layout::contains(tree, 3107768579));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("A layout neighbour knows when to update its sub") {
|
||||
GIVEN("A Neighbour") {
|
||||
std::string jsonTree =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3107768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
auto variant = protocol::Variant(jsonTree);
|
||||
auto tree = variant.to<layout::Neighbour>();
|
||||
// auto neighbour = std::interpret_cast<layout::Neighbour*>(pTree);
|
||||
auto neighbour = tree;
|
||||
THEN("When passed the same tree updateSubs() will return false") {
|
||||
REQUIRE(!neighbour.updateSubs(tree));
|
||||
}
|
||||
|
||||
auto tree1 = createNodeTree(runif(2, 5), -1);
|
||||
tree1.nodeId = neighbour.nodeId;
|
||||
THEN("When passing a different tree it will get updated") {
|
||||
REQUIRE(neighbour.updateSubs(tree1));
|
||||
REQUIRE(tree1 == neighbour);
|
||||
}
|
||||
|
||||
THEN("When current nodeId is zero then updateSubs() will return true") {
|
||||
neighbour.nodeId = 0;
|
||||
REQUIRE(neighbour.updateSubs(tree));
|
||||
REQUIRE(neighbour == tree);
|
||||
REQUIRE(neighbour.nodeId == tree.nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "painlessmesh/logger.hpp"
|
||||
using namespace painlessmesh::logger;
|
||||
|
||||
LogClass Log;
|
||||
|
||||
SCENARIO("We can log things") {
|
||||
Log.setLogLevel(ERROR | DEBUG | COMMUNICATION);
|
||||
Log(ERROR, "We should see the next %u lines\n", 3);
|
||||
Log(DEBUG, "We should see the next %u lines\n", 2);
|
||||
Log(COMMUNICATION, "We should see the next %u lines\n", 1);
|
||||
Log(ERROR, "But not the next one\n");
|
||||
Log(S_TIME, "This should not be showing\n");
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
#include "painlessmesh/ntp.hpp"
|
||||
|
||||
using namespace painlessmesh;
|
||||
|
||||
logger::LogClass Log;
|
||||
@@ -0,0 +1,174 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
#include "painlessmesh/plugin.hpp"
|
||||
#include "plugin/performance.hpp"
|
||||
|
||||
using namespace painlessmesh;
|
||||
|
||||
logger::LogClass Log;
|
||||
|
||||
class CustomPackage : public plugin::SinglePackage {
|
||||
public:
|
||||
double sensor = 1.0;
|
||||
|
||||
CustomPackage() : SinglePackage(20) {}
|
||||
|
||||
CustomPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {
|
||||
sensor = jsonObj["sensor"];
|
||||
}
|
||||
|
||||
JsonObject addTo(JsonObject&& jsonObj) const {
|
||||
jsonObj = SinglePackage::addTo(std::move(jsonObj));
|
||||
jsonObj["sensor"] = sensor;
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 1); }
|
||||
};
|
||||
|
||||
class BCustomPackage : public plugin::BroadcastPackage {
|
||||
public:
|
||||
double sensor = 1.0;
|
||||
|
||||
BCustomPackage() : BroadcastPackage(21) {}
|
||||
|
||||
BCustomPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
||||
sensor = jsonObj["sensor"];
|
||||
}
|
||||
|
||||
JsonObject addTo(JsonObject&& jsonObj) const {
|
||||
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
||||
jsonObj["sensor"] = sensor;
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 1); }
|
||||
};
|
||||
|
||||
class MockConnection : public layout::Neighbour {
|
||||
public:
|
||||
bool addMessage(TSTRING msg) { return true; }
|
||||
};
|
||||
|
||||
SCENARIO("We can send a custom package") {
|
||||
GIVEN("A package") {
|
||||
auto pkg = CustomPackage();
|
||||
pkg.from = 1;
|
||||
pkg.dest = 2;
|
||||
pkg.sensor = 0.5;
|
||||
REQUIRE(pkg.routing == router::SINGLE);
|
||||
REQUIRE(pkg.type == 20);
|
||||
WHEN("Converting it to and from Variant") {
|
||||
auto var = protocol::Variant(&pkg);
|
||||
auto pkg2 = var.to<CustomPackage>();
|
||||
THEN("Should result in the same values") {
|
||||
REQUIRE(pkg2.sensor == pkg.sensor);
|
||||
REQUIRE(pkg2.from == pkg.from);
|
||||
REQUIRE(pkg2.dest == pkg.dest);
|
||||
REQUIRE(pkg2.routing == pkg.routing);
|
||||
REQUIRE(pkg2.type == pkg.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A broadcast package") {
|
||||
auto pkg = BCustomPackage();
|
||||
pkg.from = 1;
|
||||
pkg.sensor = 0.5;
|
||||
REQUIRE(pkg.routing == router::BROADCAST);
|
||||
REQUIRE(pkg.type == 21);
|
||||
WHEN("Converting it to and from Variant") {
|
||||
auto var = protocol::Variant(&pkg);
|
||||
auto pkg2 = var.to<CustomPackage>();
|
||||
THEN("Should result in the same values") {
|
||||
REQUIRE(pkg2.sensor == pkg.sensor);
|
||||
REQUIRE(pkg2.from == pkg.from);
|
||||
REQUIRE(pkg2.routing == pkg.routing);
|
||||
REQUIRE(pkg2.type == pkg.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A package handler function") {
|
||||
auto handler = plugin::PackageHandler<MockConnection>();
|
||||
auto func = [](protocol::Variant variant) {
|
||||
auto pkg = variant.to<CustomPackage>();
|
||||
REQUIRE(pkg.routing == router::BROADCAST);
|
||||
return false;
|
||||
};
|
||||
THEN("We can pass it to handler") { handler.onPackage(20, func); }
|
||||
}
|
||||
|
||||
GIVEN("A package") {
|
||||
auto handler = plugin::PackageHandler<MockConnection>();
|
||||
auto pkg = CustomPackage();
|
||||
THEN("We can call sendPackage") {
|
||||
auto res = handler.sendPackage(&pkg);
|
||||
REQUIRE(!res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("We can add tasks to the taskscheduler") {
|
||||
GIVEN("A couple of tasks added") {
|
||||
Scheduler mScheduler;
|
||||
auto handler = plugin::PackageHandler<MockConnection>();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
auto task1 = handler.addTask(mScheduler, 0, 1, [&i]() { ++i; });
|
||||
auto task2 = handler.addTask(mScheduler, 0, 3, [&j]() { ++j; });
|
||||
auto task3 = handler.addTask(mScheduler, 0, 3, [&k]() { ++k; });
|
||||
auto task4 = handler.addTask(mScheduler, 0, 3, []() {});
|
||||
|
||||
WHEN("Executing the tasks") {
|
||||
THEN("They should be called and automatically removed") {
|
||||
REQUIRE(i == 0);
|
||||
REQUIRE(j == 0);
|
||||
mScheduler.execute();
|
||||
REQUIRE(i == 1);
|
||||
mScheduler.execute();
|
||||
REQUIRE(i == 1);
|
||||
REQUIRE(j == 2);
|
||||
// Still kept in handler, because hasn't been executed 3 times yet
|
||||
task3->disable();
|
||||
handler.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("We can add anonymous tasks to the taskscheduler") {
|
||||
GIVEN("A couple of tasks added") {
|
||||
Scheduler mScheduler;
|
||||
auto handler = plugin::PackageHandler<MockConnection>();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
handler.addTask(mScheduler, 0, 1, [&i]() { ++i; });
|
||||
handler.addTask(mScheduler, 0, 3, [&j]() { ++j; });
|
||||
handler.addTask(mScheduler, 0, 3, [&k]() { ++k; });
|
||||
|
||||
WHEN("Executing the tasks") {
|
||||
THEN("They should be called and automatically removed") {
|
||||
REQUIRE(i == 0);
|
||||
REQUIRE(j == 0);
|
||||
mScheduler.execute();
|
||||
REQUIRE(i == 1);
|
||||
mScheduler.execute();
|
||||
REQUIRE(i == 1);
|
||||
REQUIRE(j == 2);
|
||||
handler.addTask(mScheduler, 0, 1, [&i]() { ++i; });
|
||||
mScheduler.execute();
|
||||
REQUIRE(i == 2);
|
||||
handler.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 1
|
||||
#include "ArduinoJson.h"
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
typedef std::string TSTRING;
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
#include "painlessmesh/protocol.hpp"
|
||||
|
||||
using namespace painlessmesh::protocol;
|
||||
|
||||
SCENARIO("A variant knows its type", "[Variant][protocol]") {
|
||||
GIVEN("A json string with the type 9 ") {
|
||||
std::string str = "{\"type\": 9}";
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(str);
|
||||
|
||||
THEN("The variant is a Single type") {
|
||||
REQUIRE(variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A json string with the type 8 ") {
|
||||
std::string str = "{\"type\": 8}";
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(str);
|
||||
|
||||
THEN("The variant is a Broadcast type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(variant.is<Broadcast>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A json string with the type 6 ") {
|
||||
std::string str = "{\"type\": 6}";
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(str);
|
||||
|
||||
THEN("The variant is a NodeSyncReply type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(variant.is<NodeSyncReply>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A json string with the type 5 ") {
|
||||
std::string str = "{\"type\": 5}";
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(str);
|
||||
|
||||
THEN("The variant is a NodeSyncRequest type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(variant.is<NodeSyncRequest>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A json string with the type 4 ") {
|
||||
std::string str = "{\"type\": 4}";
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(str);
|
||||
|
||||
THEN("The variant is a TimeSync type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(variant.is<TimeSync>());
|
||||
}
|
||||
}
|
||||
}
|
||||
GIVEN("A json string with the type 3 ") {
|
||||
std::string str = "{\"type\": 3}";
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(str);
|
||||
|
||||
THEN("The variant is a TimeDelay type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(variant.is<TimeDelay>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SCENARIO("A variant can take a packageinterface", "[Variant][protocol]") {
|
||||
GIVEN("A Single package") {
|
||||
auto pkg = createSingle();
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(&pkg);
|
||||
THEN("The variant is a Single type") {
|
||||
REQUIRE(variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(!variant.is<TimeDelay>());
|
||||
}
|
||||
|
||||
THEN("The variant can be converted to a Single") {
|
||||
auto newPkg = variant.to<Single>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.msg == pkg.msg);
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("A variant can take any package", "[Variant][protocol]") {
|
||||
GIVEN("A Single package") {
|
||||
auto pkg = createSingle();
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(pkg);
|
||||
THEN("The variant is a Single type") {
|
||||
REQUIRE(variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(!variant.is<TimeDelay>());
|
||||
}
|
||||
|
||||
THEN("The variant can be converted to a Single") {
|
||||
auto newPkg = variant.to<Single>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.msg == pkg.msg);
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A Broadcast package") {
|
||||
auto pkg = createBroadcast(5);
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(pkg);
|
||||
THEN("The variant is a Broadcast type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(!variant.is<TimeDelay>());
|
||||
}
|
||||
|
||||
THEN("The variant can be converted to a Broadcast") {
|
||||
auto newPkg = variant.to<Broadcast>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.msg == pkg.msg);
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A NodeSyncReply package") {
|
||||
auto pkg = createNodeSyncReply(15);
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(pkg);
|
||||
THEN("The variant is a NodeSyncReply type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(!variant.is<TimeDelay>());
|
||||
}
|
||||
THEN("The variant can be converted to a NodeSyncReply") {
|
||||
auto newPkg = variant.to<NodeSyncReply>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.nodeId == pkg.nodeId);
|
||||
REQUIRE(newPkg.root == pkg.root);
|
||||
REQUIRE(newPkg.subs.size() == pkg.subs.size());
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
REQUIRE(newPkg == pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A NodeSyncReply package of random size") {
|
||||
auto pkg = createNodeSyncReply();
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(pkg);
|
||||
|
||||
THEN("The variant throws no error") { REQUIRE(!variant.error); }
|
||||
THEN("The variant is a NodeSyncReply type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(!variant.is<TimeDelay>());
|
||||
}
|
||||
THEN("The variant can be converted to a NodeSyncReply") {
|
||||
auto newPkg = variant.to<NodeSyncReply>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.nodeId == pkg.nodeId);
|
||||
REQUIRE(newPkg.root == pkg.root);
|
||||
REQUIRE(newPkg.subs.size() == pkg.subs.size());
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
REQUIRE(newPkg == pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A NodeSyncRequest package") {
|
||||
auto pkg = createNodeSyncRequest(5);
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(pkg);
|
||||
THEN("The variant is a NodeSyncRequest type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(!variant.is<TimeDelay>());
|
||||
}
|
||||
THEN("The variant can be converted to a NodeSyncRequest") {
|
||||
auto newPkg = variant.to<NodeSyncRequest>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.nodeId == pkg.nodeId);
|
||||
REQUIRE(newPkg.root == pkg.root);
|
||||
REQUIRE(newPkg.subs.size() == pkg.subs.size());
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
REQUIRE(newPkg == pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A TimeSync package") {
|
||||
auto pkg = createTimeSync();
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(pkg);
|
||||
THEN("The variant is a TimeSync type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(variant.is<TimeSync>());
|
||||
REQUIRE(!variant.is<TimeDelay>());
|
||||
}
|
||||
|
||||
THEN("The variant can be converted to a TimeSync") {
|
||||
auto newPkg = variant.to<TimeSync>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
REQUIRE(newPkg.msg.type == pkg.msg.type);
|
||||
REQUIRE(newPkg.msg.t0 == pkg.msg.t0);
|
||||
REQUIRE(newPkg.msg.t1 == pkg.msg.t1);
|
||||
REQUIRE(newPkg.msg.t2 == pkg.msg.t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A TimeDelay package") {
|
||||
auto pkg = createTimeDelay();
|
||||
WHEN("Passed to a Variant") {
|
||||
auto variant = Variant(pkg);
|
||||
THEN("The variant is a TimeDelay type") {
|
||||
REQUIRE(!variant.is<Single>());
|
||||
REQUIRE(!variant.is<Broadcast>());
|
||||
REQUIRE(!variant.is<NodeSyncReply>());
|
||||
REQUIRE(!variant.is<NodeSyncRequest>());
|
||||
REQUIRE(!variant.is<TimeSync>());
|
||||
REQUIRE(variant.is<TimeDelay>());
|
||||
}
|
||||
THEN("The variant can be converted to a TimeDelay") {
|
||||
auto newPkg = variant.to<TimeDelay>();
|
||||
REQUIRE(newPkg.dest == pkg.dest);
|
||||
REQUIRE(newPkg.from == pkg.from);
|
||||
REQUIRE(newPkg.type == pkg.type);
|
||||
REQUIRE(newPkg.msg.type == pkg.msg.type);
|
||||
REQUIRE(newPkg.msg.t0 == pkg.msg.t0);
|
||||
REQUIRE(newPkg.msg.t1 == pkg.msg.t1);
|
||||
REQUIRE(newPkg.msg.t2 == pkg.msg.t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("NodeSyncReply is backwards compatible", "[Variant][protocol]") {
|
||||
GIVEN("A json string without a base nodeId") {
|
||||
std::string old =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"subs\":[{"
|
||||
"\"nodeId\":3959373838,\"subs\":[{\"nodeId\":416992913},{\"nodeId\":"
|
||||
"1895675348,\"root\":true}]}]}";
|
||||
std::string withId =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
WHEN("Converted to a NodeSyncReply") {
|
||||
auto variant = Variant(old);
|
||||
auto nsr = variant.to<NodeSyncReply>();
|
||||
auto variantId = Variant(withId);
|
||||
auto nsrId = variantId.to<NodeSyncReply>();
|
||||
THEN("NodeId is set to from") {
|
||||
REQUIRE(nsr.from == nsr.nodeId);
|
||||
REQUIRE(nsr == nsrId);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Converted to a NodeTree") {
|
||||
auto variant = Variant(old);
|
||||
auto ns = variant.to<NodeTree>();
|
||||
auto variantId = Variant(withId);
|
||||
auto nsId = variantId.to<NodeTree>();
|
||||
auto variantReply = Variant(withId);
|
||||
auto nsrId = variantId.to<NodeSyncReply>();
|
||||
THEN("NodeId is set to from value") {
|
||||
REQUIRE(nsrId.from == ns.nodeId);
|
||||
REQUIRE(nsrId.nodeId == nsId.nodeId);
|
||||
REQUIRE(nsrId.subs == ns.subs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A json string with root explicitly set to false") {
|
||||
std::string old =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
std::string withId =
|
||||
"{\"type\":6,\"dest\":2428398258,\"root\":false,\"from\":3907768579,"
|
||||
"\"nodeId\":3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{"
|
||||
"\"nodeId\":416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
WHEN("Converted to a NodeSyncReply") {
|
||||
auto variant = Variant(old);
|
||||
auto nsr = variant.to<NodeSyncReply>();
|
||||
auto variantId = Variant(withId);
|
||||
auto nsrId = variantId.to<NodeSyncReply>();
|
||||
THEN("NodeId is set to from") { REQUIRE(nsr == nsrId); }
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A json string with subs explicitly set to empty") {
|
||||
std::string old =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true,\"subs\":[]}]}]}";
|
||||
std::string withId =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3907768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348,\"root\":true}]}]}";
|
||||
WHEN("Converted to a NodeSyncReply") {
|
||||
auto variant = Variant(old);
|
||||
auto nsr = variant.to<NodeSyncReply>();
|
||||
auto variantId = Variant(withId);
|
||||
auto nsrId = variantId.to<NodeSyncReply>();
|
||||
THEN("NodeId is set to from") { REQUIRE(nsr == nsrId); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("NodeSyncReply supports the == operator", "[Variant][protocol]") {
|
||||
GIVEN("Different NodeSyncReplies") {
|
||||
auto pkg1 = createNodeSyncReply(5);
|
||||
auto pkg2 = createNodeSyncReply(5);
|
||||
|
||||
// Same subs different base
|
||||
auto pkg3 = createNodeSyncReply(5);
|
||||
auto pkg4 = createNodeSyncReply(5);
|
||||
pkg4.subs = pkg3.subs;
|
||||
|
||||
// Same base different subs
|
||||
auto pkg5 = pkg4;
|
||||
pkg5.subs = pkg1.subs;
|
||||
THEN("They are not equal") {
|
||||
REQUIRE(pkg1 != pkg2);
|
||||
REQUIRE(pkg2 == pkg2);
|
||||
REQUIRE(!(pkg2 != pkg2));
|
||||
REQUIRE(!(pkg1 != pkg1));
|
||||
|
||||
REQUIRE(pkg3 != pkg4);
|
||||
REQUIRE(pkg3.subs == pkg4.subs);
|
||||
|
||||
REQUIRE(pkg5 != pkg4);
|
||||
REQUIRE(pkg5.subs != pkg4.subs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("A variant can printTo a package", "[Variant][protocol]") {
|
||||
GIVEN("A NodeSyncReply package printed to a string using Variant") {
|
||||
auto pkg = createNodeSyncReply(5);
|
||||
std::string str;
|
||||
auto variant = Variant(pkg);
|
||||
variant.printTo(str);
|
||||
THEN("It can be converted back into an identical pkg") {
|
||||
auto variant = Variant(str);
|
||||
auto pkg2 = variant.to<NodeSyncReply>();
|
||||
REQUIRE(pkg2 == pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("The Variant type properly carries over errors",
|
||||
"[Variant][protocol][error]") {
|
||||
GIVEN("A large and small NodeSyncReply pkg") {
|
||||
auto large_pkg = createNodeSyncReply(100);
|
||||
auto large_variant = Variant(large_pkg);
|
||||
std::string large_json;
|
||||
large_variant.printTo(large_json);
|
||||
|
||||
auto small_pkg = createNodeSyncReply(5);
|
||||
auto small_variant = Variant(small_pkg);
|
||||
std::string small_json;
|
||||
small_variant.printTo(small_json);
|
||||
|
||||
THEN("It carries over the ArduinoJson error") {
|
||||
auto large_var = Variant(large_json, 1024);
|
||||
REQUIRE(large_var.error);
|
||||
auto small_var = Variant(small_json, 1024);
|
||||
REQUIRE(!small_var.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO(
|
||||
"The construction of a Time package automatically sets the correct time "
|
||||
"sync type",
|
||||
"[protocol]") {
|
||||
GIVEN("Calling the constructor with no time") {
|
||||
auto pkg1 = TimeSync(10, 11);
|
||||
THEN("The time type is TIME_SYNC_REQUEST") {
|
||||
REQUIRE(pkg1.msg.type == TIME_SYNC_REQUEST);
|
||||
}
|
||||
}
|
||||
GIVEN("Calling the constructor with one time") {
|
||||
auto pkg1 = TimeSync(10, 11, 12);
|
||||
THEN("The time type is TIME_REQUEST") {
|
||||
REQUIRE(pkg1.msg.type == TIME_REQUEST);
|
||||
REQUIRE(pkg1.msg.t0 == 12);
|
||||
}
|
||||
}
|
||||
GIVEN("Calling the constructor with two or three times") {
|
||||
auto pkg2 = TimeSync(10, 11, 12, 13);
|
||||
auto pkg3 = TimeSync(10, 11, 12, 13, 14);
|
||||
THEN("The time type is TIME_REPLY") {
|
||||
REQUIRE(pkg2.msg.type == TIME_REPLY);
|
||||
REQUIRE(pkg2.msg.t0 == 12);
|
||||
REQUIRE(pkg2.msg.t1 == 13);
|
||||
REQUIRE(pkg3.msg.type == TIME_REPLY);
|
||||
REQUIRE(pkg3.msg.t0 == 12);
|
||||
REQUIRE(pkg3.msg.t1 == 13);
|
||||
REQUIRE(pkg3.msg.t2 == 14);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Calling the constructor with no time") {
|
||||
auto pkg1 = TimeDelay(10, 11);
|
||||
THEN("The time type is TIME_SYNC_REQUEST") {
|
||||
REQUIRE(pkg1.msg.type == TIME_SYNC_REQUEST);
|
||||
}
|
||||
}
|
||||
GIVEN("Calling the constructor with one time") {
|
||||
auto pkg1 = TimeDelay(10, 11, 12);
|
||||
THEN("The time type is TIME_REQUEST") {
|
||||
REQUIRE(pkg1.msg.type == TIME_REQUEST);
|
||||
REQUIRE(pkg1.msg.t0 == 12);
|
||||
}
|
||||
}
|
||||
GIVEN("Calling the constructor with two or three times") {
|
||||
auto pkg2 = TimeDelay(10, 11, 12, 13);
|
||||
auto pkg3 = TimeDelay(10, 11, 12, 13, 14);
|
||||
THEN("The time type is TIME_REPLY") {
|
||||
REQUIRE(pkg2.msg.type == TIME_REPLY);
|
||||
REQUIRE(pkg2.msg.t0 == 12);
|
||||
REQUIRE(pkg2.msg.t1 == 13);
|
||||
REQUIRE(pkg3.msg.type == TIME_REPLY);
|
||||
REQUIRE(pkg3.msg.t0 == 12);
|
||||
REQUIRE(pkg3.msg.t1 == 13);
|
||||
REQUIRE(pkg3.msg.t2 == 14);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("We can construct a reply to Time packages", "[protocol]") {
|
||||
GIVEN("A reply to a TIME_SYNC_REQUEST") {
|
||||
auto origPkg1 = TimeSync(10, 11);
|
||||
auto pkg1 = TimeSync(10, 11);
|
||||
pkg1.reply(12);
|
||||
auto origPkg2 = TimeDelay(10, 11);
|
||||
auto pkg2 = TimeDelay(10, 11);
|
||||
pkg2.reply(12);
|
||||
THEN("It will set t0, update type and swap from and dest.") {
|
||||
REQUIRE(pkg1.msg.type == TIME_REQUEST);
|
||||
REQUIRE(pkg2.msg.type == TIME_REQUEST);
|
||||
REQUIRE(pkg1.msg.t0 == 12);
|
||||
REQUIRE(pkg2.msg.t0 == 12);
|
||||
REQUIRE(pkg1.from == origPkg1.dest);
|
||||
REQUIRE(pkg2.from == origPkg2.dest);
|
||||
REQUIRE(pkg1.dest == origPkg1.from);
|
||||
REQUIRE(pkg2.dest == origPkg2.from);
|
||||
REQUIRE(pkg1.type == origPkg1.type);
|
||||
REQUIRE(pkg2.type == origPkg2.type);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A reply to a TIME_REQUEST") {
|
||||
auto origPkg1 = TimeSync(10, 11, 12);
|
||||
auto pkg1 = TimeSync(10, 11, 12);
|
||||
pkg1.reply(13, 14);
|
||||
auto origPkg2 = TimeDelay(10, 11, 12);
|
||||
auto pkg2 = TimeDelay(10, 11, 12);
|
||||
pkg2.reply(13, 14);
|
||||
THEN("It will set t1 and t2, update type and swap from and dest.") {
|
||||
REQUIRE(pkg1.msg.type == TIME_REPLY);
|
||||
REQUIRE(pkg2.msg.type == TIME_REPLY);
|
||||
REQUIRE(pkg1.msg.t0 == 12);
|
||||
REQUIRE(pkg2.msg.t0 == 12);
|
||||
REQUIRE(pkg1.msg.t1 == 13);
|
||||
REQUIRE(pkg2.msg.t1 == 13);
|
||||
REQUIRE(pkg1.msg.t2 == 14);
|
||||
REQUIRE(pkg2.msg.t2 == 14);
|
||||
REQUIRE(pkg1.from == origPkg1.dest);
|
||||
REQUIRE(pkg2.from == origPkg2.dest);
|
||||
REQUIRE(pkg1.dest == origPkg1.from);
|
||||
REQUIRE(pkg2.dest == origPkg2.from);
|
||||
REQUIRE(pkg1.type == origPkg1.type);
|
||||
REQUIRE(pkg2.type == origPkg2.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Package constructors work as expected", "[protocol]") {
|
||||
GIVEN("A Single package constructed with the constructor") {
|
||||
std::string str = "Blaat";
|
||||
auto pkg = Single(10, 0, str);
|
||||
THEN("Message will be set correctly") { REQUIRE(pkg.msg == "Blaat"); }
|
||||
}
|
||||
GIVEN("A Broadcast package constructed with the constructor") {
|
||||
std::string str = "Blaat";
|
||||
auto pkg = Broadcast(10, 0, str);
|
||||
THEN("Message will be set correctly") {
|
||||
REQUIRE(pkg.msg == "Blaat");
|
||||
REQUIRE(pkg.type == BROADCAST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
#include "painlessmesh/router.hpp"
|
||||
|
||||
using namespace painlessmesh;
|
||||
|
||||
logger::LogClass Log;
|
||||
|
||||
/*
|
||||
class MockConnection : public protocol::NodeTree {
|
||||
public:
|
||||
void addMessage(TSTRING msg, bool priority = false) { ++cnt; }
|
||||
|
||||
int cnt = 0;
|
||||
};
|
||||
|
||||
SCENARIO("findRoute works as expected with different types of connections") {
|
||||
GIVEN("A layout with Neighbour shared ptrs") {
|
||||
auto layout = layout::Layout<layout::Neighbour>();
|
||||
std::string jsonTree1 =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3107768579,\"nodeId\":"
|
||||
"3907768579,\"subs\":[{\"nodeId\":3959373838,\"subs\":[{\"nodeId\":"
|
||||
"416992913},{\"nodeId\":1895675348}]}]}";
|
||||
auto variant1 = protocol::Variant(jsonTree1);
|
||||
auto tree1 =
|
||||
std::make_shared<layout::Neighbour>(variant1.to<layout::Neighbour>());
|
||||
|
||||
std::string jsonTree2 =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3107768579,\"nodeId\":"
|
||||
"3907768580,\"subs\":[{\"nodeId\":3959373839,\"subs\":[{\"nodeId\":"
|
||||
"416992914},{\"nodeId\":1895675349}]}]}";
|
||||
auto variant2 = protocol::Variant(jsonTree2);
|
||||
auto tree2 =
|
||||
std::make_shared<layout::Neighbour>(variant2.to<layout::Neighbour>());
|
||||
|
||||
std::string jsonTree3 =
|
||||
"{\"type\":6,\"dest\":2428398258,\"from\":3107768579,\"nodeId\":"
|
||||
"3907768581,\"subs\":[{\"nodeId\":3959373840,\"subs\":[{\"nodeId\":"
|
||||
"416992915},{\"nodeId\":1895675350}]}]}";
|
||||
auto variant3 = protocol::Variant(jsonTree3);
|
||||
auto tree3 =
|
||||
std::make_shared<layout::Neighbour>(variant3.to<layout::Neighbour>());
|
||||
|
||||
layout.subs.push_back(tree1);
|
||||
layout.subs.push_back(tree2);
|
||||
layout.subs.push_back(tree3);
|
||||
|
||||
layout.nodeId = runif(1, 1000);
|
||||
|
||||
THEN("findRoute works") {
|
||||
auto rt = router::findRoute<layout::Neighbour>(layout, 1895675350);
|
||||
REQUIRE(rt->nodeId == 3907768581);
|
||||
rt = router::findRoute<layout::Neighbour>(layout, 1895675351);
|
||||
REQUIRE(!rt);
|
||||
}
|
||||
|
||||
THEN("It can be converted to a NodeTree") {
|
||||
auto lay = layout.asNodeTree();
|
||||
auto nt = protocol::NodeTree();
|
||||
nt.nodeId = lay.nodeId;
|
||||
for (auto &&s : lay.subs) {
|
||||
nt.subs.push_back(s);
|
||||
}
|
||||
REQUIRE(nt == lay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("routePackage should route the package correctly") {
|
||||
GIVEN("A CallbackList and layout") {
|
||||
auto cbl = router::CallbackList();
|
||||
auto lay = layout::Layout<MockConnection>();
|
||||
lay.nodeId = 1;
|
||||
lay.subs.push_back(std::make_shared<MockConnection>());
|
||||
lay.subs.back()->nodeId = 2;
|
||||
lay.subs.push_back(std::make_shared<MockConnection>());
|
||||
lay.subs.back()->nodeId = 3;
|
||||
lay.subs.push_back(std::make_shared<MockConnection>());
|
||||
lay.subs.back()->nodeId = 4;
|
||||
|
||||
// void routePackage(Layout<T>, T conn, TSTRING pkg, CallbackMap)
|
||||
WHEN("Passed a package with routing NEIGHBOUR") {
|
||||
auto pkg = createTimeSync();
|
||||
TSTRING str;
|
||||
auto var = protocol::Variant(pkg);
|
||||
REQUIRE(var.routing() == router::NEIGHBOUR);
|
||||
var.printTo(str);
|
||||
// message type NEIGHBOUR should result in a callback
|
||||
}
|
||||
|
||||
// message type BROADCAST should result in a callback and being send on
|
||||
|
||||
// message type SINGLE if destination is other then send otherwise callback
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,49 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "catch_utils.hpp"
|
||||
|
||||
WiFiClass WiFi;
|
||||
ESPClass ESP;
|
||||
|
||||
#include "painlessmesh/logger.hpp"
|
||||
|
||||
using namespace painlessmesh;
|
||||
|
||||
logger::LogClass Log;
|
||||
|
||||
SCENARIO("Fake Async classes behave similar to real ones") {
|
||||
int i = 0;
|
||||
std::string j = "";
|
||||
auto server = AsyncServer();
|
||||
AsyncClient *conn;
|
||||
server.onClient([&conn, &i, &j](void *, AsyncClient *client) {
|
||||
conn = client;
|
||||
conn->onData([&j](void *, AsyncClient *client, void *data,
|
||||
size_t len) { j = std::string((char *)data, len); },
|
||||
NULL);
|
||||
++i;
|
||||
});
|
||||
|
||||
auto client = AsyncClient(&server);
|
||||
|
||||
std::string j2 = "";
|
||||
client.onData([&j2](void *arg, AsyncClient *client, void *data,
|
||||
size_t len) { j2 = std::string((char *)data, len); },
|
||||
NULL);
|
||||
|
||||
client.connect(IPAddress(), 0);
|
||||
|
||||
THEN("server.onConnect is called") { REQUIRE(i == 1); }
|
||||
THEN("I can send data") {
|
||||
client.write("Blaat", 5);
|
||||
REQUIRE(j == "Blaat");
|
||||
|
||||
conn->write("Blaat terug", 11);
|
||||
REQUIRE(j2 == "Blaat terug");
|
||||
}
|
||||
delete conn;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
DSM2_tx implements the serial communication protocol used for operating
|
||||
the RF modules that can be found in many DSM2-compatible transmitters.
|
||||
Copyrigt (C) 2012 Erik Elmore <erik@ironsavior.net>
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include "fake_serial.hpp"
|
||||
|
||||
void FakeSerial::begin(unsigned long speed) { return; }
|
||||
|
||||
void FakeSerial::end() { return; }
|
||||
|
||||
size_t FakeSerial::write(const unsigned char buf[], size_t size) {
|
||||
using namespace std;
|
||||
ios_base::fmtflags oldFlags = cout.flags();
|
||||
streamsize oldPrec = cout.precision();
|
||||
char oldFill = cout.fill();
|
||||
|
||||
cout << "Serial::write: ";
|
||||
cout << internal << setfill('0');
|
||||
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
cout << setw(2) << hex << (unsigned int)buf[i] << " ";
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
cout.flags(oldFlags);
|
||||
cout.precision(oldPrec);
|
||||
cout.fill(oldFill);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void FakeSerial::print(const char* buf) { std::cout << buf; }
|
||||
|
||||
void FakeSerial::println() { std::cout << std::endl; }
|
||||
|
||||
FakeSerial Serial;
|
||||
13287
arduino-cli/libraries/painlessMesh-master/test/include/catch2/catch.hpp
Normal file
13287
arduino-cli/libraries/painlessMesh-master/test/include/catch2/catch.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,209 @@
|
||||
#ifndef CATCH_UTILS_H_
|
||||
#define CATCH_UTILS_H_
|
||||
|
||||
/*
|
||||
* Some helper functions to be used in catch based tests
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
#include "painlessmesh/protocol.hpp"
|
||||
|
||||
static std::random_device
|
||||
rd; // Will be used to obtain a seed for the random number engine
|
||||
static std::mt19937 gen(rd());
|
||||
|
||||
uint32_t runif(uint32_t from, uint32_t to) {
|
||||
std::uniform_int_distribution<uint32_t> distribution(from, to);
|
||||
return distribution(gen);
|
||||
}
|
||||
|
||||
uint32_t rbinom(size_t n, double p) {
|
||||
std::binomial_distribution<uint32_t> distribution(n, p);
|
||||
return distribution(gen);
|
||||
}
|
||||
|
||||
std::string randomString(uint32_t length) {
|
||||
std::string str;
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
char rnd = (char)runif(65, 90);
|
||||
str += rnd;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void randomCString(char* str, uint32_t length) {
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
char rnd = (char)runif(65, 90);
|
||||
str[i] = rnd;
|
||||
}
|
||||
str[length] = '\0';
|
||||
}
|
||||
|
||||
painlessmesh::protocol::Single createSingle(int length = -1) {
|
||||
auto pkg = painlessmesh::protocol::Single();
|
||||
pkg.dest = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
pkg.from = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
|
||||
if (length < 0) length = runif(0, 4096);
|
||||
pkg.msg = randomString(length);
|
||||
return pkg;
|
||||
}
|
||||
|
||||
painlessmesh::protocol::Broadcast createBroadcast(int length = -1) {
|
||||
auto pkg = painlessmesh::protocol::Broadcast();
|
||||
pkg.dest = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
pkg.from = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
if (length < 0) length = runif(0, 4096);
|
||||
pkg.msg = randomString(length);
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/*
|
||||
```
|
||||
{
|
||||
"dest": ...,
|
||||
"from": ...,
|
||||
"type": ...,
|
||||
"subs": [
|
||||
{
|
||||
"nodeId": ...,
|
||||
"root" : true,
|
||||
"subs": [
|
||||
{
|
||||
"nodeId": ...,
|
||||
"subs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
*/
|
||||
painlessmesh::protocol::NodeTree createNodeTree(int nodes, int contains_root) {
|
||||
auto pkg = painlessmesh::protocol::NodeTree();
|
||||
pkg.nodeId = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
if (contains_root == 0) {
|
||||
pkg.root = true;
|
||||
}
|
||||
--nodes; // The current node
|
||||
--contains_root;
|
||||
auto noSubs = runif(1, 5);
|
||||
for (uint32_t i = 0; i < noSubs; ++i) {
|
||||
if (nodes > 0) {
|
||||
if (i == noSubs - 1) {
|
||||
pkg.subs.push_back(createNodeTree(nodes, contains_root));
|
||||
} else {
|
||||
auto newNodes = 1 + rbinom(nodes - 1, 1.0 / noSubs);
|
||||
nodes -= newNodes;
|
||||
if (newNodes > 0)
|
||||
pkg.subs.push_back(createNodeTree(newNodes, contains_root));
|
||||
contains_root -= newNodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
painlessmesh::protocol::NodeSyncReply createNodeSyncReply(
|
||||
int nodes = -1, bool contains_root = true) {
|
||||
auto pkg = painlessmesh::protocol::NodeSyncReply();
|
||||
pkg.dest = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
pkg.from = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
if (nodes < 0) nodes = runif(1, 254);
|
||||
auto rt = -1;
|
||||
if (contains_root) rt = runif(0, nodes - 1);
|
||||
auto ns = createNodeTree(nodes, rt);
|
||||
pkg.subs = ns.subs;
|
||||
pkg.nodeId = ns.nodeId;
|
||||
pkg.root = ns.root;
|
||||
return pkg;
|
||||
}
|
||||
|
||||
painlessmesh::protocol::NodeSyncRequest createNodeSyncRequest(
|
||||
int nodes = -1, bool contains_root = true) {
|
||||
auto pkg = painlessmesh::protocol::NodeSyncRequest();
|
||||
pkg.dest = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
pkg.from = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
if (nodes < 0) nodes = runif(1, 254);
|
||||
auto rt = -1;
|
||||
if (contains_root) rt = runif(0, nodes - 1);
|
||||
auto ns = createNodeTree(nodes, rt);
|
||||
pkg.subs = ns.subs;
|
||||
pkg.nodeId = ns.nodeId;
|
||||
pkg.root = ns.root;
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/*
|
||||
```
|
||||
{
|
||||
"dest": 887034362,
|
||||
"from": 37418,
|
||||
"type":4,
|
||||
"msg":{
|
||||
"type":0
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"dest": 887034362,
|
||||
"from": 37418,
|
||||
"type":4,
|
||||
"msg":{
|
||||
"type":1,
|
||||
"t0":32990
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"dest": 37418,
|
||||
"from": 887034362,
|
||||
"type":4,
|
||||
"msg":{
|
||||
"type":2,
|
||||
"t0":32990,
|
||||
"t1":448585896,
|
||||
"t2":448596056,
|
||||
}
|
||||
}
|
||||
```
|
||||
*/
|
||||
painlessmesh::protocol::TimeSync createTimeSync(int type = -1) {
|
||||
auto pkg = painlessmesh::protocol::TimeSync();
|
||||
pkg.dest = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
pkg.from = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
|
||||
if (type < 0) type = runif(0, 2);
|
||||
pkg.msg.type = type;
|
||||
auto t = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
if (type >= 1) pkg.msg.t0 = t;
|
||||
if (type >= 2) {
|
||||
t += runif(0, 10000);
|
||||
pkg.msg.t1 = t;
|
||||
t += runif(0, 10000);
|
||||
pkg.msg.t2 = t;
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
painlessmesh::protocol::TimeDelay createTimeDelay(int type = -1) {
|
||||
auto pkg = painlessmesh::protocol::TimeDelay();
|
||||
pkg.dest = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
pkg.from = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
|
||||
if (type < 0) type = runif(0, 2);
|
||||
pkg.msg.type = type;
|
||||
auto t = runif(0, std::numeric_limits<uint32_t>::max());
|
||||
if (type == 1) pkg.msg.t0 = t;
|
||||
if (type == 2) {
|
||||
t += runif(0, 10000);
|
||||
pkg.msg.t1 = t;
|
||||
t += runif(0, 10000);
|
||||
pkg.msg.t2 = t;
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <cstring>
|
||||
#include "Arduino.h"
|
||||
|
||||
#define ASYNC_WRITE_FLAG_COPY \
|
||||
0x01 // will allocate new buffer to hold the data while sending (else will
|
||||
// hold reference to the data given)
|
||||
#define ASYNC_WRITE_FLAG_MORE \
|
||||
0x02 // will not send PSH flag, meaning that there should be more data to be
|
||||
// sent before the application should react.
|
||||
|
||||
class AsyncClient;
|
||||
|
||||
typedef std::function<void(void*, AsyncClient*)> AcConnectHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)>
|
||||
AcAckHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, void* data, size_t len)>
|
||||
AcDataHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, struct pbuf* pb)>
|
||||
AcPacketHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, uint32_t time)>
|
||||
AcTimeoutHandler;
|
||||
|
||||
class AsyncServer;
|
||||
|
||||
class AsyncClient {
|
||||
public:
|
||||
AsyncClient() {}
|
||||
|
||||
AsyncClient(AsyncServer* server) : mServer(server) {}
|
||||
|
||||
void setNoDelay(bool nodelay) {}
|
||||
void setRxTimeout(uint32_t timeout) {}
|
||||
void onData(AcDataHandler cb, void* arg = 0) {_recv_cb = cb;}
|
||||
void onAck(AcAckHandler cb, void* arg = 0) { _sent_cb = cb; }
|
||||
void onError(AcErrorHandler cb, void* arg = 0) {}
|
||||
void onDisconnect(AcConnectHandler cb, void* arg = 0) {_discard_cb = cb;}
|
||||
void onConnect(AcConnectHandler cb, void* arg = 0) { _connect_cb = cb; }
|
||||
|
||||
const char* errorToString(int err) { return ""; }
|
||||
|
||||
bool connected() { return mOther; }
|
||||
|
||||
bool canSend() { return true; }
|
||||
void ack(int len) {
|
||||
if (_sent_cb)
|
||||
_sent_cb(NULL, this, len, 0);
|
||||
}
|
||||
void close(bool now = false) {
|
||||
/*if (mOther && mOther->_discard_cb) {
|
||||
mOther->_discard_cb(NULL, this);
|
||||
mOther = NULL;
|
||||
}
|
||||
mServer = NULL;*/
|
||||
}
|
||||
bool connect(IPAddress ip, uint16_t port);
|
||||
size_t space() { return 1000; }
|
||||
bool send() { return true; }
|
||||
size_t write(const char* data, size_t size,
|
||||
uint8_t apiflags = ASYNC_WRITE_FLAG_COPY) {
|
||||
char* cpy[size];
|
||||
memcpy(&cpy, data, size);
|
||||
void * arg = NULL;
|
||||
if (mOther && mOther->_recv_cb) {
|
||||
mOther->_recv_cb(arg, mOther, cpy, size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool freeable() { return true; }
|
||||
int8_t abort() { return 0; }
|
||||
bool free() { return true; }
|
||||
|
||||
bool operator==(const AsyncClient &other) {
|
||||
return mOther == other.mOther;
|
||||
}
|
||||
|
||||
protected:
|
||||
AsyncServer* mServer = NULL;
|
||||
AsyncClient* mOther = NULL;
|
||||
AcConnectHandler _connect_cb = NULL;
|
||||
AcConnectHandler _discard_cb = NULL;
|
||||
AcDataHandler _recv_cb = NULL;
|
||||
AcAckHandler _sent_cb = NULL;
|
||||
};
|
||||
|
||||
class AsyncServer : public AsyncClient {
|
||||
public:
|
||||
AsyncServer() {}
|
||||
AsyncServer(uint16_t port) {}
|
||||
void onClient(AcConnectHandler cb, void* arg = 0) { _connect_cb = cb; }
|
||||
void begin() {}
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
|
||||
WL_IDLE_STATUS = 0,
|
||||
WL_NO_SSID_AVAIL = 1,
|
||||
WL_SCAN_COMPLETED = 2,
|
||||
WL_CONNECTED = 3,
|
||||
WL_CONNECT_FAILED = 4,
|
||||
WL_CONNECTION_LOST = 5,
|
||||
WL_DISCONNECTED = 6
|
||||
} wl_status_t;
|
||||
|
||||
class WiFiClass {
|
||||
public:
|
||||
void disconnect() {}
|
||||
auto status() {
|
||||
return WL_CONNECTED;
|
||||
}
|
||||
};
|
||||
|
||||
class ESPClass {
|
||||
public:
|
||||
size_t getFreeHeap() { return 1e6; }
|
||||
};
|
||||
|
||||
inline bool AsyncClient::connect(IPAddress ip, uint16_t port) {
|
||||
this->mOther = new AsyncClient();
|
||||
this->mOther->mOther = this;
|
||||
void * arg = NULL;
|
||||
if (mServer->_connect_cb)
|
||||
mServer->_connect_cb(arg, this->mOther);
|
||||
if (this->_connect_cb)
|
||||
this->_connect_cb(arg, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
DSM2_tx implements the serial communication protocol used for operating
|
||||
the RF modules that can be found in many DSM2-compatible transmitters.
|
||||
Copyrigt (C) 2012 Erik Elmore <erik@ironsavior.net>
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class FakeSerial {
|
||||
public:
|
||||
void begin(unsigned long);
|
||||
void end();
|
||||
size_t write(const unsigned char*, size_t);
|
||||
void print(const char*);
|
||||
void println();
|
||||
};
|
||||
|
||||
extern FakeSerial Serial;
|
||||
@@ -0,0 +1,39 @@
|
||||
//************************************************************
|
||||
// this is a simple example that uses the easyMesh library
|
||||
//
|
||||
// 1. blinks led once for every node on the mesh
|
||||
// 2. blink cycle repeats every BLINK_PERIOD
|
||||
// 3. sends a silly message to every node on the mesh at a random time between 1
|
||||
// and 5 seconds
|
||||
// 4. prints anything it receives to Serial.print
|
||||
//
|
||||
//
|
||||
//************************************************************
|
||||
#include <painlessMesh.h>
|
||||
|
||||
#include "painlessmesh/ota.hpp"
|
||||
#include "painlessmesh/protocol.hpp"
|
||||
#include "plugin/performance.hpp"
|
||||
|
||||
#define MESH_SSID "otatest"
|
||||
#define MESH_PASSWORD "somethingSneaky"
|
||||
#define MESH_PORT 5555
|
||||
|
||||
using namespace painlessmesh;
|
||||
|
||||
painlessMesh mesh;
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
mesh.setDebugMsgTypes(
|
||||
ERROR | CONNECTION |
|
||||
DEBUG); // set before init() so that you can see error messages
|
||||
|
||||
mesh.init(MESH_SSID, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA, 6);
|
||||
mesh.initOTA("performance");
|
||||
plugin::performance::begin(mesh, 2);
|
||||
}
|
||||
|
||||
void loop() { mesh.update(); }
|
||||
@@ -0,0 +1,20 @@
|
||||
[platformio]
|
||||
src_dir = .
|
||||
lib_extra_dirs = .piolibdeps/, ../../
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
lib_deps = ArduinoJson
|
||||
TaskScheduler
|
||||
ESPAsyncTCP
|
||||
|
||||
[env:esp32]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
lib_deps = ArduinoJson
|
||||
arduinoUnity
|
||||
TaskScheduler
|
||||
AsyncTCP
|
||||
@@ -0,0 +1,20 @@
|
||||
[platformio]
|
||||
src_dir = .
|
||||
lib_extra_dirs = .piolibdeps/, ../../
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
lib_deps = ArduinoJson
|
||||
TaskScheduler
|
||||
ESPAsyncTCP
|
||||
|
||||
[env:esp32]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
lib_deps = ArduinoJson
|
||||
arduinoUnity
|
||||
TaskScheduler
|
||||
AsyncTCP
|
||||
@@ -0,0 +1,163 @@
|
||||
//************************************************************
|
||||
// this is a simple example that uses the easyMesh library
|
||||
//
|
||||
// 1. blinks led once for every node on the mesh
|
||||
// 2. blink cycle repeats every BLINK_PERIOD
|
||||
// 3. sends a silly message to every node on the mesh at a random time between 1 and 5 seconds
|
||||
// 4. prints anything it receives to Serial.print
|
||||
//
|
||||
//
|
||||
//************************************************************
|
||||
#include <painlessMesh.h>
|
||||
|
||||
#include "painlessmesh/ota.hpp"
|
||||
#include "plugin/performance.hpp"
|
||||
|
||||
// some gpio pin that is connected to an LED...
|
||||
// on my rig, this is 5, change to the right number of your LED.
|
||||
#define LED 2 // GPIO number of connected LED, ON ESP-12 IS GPIO2
|
||||
#define SEND_FREQ 2
|
||||
|
||||
#define BLINK_PERIOD 3000 // milliseconds until cycle repeat
|
||||
#define BLINK_DURATION 100 // milliseconds LED is on for
|
||||
|
||||
#define MESH_SSID "otatest"
|
||||
#define MESH_PASSWORD "somethingSneaky"
|
||||
#define MESH_PORT 5555
|
||||
|
||||
// Prototypes
|
||||
void sendMessage();
|
||||
void receivedCallback(uint32_t from, String & msg);
|
||||
void newConnectionCallback(uint32_t nodeId);
|
||||
void changedConnectionCallback();
|
||||
void nodeTimeAdjustedCallback(int32_t offset);
|
||||
void delayReceivedCallback(uint32_t from, int32_t delay);
|
||||
|
||||
Scheduler userScheduler; // to control your personal task
|
||||
painlessMesh mesh;
|
||||
|
||||
bool calc_delay = false;
|
||||
std::list<uint32_t> nodes;
|
||||
|
||||
void sendMessage() ; // Prototype
|
||||
Task taskSendMessage( TASK_SECOND/SEND_FREQ, TASK_FOREVER, &sendMessage ); // start with a one second interval
|
||||
|
||||
// Task to blink the number of nodes
|
||||
Task blinkNoNodes;
|
||||
bool onFlag = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
pinMode(LED, OUTPUT);
|
||||
|
||||
mesh.setDebugMsgTypes(ERROR | CONNECTION | DEBUG); // set before init() so that you can see error messages
|
||||
|
||||
mesh.init(MESH_SSID, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA, 6);
|
||||
mesh.initOTA("otatest");
|
||||
|
||||
mesh.setContainsRoot(true);
|
||||
|
||||
mesh.onReceive(&receivedCallback);
|
||||
mesh.onNewConnection(&newConnectionCallback);
|
||||
mesh.onChangedConnections(&changedConnectionCallback);
|
||||
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
|
||||
mesh.onNodeDelayReceived(&delayReceivedCallback);
|
||||
|
||||
painlessmesh::plugin::performance::begin(mesh);
|
||||
|
||||
userScheduler.addTask( taskSendMessage );
|
||||
taskSendMessage.enable();
|
||||
|
||||
blinkNoNodes.set(BLINK_PERIOD, (mesh.getNodeList().size() + 1) * 2, []() {
|
||||
// If on, switch off, else switch on
|
||||
if (onFlag)
|
||||
onFlag = false;
|
||||
else
|
||||
onFlag = true;
|
||||
blinkNoNodes.delay(BLINK_DURATION);
|
||||
|
||||
if (blinkNoNodes.isLastIteration()) {
|
||||
// Finished blinking. Reset task for next run
|
||||
// blink number of nodes (including this node) times
|
||||
blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
|
||||
// Calculate delay based on current mesh time and BLINK_PERIOD
|
||||
// This results in blinks between nodes being synced
|
||||
blinkNoNodes.enableDelayed(BLINK_PERIOD -
|
||||
(mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);
|
||||
}
|
||||
});
|
||||
userScheduler.addTask(blinkNoNodes);
|
||||
blinkNoNodes.enable();
|
||||
|
||||
randomSeed(analogRead(A0));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mesh.update();
|
||||
digitalWrite(LED, !onFlag);
|
||||
}
|
||||
|
||||
uint32_t lastTime = 0;
|
||||
void sendMessage() {
|
||||
String msg = "Hello from node ";
|
||||
msg += mesh.getNodeId();
|
||||
msg += " myFreeMemory: " + String(ESP.getFreeHeap());
|
||||
mesh.sendBroadcast(msg);
|
||||
|
||||
if (calc_delay) {
|
||||
SimpleList<uint32_t>::iterator node = nodes.begin();
|
||||
while (node != nodes.end()) {
|
||||
mesh.startDelayMeas(*node);
|
||||
node++;
|
||||
}
|
||||
calc_delay = false;
|
||||
}
|
||||
|
||||
Serial.printf("Mesh stability: %u\n", mesh.stability);
|
||||
Serial.printf("Sending message: %s with delay %u\n", msg.c_str(), mesh.getNodeTime() - lastTime);
|
||||
lastTime = mesh.getNodeTime();
|
||||
}
|
||||
|
||||
void receivedCallback(uint32_t from, String & msg) {
|
||||
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
|
||||
}
|
||||
|
||||
void newConnectionCallback(uint32_t nodeId) {
|
||||
// Reset blink task
|
||||
onFlag = false;
|
||||
blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
|
||||
blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);
|
||||
|
||||
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
|
||||
Serial.printf("--> startHere: New Connection, %s\n", mesh.subConnectionJson(true).c_str());
|
||||
}
|
||||
|
||||
void changedConnectionCallback() {
|
||||
Serial.printf("Changed connections\n");
|
||||
// Reset blink task
|
||||
onFlag = false;
|
||||
blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
|
||||
blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);
|
||||
|
||||
nodes = mesh.getNodeList();
|
||||
|
||||
Serial.printf("Num nodes: %d\n", nodes.size());
|
||||
Serial.printf("Connection list:");
|
||||
|
||||
SimpleList<uint32_t>::iterator node = nodes.begin();
|
||||
while (node != nodes.end()) {
|
||||
Serial.printf(" %u", *node);
|
||||
node++;
|
||||
}
|
||||
Serial.println();
|
||||
calc_delay = true;
|
||||
}
|
||||
|
||||
void nodeTimeAdjustedCallback(int32_t offset) {
|
||||
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
|
||||
}
|
||||
|
||||
void delayReceivedCallback(uint32_t from, int32_t delay) {
|
||||
Serial.printf("Delay to node %u is %d us\n", from, delay);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[platformio]
|
||||
src_dir = .
|
||||
lib_extra_dirs = .piolibdeps/, ../../
|
||||
|
||||
[env:nodemcuv2]
|
||||
platform = espressif8266
|
||||
board = nodemcuv2
|
||||
framework = arduino
|
||||
lib_deps = ArduinoJson
|
||||
TaskScheduler
|
||||
ESPAsyncTCP
|
||||
|
||||
[env:esp32]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
lib_deps = ArduinoJson
|
||||
arduinoUnity
|
||||
TaskScheduler
|
||||
AsyncTCP
|
||||
88
arduino-cli/libraries/painlessMesh-master/test/wifi/wifi.ino
Normal file
88
arduino-cli/libraries/painlessMesh-master/test/wifi/wifi.ino
Normal file
@@ -0,0 +1,88 @@
|
||||
//************************************************************
|
||||
// this is a simple example that uses the easyMesh library
|
||||
//
|
||||
// 1. blinks led once for every node on the mesh
|
||||
// 2. blink cycle repeats every BLINK_PERIOD
|
||||
// 3. sends a silly message to every node on the mesh at a random time between 1
|
||||
// and 5 seconds
|
||||
// 4. prints anything it receives to Serial.print
|
||||
//
|
||||
//
|
||||
//************************************************************
|
||||
#include "painlessmesh/configuration.hpp"
|
||||
|
||||
#include "painlessMeshConnection.h"
|
||||
|
||||
#include "painlessmesh/mesh.hpp"
|
||||
#include "painlessmesh/tcp.hpp"
|
||||
#include "plugin/performance.hpp"
|
||||
|
||||
using namespace painlessmesh;
|
||||
using namespace logger;
|
||||
|
||||
painlessmesh::Mesh<MeshConnection> mesh;
|
||||
|
||||
std::shared_ptr<AsyncServer> pServer;
|
||||
|
||||
WiFiEventId_t eventSTADisconnectedHandler;
|
||||
WiFiEventId_t eventSTAGotIPHandler;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Log.setLogLevel(ERROR | CONNECTION | DEBUG);
|
||||
uint8_t mac[] = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
/*auto _apIp = IPAddress(10, (nodeId & 0xFF00) >> 8, (nodeId & 0xFF), 1);
|
||||
IPAddress netmask(255, 255, 255, 0);
|
||||
|
||||
WiFi.softAPConfig(_apIp, _apIp, netmask);*/
|
||||
|
||||
WiFi.softAP("otatest", "somethingSneaky", 6);
|
||||
if (WiFi.softAPmacAddress(mac) == 0) {
|
||||
Log(ERROR, "init(): WiFi.softAPmacAddress(MAC) failed.\n");
|
||||
}
|
||||
auto nodeId = painlessmesh::tcp::encodeNodeId(mac);
|
||||
if (nodeId == 0) Log(ERROR, "NodeId set to 0\n");
|
||||
Log(ERROR, "Bla %u\n", nodeId);
|
||||
mesh.init(nodeId);
|
||||
mesh.setRoot(true);
|
||||
plugin::performance::begin(mesh);
|
||||
|
||||
pServer = std::make_shared<AsyncServer>(5555);
|
||||
painlessmesh::tcp::initServer<MeshConnection>((*pServer), mesh);
|
||||
eventSTAGotIPHandler = WiFi.onEvent(
|
||||
[&](WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
// if (this->semaphoreTake()) {
|
||||
Log(CONNECTION, "eventSTAGotIPHandler: SYSTEM_EVENT_STA_GOT_IP\n");
|
||||
AsyncClient *pConn = new AsyncClient();
|
||||
painlessmesh::tcp::connect<MeshConnection>(
|
||||
(*pConn), IPAddress(192, 168, 1, 69), 5555, mesh);
|
||||
// this->tcpConnect(); // Connect to TCP port
|
||||
// this->semaphoreGive();
|
||||
//}
|
||||
},
|
||||
WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
|
||||
|
||||
eventSTADisconnectedHandler = WiFi.onEvent(
|
||||
[](WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
//if (this->semaphoreTake()) {
|
||||
Log(CONNECTION,
|
||||
"eventSTADisconnectedHandler: SYSTEM_EVENT_STA_DISCONNECTED\n");
|
||||
// WiFi.disconnect();
|
||||
// Search for APs and connect to the best one
|
||||
//this->stationScan.connectToAP();
|
||||
//this->semaphoreGive();
|
||||
//}
|
||||
},
|
||||
WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
|
||||
|
||||
|
||||
WiFi.setAutoConnect(true);
|
||||
WiFi.begin("BigBird", "eendolleman");
|
||||
Log(CONNECTION, "Beginning\n");
|
||||
mesh.addTask(TASK_SECOND, TASK_FOREVER, []() {
|
||||
Log(CONNECTION, "Connected? %d\n", WiFi.status() == WL_CONNECTED);
|
||||
});
|
||||
}
|
||||
|
||||
void loop() { mesh.update(); }
|
||||
Reference in New Issue
Block a user