nRF24 / RF24Mesh

OSI Layer 7 Mesh Networking for RF24Network & nrf24L01+ & nrf52x devices
http://nrf24.github.io/RF24Mesh
GNU General Public License v2.0
422 stars 154 forks source link

Mesh is not connecting more than 5 nodes #170

Closed ksnboopa closed 4 years ago

ksnboopa commented 4 years ago

Hi,

I have PI zero as master and arduino nano as my sensor nodes. When I connect to my nodes everything seems to works fine with samples. When I add one mode node to mesh, mesh is connected but not able to send messages to all my nodes.

Master : RPi Zero Nodes : Arduino nano boards

I will change only node ID to add any new node and upload my sketch.

Whenever I get MQTT msg, will be published to specific node. Whenever I get Mesh msg, will be published to MQTT back.

I tried all possible ways for this. Please help me on this. Or should I enable any configuration If I want to add multiple nodes while I use fixed nodeID.

Child script :

int FREQ = 98; // Subject to change
uint8_t thisNode = 5; // Unique value for each node
String moduleType = "2SWMDL";

const int outPinsLength = 2;
int eepromStartIndex = 11;
int outPins[] = { 3, 5 };
int meshUpdateInterval = 0;
RF24 radio(7, 8);
RF24Network network(radio);
RF24Mesh mesh(radio, network);

#define STATUS_LED_PIN 2
#define DATA_LED_PIN 6

int pinStatues[outPinsLength];

uint32_t displayTimer = 0;

bool powerLossHandled = false;

struct request {                 // Structure of our payload
    uint8_t nodeId;
    char action[15];
    char data[25];
};

struct response {                 // Structure of our payload
    uint8_t nodeId;
    char module[15];
    char data[25];
};

void setup() {
    Serial.begin(115200);
    pinMode(STATUS_LED_PIN, OUTPUT);
    pinMode(DATA_LED_PIN, OUTPUT);
    blink(STATUS_LED_PIN, 4);
    blink(DATA_LED_PIN, 4);
    MsTimer2::set(10, powerLossDetector);
    MsTimer2::start();
    EEPROM.begin();
    int localEEpromIndex = eepromStartIndex;
    for (int i = 0; i < outPinsLength; i++) {
        uint8_t state = EEPROM.read(localEEpromIndex);
        pinStatues[i] = state ? HIGH : LOW;
        pinMode(outPins[i], OUTPUT);
        digitalWrite(outPins[i], pinStatues[i]);
        localEEpromIndex += 1;
    }
    // Set the nodeID manually
    mesh.setNodeID(thisNode);
    // Connect to the mesh
    Serial.println(F("Connecting to the mesh..."));
    Serial.print("node ID ::");
    Serial.print(thisNode);
    Serial.print(", Freq ::");
    Serial.println(FREQ);
//  mesh.begin(FREQ, RF24_1MBPS, 60000);
    mesh.begin();
    Serial.println("setup done.. ");
}

void loop() {
    if (millis() - displayTimer >= 4000) {
        displayTimer = millis();

        mesh.update();
        if (!mesh.checkConnection()) {
            blink(STATUS_LED_PIN, 2);
            Serial.println("Renewing Address");
            mesh.renewAddress();
        } else {
            digitalWrite(STATUS_LED_PIN, LOW);
        }
        // Update mesh.. but not too frequently as all are constant node IDs
        if (meshUpdateInterval > 10) {
            Serial.println("Updating Mesh network each 30 seconds");
            meshUpdateInterval = 0;
        }
        meshUpdateInterval++;
        powerLossHandled = false;
    }
    while (network.available()) {
        RF24NetworkHeader header;
        request req;
        network.read(header, &req, sizeof(req));
        Serial.println("############## DATA RECEIVED START ##############");
        Serial.print("Action : ");
        Serial.println(req.action);
        Serial.print("Data : ");
        Serial.println(req.data);
        Serial.println("############## DATA RECEIVED END##############");
        blink(DATA_LED_PIN, 2);
        String action = String(req.action);
        if (action.equalsIgnoreCase("set")) {
            setValue(req.data);
            sendDataToMaster((String) req.action);
        } else {
            sendDataToMaster((String) req.action);
        }
    }
}

