0

Im making a program to auto detecting nodes using esp-now protocol in tree topology. Right now im having a problem in using fsm in the code, here is my code

#include <ESP8266WiFi.h>
#include <espnow.h>

#define MAX_CHILD_NODES 10 // Maximum number of child nodes
bool receive = false;
bool replyBC = false;

unsigned long currentMillis = 0;
unsigned long Checkroot_timer = 0;
unsigned long duration = random(1000, 1500);

// REPLACE WITH RECEIVER MAC Address
uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
uint8_t rootMacAddress[6]; // MAC address of the root
uint8_t sendBroadcast[6];

enum State {
  STATE_1 = 1,
  STATE_2 = 2,
  STATE_3 = 3
};

struct ChildNode {
  uint8_t macAddress[6];
  bool isNewest;
  bool isConnectedBefore;
  bool replySent; // Flag to track if a reply has been sent
};

ChildNode childNodes[MAX_CHILD_NODES]; // Array to store MAC addresses of child nodes
int numChildNodes = 0; // Counter for the number of child nodes
int newestNodeIndex = -1; // Index of the newest node in the childNodes array

State currentState = STATE_1;

void printMacAddress(const uint8_t* mac) {
  for (int i = 0; i < 6; ++i) {
  if (mac[i] < 0x10) {
    Serial.print("0");
  }
  Serial.print(mac[i], HEX);
    if (i < 5) {
      Serial.print(":");
    }
  }
  Serial.println();
}

bool isMacAddressSaved(uint8_t* mac) {
  for (int i = 0; i < numChildNodes; i++) {
    if (memcmp(mac, childNodes[i].macAddress, 6) == 0) {
      return true; // MAC address already saved
    }
  }
  return false; // MAC address not saved
}

void printMessage(const uint8_t* incomingData, uint8_t len){
  for (int i = 0; i < len; i++) {
    Serial.print((char)incomingData[i]);
  }
  Serial.println();
}

void saveMacAddress(uint8_t* mac) {
  bool isConnectedBefore = isMacAddressSaved(mac);
  if (numChildNodes < MAX_CHILD_NODES && !isConnectedBefore) {
    memcpy(childNodes[numChildNodes].macAddress, mac, 6);
    childNodes[numChildNodes].isNewest = true;
    if (newestNodeIndex >= 0) {
      childNodes[newestNodeIndex].isNewest = false;
    }
    newestNodeIndex = numChildNodes;
    numChildNodes++;

    Serial.println("New node connected");
  } 
  else if (isConnectedBefore) {
    for (int i = 0; i < numChildNodes; i++) {
      if (memcmp(mac, childNodes[i].macAddress, 6) == 0) {
        childNodes[i].isConnectedBefore = true;
        childNodes[i].replySent = false; // Reset the replySent flag for reconnected nodes
        break;
      }
    }
    Serial.println("Node reconnected");
  }
}

void sendDataToNewNode(uint8_t* mac, uint8_t* data, uint8_t len) {
  Serial.println("Data to Child has Sent");
  esp_now_send(mac, data, len);
}

void sendReplyToRoot(uint8_t* mac, uint8_t* data, uint8_t len) {
  Serial.println("Reply sent to root");
  esp_now_send(mac, data, len);
}

void sendReplyToReconnectedNode() {
  if (numChildNodes > 0) {
    uint8_t replyData[] = "You have connected before";
    for (int i = 0; i < numChildNodes; i++) {
      if (childNodes[i].isConnectedBefore && !childNodes[i].replySent) {
        sendDataToNewNode(childNodes[i].macAddress, replyData, sizeof(replyData));
        childNodes[i].replySent = true; // Set the replySent flag to true
        break; // Stop after sending the reply to the first reconnected node
      }
    }
  }
}

void printSavedMacAddresses() {
  Serial.println("Saved MAC Addresses:");
  for (int i = 0; i < numChildNodes; i++) {
    Serial.print("Node ");
    Serial.print(i + 1);
    Serial.print(": ");
    for (int j = 0; j < 6; j++) {
      Serial.print(childNodes[i].macAddress[j], HEX);
      if (j < 5) {
        Serial.print(":");
      }
    }
    Serial.print(" | ");
    if (childNodes[i].isConnectedBefore) {
      Serial.println("Connected before");
    } 
    else {
      Serial.println("New connection");
    }
  }
}

void broadcast() {
  if (!receive) {
    uint8_t dataToSend[] = "SAYA NEW NODE";
    esp_now_send(broadcastAddress, dataToSend, sizeof(dataToSend));
  }
  delay(5000);
}

