Sender für Arduino und Attiny

Nachfolgend das Sender-Programm, noch ohne Einbindung von "echten" Messwerten. Zum Testen des Programms verwende ich nicht den Testaufbau-Atmega, sondern einen Uno, um den Seriellen Monitor nutzen zu können. Die Messwerte in den Variablen Messwert1 bis Messwert 5 werden vorerst simuliert.

Am Seriellen Monitor werden die (simulierten) Messwerte sowie das Sende-Telegramm mit jedem Sendevorgang angezeigt, sofern die Variable "bool serialMonitor = true" gesetzt ist.  Erst wenn das Programm fertig ist, übertrage ich es in den Testaufbau-Atmega.


Hier noch einmal die Bedeutung der einzelnen Bytes des Sendetelegramms:


Der folgende Programmauszug soll die Speicherung der Float-Messwerte in den Sendebuffer mittels Pointer zeigen:

   #define Messwert1ByteAnf 3 //Messwert1 von Byte 3 bis 6 im Sendetelegramm
   #define Messwert1ByteEnd 6

   float Messwert[5];

   byte* pdummy = NULL; //Pointer mit 1 Byte Länge auf NULL

   //Pointer auf Messwert[0]
    float* pMesswert = &Messwert[0];

    pdummy = (byte*)pMesswert; //Pointer von dummy auf Adresse von Messwert1 setzen

    //5 * 4 Bytes von Messwert[0]-[4] in SendeBuffer[i] kopieren
    for (int i = Messwert1ByteAnf; i <= Messwert5ByteEnd; i++) 
    {
      SendeBuffer[i] = *pdummy;
      pdummy++;
    }

Für die Übertragung der jeweils 4 Bytes pro Float-Messwert in den Sendebuffer "missbrauche" ich einen Pointer mit der Länge 1 Byte, der auf die Anfangsspeicheradresse des Float-Arrays (Messwert[0]) gesetzt wird und den Inhalt dieser Adresse in den Sendebuffer kopiert. Danach wird der Pointer 19-mal um jeweils eine Speicheradresse erhöht und jedesmal wird der Inhalt in den Sendebuffer kopiert. So werden für alle 5 Messwerte insgesamt 20 Bytes (5 Floatwerte * 4 Bytes je Floatwert) in den Sendebuffer übertragen.


Anstelle von Pointer kann man auch Union-Variable nutzen. Den Anstoss dazu hat mir ein Beitrag von "ardu_arne" im www.arduinoforum.de gegeben. Er hat in seinem Beitrag  www.arduinoforum.de/arduino-Thread-Die-Bytem%C3%BChle-einfacher-Zugriff-auf-einzelne-Bytes-einer-Mehrbyte-Variablen die Verwendung von Unions ganz toll beschrieben und an Beispielen gezeigt. Ich habe deswegen auch eine Version meines Senderprogramms mit Union-Variablen hier hereingestellt.


Verwendete Libraries:

Das Programm benötigt die Libraries Metro und VirtuellWire.

Metro steuert die zeitliche Wiederholung von Vorgängen durch vorzugebende Zykluszeiten. Metro unterbricht aber nicht nach Ablauf der Zykluszeit, wie bei einer Interruptanforderung, das laufende Programm um in ein Unterprogramm zu verzweigen. Ob die Zykluszeit abgelaufen ist, muss regelmäßig z.B. innerhalb der Loopschleife abgefragt werden. Metro ist daher nicht für "zeitkritische" Aufgaben geeignet, denn wenn das Programm gerade mit anderen Dingen beschäftigt ist, erfolgt keine Unterbrechung des laufenden Programms. Ich verwende Metro sehr gerne für zeitunkritische Programme, in Worten etwa so: "Mach diese Aufgabe alle 200 Millisekunden, falls du gerade Zeit dazu hast, sonst später ...".

VirtuellWire steuert die drahtlose Übertragung des Telegramms.

Wo man die Libraries herunterladen kann findet man hier: Fremd-Libraries