void blink(uint8_t pin, uint8_t times) {
    int i = 0;
    do {
        digitalWrite(pin, LOW);
        delay(50);
        digitalWrite(pin, HIGH);
        delay(50);
        i++;
    } while (i < times);
}

void powerLossDetector() {
    if (analogRead(A3) < 920) {
        pinMode(A3, OUTPUT);
        digitalWrite(A3, HIGH);
        return;
    }
    pinMode(A3, INPUT);
    if (!powerLossHandled && analogRead(A3) > 1000) {
        // Power loss detected...
        int localEEpromIndex = eepromStartIndex;
        for (int i = 0; i < outPinsLength; i++) {
            bool state = pinStatues[i];
            EEPROM.update(localEEpromIndex, state);
            localEEpromIndex += 1;
        }
        Serial.println("Power loss detected and handled");
        powerLossHandled = true;
    }
}

void sendDataToMaster(String action) {
    String values = getAllValues();
    Serial.print("Sending...");
    response resp;
    resp.nodeId = thisNode;
    String tmpStr = action + "#" + moduleType;
    memcpy(resp.module, tmpStr.c_str(), tmpStr.length() + 1);
    memcpy(resp.data, values.c_str(), values.length() + 1);
    bool ok = mesh.write(&resp, 'M', sizeof(resp));
    if (ok){
        Serial.println("ok.");
        blink(DATA_LED_PIN, 5);
    }
    else
        Serial.println("failed.");

}

void setValue(String data) {
    data.trim();
    unsigned int length = data.length();
    if (length > 1 && length <= 5) {
        char pin = data.charAt(0);
        int index = extractSwitchIndex(pin);
        int pinStatus = extractValue(data);
        digitalWrite(outPins[index], pinStatus);
        pinStatues[index] = pinStatus;
    }
}

int extractSwitchIndex(char c) {
    int ascii = c;
    if (ascii >= 65 && ascii <= 74) {
        return ascii - 65;
    } else {
        return -1;
    }
}

int extractValue(String inputData) {
    String value = inputData.substring(2);
    return value.equals("ON") ? HIGH : LOW;
}

String getAllValues() {
    String values = "";
    for (int i = 0; i < outPinsLength; i++) {
        char state = 'T';
        String status = (pinStatues[i]) ? "ON" : "OFF";
        if (i > 0) {
            values += ",";
        }
        values += String((char) (65 + i)) + String(state) + status;
    }
    return values;
}

Master script :

RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ);
RF24Network network(radio);
RF24Mesh mesh(radio, network);

const string SERVER_ADDRESS("MQTT-SERVER");
const int QOS = 1;
const int N_RETRY_ATTEMPTS = 30;

struct request {                 // Structure of our payload
    uint8_t nodeId;
    char action[15];
    char data[25];
};

struct response {                 // Structure of our payload
    uint8_t nodeId;
    char module[15];
    char data[25];
};

string serial_no = "master-unique-id";
mqtt::async_client client(SERVER_ADDRESS, serial_no);

/////////////////////////////////////////////////////////////////////////////
class action_listener: public virtual mqtt::iaction_listener {
    string name_;

    void on_failure(const mqtt::token &tok) override
    {
        cout << name_ << " failure";
        if (tok.get_message_id() != 0)
            cout << " for token: [" << tok.get_message_id() << "]" << endl;
        cout << endl;
    }

    void on_success(const mqtt::token &tok) override
    {
        cout << name_ << " success";
        if (tok.get_message_id() != 0)
            cout << " for token: [" << tok.get_message_id() << "]" << endl;
        auto top = tok.get_topics();
        if (top && !top->empty())
            cout << "\ttoken topic: '" << (*top)[0] << "', ..." << endl;
        cout << endl;
    }

public:
    action_listener(const string &name) :
            name_(name) {
    }
};
/////////////////////////////////////////////////////////////////////////////
class callback: public virtual mqtt::callback,
        public virtual mqtt::iaction_listener

