ESP-NOW has to be on the same SSID

I was wondering if there’s anyway around this. I have a access point on top of the garage that reaches all over the property, but inside the cabin, it doesn’t quite reach. And then there’s a modem in the garage with another SSID. So I have to change the SSID on the master ESP-NOW and all the slaves to match that SSID. Doesn’t that kinda defeat the purpose of ESP-NOW for an IOT system?

I thought that ESP-NOW used direct communication between boards without involving/using WiFi

Is that not the case ?

Both the examples I’m using ask for an ssid & password. I copied the examples from randomnurdtutorials.com

nurd vs nerd
incl the whole address

https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/

ESP-Now requires the peers to be on the same WiFi channel. Connecting to the same Access Point is an easy way to do that, since an AP is always on a given channel. But an AP is not required. You can hard-code the channel in advance; or you can manually cycle through the channels trying to find your peer(s)

In your case, you could have both your APs are on the same channel; although technically this means that everything is slightly more likely to interfere with each other and maybe lower the throughput (enough to be noticeable?)

Please post one of them here, using code tags when you do

#if 1
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-esp-now-wi-fi-web-server/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.  
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <esp_now.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include "ESPAsyncWebServer.h"
#include <Arduino_JSON.h>
//#include <SoftwareSerial.h>

// Replace with your network credentials (STATION)
//const char* ssid = "Outer Limits";
const char* ssid = "FAP_7BB3";
const char* password = "twister56";
typedef unsigned char UCHAR;
// Structure example to receive data
// Must match the sender structure

typedef struct struct_message {
  int id;
  float temp;
  float hum;
  unsigned int readingId;
  int extra_cmd;
} struct_message;

struct_message incomingReadings;

// server to log into 
const char* host = "192.168.88.239";	
const uint16_t port = 5193;

UCHAR msg1[30];
UCHAR msg2[200];

JSONVar board;

WiFiClient gclient;
int msg_len;
UCHAR cmd, dest;

static UCHAR pre_preamble[] = {0xF8,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x00};

void send_msg(int msg_len, UCHAR *msg, UCHAR msg_type, UCHAR dest);
int get_msg(void);
/*
#define SOFTSERIAL_BAUD 9600
// Define pins for the first software serial port: TX on pin 6, RX on pin 7.
#define SS1_TX 6
#define SS1_RX 7
SoftwareSerial softSerial1(SS1_RX, SS1_TX);
*/
char xbyte = ' ';

AsyncWebServer server(80);
AsyncEventSource events("/events");
int new_data;

