Is er een manier om globale variabele te declareren binnen procedure? OPGELOST

Beste mensen,

Ik probeer te begrijpen hoe #ifdef en #ifndef werkt.
Als ik het goed begrijp kunnen globale variabelen alleen buiten een procedure gedeclareerd worden.
Ik kom er niet uit of het mogelijk om met #... zo'n globale variabele te declareren. Mijn kennis van de Engelse taal is daar niet toereikend voor.
Is er iemand die mij wegwijs kan maken?
Ik sluit een stuk van een programma in wat prima functioneert als ik #ifndef MASTER (en #endif) weglaat, nu wil ik dat graag met een variabel die afhandelijk is of MASTER al dan niet gedefineerd is.

Ik hoop dat er iemand is die mij op weg wil helpen.

// Constanten --------------------------------------
#define MASTER

#define SLAVE_ADDR 9
#define ANSWERSIZE 5 // Slave antwoord size


// globale variabelen ------------------------------------------------
#ifndef MASTER
String answer = "Hello";// Define string with response to Master
#endif


// Programma ---------------------------------------------------------
void setup()
#ifndef MASTER
{ Ssetup();
}
#else
{ Msetup();
}
#endif

void loop()
#ifndef MASTER
{ Sloop();
}
#else
{ Mloop();
}
#endif

Je laat het belangrijke deel van je programma weg, namelijk hoe answer gebruikt wordt.

Het gaat fout als de compiler je programma compileert voor MASTER. answer is dan onbekend maar de compiler komt het tegen in je programma en breekt zijn nek daarover.

E.g.

void Ssetup()
{
  Serial.println(answer);
}

Om dat te voorkomen, zul je de functies Ssetup() en Sloop() ook conditioneel moeten compileren. BV

#ifndef MASTER
void Ssetup()
{
  Serial.println(answer);
}
void Sloop()
{
}
#endif

Ik heb geen idee of je benadering om een (1) programma te schrijven voor zowel master als slave de juiste benadering is.

Het probleem doet zich voor in de Slave listing, niet bij Master.
Ik zal de volledige listing hier neerzetten. incl. alle comments etc.
De tabbladen Master en Slave zet ik hier ook volledig.

Zoals je kan zien is er bij de Master geen gebruik van answere vandaar dat ik het (nog steeds) niet begrijp maar je oplossing werkt wel. Dank!!!

De reden waarom ik het in 1 programma wil is; dat ik op het punt sta van een modelbaan in een museum allerlei seinen, sensoren, wissels en treinen te moeten gaan aansturen/uitlezen. Daar werken meerdere mensen aan en wordt er nogal eens wat gewijzigd dus moet ik zowel software van de Master en de Slave ook muteren. Vergeet ik iets, dan hoop ik met compileren daarop gewezen te worden. In dat geval is het makkelijk om // voor #define Master weg te halen zodat de hele Slave opnieuw wordt gecompileerd.
Ik ben een redelijke newbee en weet geen andere manier maar sta open voor andere oplossingen/ suggesties

1e Tabblad

/*
  Samengevoegd I2C Master en Slave Demo van DroneBot Workshop 2019
  Demonstreert het gebruik van de I2C bus
  Master sends character and gets reply from Slave
  voor Slave software, disable #define MASTER
  https://dronebotworkshop.com/i2c-arduino-arduino/
  op 16:33 voor vervolg
*/

// Libraries ---------------------------------------------------------
#include <Wire.h> // voor de i2c bus


// Objecten ----------------------------------------------------------


// Constanten --------------------------------------------------------
#define MASTER
#define SLAVE_ADDR 9
#define ANSWERSIZE 5 // Slave antwoord size


// globale variabelen ------------------------------------------------
//#ifndef MASTER
String answer = "Hello";// Define string with response to Master
//#endif


// Programma =========================================================
void setup()
#ifndef MASTER
{ Ssetup();
}
#else
{ Msetup();
}
#endif

void loop()
#ifndef MASTER
{ Sloop();
}
#else
{ Mloop();
}
#endif

// Einde Programma en compile directive ==============================

#if  !defined(__AVR_ATmega2560__)
#error "Oeps! Zorg ervoor dat 'Arduino Mega' is geselecteerd als board in het menu."
#endif

Tabblad Master