void OnDataSent(uint8_t *mac, uint8_t sendStatus) {
  Serial.print("Broadcast Status: ");
  if (sendStatus == 0) {
    Serial.println("Delivery success");
  } else {
    Serial.println("Delivery fail");
  }
}

void OnDataRecv(uint8_t *mac, uint8_t *incomingData, uint8_t len) {
  unsigned long currentMillis = millis();
  unsigned long Checkroot_timer = millis();
  bool rootCheck = false;
  
  switch(currentState){
    case STATE_1:
      Serial.print("Ini Case ");
      Serial.println(currentState);
      
      Serial.print("Received data from ");
      printMacAddress(mac);
      Serial.print("Received data: ");
      printMessage(incomingData, len);

      if (strncmp((char*)incomingData, "SAYA PARENT ANDA", len) == 0) {
        memcpy(rootMacAddress, mac, 6);
        Serial.println("Root MAC address saved");
        receive = true;
        currentState = STATE_2;
      } 
      else if (strncmp((char*)incomingData, "You have connected before", len) == 0){
        memcpy(rootMacAddress, mac, 6);
        Serial.println("Root MAC address saved");
        receive = true;
        currentState = STATE_2;
      }
      else if (strncmp((char*)incomingData, "Sudah ada PARENT?", len) == 0){
        uint8_t dataToSend[] = "Belum Ada PARENT";
        sendReplyToRoot(mac, dataToSend, sizeof(dataToSend));
        receive = true;
        currentState = STATE_1;
      }
      else {
        currentState = STATE_1;
      }
      break;

    case STATE_2:
      Serial.print("Ini Case ");
      Serial.println(currentState);

      Serial.print("Received data from ");
      printMacAddress(mac);
      Serial.print("Data: ");
      printMessage(incomingData, len);
      
      if (strncmp((char*)incomingData, "SAYA NEW NODE", len) == 0) {
        while (millis() - currentMillis <= duration){
        }
        uint8_t dataToSend[] = "Sudah ada PARENT?";
        esp_now_send(mac, dataToSend, sizeof(dataToSend));
        currentState = STATE_3;
      }
      else{
        currentState = STATE_2;
        break;
      }
      break;

    case STATE_3:
      while (millis() - Checkroot_timer <= 1000){
        if (strncmp((char*)incomingData, "Belum Ada PARENT", len) == 0){
          rootCheck = true;
          saveMacAddress(mac);
          printSavedMacAddresses();
          if (numChildNodes > 0) {
            if (childNodes[newestNodeIndex].isConnectedBefore && !childNodes[newestNodeIndex].replySent) {
              sendReplyToReconnectedNode();
            }
            else {
              uint8_t dataToSend[] = "SAYA PARENT ANDA";
              sendDataToNewNode(mac, dataToSend, sizeof(dataToSend));
            }
            currentState = STATE_2;
          }
        }
      }
      if (rootCheck == false){
        Serial.println("No reply");
        currentState = STATE_2;
      }
      break;
  }
}

void setup() {
  // Init Serial Monitor
  Serial.begin(115200);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  esp_now_register_send_cb(OnDataSent);

  // Register peer
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  broadcast();
}

for the state diagram looks like this and the PARENT's code it is almost similar to state 3, but without the while() function State Diagram

For example, in state 1 at currentState = STATE_2, the program doesnt move to state 2 if there is no new received message. So if there is no new received data the state still in state 1, then if the program receive new data it will go to state 2 and starts to print "Ini Case ". How do I make it so that after current_state = state_2 the program immediately runs state 2?

When I run the code and it in state 1 then currentState = STATE_2, it doesn't immediately runs state 2. It still in state 1 until the program receive new data

Varaz
  • 1
  • 1
  • Hi @Varaz, welcome to Stack Overflow. You didn't post an entire program so it's difficult to answer your question. However, the code that's supposed to switch states is in a function called `OnDataRecv()`. You seem to expect that code to run if no data is received. Why would that happen if this is a function that's looks like it's only called if data is received? – romkey Jun 28 '23 at 19:27
  • @romkey I see. So `OnDataRecv()` only works if the program receive data, doesn't it? I thought it can run although there is no data is received – Varaz Jun 28 '23 at 20:08
  • It's difficult to say for sure as we can't see your whole program. But how does it make sense for it to run if data isn't received? Why and when would it be called then? Why is it named "on data receive" if it's being called when data isn't received? – romkey Jun 28 '23 at 20:18
  • @romkey i have updated the program's code. Okay, I know where is my fault now. Then where should I put the switch states? – Varaz Jun 28 '23 at 20:21

0 Answers0