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 6float 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 Sendemodulbool 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 Sendemodulbool 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);
}
}