espressif / esp-now

A connectionless Wi-Fi communication protocol
Apache License 2.0
528 stars 93 forks source link

Callback on send set but never gets called? (AEGHB-167) #51

Closed Bazmundi closed 1 year ago

Bazmundi commented 1 year ago

I have two devices, a Wemos BT with battery (aka wemosbat in PlatformIO), and a Wemos D1 R32, with the wemosbat to send a message to the R32 whenever a switch is closed on an input. The wemosbat is the "Pedal" (switch) and the D1 R32 is the "Pump". The wemosbat is the AP and the D1 R32 the STA.

Both fire up, connect, at least between the STA and the AP (I've used this scheme plenty of times for MQTT work but not with ESPNow - first time). However, while I set the callback on send, it never gets called?

Both use:

void InitESPNow(void) {
  //If the initialization was successful
  if (esp_now_init() == ESP_OK) {
    Serial.println("\nESPNow Init Success");
  }
  //If there was an error
  else {
    Serial.println("ESPNow Init Failed");
    ESP.restart();
  }
}

PEDAL

The callback on send is simple and aims to set a flag locally if the callback is successful, since that hints to the PEDAL that the PUMP is present.

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {

  Serial.println("Sent to pump"); 

  if (status == ESP_NOW_SEND_SUCCESS) {
    Serial.println( "Delivery Success");
    pumpPresence = PUMP_PRESENT;
  } else {
    Serial.println( "Delivery Fail");
    pumpPresence = PUMP_UNAVAILABLE;
  }
}

Setup reports ESPNow initialise and peer attached.

IPAddress local_ip(192,168,4,100);  // set IP of AP device
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);

// ESP-NOW message for pump
uint8_t pumpAddress[] = {0x84, 0xCC, 0xA8, 0x2C, 0x0D, 0xA8};

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

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    return;
  }

  // setup access point
  WiFi.mode(WIFI_AP);
  delay(1500);
  WiFi.softAPConfig(local_ip, gateway, subnet);
  WiFi.softAP(ssid, password /* , channel, hide_SSID, max_connection*/);
  delay(1500);
  Serial.print("\nMAC: ");
  Serial.println(WiFi.macAddress());

  // start ESP-NOW
  InitESPNow();

  esp_err_t cbstatus = esp_now_register_send_cb(OnDataSent);

  switch (cbstatus)
  {
  case ESP_OK:
    Serial.println("cb registered");
    break;

  case ESP_ERR_ESPNOW_NOT_INIT:
    Serial.println("ESP NOW not actually initiated");
    break;

  case ESP_ERR_ESPNOW_INTERNAL:
    Serial.println("ESP NOW internal error");
    break;

  default:
    Serial.println("Unknown error in cb registration");
    break;
  }

  // setup pedal input
  bounce.attach( BOUNCE_PIN ,  INPUT );
  bounce.interval(5); // interval in ms
  displayUpdate(bounce.read(),pumpPresence);

  // LED SETUP
  int ledState = HIGH;
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, ledState);

  // add peer
  memcpy(peerInfo.peer_addr, pumpAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;

  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  } else {
    Serial.println("peer added");

  }

}

The main loop will halt and report no PUMP.

void findPump (void) {
  while (not pumpPresence) {
    pumpMessage.pumpFlag_msg = PUMP_CONNECT;

    esp_err_t result = esp_now_send(pumpAddress, (uint8_t *) &pumpMessage, sizeof(pumpMessage));

    if (result == ESP_OK) {
      Serial.println("Find pump success");
      pumpPresence = PUMP_PRESENT;
    }
    else {
      Serial.println("Pump not there");
      pumpPresence = PUMP_UNAVAILABLE;
      delay(2000);
    }
  }
}

// forever do
void loop() {

  findPump();

  ...
}

It is iffy currently, having the PUMP PRESENCE set both in callback and findPump, but I am still trying to understand where is best to do this. I am assuming it will be in the callback, but as I am stuck presently, with the callback not firing, this is something to address once the callback is working.

Console reports at start up, so no debug message from callback and PUMP PRESENCE is never set to true:

MAC: 80:7D:3A:A3:F9:58
ESPNow Init Success
cb registered
peer added
Pump not there
Pump not there
Pump not there

...

PUMP

Pump sets up with:

IPAddress local_IP(192, 168, 4, 110);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);

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

  // setup access point
  WiFi.mode(WIFI_STA);

  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("STA Failed to configure");
  } else {
    Serial.print("STA configure as local IP: ");
    Serial.print(local_IP);
    Serial.print(" gateway: ");
    Serial.print(gateway);
    Serial.print(" subnset: ");
    Serial.println(subnet);
  }

  WiFi.begin(ssid, password);

  while(WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(100);
  }   

  Serial.print("\nMAC: ");
  Serial.println(WiFi.macAddress());

  // start ESP-NOW
  InitESPNow();

  esp_now_register_recv_cb(OnDataRecv);

  // LED SETUP
  int ledState = HIGH;
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, ledState);

}

Console reports:

STA configure as local IP: 192.168.4.110 gateway: 192.168.4.1 subnset: 255.255.255.0
.
 MAC: 84:CC:A8:2C:0D:A8

ESPNow Init Success

The single "." indicating STA finding and connecting to AP. MAC address of pedal matches that embedded in PEDAL code. ESPNOW inits. No callback onReceive raised.

void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {

  Serial.print(" Pedal press received "); 

  memcpy(&pumpMessage, incomingData, sizeof(pumpMessage));
  Serial.print("\r\nLast Packet Send Status:\t");

  if (not pumpMessage.pumpFlag_msg) {
    Serial.println( "Pedal first contact");
  }
  else {
    Serial.println( "Pedal pressed");
    togglePumpFlag = !togglePumpFlag;
  } 
}

UPSHOT

If this were simply subnets not set up properly, I would assume I would happily be receiving send callback indicating unsuccessful send.

Bazmundi commented 1 year ago

Okay, I am have added more decoding of return from esp_now_send, to get a ESP NOW peer wifi mismatch (aka ESP_ERR_ESPNOW_IF). Is there a boilerplate for setting up an AP/STA for working with ESP-NOW, please?

Bazmundi commented 1 year ago

Okay, so I tried the following:

From:

  // setup access point
  WiFi.mode(WIFI_AP);

To:

  // setup access point
  WiFi.mode(WIFI_AP_STA);
  WiFi.disconnect();

That seems to have fixed the problem.

I'll do some further checking and report back.

Bazmundi commented 1 year ago

Seems fine. I just have to sort now how to get the pair to reconnect if, say, the pump gets turned on then off. It seems to need some more smarts added. The function descriptions in the resources don't help with understanding the function relational behaviour.

I can get by with starting the pump, then starting the pedal and they will connect.

Bazmundi commented 1 year ago

So, I am finding that people are either:

I got away with AP_STA and WiFi.disconnect() on "master".

Is anyone right? Are they all right? Are any of them wrong?

lhespress commented 1 year ago

@Bazmundi Any update about this?

Bazmundi commented 1 year ago

saga resolved here

Basically, looking at the idea that the framework uses lower end of OSI stack, it makes sense that it is, for both master/slave, to use:

WiFi.mode(WIFI_STA);
WiFi.disconnet();

I note, however, the odd occasional message that does not get through. Infrequent, and the error reports seem to be link up, but message not received, but I suspect that is simply par for the course, and some interference. For my application, the problem is solved by pressing the button a second time, no harm done.

lhespress commented 1 year ago

@Bazmundi Good news, please close the issue if it has solved.