#endif
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) { 
  // Copies the sender mac address to a string
/*
  char macStr[18];
  Serial.print("Packet received from: ");
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
*/
	memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));
	board["id"] = incomingReadings.id;
	board["temperature"] = incomingReadings.temp;
	board["humidity"] = incomingReadings.hum;
	board["readingId"] = String(incomingReadings.readingId);
	board["extra_cmd"] = String(incomingReadings.extra_cmd);
	String jsonString = JSON.stringify(board);
	events.send(jsonString.c_str(), "new_readings", millis());

	Serial.printf("\r\nBoard ID %u: %u bytes\r\n", incomingReadings.id, len);
	Serial.printf("t value: %4.2f \r\n", incomingReadings.temp);
	Serial.printf("h value: %4.2f \r\n", incomingReadings.hum);
	Serial.printf("readingID value: %d \r\n", incomingReadings.readingId);
	Serial.printf("extra cmd: %d \r\n", incomingReadings.extra_cmd);
	Serial.println();

	sprintf((char *)msg2, "%u %4.2f %4.2f %d %d",incomingReadings.id, incomingReadings.temp, incomingReadings.hum, incomingReadings.readingId, incomingReadings.extra_cmd);
	msg_len = strlen((const char *)msg2);
	if(incomingReadings.extra_cmd == 74)
		cmd = 74;
	else cmd = 73;
	send_msg(msg_len, msg2, cmd, dest);
}
#if 1
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP-NOW DASHBOARD</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p {  font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #2f4468; color: white; font-size: 1.7rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 500px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
    .reading { font-size: 2.8rem; }
    .packet { color: #bebebe; }
    .card.temperature { color: #fd7e14; }
    .card.humidity { color: #1b78e2; }
  </style>
</head>
<body>
  <div class="topnav">
    <h3>ESPNOW DASHBOARD</h3>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card temperature">
        <h4><i class="fas fa-thermometer-half"></i> BOARD #1 - TEMPERATURE</h4><p><span class="reading"><span id="t1"></span> &deg;C</span></p><p class="packet">Reading ID: <span id="rt1"></span></p>
      </div>
      <div class="card humidity">
        <h4><i class="fas fa-tint"></i> BOARD #1 - HUMIDITY</h4><p><span class="reading"><span id="h1"></span> &percnt;</span></p><p class="packet">Reading ID: <span id="rh1"></span></p>
      </div>
      <div class="card temperature">
        <h4><i class="fas fa-thermometer-half"></i> BOARD #2 - TEMPERATURE</h4><p><span class="reading"><span id="t2"></span> &deg;C</span></p><p class="packet">Reading ID: <span id="rt2"></span></p>
      </div>
      <div class="card humidity">
        <h4><i class="fas fa-tint"></i> BOARD #2 - HUMIDITY</h4><p><span class="reading"><span id="h2"></span> &percnt;</span></p><p class="packet">Reading ID: <span id="rh2"></span></p>
      </div>
    </div>
  </div>
<script>

if (!!window.EventSource) {
 var source = new EventSource('/events');
 
 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);
 
 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);
 
 source.addEventListener('new_readings', function(e) {
  console.log("new_readings", e.data);
  var obj = JSON.parse(e.data);
  document.getElementById("t"+obj.id).innerHTML = obj.temperature.toFixed(2);
  document.getElementById("h"+obj.id).innerHTML = obj.humidity.toFixed(2);
  document.getElementById("rt"+obj.id).innerHTML = obj.readingId;
  document.getElementById("rh"+obj.id).innerHTML = obj.readingId;
 }, false);
}
</script>
</body>
</html>)rawliteral";
#endif
void setup() 
{
// Initialize Serial Monitor
//	softSerial1.begin(SOFTSERIAL_BAUD);
	Serial.begin(115200);
	new_data = 0;
  // Set the device as a Station and Soft Access Point simultaneously
	WiFi.mode(WIFI_AP_STA);
//  WiFi.mode(WIFI_STA);
  
  // Set device as a Wi-Fi Station
	WiFi.begin(ssid, password);
	while (WiFi.status() != WL_CONNECTED) 
	{
		delay(1000);
		Serial.println("Setting as a Wi-Fi Station..");
	}
	Serial.print("Station IP Address: ");
	Serial.println(WiFi.localIP());
	Serial.print("Wi-Fi Channel: ");
	Serial.println(WiFi.channel());

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

// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info

	esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));

	server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
	{
		request->send(200, "text/html", index_html);
	});

	//  server.onNotFound(notFound);
	server.begin();

	events.onConnect([](AsyncEventSourceClient *client)
	{
		if(client->lastId())
		{
			Serial.printf("Client reconnected! Last message ID that it got is: %u\n", (unsigned int)client->lastId());
		}
		// send event with message "hello!", id current millis
		// and set reconnect delay to 1 second
		client->send("hello!", NULL, millis(), 10000);
	});
	server.addHandler(&events);
	server.begin();
}

