2 Nanos synchron laufen lassen (z.B. über 433 Mhz

Hallo,

ich brauche eine technische Unterstützung!
Für ein Projekt habe ich zwei räumlich getrennte Nanos (gleicher Raum, ca. 5m Entfernung, kein Sichtkontakt) die jeweils über ein Relais eine Leuchte schalten.
Diese bleiben unterschiedlich lange an und auch aus (Sekundenbereich). Also ein unregelmäßiges blinken.
Nun möchte ich, das dieses unregelmäßige blinken zwischen den beiden Instanzen gekoppelt wird. Heißt, ich möchte nach beispielsweise 40 verschieden, unsynchronen Blinken, das beide Lampen synchron an gehen. Ein Loop besteht einfach aus ca. 80 HIGH - delay - LOW - delay Blöcken und dauert ca 20 Minuten.

Ich bin ein ziemlicher Laie, darum erstmal die Frage, wie koppel ich die beiden technisch am sinnvollsten und vor allem einfachsten? (vllt 433 MHZ Modul)
Und wäre es genau genug, wenn ich beide Arduinos einfach nur am Anfang des Loops synchroniesieren würde?

Vielen Dank!

Am einfachsten wären 2 Drähte bzw. wenn sie über die Stromversorgung einen gemeinsamen GND hätten, ein zusätzlicher Draht.

@ikaruga
Auch solltest du mal definieren, was du unter synchron verstehst.
Auch wenn beide Nanos einen 16MHz Resonator haben, laufen die noch lange nicht Synchron. Es gibt da schon Abweichungen die nicht unerheblich sind. Synchron bekommst du die, wenn sie aus einer Taktquelle gespeist werden.
Wenn du die mit 433MHz verbinden willst, wird das sicher nicht synchron, du hast immer eine gewisse Verschiebung drin.

Warum überhaut Nanos?

Bei den Anforderungen würde ich eher zu den ESP8266-01S tendieren.
z.B. ESP8266 ESP-01S WLAN Transceiver Wireless Modul WiFi Relais Adapter DIY Arduino | eBay

Hi,
danke für eure Antworten.

Also in meinem Fall würde ich maximal 100ms Versatz als ausreichend Synchron ansehen.
Sollen halt mehrere Tage durchlaufen.

Die Nanos habe ich noch da gehabt und zum Testen reicht es mir. Aber ja, bevor ich mir zwei WLAN Shilds kaufe, würde ich wahrscheinlich zwei ESPs kaufen. Danke für den Link!

Drähte würde ich vermeiden wollen, da ich diese, wenn nicht auf dem kurzen Weg verlegt werden, also durch den Raum, ca. 20 m lang sein müssten. Aber angenommen, ich finde einen cleveren Weg den Draht zu verstecken, wäre dann die serielle Kommunikation der beiden Arduinos eine Möglichkeit?

Warum so kompliziert? Der eine legt ein High-Signal auf die Leitung, wenn der Sync-Zeitpunkt erreicht ist und dann tut der Andere was er tun soll.

Aber wenn Du sowieso Leitungen legen könntest, warum dann 2 MC?

Gruß Tommy

Weil ich keine Leitung legen will wenn es auch anders möglich ist.
Aber es stimmt wohl, wenn ich Kabel legen würde, könnte ich es auch nur mit einem MC machen. :slight_smile:

Allerdings möchte ich, aus verschiedenen, nicht unbedingt technischen Gründen, keine Kabel zwischen den Geräten verlegen.

In der Zwischenzeit könnte ich mir eine Verbindung mit zwei ESP8266 vorstellen (wahrscheinlich direkt zwei NodeMCU, da ich diese auch nach Ende dieses Projekts gut weiter verwenden kann).

Einen könnte man doch dann einen als Accesspoint laufen lassen der den anderen einfach die Befehle zukommen lässt?! (Wie das auch immer funktionieren mag, das ist dann für mich absolutes Neuland.)

man kann das mit "klassischem WLAN" machen. Speziell für ESP8266 oder ESP32 gibt es noch eine weitere Möglichkeit: ESP-NOW.

Das ist ein speziell vom Hersteller der ESP8266 / ESP32 chips entwickeltes Protokoll mit dem ESP8266/ESP32-boards miteinander wireless Daten senden/empfangen können.

Wenn du die beiden Boards mit Netzteil betreibst ist es ziemlich egal ob man nun "klassisches WLAN" und UDP oder ESP-NOW zum Daten senden/empfangen nimmt. (Mit eingeschaltetem WLAN zieht der Mirocontroller 80 mA während des Sendens 300 mA.)

Wenn ein ESP als AP läuft dann ist UDP ein bißchen einfacher weil man weniger Zeilen Code braucht als bei ESP-NOW

Wenn du es per ESP machen willst:

  • board-Unterstützung installieren

dann frage nach einem geeigneten Demo-Sketch dafür. Es ist wie immer: es gibt eine Menge schlecht dokumentierten Code und wenig gut dokumentierten Code der die Fehlersuche vereinfacht.

Wenn es möglichst preiswert sein soll

Wenn es 6,50 Euro pro board kosten darf

vgs

Sicher nicht die einfachste. Der eine blinkt an dem Draht (im Sekundentakt), der andere weiß dadurch, wann eine Sekunde anfängt und kann sich synchronisieren, wenn er will.
Das ist auf 20m störunempfindlicher als TTL Serial.

Evtl. kann man mit ein wenig Draht ja doch Sichtkontakt zwischen einer IR-LED und einem IR-Empfänger herstellen...

Da empfehle ich auch die schon genannte Version mit dem ESP8266 (Wemos D1 mini) und ESP-Now. Das sollte für deine Anwendung reichen. Der 1. (Server) gibt vor wann der 2. (Slave) was machen soll.
Bei ESP-Now braucht es keinen AP, sondern die beiden kommunizieren direkt miteinander.

Meine neuen Module sind angekommen. Geld spielt hier keine Rolle... :sweat_smile:

Dann werde ich mich einmal mit ESP-Now auseinandersetzen

Hier ist ein Demo-Code

#include <ESP8266WiFi.h> // for use with ESP32 use file "WiFi.h"
#include <espnow.h>      // for use with ESP32  use file "esp_now.h" (underline in the middle)
// don't try to adapt this code to ESP32-modules
// ESP32-modules need another library with SUBSTANTIAL differencies
// go search for a demo-code that is written for ESP32



// if you want to receive ESP-NOW-messages reliably you have to strictly avoid 
// the command delay(). Delay blocks the whole CPU and if a ESP-NOW-Message
// is send while the receivers CPU  is blocked the receiving will fail
// so use a timing-function based on millis() like the one below instead.
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

unsigned long SendDataTimer;

//#############################################################################################
// list of MAC-Adresses of all receivers:

// important note: ESP-NOW sending is based on the RECEIVERS Mac-adress.
// this means for every ESP8266-modul that shall receive a ESP-NOW-Messages
// you have to execute register a peer in the Setup-function
// esp_now_add_peer(ESP_NOW_MAC_adr Of Recv, ESP_NOW_ROLE_COMBO, 1, NULL, 0); 

// and you have to execute the command 
// esp_now_send(ESP_NOW_MAC_adr Of Recv, (uint8_t *) &myESP_NOW_Data, sizeof(myESP_NOW_Data));
uint8_t ESP_NOW_MAC_adrOfRecv[] = { 0xF4, 0xCF, 0xA2, 0xD1, 0x3D, 0x28 }; // Board 0x06 sendet an Board 0x28

char MAC_adrOfRecv_as_AoC[18];

//##############################################################################################


// Structure example to send data. Must match the receiver structure
typedef struct MyESP_NOW_Data_type {
  char MyESP_NOW_MsgStr[128];
  int  MyESP_NOW_Int;
} MyESP_NOW_Data_type;

// Create a "variable" called myESP_NOW_Data of variable-type MyESP_NOW_Data_type
MyESP_NOW_Data_type my_received_ESP_NOW_Data;      //neu 2020.07.14-15:00
MyESP_NOW_Data_type my_READYtoSEND_ESP_NOW_Data;   //neu 2020.07.14-15:00

void ESP_NOW_SendData()
{
  // Set values to send
  strcpy(my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr, "HI I'M SENDING EVERY TWO SECONDS COUNTiNG UP + 6");
  my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_Int = my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_Int + 6; 

  // Send message via ESP-NOW  
  esp_now_send(ESP_NOW_MAC_adrOfRecv, (uint8_t *) &my_READYtoSEND_ESP_NOW_Data, sizeof(my_READYtoSEND_ESP_NOW_Data));
  Serial.println("esp_now_send(ESP_NOW_MAC_adrOfRecv, (uint8_t *) &my_READYtoSEND_ESP_NOW_Data, sizeof(my_READYtoSEND_ESP_NOW_Data)); done");
  Serial.print("I am the board with the MAC-Adress ");
  Serial.println(WiFi.macAddress()); 
  Serial.print("and I try to send my ESP-NOW-Data to the board with MAC-Adress ");
  Serial.println(MAC_adrOfRecv_as_AoC); 
  
  // if sending has finished function OnDataSent is called
}


void ESP_Now_setup()
{
  WiFi.mode(WIFI_STA);
  Serial.println("WiFi.mode(WIFI_STA); done");
  WiFi.disconnect(); // for strange reasons WiFi.disconnect() makes ESP-NOW work
  Serial.println("WiFi.disconnect(); done");
  
  // Init ESP-NOW
  if (esp_now_init() != 0) 
  {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  Serial.println("esp_now_init() was successful");

  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
  Serial.println("esp_now_set_self_role(ESP_NOW_ROLE_COMBO) done");

  esp_now_register_recv_cb(OnDataRecv);
  Serial.println("esp_now_register_recv_cb(OnDataRecv); done");
  
  esp_now_register_send_cb(OnDataSent);
  Serial.println("esp_now_register_send_cb(OnDataSent); done");
  
  // each receiving unit must be registered. Register peer
  esp_now_add_peer(ESP_NOW_MAC_adrOfRecv, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
  Serial.println("esp_now_add_peer(MAC_adressOfReceivingUnit, ESP_NOW_ROLE_COMBO, 1, NULL, 0) done");  

  for (int i=0;i<6;i++) {
    strcat (MAC_adrOfRecv_as_AoC, hex02str(ESP_NOW_MAC_adrOfRecv[i])   );
    if (i < 6) {
      strcat (MAC_adrOfRecv_as_AoC, ":" );    
    }  
  }  
  MAC_adrOfRecv_as_AoC[17] = 0;
  strupr(MAC_adrOfRecv_as_AoC); // make letters UPPERCASE
  Serial.print("MAC-Adress of Receiver is ");
  Serial.println(MAC_adrOfRecv_as_AoC);
}

void PrintFileNameDateTime()
{
  Serial.print("Code running comes from file ");
  Serial.println(__FILE__);
  Serial.print("compiled ");
  Serial.print(__DATE__);
  Serial.println(__TIME__);  
}

// print macadress of THIS device 
// prepared for inserting code by copy & paste
// that will be flashed into receiving partner 

void PrintWiFiMacAdress()
{
  char MacAdr_AoC[18];  //suffix _AoC for easier remembering variable-type is ArrayOfChar
  char HexByteDigits[3];
  
  for (uint8_t i = 0; i < 18; i = i + 1)
  { 
    MacAdr_AoC[i] = WiFi.macAddress()[i];
  } 
  MacAdr_AoC[17] = 0; // zero to terminate the string
  Serial.print("ESP Board Wifi.macAddress:  ");
  Serial.println(MacAdr_AoC);
  
  Serial.println();
  Serial.println("copy the line below and replace the codeline");
  Serial.println("uint8_t ESP_NOW_MAC_adrOfRecv[] = { 0x60, 0x01, 0x94, 0x70, 0xAE, 0x7B };");
  Serial.println("inside the code with the copied line from the serial monitor");
   
  Serial.println();
  Serial.print("uint8_t ESP_NOW_MAC_adrOfRecv[] = {");
  for (uint8_t i = 0; i < 16; i = i + 3)
  { 
    HexByteDigits[0] = MacAdr_AoC[i];
    HexByteDigits[1] = MacAdr_AoC[i+1];
    HexByteDigits[2] = 0; // zero for terminating the string
    Serial.print("0x");
    Serial.print(HexByteDigits);
    if (i < 14) Serial.print(", ");
  }  
  Serial.println(" };");
  Serial.println();      
}

char* hex02str(uint8_t b)  {
 static char str[]="FF"; // RAM für einen 2-Zeichen string reservieren.
  snprintf(str,sizeof(str),"%02x",b);
  return str;
}


// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) 
{
  Serial.print("Last Send Status: ");
  if (sendStatus == 0)
    { Serial.println("Delivery Success"); }
  else
    { Serial.println("Delivery failed"); }
}


// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t NoOfBytesRcv) 
{ 
  // copy data bytewise from variable incomingData to variable my_received_ESP_NOW_Data
  memcpy(&my_received_ESP_NOW_Data, incomingData, sizeof(my_received_ESP_NOW_Data));

  Serial.print("No of Bytes received: ");
  Serial.println(NoOfBytesRcv);

  //these lines must match the variables inside the ESP_NOW-data-structure
  Serial.print("Array of Char: #");
  Serial.print(my_received_ESP_NOW_Data.MyESP_NOW_MsgStr);
  Serial.println("#");
  
  Serial.print("Int: ");
  Serial.println(my_received_ESP_NOW_Data.MyESP_NOW_Int);

  Serial.println();
}

 
void setup() 
{
  // Init Serial Monitor
  Serial.begin(115200);
  Serial.println();  // a carriage return to make sure serial-output begins in colum 1 of a new line
  PrintFileNameDateTime();
  // Set device as a Wi-Fi Station
  ESP_Now_setup();

  PrintWiFiMacAdress();  
}
 
void loop() 
{ 
  // check if timer-intervall is over
  if (TimePeriodIsOver(SendDataTimer,2000) )  //neu 2020.07.14-15:00
    {
     Serial.println("SendData");
     ESP_NOW_SendData();
     Serial.println("SendData done");
    }
}

vgs

1 Like

Der wesentliche Schritt ist die MAC-Adressen auf deine Boards anzupassen

Hier gibt es gute Schritt für Schritt Anleitungen für ESP8266 und ESP32

Danke! Ich habe meine Problem soweit mit den beiden NodeMCU über ESP-Now lösen können. Was ich machen wollte funktioniert. Vielen Dank an alle die hier geholfen haben!

Das ist doch super und danke für die Rückmeldung.
Und für alle, die mitlesen, nach welchem Beispiel hast du es nachgebaut?

Mein Code für ESP-Now beruht im großen und ganzen auf dem Tutorial, was auch @Plumps gepostet hat (ESP8266).
Da dieses für meine Zwecke vollkommen ausreichend ist. Das Beispiel von @StefanL38 sieht auch sehr gut aus, aber da benötige ich mehr Zeit für die Einarbeit, die ich gerade für den Aufbau nicht habe, da dieser nächte Woche stehen muss.

Meine Lampensynchronisation beruht auf dieser Überlegung:
Der Sender läuft einfach seinen Rythmus durch und schaltet seine Lampe über einen Digitalausgang und gibt zusätzlich dem Empfänger zu bestimmten Zeiten das Signal um dessen Lampe auszuschalten. Da die Ausschaltzeit im Gegensatz zur Leuchtdauer immer gleich ist, ist die Lampe standartmäßig an beim Emfänger.

Mein Demo-Code enthält diese Zeilen die ein struct definieren

Wenn du nur eine Lampe synchronisiert ausschalten möchtest dann reduziert sich das Datensenden auf ein byte

byte myDataByte;

statt

esp_now_send(ESP_NOW_MAC_adrOfRecv, (uint8_t *) &my_READYtoSEND_ESP_NOW_Data, sizeof(my_READYtoSEND_ESP_NOW_Data));

mit einem byte

esp_now_send(ESP_NOW_MAC_adrOfRecv, myDataByte, 1);  // sende 1 byte

Lampe an myDataByte = 1;
Lampe aus myDataByte = 0;

vgs

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.