Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

179 changes: 131 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,177 @@
#Intro to easyMesh
easyMesh is a library that takes care of the particulars for creating a simple mesh network using Arduino and esp8266. The goal is to allow the programmer to work with a mesh network without having to worry about how the network is structured or managed.
# Intro to painlessMesh

###True ad-hoc netoworking
easyMesh is a true ad-hoc network, meaning that no-planning, central controller, or router is required. Any system of 1 or more nodes will self-organize into fully functional mesh. The maximum size of the mesh is limited (i think) by the amount of memory in the heap that can be allocated to the sub-connections buffer… and so should be really quite high.
painlessMesh is a library that takes care of the particulars of creating a simple mesh network using esp8266 and esp32 hardware. The goal is to allow the programmer to work with a mesh network without having to worry about how the network is structured or managed.

###JSON based
easyMesh uses JSON objects for all its messaging. There a couple of reasons for this. First, it makes the code and the messages human readable and easy to understand and second, it makes it easy to integrate easyMesh with javascript front-ends, web applications, and other apps. Some performance is lost, but I haven’t been running into performance issues yet. Converting to binary messaging would be fairly straight forward if someone wants to contribute.
### True ad-hoc networking

###Wifi & Networking
easyMesh is designed to be used with Arduino, but it does not use the Arduino WiFi libraries, as I was running into performance issues (primarily latency) with them. Rather the networking is all done using the native esp8266 SDK libraries, which are available through the Arduino IDE. Hopefully though, which networking libraries are used won’t matter to most users much as you can just include the .h, run the init() and then work the library through the API.
painlessMesh is a true ad-hoc network, meaning that no-planning, central controller, or router is required. Any system of 1 or more nodes will self-organize into fully functional mesh. The maximum size of the mesh is limited (we think) by the amount of memory in the heap that can be allocated to the sub-connections buffer and so should be really quite high.

###easyMesh is not IP networking
easyMesh does not create a TCP/IP network of nodes. Rather each of the nodes is uniquely identified by its 32bit chipId which is retrieved from the esp8266 using the system_get_chip_id() call in the SDK. Every esp8266 will have a unique number. Messages can either be broadcast to all of the nodes on the mesh, or sent specifically to an individual node which is identified by its chipId.
### JSON based

###Examples
demoToy is currently the only example. It is kind of complex, uses a web server, web sockets, and neopixel animations, so it is not really a great entry level example. That said, it does some pretty cools stuff… here is a video of the demo.
painlessMesh uses JSON objects for all its messaging. There are a couple of reasons for this. First, it makes the code and the messages human readable and painless to understand and second, it makes it painless to integrate painlessMesh with javascript front-ends, web applications, and other apps. Some performance is lost, but I haven’t been running into performance issues yet. Converting to binary messaging would be fairly straight forward if someone wants to contribute.

https://www.youtube.com/watch?v=hqjOY8YHdlM&feature=youtu.be
### Wifi & Networking

###Dependencies
easyMesh makes use of the following libraries. They can both be installed through Arduino Library Manager
- SimpleList *** Available here... https://github.com/blackhack/ArduLibraries/tree/master/SimpleList
- ArduinoJson *** Available here... https://github.com/bblanchon/ArduinoJson
painlessMesh is designed to be used with Arduino, but it does not use the Arduino WiFi libraries, as we were running into performance issues (primarily latency) with them. Rather the networking is all done using the native esp32 and esp8266 SDK libraries, which are available through the Arduino IDE. Hopefully though, which networking libraries are used won’t matter to most users much as you can just include painlessMesh.h, run the init() and then work the library through the API.

#easyMesh API
Using easyMesh is easy!
### painlessMesh is not IP networking

