Go Down

Topic: VirtualWire (Read 5003 times) previous topic - next topic

Andi81

Hallo Leute!
Hab heut endlich mein 433 MHz RF link kit bekommen.
Das Problem: Wenn ich den Beispielcode hochlade, gibt mir der Reciever Arduino bei jedem Empfangen immer 5 mal Hello aus. So ähnlich ists beim Empfangen von zb einem AnalogRead. Da bekomme ich immer 3 mal den gleiche Wert ausgegeben. Ich nehm mal an es liegt am  char msg[24]
Hier der Code: Transmitter
Code: [Select]

#include <VirtualWire.h>

void setup()
{
    Serial.begin(9600);   // Debugging only
    Serial.println("setup");

    // Initialise the IO and ISR
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_setup(2000); // Bits per sec
}

void loop()
{
  int Wert = analogRead(A0);
  char msg[24];
  sprintf(msg, "%i", Wert); 

    digitalWrite(13, true); // Flash a light to show transmitting
    vw_send((uint8_t *)msg, strlen(msg));
    vw_wait_tx(); // Wait until the whole message is gone
    digitalWrite(13, false);
    delay(2000);
}

Müssen die Daten eig. immer als char versendet werden?
Danke und lg Andi

mkl0815

Ich denke nicht, das Dein char[24] das Problem ist, das dient nur als Buffer für den String den Du mittels "sprintf" erzeugst.
Du kannst auch "bytes" übertragen. Theoretisch kannst Du beliebige Datentypen schicken, übertragen werden sie aber immer als einzelne Bytes.
Es ist dann Aufgabe des Empfängers, den ankommenden Datenstrom aus bytes wieder in ein sinnvolles Format zu bekommen.
Der Code zum Senden wiederholt das Senden alle 2 Sekunden in der loop. Da Du den Code des Empfängers nicht gepostet hast, kann ich nur vermuten, das dieser den auch mehrmals empfängt.

Andi81

Danke für die Antwort! Der Reciever Code sieht so aus:
Code: [Select]

#include <VirtualWire.h>

void setup()
{
    Serial.begin(9600); // Debugging only
    Serial.println("setup");

    // Initialise the IO and ISR
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_setup(2000); // Bits per sec

    vw_rx_start();       // Start the receiver PLL running
}

void loop()
{
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
int i;

        digitalWrite(13, true); // Flash a light to show received good message
// Message with a good checksum received, dump it.
Serial.print("Got: ");

for (i = 0; i < buflen; i++)
{
    Serial.print(buf[i], HEX);
    Serial.print(" ");
}
Serial.println("");
        digitalWrite(13, false);
    }
}


mkl0815

Poste mal bitte noch die Ausgabe der seriellen Konsole. Evtl. sieht man da ja, ob die einmal empfangene Daten dreimal ausgegeben werden, oder ob die Daten dreimal empfangen werden.
Wenn vor jeder Zeile "Got: ...." steht, dann wurde es 3 Mal empfangen.
Btw. welches Funk-Kit hast Du bestellt?

Andi81

Also die Konsole gibt sowas aus:
Quote

setup
Got: 32 37 30
Got: 32 37 30
Got: 32 37 30
Got: 32 36 37
Got: 36 35
Got: 32 34 37
Got: 32 36 37
Got: 32 37 31
Got: 32 36 39
Got: 32 36 39
Got: 32 36 39
Got: 32 36 39

Interessant finde ich ja, dass einmal nur 2 Werte ausgegeben werden.Und mittlerweile sind die Werte auch unterschiedlich.
Das Funk-kit ist wieder mal ne Spielerei von Seeed Studio http://www.seeedstudio.com/depot/grove-433mhz-simple-rf-link-kit-p-1062:cd89fef7ffdd490db800357f47722b20.html
Ich dachte mir, die 5,5 Dollar kann ich mir schon noch leisten ;)

mkl0815

Folgender Code sorgt für die 3 Werte:
Code: [Select]

for (i = 0; i < buflen; i++)
{
    Serial.print(buf[i], HEX);
    Serial.print(" ");
}

Der Sender nimmt eine Zahl zwischen 0 und 1023 und gibt diese als String aus.
Aus "125" wird dann "1","2","5", also 3 Zeichen. Es werden also 3 bytes Übertragen.
Der Ausgabecode gibt jedes Zeichen das er bekommen hat aber als "Hex-Wert" des Zeichens aus.
"30" ist z.B. das Zeichen "0", "31" das Zeichen "1" usw.
32 37 30 entspricht also der Zeichenkette 270
36 35 sind dann 65
Ändere den Code einfach in
Code: [Select]

for (i = 0; i < buflen; i++)
    Serial.print((char)buf[i]);

Dann bekommst Du den eigentlichen Wert als einzelne Zeichen ausgegeben.

Andi81

Vielen Dank jetzt funktioniert es!!!  :)
Mein Tag ist gerettet

mkl0815

Schön, das es klappt.
Wenn Du beliebige Daten übertragen willst, erzeugst Du Dir am besten ein "struct" (siehe http://www.proggen.org/doku.php?id=c:tutorial:struct)
Dessen Länge und den Zeiger auf eine Instanz des struct kannst Du dann der Sendefunktion geben.

Code: [Select]

struct Data{
  unsigned int zahl1;
  float x;
  char[5];
};


Auf der Empfangsseite bekommst Du ja wieder einen Buffer der gleichen Länge wie das gesendete struct. Damit kannst Du diesen Buffer dann einfach auf den Typ Deines structs casten.
Code: [Select]

struct Data *data = (struct Data*)buf;


Aufpassen muss man aber bei Strings (char*) im struct. Hier steht dann nicht der String direkt drin, sondern nur der Zeiger auf den eigentlichen String. Hier ist dann zusätzlicher Aufwand nötig-

Andi81

Vielen Dank für die Info und den Link!
Die Seite sieht sehr interessant aus! Vielleicht hilft sie mir das Programmieren an sich besser zu verstehen  :)