{
    int nretry_;
    mqtt::async_client &cli_;
    mqtt::connect_options &connOpts_;
    action_listener subListener_;

    void reconnect() {
        this_thread::sleep_for(chrono::milliseconds(2500));
        try {
            cli_.connect(connOpts_, nullptr, *this);
        } catch (const mqtt::exception &exc) {
            cerr << "Error: " << exc.what() << endl;
            exit(1);
        }
    }

    void on_failure(const mqtt::token &tok) override
    {
        cout << "Connection attempt failed" << endl;
        if (++nretry_ > N_RETRY_ATTEMPTS)
            exit(1);
        reconnect();
    }

    void on_success(const mqtt::token &tok) override
    {
    }

    void connected(const string &cause) override
    {
        cout << "\nConnection success" << endl;
        cout << "SERIAL NO : " << serial_no << endl;
        cli_.subscribe("$inzion/" + serial_no + "/control", QOS, nullptr,
                subListener_);
    }
    void connection_lost(const string &cause) override
    {
        cout << "\nConnection lost" << endl;
        if (!cause.empty())
            cout << "\tcause: " << cause << endl;

        cout << "<> Reconnecting...\n" << endl;
        nretry_ = 0;
        reconnect();
    }

    void sendToNode(RF24NetworkHeader header, request req,
            uint16_t _directAddress, string sendType) {
        cout << ">> TO ID: '" << (int) req.nodeId << ", TYPE : '" << sendType
                << ", ADDR : '" << (int) _directAddress << ", DATA : "
                << req.data << ", ACTION : " << req.action << endl;
        for (uint8_t i = 0; i < 2; i++) {
                        //if (mesh.write(_directAddress, &req, 'F' , sizeof(request))) {
            if (network.write(header, &req, sizeof(request)/*, _directAddress*/)) {
                cout << "SENDING OK." << endl;
                break;
            }
            usleep(500 * i);
            cout << "SENDING FAILED!! Retry counter is " << (int) i << endl;
        }
    }

    void action_individual(json parsedJson) {
        cout << ">> UNICAST STARTED" << endl;
        string action = parsedJson["action"];
        string _chip_id = parsedJson["chipId"];
        string data = parsedJson["data"];

        request req;
        req.nodeId = stoi(_chip_id);
        memcpy(req.action, action.c_str(), action.length() + 1);
        memcpy(req.data, data.c_str(), data.length() + 1);

        uint16_t _dest_address;
        for (int i = 0; i < mesh.addrListTop; i++) {
            if (mesh.addrList[i].nodeID == req.nodeId) {
                _dest_address = mesh.addrList[i].address;
                break;
            }
        }

        RF24NetworkHeader header(_dest_address);
        sendToNode(header, req, _dest_address, "action");
        cout << ">> UNICAST ENDED" << endl;
    }

    void action_broadcast(json parsedJson) {
        cout << ">> BROADCAST TO ALL NODES STARTED" << endl;
        string action = parsedJson["action"];
        for (int i = 0; i < mesh.addrListTop; i++) {
            if (mesh.addrList[i].nodeID != 0) {
                request req;
                memcpy(req.action, action.c_str(), action.length() + 1);
                RF24NetworkHeader header(mesh.addrList[i].address);
                sendToNode(header, req, mesh.addrList[i].address, "scan");
            }
        }
        cout << ">> BROADCAST TO ALL NODES ENDED" << endl;
    }

    void message_arrived(mqtt::const_message_ptr msg) override
    {
        cout << ">> RCVD MSG TOPIC: " << msg->get_topic() << ", PAYLOAD: "
                << msg->to_string() << endl;

        string receivedString = msg->to_string();
        json parsedJson;
        try {
            parsedJson = json::parse(receivedString);
            string action = parsedJson["action"];
            if (action.compare("scan") == 0) {
                action_broadcast(parsedJson);
            } else {
                action_individual(parsedJson);
            }
        } catch (json::exception &e) {
            cout << "message: " << e.what() << '\n' << "exception id: " << e.id
                    << endl;
            return;
        }
    }

public:
    callback(mqtt::async_client &cli, mqtt::connect_options &connOpts) :
            nretry_(0), cli_(cli), connOpts_(connOpts), subListener_(
                    "Subscription") {
    }
};
/////////////////////////////////////////////////////////////////////////////