void Msetup()
{ Wire.begin();  // Initialiseert I2C communicatie als Master
  Serial.begin(9600);  // Setup serial monitor
  Serial.println("I2C Master Demonstration");
}

void Mloop()
{ delay(50);
  Serial.println("Write data to slave");
  Wire.beginTransmission(SLAVE_ADDR);  // Write a charatre to the Slave
  Wire.write(0);
  Wire.endTransmission();
  Serial.println("Receive data");
  Wire.requestFrom(SLAVE_ADDR, ANSWERSIZE);  // Read response from Slave Read back 5 characters
  String response = "";
  while (Wire.available())  // Add characters to string
  { char b = Wire.read();
    response += b;
  }
  Serial.println(response);  // Print to Serial Monitor
}

tabblad Slave

void Ssetup()
{ Wire.begin(SLAVE_ADDR);  // Initialize I2C communications as Slave
  Wire.onRequest(requestEvent);   // Function to run when data requested from master
  Wire.onReceive(receiveEvent);  // Function to run when data received from master
  Serial.begin(9600);  // Setup Serial Monitor
  Serial.println("I2C Slave Demonstration");
}

void Sloop()
{ delay(50);
}


void receiveEvent()
{ while (0 < Wire.available())  // Read while data received
    byte x = Wire.read();
  Serial.println("Receive event");  // Print to Serial Monitor
}

void requestEvent()
{ byte response[ANSWERSIZE];  // Setup byte variable in the correct size
  for (byte i = 0; i < ANSWERSIZE; i++)   // Format answer as array
    response[i] = (byte)answer.charAt(i);
  Wire.write(response, sizeof(response));  // Send response back to Master
  Serial.println("Request event");  // Print to Serial Monitor
}

De compiler laat ongebruikte variabelen en functies meestal weg, maar niet altijd.

Ik zou twee aparte sketchen maken, en dan de bestanden zorgvuldig beheren. Maar als dit werkt, dan kan dit ook.

Is dit misschien mooier ?

void setup()
{
#if defined(MASTER)
  MasterSetup();
#else
  SlaveSetup();
#endif
}

Wij gebruiken nooit leesbare tekst voor de I2C bus, maar altijd digitale gegevens (de variabele zelf).
Je kunt meerdere soorten variabelen in een 'struct' stoppen, dan krijg je dit: Use I2C for communication between Arduinos - Exhibition / Gallery - Arduino Forum.

Het is belangrijk om te definiëren welke gegevens over de I2C bus gaan. Het eenvoudigste is een enkele byte, maar het kan ook een array van variabelen zijn of een 'struct'.
Een leesbare tekst is een slechte oplossing, maar als je daarvoor kiest dan is ook daar een goede beschrijving voor nodig.

Het is beter om geen String object en geen Serial functies in de onRequest en onReceive handler te gebruiken.

Tot mijn stomme verbazing is de oplossing super simpel maar eest wat anders.

1- De Tabbladen Master en Slave zijn letterlijke kopieën van de site dronebot workshop zie de link

Ik heb alleen wat nederlands commentaar toegevoegd / vertaald.

Dan de oplossing waar ik bij toeval achter kwam en onvoorstelbaar simpel is.

Bij meerdere tabbladen is het blijkbaar mogelijk om op zichzelf staande programma's te gebruiken die met een directive afhankelijk gecompileerd kunnen worden.
Ter verduidelijking mijn laatste volledige listings.

Eerste tabblad

/*  Samengevoegd programma om 2 arduino mega's met elkaar te laten communiceren.
    De oorspronkelijke Master en Slave software is van dronebot workshop en de tutorials zijn te vinden op:
    https://dronebotworkshop.com/i2c-arduino-arduino/
    Het geheel bestaat uit 3 tabbladen waarvan tabblad Master en tabblad Slave letterlijk zijn overgenomen
    van voornoemde tutorials en voor dit samengestelde programma voorzien van de compile directive #ifdef Master
    en #ifndef Master.
    Voor mijn persoonlijk gebruik heb ik de directive toegevoegd die een foutmelding geeft ingeval er een ander
    board dan een Mega gekozen is. Deze directive heb ik voor het plaatsen op het forum tijdelijk disabled met
    multiline comment tekens.
    
    Dit tabblad bevat alleen de define Master waardoor de compiler de software voor het Master board wordt 
    gegenereerd. Wordt deze constante verwijderd of met single qoute als commentaar gemaakt, dan wordt de Slave
    software gegenereerd.

*/