rz259


Code: [Select]

struct Data{
  unsigned int zahl1;
  float x;
  char[5];
};


Auf der Empfangsseite bekommst Du ja wieder einen Buffer der gleichen Länge wie das gesendete struct. Damit kannst Du diesen Buffer dann einfach auf den Typ Deines structs casten.
Code: [Select]

struct Data *data = (struct Data*)buf;


Aufpassen muss man aber bei Strings (char*) im struct. Hier steht dann nicht der String direkt drin, sondern nur der Zeiger auf den eigentlichen String. Hier ist dann zusätzlicher Aufwand nötig-

Aber du hast ja schon schön vorgemacht, wie man Strings als char[] mit fester Länge speichern kann - das sollte eigentlich ausreichen.

Rudi

mkl0815


Aber du hast ja schon schön vorgemacht, wie man Strings als char[] mit fester Länge speichern kann - das sollte eigentlich ausreichen.

Das ist richtig, aber ein "char* text" ist auch in einem struct schnell geschrieben, weil man das halt immer so macht. Dann später den Fehler zu finden ist um so schwerer. Zumal es beim Arduino kein Memoryprotection gibt und der Zeiger auf dem empfangenden Arduino zwar in die Wüste zeigt, aber durchaus gültig ist.
Mario.

rz259

Memoryprotection - naja, das kommt darauf an, worauf der Pointer zeigt - unter DOS gab's auch keine Memoryprotection und da auch bei modernen Betriebssystemen normalerweise malloc nicht direkt den Speicher vom Betriebssystem anfordert, sondern seine eigene Speicherverwaltung implementiert, ist das auch dort nicht unbedingt gewährleistet.

Rudi

mkl0815

Quote
und da auch bei modernen Betriebssystemen normalerweise malloc nicht direkt den Speicher vom Betriebssystem anfordert, sondern seine eigene Speicherverwaltung implementiert, ist das auch dort nicht unbedingt gewährleistet.

Das bezweifle ich aber mal ganz stark. Das wäre ja das Sicherheitsloch, um als normales Programm auf den Speicher anderer Prozesse zugreifen zu können. Für den "Schutz" des Speichers sorgt die libc, soweit ich weiss.
Schaut man sich z.B. unter Linux den Ablauf des folgenden simplen Programms an, sieht man z.B. die "mprotect()" Aufrufe:
Code: [Select]

#include <stdlib.h>
#define MEMSIZE 102400
void main(int argc, char** argv) {
    void* pdata = malloc(MEMSIZE);
}

Quote

strace ./memtest
execve("./memtest", ["./memtest"], [/* 14 vars */]) = 0
brk(0)                                  = 0x8758000
uname({sys="Linux", node="c473s06", ...}) = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76f2000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=20200, ...}) = 0
mmap2(NULL, 20200, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb76ed000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220o\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1442372, ...}) = 0
mmap2(NULL, 1448456, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb758b000
mmap2(0xb76e7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15c) = 0xb76e7000
mmap2(0xb76ea000, 10760, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb76ea000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb758a000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb758a6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
open("/dev/urandom", O_RDONLY)          = 3
read(3, "\23\341\276", 3)               = 3
close(3)                                = 0
mprotect(0xb76e7000, 8192, PROT_READ)   = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
mprotect(0xb7710000, 4096, PROT_READ)   = 0
munmap(0xb76ed000, 20200)               = 0
brk(0)                                  = 0x8758000
brk(0x8792000)                          = 0x8792000
exit_group(141918216)                   = ?

rz259

Ok, habe mich etwas unpräzise ausgedrückt - das mit der Memory Protection gegenüber anderen Prozessen mag schon sein, aber wenn man das Logfile ansieht, dann sieht man, dass da der Schutz offensichtlich auf Pageebene funktioniert.

Wenn man aber innerhalb einer Page die Pointer falsch zuweist oder auf einen anderen Speicherbereich innerhalb der eigenen Anwendung, dann hilft das auch nichts - das war es eigentlich, was ich gemeint hatte.
Sorry, hatte mich falsch ausgedrückt.

Rudi

mkl0815

Aber das ist genau der Punkt. Du überträgst ein struct von einem System auf ein anderes. Die Wahrscheinlichkeit, das Du auf dem 2. System das gleiche Speicherlayout hast und damit der Pointer aus dem struct auf Deine eigene Memory-Page zeigt ist verschwindend gering. Somit hättest Du gute Chancen, das der Speicherschutz zuschlägt, sobald Du den String aus dem struct verwenden willst.
Beim Arduino gibt es keine Pages, der Speicher wird linear adressiert. Dazu kommt, das bei den meisten µControllern eine Harvard-Architektur eingesetzt wird, also getrennter Programm und Datenspeicher. Beim Arduino / Atmel ist das Flash-Ram für den Code und SRAM für die Daten. Moderne PCs setzen auf die von Neumann-Architektur, bei der Programm und Daten in einem gemeinsamen Speicher liegen. Somit kann man sich durch falsche Pointer auf dem Arduino zumindest nicht den eigenen Programmcode zerstören.

Go Up