Raspberry Pi + Arduino I2C Mehrere Variablen(Bytes)

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hi,

    nach dem die Kommunikation zwischen dem Arduino Uno und dem Raspberry Pi über USB so toll geklappt hat dachte ich mir ich kann auf mal I2C testen da ich dann auch Mikrocontroller ohne USB verwenden kann.

    Nach dieser Anleitung bin ich vorgegangen und es funktioniert auch wie es soll, aber nur mit Zahlen zwischen 0 und 255(Ein Byte oder?). Nach ein bisschen googeln fand ich heraus das das bei I2C so ist und man damit klar kommen muss. Gut aber man kann ja mehrere Zahlen hintereinander senden, das geht wohl auch im Paket.

    Für den Arduino funktioniert das mit:

    Code
    Wire.write(input,8); // byte input[8] = {83,255,83,255,90,45,0,0};


    allerdings empfange ich auf dem Pi (mit dem Beispielskript aus der Anleitung) nur "83" also die erste Zahl.

    Wie kann der Pi alle 8 auslesen?

    Wie kann ich vom Pi aus mehrere Variablen (oder eine Liste) an den Arduino senden?

    MfG c3ntry

  • Raspberry Pi + Arduino I2C Mehrere Variablen(Bytes)? Schau mal ob du hier fündig wirst!

  • Moin,

    ich sende derzeit lediglich ziwschen Arduinos über I2C hin und her ...
    Der RPi kommt erst in den nächsten Tagen ins Spiel ... da fehlt mir noch eine zündende Idee betreffs Signalisierung.
    Aber vielleicht hilft Dir das -> hier <- ein wenig weiter. Das ist die Kommunukation des RPi mit einem A/D-Wandler, der über I2C angeschlossen ist.
    Wenn ich den RPi im Verbund mit den Arduinos habe, dann kann ich Dir mehr sagen.

    cu,
    -ds-

  • ob nur ein Byte übertragen wird oder mehrere entscheidet

    i2c_readAck(); zu i2c_readNak();

    nach dem ersten gelesenen Byte muss mit i2c_readAck geantwortet werden ! (es wird auf mehr gewartet)
    erst nach dem letzten gelesenen Byte darf/ muss mit i2c_readNak(); geantwortet werden ! (es wird auf kein weiteres Byte mehr gewartet)

    ich tippe mal du antwortest mit: i2c_readNak(); was eben nur ein Byte liest.

    nachzulesen hier:
    http://homepage.hispeed.ch/peterfleury/avr-software.html
    unter I2C

    Code
    i2c_write(0x00);                         // write address = 0
            i2c_rep_start(Dev24C02+I2C_READ);        // set device address and read mode
            ret = i2c_readAck();                       // read one byte form address 0
            ret = i2c_readAck();                       //  "    "    "    "     "    1
            ret = i2c_readAck();                       //  "    "    "    "     "    2
            ret = i2c_readNak();                       //  "    "    "    "     "    3

    lasst die PIs & ESPs am Leben !
    Energiesparen:
    Das Gehirn kann in Standby gehen. Abschalten spart aber noch mehr Energie, was immer mehr nutzen. Dieter Nuhr
    (ich kann leider nicht schneller fahren, vor mir fährt ein GTi)

  • Mhh leider hab ich von C nicht soviel Ahnung...:s

    Allerdings ist das

    Code
    i2c_smbus_write_byte_data(client, reg, value);


    ja "relativ ähnlich" zu Python.

    Allerdings verwirren mich die 3 Argumente:
    - "client" = Slave Adresse
    - "reg" = Register? Was ist das? Hat ein Arduino Slave sowas?:helpnew:
    - "value" = Ein Byte, Viele Bytes ???

    Hier ist das ganze ja erklärt, allerdings weiß ich nicht welchen von den Befehlen ich jetzt in welchem Zusammenhang verwenden soll/kann und eben die Frage was dieses "reg" Parameter ist/kann/soll?

  • Hi c3ntry,

    ich kann Dir das, wenn überhaupt, leider nur in C vermitteln.
    Wenn Du Python lieber hättest, dann frag mal im Python-Subforum nach, wie die I2C Kommunikation funktioniert - von diesem Geschlängel habe ich nämlich gar keinen Plan. Das sollte dann in Verbindung mit der wiringPi oder der pigpio Library realisierbar sein.

    In C würde ich vorschlagen, Du geduldest Dich noch ein wenig und ich poste Dir hier dann mal eine grundlegende I2C Kommunikation Arduino <-> Rpi, die ich dann mit pigpio stricke. Ich denke, spätestens morgen um die Zeit müsste da was funktionfähiges fertig sein, weil das eh gerade auf meiner Agenda steht.

    Zwischen mehreren Arduinos über I2C zu reden hast Du bereits hinbekommen? Dazu hätte ich hier auch mal zusammengefummelt ( -> hier <-). Da ist auch eine imho sehr gute Erklärung verlinkt. Eventuell ist ja auch -> hier <- noch was Brauchbares für Dich dabei.

    cu,
    -ds-

  • Hi,
    ja das Problem ist das ich nur einen Arduino (Uno) habe und ein paar ATmega328 und ATtiny2313 habe, aber keinen Programmer (war so auf gut Glück man soll die ja auch mit dem Arduino Board Programmieren können...) das heißt zwischen zwei Arduinos kann ich grad schlecht testen:D

    Ok ich werd schauen was ich mit dem C-Code dann Anfangen kann, meine Frage währe jetzt noch was "char cmd" bedeuten könnte, weil diese Write-Befehle haben ja sowohl in C als auch in Python drei Argumente, von denen ich eben "char cmd" bzw. "reg" nicht zuordnen kann...
    Was sind diese Register bei normalen I2C Slaves?

    MfG c3ntry

  • Hi,

    also ich versuchs mal auf die Schnelle:
    client ist der filedescriptor, über den quasi der Weg zum ausgewählten slave führt ( durch den open auf den Bus und die Adressierung eines Clients durch den ioctl mit der slave-Adresse)
    reg ist das Datenregister (siehe auch -> hier <- ca. in der Mitte des Beitrags) des Slave.
    Meist ist es ja so, dass ein slave Baustein ein Sensor, ein A/D Wandler, ein Display oder irgend eine andere Komponente ist. Dieser Slave-Baustein hat in der Regel diverse Register zur Steuerung. In reg wird dann eben das Datenregister angegeben, das Du lesen oder schreiben willst.
    Ich würde sagen, dass Du das entweder frei verwenden oder einfach ignorieren kannst, wenn Du allein Herr der Kommunikation bist.
    value ist schliesslich das Byte, das Du schreiben willst.

    Zum Thema Arduino/AVR-Chips:
    Du kannst den UNO als Programmer nutzen ( guck z.B. mal -> hier <- ).
    Du kannst auch den RPi als Programmer für AVRs nutzen (da gibts -> hier <- was dazu).

    Und, wie gesagt, schau Dir vielleicht mal diese Seite an ... die ist imho echt klassen gemacht.

    ok soweit?

    //Nachtrag:
    -> Hier <- sind z.B. mal die pigpio-Funktionen für I2C im Überblick.
    Das sieht doch schon wesentlich charmanter aus, oder?


    -ds-

  • Hi c3ntry,
    kurzer Zwischenstatus:
    Es ist kein Problem, mit ein paar Zeilen Code über I2C einen String zum Arduino zu senden (Code-Snippet Raspi-Seite, verwendet pigpio-Library:

    Auf der Arduino-Seite sieht das etwa so aus:

    Das klappt perfekt, was noch fehlt ist eine Idee, wie ich das mit dem Senden in die andere Richtung mache (also vom Arduino zum Raspi).
    Da brauch ich noch ein Stück Logik, die zu meinem Vorhaben passt.
    Ich werd' auf alle Fälle eine Anleitung hier hereinstellen, wenn das ausgegoren ist.
    Jedenfalls kann mein RPi jetzt theoretisch mit vier Arduino-Subcontrollern reden.
    Näheres dazu später.

    cheers,
    -ds-


  • Das klappt perfekt, was noch fehlt ist eine Idee, wie ich das mit dem Senden in die andere Richtung mache (also vom Arduino zum Raspi).

    wo klemmts ? auf der PI oder Arduino seite ?

    du bist daoch auch C-sprachlich

    c3ntry
    22. August 2014 um 17:32

    schau in die Sourcen....

    lasst die PIs & ESPs am Leben !
    Energiesparen:
    Das Gehirn kann in Standby gehen. Abschalten spart aber noch mehr Energie, was immer mehr nutzen. Dieter Nuhr
    (ich kann leider nicht schneller fahren, vor mir fährt ein GTi)

  • Danke, aber da geht's mehr um das wo statt um das wie.
    Schliesslich sollen die AVRs und der RPi gepflegt miteinander kommunizieren und nicht einer den anderen totquatschen ;) ...
    N'büschen Logik halt - wann sendet wer und was .... ist aber nur noch eine Denksport-Aufgabe.

    cu,
    -ds-

  • So, also jetzt hab' ich mal eine rudimentäre Kommunikation fertig.
    Das klappt recht gut. In den nächsten Tagen bastle ich im Rahmen meiner Anleitungen noch ein ausführlicheres Tutorial zur Kommunikation RPi <-> Arduine über I2C.

    Das hier wäre zunächst mal der C-Code für die Raspi-Seite. Ich verwende die pigpio-Bibliothek - also bitte vorher runterladen und nach Anleitung installieren, falls noch nicht geschehen (-> hier <- geht's zur Download-Seite).

    Spoiler anzeigen


    /*
    * Master Control for ROBOT
    *
    * gcc -o MasterCtrl MasterCtrl.c -lpigpio -lrt -lpthread
    *
    * !!! sudo ./MasterCtrl !!!
    *
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pigpio.h>

    #define I2C_BUS_NO 1
    #define I2C_SLAVE_SONIC 8
    #define SIG_GPIO_SONIC 17
    #define I2C_MSG_LEN 32

    int Critical = 0;

    // callback
    void alertSonic(int gpio, int level, uint32_t tick ) //void* udata
    {
    Critical = 1;
    // printf("gpio %d became %d at %d\n", gpio, level, tick);
    }


    main()
    {
    int i2cHandleSonic;

    char i2cInBuf[I2C_MSG_LEN];
    char i2cOutBuf[I2C_MSG_LEN];

    int rc;
    int i;

    if (gpioInitialise() < 0)
    {
    fprintf(stderr, "failed to initialize pigpio!\n");
    exit(1);
    }

    gpioSetMode(SIG_GPIO_SONIC, PI_INPUT);

    gpioSetAlertFunc(SIG_GPIO_SONIC, alertSonic);

    i2cHandleSonic = i2cOpen( I2C_BUS_NO, I2C_SLAVE_SONIC, 0);

    for(i=0; i<20; i++)
    {
    if( Critical )
    {
    fprintf(stderr, "CRITICAL CONDITION!\n");
    Critical = 0;
    sprintf(i2cOutBuf, "Request #%d", i);
    rc = i2cWriteDevice(i2cHandleSonic, i2cOutBuf, strlen(i2cOutBuf) );
    sleep(1);
    memset(i2cInBuf, '\0', sizeof(i2cInBuf));
    rc = i2cReadDevice(i2cHandleSonic, i2cInBuf, I2C_MSG_LEN);
    *strchr(i2cInBuf, 0x0ff) = '\0';
    fprintf(stderr, "rc is %d [%s]\n", rc, i2cInBuf);
    }
    else
    {
    fprintf(stderr, "distance not critical\n");
    }
    sleep(1);
    }

    i2cClose(i2cHandleSonic);

    gpioTerminate();
    }


    Im Programm passiert folgendes: die Library wird initialisiert (gpioInitialise), GPIO17 (Pin #11) wird als Eingang geschaltet (gpioSetMode). Ausserdem wird ein Handler installiert, der angestossen wird, sobald GPIO17 seinen Zustand ändert (gpioSetAlertFunc). Diese Funktion setzt das globale Flag Critical.
    Anschliessend wird jetzt die Verbindung zu einem Arduino über I2C hergestellt, der die I2C-Adresse 8 hat (i2cOpen).
    In einer Schleife wird jetzt nachgeschaut, ob Critical gesetzt ist. Wenn ja, wird ein Datenpuffer über I2C zum Arduino geschickt (i2cWriteDevice).
    Dann wird noch auf eine Antwort gewartet und diese dann ausgelesen (i2cReadDevice).
    Das mal im Groben zum Ablauf des Programms auf Raspberry-Seite.

    Spoiler anzeigen


    ...
    #include <Wire.h>
    ...
    const int pin2Master = 2;
    ...
    #define I2C_SLAVE_SONIC 8
    #define SERIAL_BAUD 57600
    #define I2C_BUF_SIZE 32

    volatile char i2cInBuf[I2C_BUF_SIZE];
    char i2cOutBuf[I2C_BUF_SIZE];
    volatile int i2cInIdx;
    volatile boolean i2cMasterRequest;

    long distance;
    ...
    void i2cReceive(int howMany)
    {
    while( Wire.available() ) // loop through all
    {
    char c = Wire.read(); // receive byte as a character
    if( i2cInIdx < (I2C_BUF_SIZE - 1) ) // 14 because of loop all but last
    {
    i2cInBuf[i2cInIdx++] = c;
    }
    }
    i2cInBuf[i2cInIdx] = '\0';
    // Serial.println((char*) i2cInBuf); // print the request
    i2cMasterRequest = true;
    i2cInIdx = 0;
    }


    void i2cMasterSignalOn(void)
    {
    digitalWrite(pin2Master, HIGH);
    }

    void i2cMasterSignalOff(void)
    {
    digitalWrite(pin2Master, LOW);
    }

    void i2cAlarmMaster(void)
    {
    i2cMasterSignalOn();
    delay(10);
    i2cMasterSignalOff();
    }

    void i2cRequest(void)
    {
    sprintf(i2cOutBuf, "-> %ld cm", distance);
    Wire.write((uint8_t*) i2cOutBuf, strlen(i2cOutBuf));
    }

    ...

    void setup() {

    i2cInIdx = 0;
    i2cMasterRequest = false;

    Serial.begin(SERIAL_BAUD);

    Wire.begin(I2C_SLAVE_SONIC); // join i2c bus
    Wire.onReceive(i2cReceive); // register event
    Wire.onRequest(i2cRequest); // register request
    ...
    pinMode(pin2Master, OUTPUT);
    digitalWrite(pin2Master, LOW);
    ...
    }

    ...

    void loop() {

    char c;
    long duration;
    static int rLED,yLED,gLED;
    int r, y, g;

    ...

    digitalWrite(pinTrigger, LOW);
    delayMicroseconds(2);
    digitalWrite(pinTrigger, HIGH);

    delayMicroseconds(10);
    digitalWrite(pinTrigger, LOW);
    duration = pulseIn(pinEcho, HIGH);

    distance = (duration/2) / 29.1;

    if( distance <= 10 )
    {
    digitalWrite(pinLED_R, LOW);
    digitalWrite(pinLED_Y, HIGH);
    digitalWrite(pinLED_G, HIGH);
    }
    else
    {
    if( distance > 10 && distance <= 30 )
    {
    i2cAlarmMaster();
    digitalWrite(pinLED_R, HIGH);
    digitalWrite(pinLED_Y, LOW);
    digitalWrite(pinLED_G, HIGH);
    }
    else
    {
    digitalWrite(pinLED_R, HIGH);
    digitalWrite(pinLED_Y, HIGH);
    digitalWrite(pinLED_G, LOW);
    }
    }

    if( i2cMasterRequest == true )
    {
    Serial.print("I2C request: ");
    Serial.println((char*) i2cInBuf); // print the request
    i2cMasterRequest = false;
    }

    delay(100);
    }


    Der Ablauf auf der Arduino-Seite ist dann in etwa so:
    Für die Events Receive und Request wird jeweils eine Callback-Funktion definiert.
    Nach dem Anmelden am I2C Bus, tut der Arduino seinen Job: er fragt einen Ultraschall-Sensor ab.
    Befindet sich ein Hindernis/Gegenstand in einer bestimmten Entfernung, dann signalisiert er dem Master-Controller (Raspberry) dieses Ereignis indem er den Pin, der mit dem Master verbunden ist, auf HIGH setzt.
    Der Master (Raspberry) startet eine Anfrage (Request-Callback) und der Arduino sendet den Abstand züruck.
    Der Receive-Callback wird angestossen, wenn Daten auf dem I2C Bus verfügbar sind. Die werden ausgelesen und über die serielle Schnitstelle ausgegeben.
    Soviel mal ganz grob im Überblick, wie in etwa das funktioniert.

    Wie gesagt, ich mache daraus noch eine ausführliche Anleitung mit Schaltplan und allem drum und dran. Kann aber noch ein bisschen dauern.
    Bis dahin hoffe ich reicht das erst mal als erste Info.

    cheers,
    -ds-

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!