Difference between revisions of "Low Powered Mesh Network stack"

From Embedded Systems Learning Academy
Jump to: navigation, search
Line 1: Line 1:
 
== Intro ==
 
== Intro ==
This article describes the mesh network design.  The examples of using the '''[https://sourceforge.net/projects/lowpoweredmesh/ source code (Sourceforge)]''' are described towards the end of '''[[Interactive_Wireless_Nodes_Project | Wireless Nodes Projects]]'''.   
+
This article describes the mesh network design.  The examples of using the '''[https://sourceforge.net/projects/lowpoweredmesh/ source code (Sourceforge)]''' are described towards the end of '''[[Interactive_mesh_Nodes_Project | Wireless Nodes Projects]]'''.   
  
 
== Features ==
 
== Features ==
Line 85: Line 85:
  
 
=== Ping! ===
 
=== Ping! ===
This code sample not only shows how to "ping" another node, but it also shows how we can ask for an "ACK" or acknowledgement that the packet was delivered.  Note that we send '''<code>mesh_pkt_ack</code>''' type of packet, and then we wait some time for an acknowledgement packet through '''<code>wireless_get_ack_pkt()</code>'''
+
This code sample not only shows how to "ping" another node, but it also shows how we can ask for an "ACK" or acknowledgement that the packet was delivered.  Note that we send '''<code>mesh_pkt_ack</code>''' type of packet, and then we wait some time for an acknowledgement packet through '''<code>mesh_get_ack_pkt()</code>'''
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
Line 101: Line 101:
 
     const char max_hops = 2;
 
     const char max_hops = 2;
 
     mesh_packet_t pkt;
 
     mesh_packet_t pkt;
     wireless_send(addr, mesh_pkt_ack, NULL, 0, max_hops);
+
     mesh_send(addr, mesh_pkt_ack, NULL, 0, max_hops);
  
 
     /* Turn LED on or off based on if we get ACK packet within 100ms */
 
     /* Turn LED on or off based on if we get ACK packet within 100ms */
     if (wireless_get_ack_pkt(&pkt, 100)) {
+
     if (mesh_get_ack_pkt(&pkt, 100)) {
 
         LE.on(led_num);
 
         LE.on(led_num);
 
     }
 
     }
Line 168: Line 168:
 
     * with our data, and an auto-ack is not performed.
 
     * with our data, and an auto-ack is not performed.
 
     */
 
     */
     wireless_send(addr, mesh_pkt_ack_app, &cmd, 1, hops);
+
     mesh_send(addr, mesh_pkt_ack_app, &cmd, 1, hops);
  
 
     mesh_packet_t pkt;
 
     mesh_packet_t pkt;
     if (wireless_get_ack_pkt(&pkt, 100)) {
+
     if (mesh_get_ack_pkt(&pkt, 100)) {
 
         /* We need to deform packet the same way it was formed */
 
         /* We need to deform packet the same way it was formed */
         /* Parameters should be same after 4th parameter into wireless_form_pkt() */
+
         /* Parameters should be same after 4th parameter into mesh_form_pkt() */
 
         uint16_t light = 0;
 
         uint16_t light = 0;
         wireless_deform_pkt(&pkt, 1, &light, sizeof(light));
+
         mesh_deform_pkt(&pkt, 1, &light, sizeof(light));
 
     }
 
     }
 
}
 
}
Line 184: Line 184:
 
     uint16_t light = 0;
 
     uint16_t light = 0;
  
     if (wireless_get_rx_pkt(&pkt, 100)) {
+
     if (mesh_get_rx_pkt(&pkt, 100)) {
 
         /* Check if we were asked for an application ACK */
 
         /* Check if we were asked for an application ACK */
         if (wireless_is_ack_required(&pkt)) {
+
         if (mesh_is_ack_required(&pkt)) {
  
 
             /* Send the packet back based on the commanded byte */
 
             /* Send the packet back based on the commanded byte */
Line 194: Line 194:
 
                     /* Send packet back to network source: pkt.nwk.src */
 
                     /* Send packet back to network source: pkt.nwk.src */
 
                     light = LS.getRawValue();
 
                     light = LS.getRawValue();
                     wireless_form_pkt(&pkt, pkt.nwk.src, mesh_pkt_ack_rsp, 1,
+
                     mesh_form_pkt(&pkt, pkt.nwk.src, mesh_pkt_ack_rsp, 1,
 
                                       1,
 
                                       1,
 
                                       &light, sizeof(light));
 
                                       &light, sizeof(light));
Line 208: Line 208:
 
             }
 
             }
  
             wireless_send_formed_pkt(&pkt);
+
             mesh_send_formed_pkt(&pkt);
 
         }
 
         }
 
     }
 
     }