void publish_response_to_mqtt(const char *arr) {
    client.publish("TOPIC", arr, strlen(arr), 1,
            false);
    cout << "\n >>> SENT TO MQTT" << endl;
}
/////////////////////////////////////////////////////////////////////////////
string convert_to_mqtt_msg(response resp) {
    json respJson;
    respJson["chipId"] = resp.nodeId;
    respJson["module"] = resp.module;

    stringstream ss(resp.data);
    vector < string > values;

    while (ss.good()) {
        string substr;
        getline(ss, substr, ',');
        values.push_back(substr);
    }

    respJson["values"] = values;
    cout << "<< " << respJson << endl;
    return respJson.dump();
}
/////////////////////////////////////////////////////////////////////////////

int main(int argc, char **argv) {
    // Initiating MQTT

    mqtt::connect_options connOpts;
    connOpts.set_keep_alive_interval(20);
    connOpts.set_clean_session(true);
    callback cb(client, connOpts);
    client.set_callback(cb);
    try {
        cout << "Connecting to the MQTT server..." << flush;
        client.connect(connOpts, nullptr, cb);
    } catch (const mqtt::exception&) {
        cerr << "\nERROR: Unable to connect to MQTT server: '" << SERVER_ADDRESS
                << "'" << endl;
    return 1;
    }

    delay(10000);
    uint8_t channel = 97;
    printf("start\n");
    mesh.setNodeID(0);
    mesh.begin();
    radio.printDetails();
    printf("\nChannel is : %u", radio.getChannel());

    while (1) {
        mesh.update();
        mesh.DHCP();
        while (network.available()) {
            RF24NetworkHeader header;
            network.peek(header);

            response dat;
            switch (header.type) {
            case 'M': {
                network.read(header, &dat, sizeof(response));
                cout << "<< GOT FROM NODE : " << (int) dat.nodeId << ", MODULE: "
                        << dat.module << ", DATA: " << dat.data << endl;
                process_node_response(dat);
                break;
            }
            default: {
                network.read(header, 0, 0);
                printf("Rcv bad type %d from 0%o\n", header.type,
                        header.from_node);
                break;
            }
            }
        }
        delay(2);
    }
    return 0;
}
Avamander commented 4 years ago

You aren't calling mesh.update() often enough for any reliable node-to-node communication required for the mesh to function.

ksnboopa commented 4 years ago

@Avamandar,

I am calling it in master even without delay and in child node in every 4 seconds.

Could that be a problem?

I’ll change it without delay in child and let me check.

ksnboopa commented 4 years ago

No luck yet.

Avamander commented 4 years ago

If you take the bare examples, do they work on your hardware and with this node count?

ksnboopa commented 4 years ago

With bare examples I have not tested it yet. We will change implementation to RF24Ethernet and check it and let know

ksnboopa commented 4 years ago

@Avamander,

It was memory issue. my SRAM was used more than 73%. Due to which program was not stable enough as atmel 328 has 2048bytes only. Already Ethernet along with rf24mesh took around 68% progmem and 65% SRAM for mqtt_basic.ini itself.

I am just monitoring my devices and it’s stability with 8 nodes along with Raspberry gateway. I will post results after a week monitoring.

Qn. I am planning to connect 100 nodes with a single Raspberry gateway with data rate as 1 mbps. Will it be a good enough design or do I need to include any extra care with code and config.?

Anyways thanks a lot for your support and this awesome library guys...

Avamander commented 4 years ago

It was memory issue. my SRAM was used more than 73%.

Wrap every string in your code into F() for example F("This is a string") that will reduce RAM usage.

Will it be a good enough design or do I need to include any extra care with code and config.?

The library can not handle collisions, either do scheduling or retransmissions if you need arrival.

I am planning to connect 100 nodes with a single Raspberry gateway with data rate as 1 mbps.

If you reset your master node then you either have to load address allocations from disk and/or implement a ping-pong scheme to make sure nothing is lost. Have your nodes contact the master and expect a reply, that way the node will know the address it has is valid, if the address is not valid but your node uses it it'll cause issues.