Das eigentliche Sendeprogramm ist sowohl für Arduino als auch für Attiny verwendbar, da es keine Attiny-spezifischen Libraries verwendet! Nur die Verwendung des Seriellen Monitors zu Testzwecken ist mit dem Attiny so nicht möglich. Erst wenn die Controller mit Messwertaufnehmer oder z.B.mit LCD- oder 7-Segmentanzeiger beschaltet werden, ist eine Trennung in Arduino- und Attiny-Version wegen der meist unterschiedlichen Libraries erforderlich.

Nachtrag für Attiny: Sobald echte Messwerte von Sensoren über I2C-Schnittstelle mit  der Library TinyWireM abgeholt werden, funktioniert das Sendeprogramm nicht, da sich offensichtlich TinyWireM und Metro nicht "vertragen" (Timerproblem?) Daher habe ich eine Variante des Sendeprogramms ohne "Metro" hier: Senderprogr. f. Attiny

Sendeprogramm für Arduino mit Verwendung von Pointer:

//Projekt Funkuebertragung
//Sender.cpp
//Code fuer Arduino
//Author Retian
//Version 2.0

//Senderprogramm nur zum Testen des Senders mit simulierten Messwerten!
//Verwendung von Pointer mit float-Array


#include <Metro.h>
#include <VirtualWire.h>

//Zyklus Messwertabfrage
Metro Zyklus1Metro = Metro(2000);
//Zyklus Senden der Messdaten, Zeit wird per Zufallszahl geaendert
Metro Zyklus2Metro = Metro(5500);

#define LED 13
#define txPin 12 //Ausgang zum Sendemodul

bool serialMonitor = true; //true, wenn Kontrollwerte am Seriellen Monitor ausgegeben werden sollen

String SlaveAdresse = "#2"; //ID des Senders

const int anzBytes = 27; //Telegrammlaenge
byte SendeBuffer[anzBytes];
byte buflen = anzBytes;
byte checkSum = 0;


//Definiton des Sendertelegramms
#define SenderIDByteAnf 0 //Byte 0-1
#define SenderIDByteEnd 1
#define MessungIDByte 2 //Byte 2 derzeit Reserve (=0)
#define Messwert1ByteAnf 3 //Byte 3-6
#define Messwert1ByteEnd 6
#define Messwert2ByteAnf 7 //Byte 7-10
#define Messwert2ByteEnd 10
#define Messwert3ByteAnf 11 //Byte 11-14
#define Messwert3ByteEnd 14
#define Messwert4ByteAnf 15 //Byte 15-18
#define Messwert4ByteEnd 18
#define Messwert5ByteAnf 19 //Byte 19-22
#define Messwert5ByteEnd 22
//#define Reserve 23
//#define Reserve 24
//#define Reserve 25
#define CheckSumByte 26 //Byte 26
#define CheckSumByteAnf 0
#define CheckSumByteEnd 25


float Messwert[5];

byte* pdummy = NULL; //Pointer mit 1 Byte Laenge auf NULL

//Pointer auf Messwert[0]
float* pMesswert = &Messwert[0];


void setup() {
  if (serialMonitor == true) Serial.begin(115200);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);
   //Deklarationen Library VirtualWire
  vw_set_tx_pin(txPin); //Setze tx-Pin
  vw_setup(1000); //Uebertragungsrate 1000 Bit/Sekunde
}