void loop() 
{
	int i;
	static unsigned long lastEventTime = millis();
	static const unsigned long EVENT_INTERVAL_MS = 5000;
	if ((millis() - lastEventTime) > EVENT_INTERVAL_MS) 
	{
		events.send("ping",NULL,millis());
		Serial.println("ping");
		lastEventTime = millis();
	}

	if (!gclient.connect(host, port)) 
	{
		Serial.println("connection failed");
		delay(5000);
		return;
	}
	// This will send a string to the server
	//  Serial.println("sending data to server");
	msg1[0] = 0;	// destination
	strcpy((char *)msg2,"wifi client\0");
	msg_len = strlen((const char *)msg2);
	strncpy((char *)(msg1+1), (const char *)msg2, msg_len);

	if (gclient.connected())
	{
		Serial.println("sending client name");
		send_msg(msg_len, msg2, 72, 5);		// 72 is SET_CLIENT_NAME
	}
	Serial.println("connected");
	cmd = 73;		// SEND_IOT_VALUES cmd
	dest = 5;
	Serial.println("start sending data");
	for(;;)
	{
		delay(100);
	}
}
#if 1
/*********************************************************************/
// get preamble & msg len from client
// preamble is: {0xF8,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,
// msg_len(lowbyte),msg_len(highbyte),0x00,0x00,0x00,0x00,0x00,0x00}
// returns message length
int get_msg(void)
{
int len;
UCHAR low, high;
int ret;
int i;
/*
UCHAR preamble[10];
ret = recv_tcp(preamble,8,1);
//	printf("ret: %d\n",ret);
if(ret < 0)
{
printf("ret < 0");
}
if(memcmp(preamble,pre_preamble,8) != 0)
{
printf("bad preamble\n");
//		uSleep(2,0);
return -1;
}
ret = recv_tcp(&low,1,1);
ret = recv_tcp(&high,1,1);
//	printf("%02x %02x\n",low,high);
len = 0;
len = (int)(high);
len <<= 4;
len |= (int)low;
*/
return len;
}
/*********************************************************************/
// send the preamble, msg len, msg_type & dest (dest is index into client table)
void send_msg(int msg_len, UCHAR *msg, UCHAR msg_type, UCHAR dest)
{
	int ret;
	int i;
	UCHAR temp[2];

	//	if(dest > MAX_CLIENTS)
	//		return;

	//	if(test_sock())
	if(1)
	{
		gclient.write((uint8_t *)&pre_preamble[0],8);
		temp[0] = (UCHAR)(msg_len & 0x0F);
		temp[1] = (UCHAR)((msg_len & 0xF0) >> 4);
		//		printf("%02x %02x\n",temp[0],temp[1]);

		gclient.write((uint8_t *)&temp[0],1);
		delay(10);
		gclient.write((uint8_t *)&temp[1],1);
		delay(10);
		gclient.write((uint8_t *)&msg_type,1);
		delay(10);
		gclient.write((uint8_t *)&dest,1);
		delay(10);

		for(i = 0;i < msg_len;i++)
		{
			gclient.write((uint8_t *)&msg[i],1);
			delay(10);
		}
	}
}
#endif

This is for the master. The send_msg() at the bottom is where it sends the data collected from the slaves back to the server which is just a raspberry pi running a tcpip server along with some other controller cards running as clients. The esp master logs into the server as another client.

Here is the code for one of the slaves. I’m just pumping out fake data sense I don’t have any real sensors hooked up.

#if 1
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-esp-now-wi-fi-web-server/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <esp_now.h>
#include <esp_wifi.h>
#include <WiFi.h>
//#include <Adafruit_Sensor.h>
//#include <DHT.h>

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 1

// Digital pin connected to the DHT sensor
#define DHTPIN 4  

// Uncomment the type of sensor in use:
//#define DHTTYPE    DHT11     // DHT 11
#define DHTTYPE    DHT22     // DHT 22 (AM2302)
//#define DHTTYPE    DHT21     // DHT 21 (AM2301)

//DHT dht(DHTPIN, DHTTYPE);

float h1;
float t1;

//MAC Address of the receiver 
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

//Structure example to send data
//Must match the receiver structure
typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
	int extra_cmd;
} struct_message;

esp_now_peer_info_t peerInfo;

//Create a struct_message called myData
struct_message myData;

unsigned long previousMillis = 0;   // Stores last time temperature was published
const long interval = 120000;        // Interval at which to publish sensor readings

unsigned int readingId = 0;

// Insert your SSID
constexpr char WIFI_SSID[] = "FAP_7BB3";
//constexpr char WIFI_SSID[] = "Outer Limits";

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
      for (uint8_t i=0; i<n; i++) {
          if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
              return WiFi.channel(i);
          }
      }
  }
  return 0;
}
#endif
float readDHTTemperature(float t) {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  // Read temperature as Celsius (the default)
  //float t = dht.readTemperature();
  float t2 = t + 0.01;
  if(t2 > 20.0)
  {
	  t2 = 0.01;
	  Serial.println("reset");
  }
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float t = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(t2)) {    
    Serial.println("Failed to read from DHT sensor!");
    return 0;
  }
  else {
    Serial.println(t2);
    return t2;
  }
}