First include the library and create an easyMesh object like this…
painlessMesh does not create a TCP/IP network of nodes. Rather each of the nodes is uniquely identified by its 32bit chipId which is retrieved from the esp8266/esp32 using the `system_get_chip_id()` call in the SDK. Every node will have a unique number. Messages can either be broadcast to all of the nodes on the mesh, or sent specifically to an individual node which is identified by its `nodeId.

### Limitations and caveats

- Try to avoid using `delay()` in your code. To maintain the mesh we need to perform some tasks in the background. Using `delay()` will stop these tasks from happening and can cause the mesh to lose stability/fall apart. Instead we recommend using the scheduler included in `painlessMesh`. That scheduler is a slightly modified version of the [TaskScheduler](http://playground.arduino.cc/Code/TaskScheduler) library. Documentation can be found [here](http://www.smart4smart.com/TaskScheduler.pdf). For other examples on how to use the scheduler see the example folder.
- `painlessMesh` uses the sdk provided by [esp8266](http://espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf)/[esp32](https://esp-idf.readthedocs.io/en/latest/index.html). Please be aware that as a result `painlessMesh` is incompatible with using the `WiFi.h` wrappers provided by the vendor, because both `painlessMesh` and the `WiFi.h` try to bind to the same events (e.g. disconnect event). Instead if you want to use the WiFi chip you will need to use same sdk as `painlessMesh`.
- Try to be conservative in the number of messages (and especially broadcast messages) you sent per minute. This is to prevent the hardware from overloading. Both esp8266 and esp32 are limited in processing power/memory, making it easy to overload the mesh and destabilise it. And while `painlessMesh` tries to prevent this from happening, it is not always possible to do so.
- Messages can go missing or be dropped due to high traffic and you can not rely on all messages to be delivered. One suggestion to work around is to resend messages every so often. Even if some go missing, most should go through. Another option is to have your nodes send replies when they receive a message. The sending nodes can the resend the message if they haven’t gotten a reply in a certain amount of time.

### Examples

StartHere is a basic how to use example. It blinks built-in LED (in ESP-12) as many times as nodes are connected to the mesh. Further examples are under the examples directory and shown on the platformio [page](http://platformio.org/lib/show/1269/painlessMesh).

### Dependencies

painlessMesh makes use of the following library, which can be installed through the Arduino Library Manager

- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)

If platformio is used to install the library, then the dependency will be installed automatically.

# Getting help

There is help available on the [wiki](https://gitlab.com/BlackEdder/painlessMesh/wikis/home) and you can also reach us on our [gitter channel](https://gitter.im/painlessMesh/Lobby)

# painlessMesh API

Using painlessMesh is painless!

First include the library and create an painlessMesh object like this…

```
#include <easyMesh.h>
easyMesh mesh;
#include <painlessMesh.h>
painlessMesh mesh;
```

##Member Functions
## Member Functions

### void painlessMesh::init(String ssid, String password, uint16_t port = 5555, enum nodeMode connectMode = STA_AP, _auth_mode authmode = AUTH_WPA2_PSK, uint8_t channel = 1, phy_mode_t phymode = PHY_MODE_11G, uint8_t maxtpw = 82, uint8_t hidden = 0, uint8_t maxconn = 4)

###void easyMesh::init( String prefix, String password, uint16_t port )
Add this to your setup() function.
Initialize the mesh network. This routine does the following things…
Initialize the mesh network. This routine does the following things.

- Starts a wifi network
- Begins searching for other wifi networks that are part of the mesh
- Logs on to the best mesh network node it finds… if it doesn’t find anything, it starts a new search in 5 seconds.

prefix = the name of your mesh. The wifi ssid of this node will be prefix + chipId
password = wifi password to your mesh
port = the TCP port that you want the mesh server to run on
`ssid` = the name of your mesh. All nodes share same AP ssid. They are distinguished by BSSID.
`password` = wifi password to your mesh.
`port` = the TCP port that you want the mesh server to run on. Defaults to 5555 if not specified.
[`connectMode`](https://gitlab.com/BlackEdder/painlessMesh/wikis/connect-mode:-ap_only,-sta_only,-sta_ap-mode) = switch between AP_ONLY, STA_ONLY and STA_AP (default) mode

### void painlessMesh::stop()

Stop the node. This will cause the node to disconnect from all other nodes and stop/sending messages.

### void painlessMesh::update( void )

###void easyMesh::update( void )
Add this to your loop() function
This routine runs various maintainance tasks... Not super interesting, but things don't work without it.

### void painlessMesh::onReceive( &amp;receivedCallback )

###void easyMesh::setReceiveCallback( &receivedCallback )
Set a callback routine for any messages that are addressed to this node. The callback routine has the following structure…
Set a callback routine for any messages that are addressed to this node. Callback routine has the following structure.

`void receivedCallback( uint32_t from, String &msg )`
`void receivedCallback( uint32_t from, String &amp;msg )`

Every time this node receives a message, this callback routine will the called. “from” is the id of the original sender of the message, and “msg” is a string that contains the message. The message can be anything. A JSON, some other text string, or binary data.

### void painlessMesh::onNewConnection( &amp;newConnectionCallback )

This fires every time the local node makes a new connection. The callback has the following structure.

`void newConnectionCallback( uint32_t nodeId )`

`nodeId` is new connected node ID in the mesh.

### void painlessMesh::onChangedConnections( &amp;changedConnectionsCallback )

This fires every time there is a change in mesh topology. Callback has the following structure.

`void onChangedConnections()`

There are no parameters passed. This is a signal only.

###void easyMesh::setNewConnectionCallback( &newConnectionCallback )
This fires every time the local node makes a new connection. The callback has the following structure…
### bool painlessMesh::isConnected( nodeId )

`void newConnectionCallback( bool adopt )`
Returns if a given node is currently connected to the mesh.

`adopt` is a boolean value that indicates whether the mesh has determined to adopt the remote nodes timebase or not. If `adopt == true`, then this node has adopted the remote node’s timebase.
`nodeId` is node ID that the request refers to.

The mesh does a simple calculation to determine which nodes adopt and which nodes don’t. When a connection is made, the node with the smaller number of connections to other nodes adopts the timebase of the node with the larger number of connections to other nodes. If there is a tie, then the AP (access point) node wins.
### void painlessMesh::onNodeTimeAdjusted( &amp;nodeTimeAdjustedCallback )

######Example 1:
There are two separate meshes (Mesh A and Mesh B) that have discovered each other and are connecting. Mesh A has 7 nodes and Mesh B has 8 nodes. When the connection is made, Mesh B has more nodes in it, so Mesh A adopts the timebase of Mesh B.
This fires every time local time is adjusted to synchronize it with mesh time. Callback has the following structure.

######Example 2:
A brand new mesh is starting. There are only 2 nodes (Node X and Node Y) and they both just got turned on. They find each other, and as luck would have it, Node X connects as a Station to the wifi network established by Node Y’s AP (access point)… which means that Node X is the wifi client and Node Y is the wifi server in the particular relationship. In this case, since both nodes have zero (0) other connections, Node X adopts Node Y’s timebase because the tie (0 vs 0) goes to the AP.
`void onNodeTimeAdjusted(int32_t offset)`

`offset` is the adjustment delta that has benn calculated and applied to local clock.

### void onNodeDelayReceived(nodeDelayCallback_t onDelayReceived)

This fires when a time delay masurement response is received, after a request was sent. Callback has the following structure.

`void onNodeDelayReceived(uint32_t nodeId, int32_t delay)`

`nodeId` The node that originated response.

`delay` One way network trip delay in microseconds.

### bool painlessMesh::sendBroadcast( String &amp;msg)

###bool easyMesh::sendBroadcast( String &msg)
Sends msg to every node on the entire mesh network.

returns true if everything works, false if not. Prints an error message to Serial.print, if there is a failure.

###bool easyMesh::sendSingle(uint32_t dest, String &msg)
### bool painlessMesh::sendSingle(uint32_t dest, String &amp;msg)

Sends msg to the node with Id == dest.

returns true if everything works, false if not. Prints an error message to Serial.print, if there is a failure.

###uint16_t easyMesh::connectionCount()
Returns the total number of nodes connected to this mesh.
### String painlessMesh::subConnectionJson()

Returns mesh topology in JSON format.

### SimpleList<uint32_t> painlessMesh::getNodeList()

Get a list of all known nodes. This includes nodes that are both directly and indirectly connected to the current node.

### uint32_t painlessMesh::getNodeId( void )

###uint32_t easyMesh::getChipId( void )
Return the chipId of the node that we are running on.

###uint32_t easyMesh::getNodeTime( void )
Returns the mesh timebase microsecond counter. Rolls over 71 minutes from startup of the first node.
### uint32_t painlessMesh::getNodeTime( void )

Returns the mesh timebase microsecond counter. Rolls over 71 minutes from startup of the first node.

Nodes try to keep a common time base synchronizing to each other using [an SNTP based protocol](https://gitlab.com/BlackEdder/painlessMesh/wikis/mesh-protocol#time-sync)

### bool painlessMesh::startDelayMeas(uint32_t nodeId)

Sends a node a packet to measure network trip delay to that node. Returns true if nodeId is connected to the mesh, false otherwise. After calling this function, user program have to wait to the response in the form of a callback specified by `void painlessMesh::onNodeDelayReceived(nodeDelayCallback_t onDelayReceived)`.

nodeDelayCallback_t is a funtion in the form of `void (uint32_t nodeId, int32_t delay)`.

# Included dependencies

`painlessMesh` includes two other projects within its source directory. These projects were included directly in a slightly modified form. The original version of the two projects are:

- [AsyncTCP](https://github.com/me-no-dev/AsyncTCP)
- [TaskScheduler](http://playground.arduino.cc/Code/TaskScheduler)

# Funding

Most development of painlessMesh has been done as a hobby, but some specific features have been funded by the companies listed below:

![Sowillo](http://sowillo.com/wp-content/uploads/2016/08/Logo-Sowillo-1.png)

[Sowillo](http://sowillo.com/en/)
61 changes: 61 additions & 0 deletions examples/basic/basic.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//************************************************************
// this is a simple example that uses the painlessMesh library
//
// 1. sends a silly message to every node on the mesh at a random time betweew 1 and 5 seconds
// 2. prints anything it recieves to Serial.print
//
//
//************************************************************
#include "painlessMesh.h"

#define MESH_PREFIX "whateverYouLike"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

void sendMessage() ; // Prototype so PlatformIO doesn't complain

painlessMesh mesh;
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

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) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
Serial.printf("Changed connections %s\n",mesh.subConnectionJson().c_str());
}

void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
Serial.begin(115200);

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages

mesh.init( MESH_PREFIX, MESH_PASSWORD, MESH_PORT );
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

mesh.scheduler.addTask( taskSendMessage );
taskSendMessage.enable() ;
}

void loop() {
mesh.update();
}

void sendMessage() {
String msg = "Hello from node ";
msg += mesh.getNodeId();
mesh.sendBroadcast( msg );
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}
39 changes: 39 additions & 0 deletions examples/bridge/bridge.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//************************************************************
// this is a simple example that uses the painlessMesh library to
// connect to a node on another network. Please see the WIKI on gitlab
// for more details
// https://gitlab.com/BlackEdder/painlessMesh/wikis/bridge-between-mesh-and-another-network
//************************************************************
#include "painlessMesh.h"

#define MESH_PREFIX "whateverYouLike"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

#define STATION_SSID "mySSID"
#define STATION_PASSWORD "myPASSWORD"
#define STATION_PORT 5555
uint8_t station_ip[4] = {10,10,10,1}; // IP of the server

painlessMesh mesh;

void setup() {
Serial.begin(115200);
mesh.setDebugMsgTypes( ERROR | STARTUP | CONNECTION ); // set before init() so that you can see startup messages


// Channel set to 6. Make sure to use the same channel for your mesh and for you other
// network (STATION_SSID)
mesh.init( MESH_PREFIX, MESH_PASSWORD, MESH_PORT, STA_AP, AUTH_WPA2_PSK, 6 );
mesh.stationManual(STATION_SSID, STATION_PASSWORD, STATION_PORT,
station_ip);
mesh.onReceive(&receivedCallback);
}

void loop() {
mesh.update();
}

void receivedCallback( uint32_t from, String &msg ) {
Serial.printf("bridge: Received from %u msg=%s\n", from, msg.c_str());
}
10 changes: 10 additions & 0 deletions examples/bridge/platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[platformio]
src_dir = .
lib_extra_dirs = .piolibdeps/, ../../

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
#lib_deps =
# painlessMesh
Loading