void loop() {

  //Ermittlung der Messwerte
  if (Zyklus1Metro.check() == 1)
  {
    //Hier werden spaeter die Messwerte abgefragt bzw. ermittelt
  }

  //Uebermittlung an Empfaenger
  if (Zyklus2Metro.check() == 1)
  {
    //Beschreiben des Sendetelegramms SendeBuffer[i]

    //Sender-ID ins Telegramm schreiben
    char ZwiBuf[3];
    SlaveAdresse.toCharArray(ZwiBuf, 3); //String "SlaveAdresse" in CharArray umwandeln
    for (int i = SenderIDByteAnf; i <= SenderIDByteEnd; i++) SendeBuffer[i] = (char)ZwiBuf[i];

    SendeBuffer[MessungIDByte] = 0;//Reserve, derzeit nicht in Verwendung

    //Messwerte ins Telegramm schreiben

    //Kopieren der Messergebnisse zu "Messwert1-5"
    //z.B. Messwert1 = MCP9808_Temp (Temperatur von MCP9808-Aufnehmer)
    //derzeit Messwerte zum Testen simuliert


    Messwert[0] = 0;
    Messwert[1] = 14.46;
    Messwert[2] = 0;
    Messwert[3] = 12.27;
    Messwert[4] = 0;
   
    pdummy = (byte*)pMesswert; //Pointer von dummy auf Adresse von Messwert setzen

    //5 * 4 Bytes von Messwert[0]-[4] in SendeBuffer[i] kopieren
    for (int i = Messwert1ByteAnf; i <= Messwert5ByteEnd; i++) 
    {
      SendeBuffer[i] = *pdummy;
      pdummy++;
    }

    //Ermittlung der Checksumme
    checkSum = 0;
    for (byte i = CheckSumByteAnf; i <= CheckSumByteEnd; i++)
    {
      CheckByte(SendeBuffer[i], i);
    }
    SendeBuffer[CheckSumByte] = checkSum;

    //Wenn Telegramm gesendet wird, LED 1x blinken
    digitalWrite(LED, HIGH);
    delay(10);
    digitalWrite(LED, LOW);

    //Senden des Telegramms und warten bis alle Daten gesendet sind
    vw_send((byte*)SendeBuffer, buflen);
    vw_wait_tx();

    //Naechste Uebertragungzeit per Zufallszahl 1 bis 8 Sekunden auswaehlen
    Zyklus2Metro.interval((int)(random(1, 8) * 1000));

    //Anzeige der Messwerte und des Telegramms
    if (serialMonitor == true)
    {
      Serial.println();
      Serial.print("Messwert1: ");
      Serial.println(Messwert[0]);
      Serial.print("Messwert2: ");
      Serial.println(Messwert[1]);
      Serial.print("Messwert3: ");
      Serial.println(Messwert[2]);
      Serial.print("Messwert4: ");
      Serial.println(Messwert[3]);
      Serial.print("Messwert5: ");
      Serial.println(Messwert[4]);

      Serial.print("Telegramm: ");
      for (int i = 0; i < 27; i++)
      {
        Serial.print(SendeBuffer[i]);
        Serial.print(" ");
      }
      Serial.println();
    }
  }
}


//Checksummenbildung
void CheckByte(byte buf, byte i)
{
  for (int j = 0; j < 8; j++)
  {
    if (bitRead(buf, j) == 1) checkSum += (i + j + 1);
  }
}

Sendeprogramm für Arduino mit Verwendung von Union-Variablen:

//Projekt Funkuebertragung
//Sender.cpp
//Code fuer Arduino
//Author Retian
//Version 3.0

//Senderprogramm nur zum Testen des Senders mit simulierten Messwerten!
//Verwendung von Union-Variable statt Pointer


#include <Metro.h>
#include <VirtualWire.h>

//Zyklus Messwertabfrage
Metro Zyklus1Metro = Metro(2000);
//Zyklus Senden der Messdaten, Zeit wird per Zufallszahl geaendert
Metro Zyklus2Metro = Metro(5500);

#define LED 13
#define txPin 12 //Ausgang zum Sendemodul

bool serialMonitor = true; //true, wenn Kontrollwerte am Seriellen Monitor ausgegeben werden sollen

String SlaveAdresse = "#2"; //ID des Senders

const int anzBytes = 27; //Telegrammlaenge
byte SendeBuffer[anzBytes];
byte buflen = anzBytes;
byte checkSum = 0;


//Definiton des Sendertelegramms
#define SenderIDByteAnf 0 //Byte 0-1
#define SenderIDByteEnd 1
#define MessungIDByte 2 //Byte 2 derzeit Reserve (=0)
#define Messwert1ByteAnf 3 //Byte 3-6
#define Messwert1ByteEnd 6
#define Messwert2ByteAnf 7 //Byte 7-10
#define Messwert2ByteEnd 10
#define Messwert3ByteAnf 11 //Byte 11-14
#define Messwert3ByteEnd 14
#define Messwert4ByteAnf 15 //Byte 15-18
#define Messwert4ByteEnd 18
#define Messwert5ByteAnf 19 //Byte 19-22
#define Messwert5ByteEnd 22
//#define Reserve 23
//#define Reserve 24
//#define Reserve 25
#define CheckSumByte 26 //Byte 26
#define CheckSumByteAnf 0
#define CheckSumByteEnd 25