Line 229: Line 229:
  
 
     /* Send a packet without an ACK or retries */
 
     /* Send a packet without an ACK or retries */
     wireless_send(addr, mesh_pkt_nack, "HELLO", 5, hops);
+
     mesh_send(addr, mesh_pkt_nack, "HELLO", 5, hops);
  
 
     /* Send a packet to everyone.  ACK not possible for broadcast messages */
 
     /* Send a packet to everyone.  ACK not possible for broadcast messages */
     wireless_send(MESH_BROADCAST_ADDR, mesh_pkt_nack, "HELLO", 5, hops);
+
     mesh_send(MESH_BROADCAST_ADDR, mesh_pkt_nack, "HELLO", 5, hops);
  
 
     /* Send a "PING" Packet with an ACK request */
 
     /* Send a "PING" Packet with an ACK request */
     wireless_send(addr, mesh_pkt_ack, NULL, 0, hops);
+
     mesh_send(addr, mesh_pkt_ack, NULL, 0, hops);
 
     /* Wait for response of ping packet and print it */
 
     /* Wait for response of ping packet and print it */
     if (wireless_get_ack_pkt(&pkt, 1000)) {
+
     if (mesh_get_ack_pkt(&pkt, 1000)) {
 
         printf("Node response: ");
 
         printf("Node response: ");
 
         for (int i=0; i < pkt.info.data_len; i++) {
 
         for (int i=0; i < pkt.info.data_len; i++) {
Line 246: Line 246:
  
 
     /* Send a packet with "HELLO" (which is 5 data bytes) with an ACK request*/
 
     /* Send a packet with "HELLO" (which is 5 data bytes) with an ACK request*/
     wireless_send(addr, mesh_pkt_ack, "HELLO", 5, hops);
+
     mesh_send(addr, mesh_pkt_ack, "HELLO", 5, hops);
 
     /* We should wait for the ACK if it was requested.  ACK typically takes 10ms per hop */
 
     /* We should wait for the ACK if it was requested.  ACK typically takes 10ms per hop */
     if (wireless_get_ack_pkt(&pkt, 25)) {
+
     if (mesh_get_ack_pkt(&pkt, 25)) {
 
     }
 
     }
  
 
     /* Flush all incoming data (maybe for stale ACKs) */
 
     /* Flush all incoming data (maybe for stale ACKs) */
     wireless_flush_rx();
+
     mesh_flush_rx();
  
 
     /* Send a packet and wait for ack for 1000ms (1 sec) */
 
     /* Send a packet and wait for ack for 1000ms (1 sec) */
     wireless_send(addr, mesh_pkt_ack, "HELLO", 5, hops);
+
     mesh_send(addr, mesh_pkt_ack, "HELLO", 5, hops);
     if (wireless_get_ack_pkt(&pkt, 1000)) {
+
     if (mesh_get_ack_pkt(&pkt, 1000)) {
 
         /* Got an ACK */
 
         /* Got an ACK */
 
     }
 
     }
  
 
     /* Try to get a packet destined for us with 100ms timeout*/
 
     /* Try to get a packet destined for us with 100ms timeout*/
     if (wireless_get_rx_pkt(&pkt, 100)) {
+
     if (mesh_get_rx_pkt(&pkt, 100)) {
 
     }
 
     }
  
Line 267: Line 267:
 
     int var1 = 0;
 
     int var1 = 0;
 
     float var2 = 0;
 
     float var2 = 0;
     wireless_form_pkt(&pkt, addr, mesh_pkt_ack, hops,
+
     mesh_form_pkt(&pkt, addr, mesh_pkt_ack, hops,
 
                       2,                    /* 2 Pairs below */
 
                       2,                    /* 2 Pairs below */
 
                       &var1, sizeof(var1),  /* Pair 1 */
 
                       &var1, sizeof(var1),  /* Pair 1 */
 
                       &var2, sizeof(var2));  /* Pair 2 */
 
                       &var2, sizeof(var2));  /* Pair 2 */
 
     /* Packet was formed above, now send it */
 
     /* Packet was formed above, now send it */
     wireless_send_formed_pkt(&pkt);
+
     mesh_send_formed_pkt(&pkt);
  
 
     /* Manually set data bytes of a packet, but need to form the header first */
 
     /* Manually set data bytes of a packet, but need to form the header first */
     wireless_form_pkt(&pkt, addr, mesh_pkt_ack, hops, 0);
+
     mesh_form_pkt(&pkt, addr, mesh_pkt_ack, hops, 0);
 
     /* Modify the data bytes AFTER forming the packet header */
 
     /* Modify the data bytes AFTER forming the packet header */
 
     pkt.data[0] = 10;
 
     pkt.data[0] = 10;
Line 282: Line 282:
 
     pkt.info.data_len = 3;
 
     pkt.info.data_len = 3;
 
     /* Finally, send the packet */
 
     /* Finally, send the packet */
     wireless_send_formed_pkt(&pkt);
+
     mesh_send_formed_pkt(&pkt);
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>

Revision as of 01:58, 5 June 2014

Intro

This article describes the mesh network design. The examples of using the source code (Sourceforge) are described towards the end of Wireless Nodes Projects.

Features

  • Addressed network with auto-retries, and auto-acknowledge
  • Minimal RAM/ROM footprint, with NO HEAP usage
  • Each node can participate in the mesh network to deliver packets to a destined node.
  • Each packet may dictate maximum hops it can take.
  • Self-Healing Routes.
    Routes are rediscovered and deleted as appropriate.


Acknowledgment

Inspiration was taken from Atmel's lightweight mesh, however, the code was completely rebuilt from the ground up and detailed testing was performed during each development step. In general, simplicity was favored and a clean API was targeted such that integration with projects for the open-source community can benefit.


Detailed Features

  • Each packet sent can use existing route, and if route has changed, a new route is automatically discovered using a special retry packet.
  • Each node's ACK contains some piggy-back data about the node itself.
    This data includes information about its routing table, and other statistics
  • Duplicate packets are absorbed but an ACK is still replied if its a duplicate, but a retry packet.
  • An ACK packet or a response to an ACK all use retries; even the repeating nodes participate to make sure the packet is delivered.

There is no "out-of-the-box" support for sleeping nodes, however, any sleeping node can wake up, transmit a packet and go back to sleep without affecting the rest of the network. This mesh network design doesn't rely on any node to be a medium as routes can change dynamically. With this said, there are a few improvements that could be made :

  • Use RSSI to dictate if a route should be changed.
  • Use a counter to prefer a route that sends us data more often.

Payload

The minimum payload is 9 bytes, of which, 8 bytes will be the mesh header overhead. The higher the payload, the higher the efficiency of the network. The eight bytes of payload header contains the network source and destination information, along with packet type and hop count information.

For example, if the payload size is 32 bytes, then 8 bytes are used by the network header and 24 bytes are free to be used. Please take a look at the mesh_packet_t structure to understand the data of the wireless packet. The only relevant fields you need to know about are :

  • data_len : Size of the valid data bytes
  • data[0] - data[23] : Actual data bytes.
Wireless Packet Structure
4 Byte Header 2 Byte Network Address 2 Byte Physical Address 24 Byte User Data
B0 B1 B2 B3 B4 B5 B6 B7 B8 - B31
data_len Do not use data[0] - data[23]

Example Mesh Network

Node Path Discovery

This section shows how a node N1 sends a packet to N4 and discovers the path to N4. There are a few points to note :

  • N1 is essentially sending a "broadcast" message hoping to find N4.
    All nodes in between will repeat this packet once.
    The number of hops the packet can take can be configured though.
  • If N1 doesn't get an ACK back from N4, it will retry a couple of times (default configuration).


N1 trying to send packet to N4, N2 updates its routing table.
N3 (intermediate node) repeating packet, N3 now knows about N1 and N2
N4 receives packet, half routes discovered


ACK Packet Path

Since the ACK packet knows its path (MAC DST is known), intermediate nodes become responsible to deliver the packet to destined node. The same is true when an original packet is sent after this route discovery; if N1 resends a different packet to N4, the next time N2 and N3 will become the responsible nodes to deliver the packet to N4 without N1 having to retry. N1 will only retry if its next destination is not heard repeating the packet, or the ACK is not received back after the timeout.


N4 sending ACK, will ensure N3 repeats it, N3 now knows about N4
N3 sending ACK, will ensure N2 repeats it, N2 now knows about N4
N2 sending ACK, and hoping for the best :)

Route Updates

Path will be healed if N2 disappears


Ping!

This code sample not only shows how to "ping" another node, but it also shows how we can ask for an "ACK" or acknowledgement that the packet was delivered. Note that we send mesh_pkt_ack type of packet, and then we wait some time for an acknowledgement packet through mesh_get_ack_pkt()

#include <stdio.h>
#include "io.hpp"
#include "wireless.h"
#include "soft_timer.hpp"

void ping_and_set_led(uint8_t addr, uint8_t led_num)
{
    /* Sending NULL packet is a "PING" packet.
     * No special code is required at the other node since the
     * other node will automatically send the ACK back.
     */
    const char max_hops = 2;
    mesh_packet_t pkt;
    mesh_send(addr, mesh_pkt_ack, NULL, 0, max_hops);

    /* Turn LED on or off based on if we get ACK packet within 100ms */
    if (mesh_get_ack_pkt(&pkt, 100)) {
        LE.on(led_num);
    }
    else {
        LE.off(led_num);
        printf("No response from %i\n", addr);
    }
}

int main(void)
{
    /* Keep an enum of addresses in all your nodes */
    enum {
         lights_addr = 100,
         sensor_addr = 125,
         commander_addr = 200,
    };

    /* Use a software timer "object" with 2000ms duration. */
    SoftTimer ping_timer;
    ping_timer.reset(2000);

    while (1) {
        /* If timer expires, restart it again and ping everyone. */
        if (ping_timer.expired()) {
            ping_timer.restart();

            ping_and_set_led(lights_addr, 1);
            ping_and_set_led(sensor_addr, 2);
        }
    }

    return 0;
}


Send & Receive Data Variables

In this example, a commander sends an "Application Acknowledge" packet, meaning that the destination will have to manually send an acknowledge packet back. In other words, instead of auto-acknowledge, we send the ACK back with the requested data. Furthermore, we show you how data variables can be exchanged between two boards. Note that the wireless can only transport 24-bytes through a single packet.

#include <stdio.h>
#include <stdint.h>
#include "wireless.h"
#include "io.hpp"

    /* Common between commander and sensor node
     * This identifies "what" the commander is asking for
     */
    enum {
        req_light = 1, /* Request light sensor reading       */
        req_all = 2,
    };

void commander(void)
{
    char hops = 1;
    char addr = 100;
    char cmd  = req_light; /* Request light sensor data */

    /* mesh_pkt_ack_app means the destination should ACK manually
     * with our data, and an auto-ack is not performed.
     */
    mesh_send(addr, mesh_pkt_ack_app, &cmd, 1, hops);

    mesh_packet_t pkt;
    if (mesh_get_ack_pkt(&pkt, 100)) {
         /* We need to deform packet the same way it was formed */
         /* Parameters should be same after 4th parameter into mesh_form_pkt() */
         uint16_t light = 0;
         mesh_deform_pkt(&pkt, 1, &light, sizeof(light));
    }
}

void sensor(void)
{
    mesh_packet_t pkt;
    uint16_t light = 0;

    if (mesh_get_rx_pkt(&pkt, 100)) {
        /* Check if we were asked for an application ACK */
        if (mesh_is_ack_required(&pkt)) {

            /* Send the packet back based on the commanded byte */
            const char cmd = pkt.data[0];
            switch (cmd) {
                case req_light :
                    /* Send packet back to network source: pkt.nwk.src */
                    light = LS.getRawValue();
                    mesh_form_pkt(&pkt, pkt.nwk.src, mesh_pkt_ack_rsp, 1,
                                      1,
                                      &light, sizeof(light));
                break;

                case req_all :
                    /* TODO: You figure this out.  You can send up to 24 bytes of data */
                break;

                default:
                    printf("ERROR: Invalid data requested!\n");
                break;
            }

            mesh_send_formed_pkt(&pkt);
        }
    }
}


Wireless Functions Quick Reference

#include <stdio.h>
#include <stdint.h>
#include "wireless.h"

void quick_reference(void)
{
    char hops = 1;
    char addr = 100;
    mesh_packet_t pkt;

    /* Send a packet without an ACK or retries */
    mesh_send(addr, mesh_pkt_nack, "HELLO", 5, hops);

    /* Send a packet to everyone.  ACK not possible for broadcast messages */
    mesh_send(MESH_BROADCAST_ADDR, mesh_pkt_nack, "HELLO", 5, hops);

    /* Send a "PING" Packet with an ACK request */
    mesh_send(addr, mesh_pkt_ack, NULL, 0, hops);
    /* Wait for response of ping packet and print it */
    if (mesh_get_ack_pkt(&pkt, 1000)) {
        printf("Node response: ");
        for (int i=0; i < pkt.info.data_len; i++) {
            putchar( pkt.data[i] );
        }
        printf("\n");
    }

    /* Send a packet with "HELLO" (which is 5 data bytes) with an ACK request*/
    mesh_send(addr, mesh_pkt_ack, "HELLO", 5, hops);
    /* We should wait for the ACK if it was requested.  ACK typically takes 10ms per hop */
    if (mesh_get_ack_pkt(&pkt, 25)) {
    }

    /* Flush all incoming data (maybe for stale ACKs) */
    mesh_flush_rx();

    /* Send a packet and wait for ack for 1000ms (1 sec) */
    mesh_send(addr, mesh_pkt_ack, "HELLO", 5, hops);
    if (mesh_get_ack_pkt(&pkt, 1000)) {
        /* Got an ACK */
    }

    /* Try to get a packet destined for us with 100ms timeout*/
    if (mesh_get_rx_pkt(&pkt, 100)) {
    }

    /* Send a packet with two data variables */
    int var1 = 0;
    float var2 = 0;
    mesh_form_pkt(&pkt, addr, mesh_pkt_ack, hops,
                      2,                     /* 2 Pairs below */
                      &var1, sizeof(var1),   /* Pair 1 */
                      &var2, sizeof(var2));  /* Pair 2 */
    /* Packet was formed above, now send it */
    mesh_send_formed_pkt(&pkt);

    /* Manually set data bytes of a packet, but need to form the header first */
    mesh_form_pkt(&pkt, addr, mesh_pkt_ack, hops, 0);
    /* Modify the data bytes AFTER forming the packet header */
    pkt.data[0] = 10;
    pkt.data[1] = 11;
    pkt.data[2] = 12;
    pkt.info.data_len = 3;
    /* Finally, send the packet */
    mesh_send_formed_pkt(&pkt);
}


Troubleshooting

My boards cannot communicate.

  • Double check sys_config.h to make sure wireless channels are the same.
  • Did you use correct source and destination address? Double check the wireless address set at sys_config.h
  • Are the wireless boards too far away from each other?
  • Try doing Project --> Clean and build/compile your code again.


Sometimes wireless doesn't work

  • This is normal because sometimes wireless packets do not make it through due to interference.
    Try using another wireless channel.
    The wireless mesh software will retry packets a few times, but in case packets still do not make it through your software needs to be able to cope with this behavior.