float readDHTHumidity(float h) 
{
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  //float h = dht.readHumidity();
  float h2 = h + 0.01;

  if(h2 > 20.0)
  {
	  h2 = 0.01;
  }
  if (isnan(h2)) 
  {
    Serial.println("Failed to read from DHT sensor!");
    return 0;
  }
  else 
  {
    Serial.println(h2);
    return h2;
  }
}

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  //Init Serial Monitor
  Serial.begin(115200);
  //dht.begin();
 
  // Set device as a Wi-Fi Station and set channel
  WiFi.mode(WIFI_STA);

  int32_t channel = getWiFiChannel(WIFI_SSID);

  WiFi.printDiag(Serial); // Uncomment to verify channel number before
  esp_wifi_set_promiscuous(true);
  esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
  esp_wifi_set_promiscuous(false);
  WiFi.printDiag(Serial); // Uncomment to verify channel change after

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    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_register_send_cb(OnDataSent);
  esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataSent));
  
  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;
    //Set values to send
    myData.id = BOARD_ID;
	Serial.print("loop: ");
	Serial.print(t1);
	Serial.print(" ");
	Serial.println(h1);
    t1 = myData.temp = readDHTTemperature(t1);
    h1 = myData.hum = readDHTHumidity(h1);
	myData.extra_cmd = 0;
    myData.readingId = readingId++;
     
    //Send message via ESP-NOW
    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    if (result == ESP_OK) {
      Serial.println("Sent with success");
    }
    else {
      Serial.println("Error sending the data");
    }
  }
}


ESP-NOW is not using the WiFi connection in that sketch as far as I can see

One thing to note, the tcpip client server uses preambles so the send_msg() sends a preamble first before sending the data.

So then esp-now uses the 2.4GHz radio but they don’t have to go threw the wifi network? Maybe that’s the problem if the slaves/masters are out of range.

Correct - they do their own espnow thing, like with a SoftAP.

I estimate their range approx 50 feet.

With clear line of sight, people have reported 100 m communication. Your WiFi access point is not required. This person claims over 400 m with multiple units.

I was gonna say, I did a test of the master just outside the door of the garage and set up the 1st slave about 50 yards away, clear line of sight and it was fine. I was trying to see if I could put another slave further on down (past the 1st slave) but couldn't pin it up on anything in this wind.

One slave could relay a message to a second slave, thus extending the effective range

FYI in the esp-idf version:

github.com

there’s an option to set long range mode on line 99 in the above file:

 ...
 ESP_ERROR_CHECK( ret );
    ESP_ERROR_CHECK( esp_netif_init());
    ESP_ERROR_CHECK( esp_event_loop_create_default() );
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
    ESP_ERROR_CHECK( esp_wifi_set_mode(MY_ESPNOW_WIFI_MODE) );
    ESP_ERROR_CHECK( esp_wifi_start() );
#if MY_ESPNOW_ENABLE_LONG_RANGE
    ESP_ERROR_CHECK( esp_wifi_set_protocol(MY_ESPNOW_WIFI_IF, WIFI_PROTOCOL_11B|WIFI_PROTOCOL_11G|WIFI_PROTOCOL_11N|WIFI_PROTOCOL_LR) );
#endif
    ESP_ERROR_CHECK( esp_now_init() );
    ESP_ERROR_CHECK( esp_now_register_recv_cb(recv_cb) );
    ESP_ERROR_CHECK( esp_now_set_pmk((const uint8_t *)MY_ESPNOW_PMK) );
...

Huh

Looks like it's supported in the Arduino esp32 core

/**
 * enable WiFi long range mode
 * @param enable
 */
void WiFiGenericClass::enableLongRange(bool enable) {
  _long_range = enable;
}

/**
 * set new mode
 * @param m WiFiMode_t
 */
bool WiFiGenericClass::mode(wifi_mode_t m) {
  wifi_mode_t cm = getMode();
  if (cm == m) {
    return true;
  }

  // ...

  if (_long_range) {
    if (m & WIFI_MODE_STA) {
      err = esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR);
      if (err != ESP_OK) {
        log_e("Could not enable long range on STA! %d", err);
        return false;
      }
    }
    if (m & WIFI_MODE_AP) {
      err = esp_wifi_set_protocol(WIFI_IF_AP, WIFI_PROTOCOL_LR);
      if (err != ESP_OK) {
        log_e("Could not enable long range on AP! %d", err);
        return false;
      }
    }
  }

although that's LR-only, not the combined BGN+LR.