union unionfloatByteVar {float Messwert[5]; byte MesswertByte[20];};
unionfloatByteVar floatByte;


void setup() {
  if (serialMonitor == true) Serial.begin(115200);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);

  //Deklarationen Library VirtualWire
  vw_set_tx_pin(txPin); //Setze tx-Pin
  vw_setup(1000); //Uebertragungsrate 1000 Bit/Sekunde
}


void loop() {

  //Ermittlung der Messwerte
  if (Zyklus1Metro.check() == 1)
  {
    //Hier werden spaeter die Messwerte abgefragt bzw. ermittelt
  }

  //Uebermittlung an Empfaenger
  if (Zyklus2Metro.check() == 1)
  {
    //Beschreiben des Sendetelegramms SendeBuffer[i]

    //Sender-ID ins Telegramm schreiben
    char ZwiBuf[3];
    SlaveAdresse.toCharArray(ZwiBuf, 3); //String "SlaveAdresse" in CharArray umwandeln
    for (int i = SenderIDByteAnf; i <= SenderIDByteEnd; i++) SendeBuffer[i] = (char)ZwiBuf[i];

    SendeBuffer[MessungIDByte] = 0;//Reserve, derzeit nicht in Verwendung

    //Messwerte ins Telegramm schreiben

    //Kopieren der Messergebnisse zu "Messwert1-5"
    //z.B. Messwert1 = MCP9808_Temp (Temperatur von MCP9808-Aufnehmer)
    //derzeit Messwerte zum Testen simuliert
    floatByte.Messwert[0] = 0;
    floatByte.Messwert[1] = 11.456;
    floatByte.Messwert[2] = 0;
    floatByte.Messwert[3] = 16.27;
    floatByte.Messwert[4] = 0;

    byte j = 0;
   
    for (int i = Messwert1ByteAnf; i <= Messwert5ByteEnd; i++)
    {
      SendeBuffer[i] = floatByte.MesswertByte[j];
      j++;
    }

    //Ermittlung der Checksumme
    checkSum = 0;
    for (byte i = CheckSumByteAnf; i <= CheckSumByteEnd; i++)
    {
      CheckByte(SendeBuffer[i], i);
    }
    SendeBuffer[CheckSumByte] = checkSum;

    //Wenn Telegramm gesendet wird, LED 1x blinken
    digitalWrite(LED, HIGH);
    delay(10);
    digitalWrite(LED, LOW);

    //Senden des Telegramms und warten bis alle Daten gesendet sind
    vw_send((byte*)SendeBuffer, buflen);
    vw_wait_tx();

    //Naechste Übertragungzeit per Zufallszahl 1 bis 8 Sekunden auswaehlen
    Zyklus2Metro.interval((int)(random(1, 8) * 1000));

    //Anzeige der Messwerte und des Telegramms
    if (serialMonitor == true)
    {
      Serial.println();
      Serial.print("Messwert1: ");
      Serial.println(floatByte.Messwert[0]);
      Serial.print("Messwert2: ");
      Serial.println(floatByte.Messwert[1]);
      Serial.print("Messwert3: ");
      Serial.println(floatByte.Messwert[2]);
      Serial.print("Messwert4: ");
      Serial.println(floatByte.Messwert[3]);
      Serial.print("Messwert5: ");
      Serial.println(floatByte.Messwert[4]);

      Serial.print("Telegramm: ");
      for (int i = 0; i < 27; i++)
      {
        Serial.print(SendeBuffer[i]);
        Serial.print(" ");
      }
      Serial.println();
    }
  }
}


//Checksummenbildung
void CheckByte(byte buf, byte i)
{
  for (int j = 0; j < 8; j++)
  {
    if (bitRead(buf, j) == 1) checkSum += (i + j + 1);
  }
}


Weiter zum Empfänger: Empfangsprogramm