// Constanten ------------------------------------------------------------
#define MASTER

// Programma =======================================================

Tweede tabblad

#ifdef MASTER
/*
  I2C Master Demo
  i2c-master-demo.ino
  Demonstrate use of I2C bus
  Master sends character and gets reply from Slave
  DroneBot Workshop 2019
  https://dronebotworkshop.com/i2c-arduino-arduino/ 
*/

// Libraries --------------------------------------
#include <Wire.h>                                                   // voor de i2c bus


// Objecten --------------------------------------


// Constanten --------------------------------------------------------
#define SLAVE_ADDR 9
#define ANSWERSIZE 5                                                // Slave antwoord size

// globale variabelen ------------------------------------------------


// Programma =========================================================
void setup()
{ Wire.begin();                                                     // Initialiseert I2C communicatie als Master
  Serial.begin(9600);                                               // Setup serial monitor
  Serial.println("I2C Master Demonstration");
}

void loop()
{ delay(50);
  Serial.println("Write data to slave");
  Wire.beginTransmission(SLAVE_ADDR);                               // Write a charatre to the Slave
  Wire.write(0);                                                    // hiermee vraag je de slave om data
  Wire.endTransmission();                                           // stopt zenden
  Serial.println("Receive data");
  Wire.requestFrom(SLAVE_ADDR, ANSWERSIZE);                         // Read response from Slave Read back 5 characters
  String response = "";                                             // maakt variabele leeg
  while (Wire.available())                                          // Add characters to string
  { char b = Wire.read();                                           // leest karakter uit slave
    response += b;                                                  // plakt gelezen karakter aan reeds ontvangen karakters
  }
  Serial.println(response);                                         // Print to Serial Monitor
}

// Einde Programma en compile directive ==============================
#endif
/*
#if  !defined(__AVR_ATmega2560__)
#error "Oeps! Zorg ervoor dat 'Arduino Mega' is geselecteerd als board in het menu."
#endif
*/

Derde tabblad

#ifndef MASTER
/*
  I2C Slave Demo
  i2c-slave-demo.ino
  Demonstrate use of I2C bus
  Slave receives character from Master and responds
  DroneBot Workshop 2019
  https://dronebotworkshop.com/i2c-arduino-arduino/
*/

// Libraries ---------------------------------------------------------
#include <Wire.h>


// Objecten ----------------------------------------------------------


// Constanten --------------------------------------------------------
#define SLAVE_ADDR 9
#define ANSWERSIZE 5                                                // Slave antwoord size


// globale variabelen ------------------------------------------------
String answer = "Hello";                                            // Define string with response to Master

// Programma =========================================================
void setup()
{ Wire.begin(SLAVE_ADDR);                                           // Initialize I2C communications as Slave
  Wire.onRequest(requestEvent);                                     // Function to run when data requested from master
  Wire.onReceive(receiveEvent);                                     // Function to run when data received from master
  Serial.begin(9600);                                               // Setup Serial Monitor
  Serial.println("I2C Slave Demonstration");
}

void loop()
{ delay(50);                                                        // Time delay in loop
}

void receiveEvent()                                                 // het karakter dat de master zend
{ while (0 < Wire.available())                                      // Read while data received
    byte x = Wire.read();
  Serial.println("Receive event");                                  // Print to Serial Monitor
}

void requestEvent()                                                 // de 5 karakters die de master wil ontvangen
{ byte response[ANSWERSIZE];                                        // omdat 5x een byte wordt verwacht moet het antwoord uit 5 bytes bestaan
  for (byte i = 0; i < ANSWERSIZE; i++)                             // hier wordt hello omgezet in een array van 5x een byte
    response[i] = (byte)answer.charAt(i);                           // hier worden de bytes 1 voor 1 klaat gezet
  Wire.write(response, sizeof(response));                           // Send response back to Master
  Serial.println("Request event");                                  // Print to Serial Monitor
}

// Einde Programma en compile directive ==============================
#endif
/*
#if  !defined(__AVR_ATmega2560__)
#error "Oeps! Zorg ervoor dat 'Arduino Mega' is geselecteerd als board in het menu."
#endif
*/