[Arduino] 1 Master / Meerdere Slaves communicatie (RS485?)

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • jeroenk13
  • Registratie: Juli 2014
  • Laatst online: 07-10 20:32
Mijn vraag

Ik ben op zoek naar iemand die zelf een soortgelijk projectje is begonnen / heeft gedaan of mensen die er verstand hebben van communicatie tussen meerdere Arduino's over een afstand van ongeveer 20m.

Ik ben op dit moment al in bezit van een paar RS485 Shield.


Wat ik al gevonden of geprobeerd heb

Mijn doel: 1 Master Aruino met meerdere slaves (Arduino nano's) te laten communiceren via een kabel. De data die ik wil versturen zijn waardes van meerdere temperatuursensoren die aan de slaves hangen. Het uitlezen van de temperatuur etc gaat allemaal goed, het laatste puntje is nu alleen de communicatie.

Ik heb het project van 'Nick Gammon' al bekeken (https://www.gammon.com.au/forum/?id=11428) en werkend gehad, alleen die is slechts in staat tot het verzenden van enkele bytes, en niet aanpasbaar is voor mijn doeleind.

Heeft iemand een soortgelijk project gedaan / weet hoe je wired communicatie kan doen tussen Arduino's ?
En is dat via RS485 of zijn er andere standaarden die beter zijn ?

[ Voor 5% gewijzigd door jeroenk13 op 29-06-2017 19:03 ]

Alle reacties


Acties:
  • 0 Henk 'm!

  • DixxyJS
  • Registratie: Juni 2017
  • Laatst online: 14-09-2021
Kijk eens op mysensors.org

Acties:
  • 0 Henk 'm!

  • zerok
  • Registratie: November 2001
  • Laatst online: 05-10 16:38

zerok

geen

Als je zelf de communicatie wil programmeren kan dat prima met rs485. Dat is voor de arduino een serieele port en evt een pin om de richting te bepalen.

Belangrijk voor rs485 is dat de verschillende modules niet door elkaar praten. Makkelijkste is dat de master altijd de communicatie begint en dat slaves alleen dan reageren.

Simpele opzet:
Master verstuurd 4 bytes: <start byte> <slave adres> < commando> <checksum>

De slave antwoord (alleen als het een geldig pakket is EN het adres klopt) met n+2 bytes:
<start byte> <data byte 1> <data byte 2> ... <data byte n> <checksum>

<start byte> kan een willekeurig gekozen getal zijn (0 [0x00] en 255 [0xff] zou ik niet doen omdat dan alle bits hetzelfde zijn en dan kan bij hardware storingen voorkomen)

<checksum> kan een simpele xor over alle bytes zijn. Als de verstuurde checksum niet overeen komt de zelf uit gerekende dan is er iets mis gegaan en moet het bericht genegeerd worden. https://stackoverflow.com/a/3215248

Verder is handig om wat timeouts in te stellen zodat de master/slave niet oneindig aan het wachten is als er iets mis gaat met de communicatie.

"never argue with idiots they drag you down to their level and beat you with experience" dilbert


Acties:
  • 0 Henk 'm!

  • jeroenk13
  • Registratie: Juli 2014
  • Laatst online: 07-10 20:32
zerok schreef op zaterdag 1 juli 2017 @ 15:55:
Als je zelf de communicatie wil programmeren kan dat prima met rs485. Dat is voor de arduino een serieele port en evt een pin om de richting te bepalen.

Belangrijk voor rs485 is dat de verschillende modules niet door elkaar praten. Makkelijkste is dat de master altijd de communicatie begint en dat slaves alleen dan reageren.

Simpele opzet:
Master verstuurd 4 bytes: <start byte> <slave adres> < commando> <checksum>

De slave antwoord (alleen als het een geldig pakket is EN het adres klopt) met n+2 bytes:
<start byte> <data byte 1> <data byte 2> ... <data byte n> <checksum>

<start byte> kan een willekeurig gekozen getal zijn (0 \[0x00] en 255 \[0xff] zou ik niet doen omdat dan alle bits hetzelfde zijn en dan kan bij hardware storingen voorkomen)

<checksum> kan een simpele xor over alle bytes zijn. Als de verstuurde checksum niet overeen komt de zelf uit gerekende dan is er iets mis gegaan en moet het bericht genegeerd worden. https://stackoverflow.com/a/3215248

Verder is handig om wat timeouts in te stellen zodat de master/slave niet oneindig aan het wachten is als er iets mis gaat met de communicatie.
Ik heb RS485 ook al werkend gehad, ik weet de opzet van master en slave etc, alleen het probleem ligt nu bij het verzenden van een packet.

Bron: https://www.gammon.com.au/forum/?id=11428

Bij de Slave word dit opgegeven om te verzenden:

code:
1
sendMsg (fWrite, msg, sizeof msg);


de msg word opgebouwd uit dit:

code:
1
2
3
4
byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
    };


Het is me gelukt om individuele bytes te versturen.
Hoe kan je bijvoorbeeld een zin versturen zoals:

code:
1
Temp1: 22.24 - Temp2: 21.46



Ik heb geprobeerd om het te veranderen naar een String, alleen zonder succes.
Ik had ook iets gelezen over Char* alleen geen idee of zoiets kan werken

Alvast bedankt
-Jeroen

Acties:
  • 0 Henk 'm!

  • Sissors
  • Registratie: Mei 2005
  • Niet online
Je kan simpelweg dat in een char* zetten (of char[], komt op hetzelfde neer), en die moet je misschien nog casten naar een byte: (byte)jouwChar als je hem verstuurd. Gezien ze allebei 8-bits zijn moet dat geen probleem zijn.

Maar de grotere vraag is waarom je al die tekst zou willen versturen over je verbinding. Enkel de temperatuur oversturen, met mogelijk nog iets van een informatie byte wat je gaat versturen, moet toch genoeg zijn?

Acties:
  • 0 Henk 'm!

  • jeroenk13
  • Registratie: Juli 2014
  • Laatst online: 07-10 20:32
Sissors schreef op zondag 2 juli 2017 @ 17:13:
Je kan simpelweg dat in een char* zetten (of char[], komt op hetzelfde neer), en die moet je misschien nog casten naar een byte: (byte)jouwChar als je hem verstuurd. Gezien ze allebei 8-bits zijn moet dat geen probleem zijn.

Maar de grotere vraag is waarom je al die tekst zou willen versturen over je verbinding. Enkel de temperatuur oversturen, met mogelijk nog iets van een informatie byte wat je gaat versturen, moet toch genoeg zijn?
Maar de grotere vraag is waarom je al die tekst zou willen versturen over je verbinding.
Mede om te zien of/hoe dat mogelijk is, en omdat de 'berichten' zullen bestaan uit meer dan 2 metingen.

Hoe kan ik de char[] gebruiken in combinatie met de opbouw van het bericht aangezien het eerste bitje de device ID is, en daarna de overige info komt. Hoe moet je dit 'verwoorden' in het geval van een char[] ?

code:
1
2
3
4
byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
    };

Acties:
  • +1 Henk 'm!

  • Butz0rs
  • Registratie: Juli 2006
  • Laatst online: 12:31
Je moet msg groter maken en hier de string in stoppen, zoiets:

C:
1
2
3
4
5
6
7
char *str = "Temp1: 22.24 - Temp2: 21.46"
byte msglen = 1 + strlen(str);
byte *msg = new byte[msglen];
msg[0] = 0;
memcpy(msg + 1, str, strlen(str));
sendMsg(fWrite, msg, msglen);
delete msg;

[ Voor 20% gewijzigd door Butz0rs op 03-07-2017 00:24 . Reden: geen sizeof ]


Acties:
  • 0 Henk 'm!

  • jeroenk13
  • Registratie: Juli 2014
  • Laatst online: 07-10 20:32
Butz0rs schreef op zondag 2 juli 2017 @ 19:45:
Je moet msg groter maken en hier de string in stoppen, zoiets:

C:
1
2
3
4
5
6
char *str = "Temp1: 22.24 - Temp2: 21.46"
byte *msg = new byte[1 + sizeof(str)];
msg[0] = 0;
memcpy(msg + 1, str, sizeof(str));
sendMsg(fWrite, msg, sizeof(msg));
delete msg;
Ik heb het volgende geprobeerd en weet nu niet zo goed of het mis gaat aan de kant van de master of aan de kant van de slave.

(deel) Slave code

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    char *str = "T1: 22.24";
    byte *msg = new byte[1 + sizeof(str)];
    msg[0] = 0;  // Device ID 0
    msg[1] = 3; // Exec code voor de master
    msg[2] = 66; // random byte voor check
    memcpy(msg + 3, str, sizeof(str)); //msg+3
 
    
    delay (25);  // give the master a moment to prepare to receive
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending

    Serial.print("Sending message: ");
    Serial.print(msg[2]);
    Serial.println("");
    
    sendMsg(fWrite, msg, sizeof(msg));
    delete msg;


De seriele monitor geeft dit aan: Sending message: 66 - zoals verwacht.


(deel) Master code

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  // receive response  
  byte buf [10];
  
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf));
  
  if (received)
    {
    Serial.println("Received something");
    if (buf [0] != 0) { 
      Serial.println("not my device");
      return;  // not my device
    } 
      
    if (buf [1] != 3) { 
      Serial.println("unknown command");
      return;  // unknown command
    }

    Serial.print("Received: ");
    Serial.print(buf[2]);
    Serial.println("");

    delete buf;

   }


echter weergeeft buf[2]: Received: 34. niet het getal wat ik stuurde.

als ik voor de slave dit bericht gebruik:
code:
1
2
3
4
5
   byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
       8,
    };


dan komt hij wel goed aan met als getal (zoals verwacht): 8

Ik vermoed dat het mis gaat aan de kant van de master. Kan iemand dit bevestigen en mij in de goede richting sturen ?

Acties:
  • 0 Henk 'm!

  • Butz0rs
  • Registratie: Juli 2006
  • Laatst online: 12:31
jeroenk13 schreef op zondag 2 juli 2017 @ 23:24:
[...]


Ik heb het volgende geprobeerd en weet nu niet zo goed of het mis gaat aan de kant van de master of aan de kant van de slave.

(deel) Slave code

code:
1
2
3
4
5
6
    char *str = "T1: 22.24";
    byte *msg = new byte[1 + sizeof(str)];
    msg[0] = 0;  // Device ID 0
    msg[1] = 3; // Exec code voor de master
    msg[2] = 66; // random byte voor check
    memcpy(msg + 3, str, sizeof(str)); //msg+3
Hier gaat het in ieder geval al fout. Je alloceert te weinig ruimte voor msg. Je moet minimaal de grootte van de string + de drie extra bytes alloceren.

Acties:
  • 0 Henk 'm!

  • jeroenk13
  • Registratie: Juli 2014
  • Laatst online: 07-10 20:32
Butz0rs schreef op zondag 2 juli 2017 @ 23:41:
[...]


Hier gaat het in ieder geval al fout. Je alloceert te weinig ruimte voor msg. Je moet minimaal de grootte van de string + de drie extra bytes alloceren.
Oeps, ik ging er vanuit dat dat bij de memcpy() moest.
Ik zal het morgen even uitproberen.

Hoe moet de ontvanger kant eruit zien om het bericht weer aan elkaar te plakken?

Zal zoiets werken? (Tussen het received() gedeelte)

code:
1
2
3
4
String recv;
for (int i=0; i <= buf.length; i++){
      recv += buf[i];
   }


Of zijn er betere methodes?

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 27-09 13:03
sizeof(str) geeft je de grootte van de pointer, niet de lengte van de string,

Anyways, ik zou het niet naar een string converteren maar direct van/naar bytes (de)serializen:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uint32_t temp = 50;

uint8_t buffer[ 1 + 1 + 4 + 1 ] = {0};

uint8_t *ptr = buffer;

*ptr++ = 0x01; // device_id;
*ptr++ = 0x03; // temp_id;

*ptr++ = (uint8_t)( temp >> 24 );
*ptr++ = (uint8_t)( temp >> 16 );
*ptr++ = (uint8_t)( temp >> 8 );
*ptr++ = (uint8_t)( temp >> 0 );

*ptr++ = crc8( buffer, sizeof( buffer ) - 1 );


Aan de andere kant kun je de temperatuur deserializen door terug te shiften,

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Butz0rs
  • Registratie: Juli 2006
  • Laatst online: 12:31
jeroenk13 schreef op zondag 2 juli 2017 @ 23:53:
[...]


Oeps, ik ging er vanuit dat dat bij de memcpy() moest.
Daar moet het ook.
jeroenk13 schreef op zondag 2 juli 2017 @ 23:53:
[...]
Zal zoiets werken? (Tussen het received() gedeelte)

code:
1
2
3
4
String recv;
for (int i=0; i <= buf.length; i++){
      recv += buf[i];
   }


Of zijn er betere methodes?
Ongeveer, maar wel 0-based (< ipv <=):
code:
1
2
3
for (int i=3; i < received; i++){
      recv += buf[i];
}

Geen idee of er betere methodes zijn. Ik ken het Arduino String formaat verder niet.

delete buf;
hoort ook niet. Delete hoeft alleen als je new gebruikt.
farlane schreef op maandag 3 juli 2017 @ 00:05:
sizeof(str) geeft je de grootte van de pointer, niet de lengte van de string
|:( Idd, hier bedoel ik natuurlijk strlen(). Heb m'n eerste bericht gewijzigd.

[ Voor 21% gewijzigd door Butz0rs op 03-07-2017 00:25 ]


Acties:
  • 0 Henk 'm!

  • jeroenk13
  • Registratie: Juli 2014
  • Laatst online: 07-10 20:32
farlane schreef op maandag 3 juli 2017 @ 00:05:
sizeof(str) geeft je de grootte van de pointer, niet de lengte van de string,

Anyways, ik zou het niet naar een string converteren maar direct van/naar bytes (de)serializen:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uint32_t temp = 50;

uint8_t buffer[ 1 + 1 + 4 + 1 ] = {0};

uint8_t *ptr = buffer;

*ptr++ = 0x01; // device_id;
*ptr++ = 0x03; // temp_id;

*ptr++ = (uint8_t)( temp >> 24 );
*ptr++ = (uint8_t)( temp >> 16 );
*ptr++ = (uint8_t)( temp >> 8 );
*ptr++ = (uint8_t)( temp >> 0 );

*ptr++ = crc8( buffer, sizeof( buffer ) - 1 );


Aan de andere kant kun je de temperatuur deserializen door terug te shiften,
Ik heb geen idee wat ik hier aan heb / hoe ik het moet implementeren. Ik ga er vanuit dat dit voor de Slave kant is voor het verzenden?
Butz0rs schreef op maandag 3 juli 2017 @ 00:08:
[...]

Daar moet het ook.


[...]


Ongeveer, maar wel 0-based (< ipv <=):
code:
1
2
3
for (int i=3; i < received; i++){
      recv += buf[i];
}

Geen idee of er betere methodes zijn. Ik ken het Arduino String formaat verder niet.

delete buf;
hoort ook niet. Delete hoeft alleen als je new gebruikt.


[...]

|:( Idd, hier bedoel ik natuurlijk strlen(). Heb m'n eerste bericht gewijzigd.
Heb ook de nieuwe code uitgeprobeerd maar hij doet niet wat hij zou moeten doen.

Hierbij de hele code: Master

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "RS485_protocol.h"
#include <SoftwareSerial.h>


const byte ENABLE_PIN = 2;
const byte LED_PIN = 13;

SoftwareSerial rs485 (6, 7);  // receive pin, transmit pin

// callback routines
  
void fWrite (const byte what)
  {
  rs485.write (what);  
  }
  
int fAvailable ()
  {
  return rs485.available ();  
  }

int fRead ()
  {
  return rs485.read ();  
  }



unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change :
const long interval = 3000;           // interval at which to blink (milliseconds)




void setup()
{
  Serial.begin(9600);
  Serial.println("=== Setup ===");
  Serial.println("hw_id: 0 (MASTER)");
  rs485.begin(28800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
  Serial.println("==/ Setup ===");


}  // end of setup
  
byte old_level = 0;

void loop()
{


  byte level = 14;
      
  // assemble message
  byte msg [] = { 
     1,    // device 1
     2,    // turn light on
     level // to what level
  };

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;


    /* SEND MESSAGE EVERY X TIME */

  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  Serial.print("Sending message: ");
  Serial.print(msg[2]);
  Serial.println("");


   sendMsg (fWrite, msg, sizeof msg);
  while (!(UCSR0A & (1 << UDRE0))) // Wait for empty transmit buffer
  {
    UCSR0A |= 1 << TXC0; // mark transmission not complete
  }
  while (!(UCSR0A & ( 1 << TXC0))); // Wait for the transmission to complete

  digitalWrite (ENABLE_PIN, LOW);  // disable sending  

  }



  // receive response  
  byte buf[20];
  
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf));
  
  if (received)
    {
    Serial.println("Received something");
    if (buf [0] != 0) { 
      Serial.println("not my device");
      return;  // not my device
    } 
      
    if (buf [1] != 3) { 
      Serial.println("unknown command");
      return;  // unknown command
    }
    
    Serial.println("Received: ");
    Serial.println(buf[0]);  /* prints: 0  - goed */
    Serial.println(buf[1]);  /* prints: 3  - goed */
    Serial.println(buf[2]);  /* print niet wat het zou moeten */

    /* poging tot String bouwen */
    String recv;
    for (int i=0; i < 20; i++){
          recv += buf[i];
     }

    Serial.println("Totale string: ");
    Serial.println(recv);

    /* prints: 030010800616014816152030611088255 - lijkt me niet helemaal correct */

   } 
  


 

}  // end of loop



Slave

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <SoftwareSerial.h>
#include "RS485_protocol.h"

SoftwareSerial rs485 (6, 7);  // receive pin, transmit pin
const byte ENABLE_PIN = 2;

void fWrite (const byte what)
  {
  rs485.write (what);  
  }
  
int fAvailable ()
  {
  return rs485.available ();  
  }

int fRead ()
  {
  return rs485.read ();  
  }
  
void setup()
{
  Serial.begin(9600);
  Serial.println("=== Setup ===");
  Serial.println("hw_id: 1 (SLAVE)");
  rs485.begin(28800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  Serial.println("==/ Setup ===");
}

void loop()
{
  byte buf [10];
  
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf));
  
  if (received)
    {
    if (buf [0] != 1) { 
      Serial.println("not my device");
      return;  // not my device
    } 
      
    if (buf [1] != 2) { 
      Serial.println("unknown command");
      return;  // unknown command
    }

    Serial.print("Received: ");
    Serial.print(buf[2]);
    Serial.println("");

    /* default msg
    byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
       8,
    };

    */

    char *str = "test";
    byte msglen = 3 + strlen(str);
    byte *msg = new byte[msglen];
    msg[0] = 0;  // Device ID 0
    msg[1] = 3; // Exec code voor de master
    msg[2] = 6; // random byte voor check
    memcpy(msg + 3, str, strlen(str));


    delay (25);  // give the master a moment to prepare to receive
    
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending

    Serial.print("Sending message: ");
    Serial.print(msg[2]);
    Serial.println("");
    
    sendMsg(fWrite, msg, sizeof(msg));
    delete msg;
    
    while (!(UCSR0A & (1 << UDRE0))) // Wait for empty transmit buffer
    {
      UCSR0A |= 1 << TXC0; // mark transmission not complete
    }
    while (!(UCSR0A & ( 1 << TXC0))); // Wait for the transmission to complete
 
    digitalWrite (ENABLE_PIN, LOW);  // disable sending

   }  // end if something received
   
}  // end of loop


Kan iemand mij uitleggen waar het precies mis gaat?

Acties:
  • 0 Henk 'm!

  • Butz0rs
  • Registratie: Juli 2006
  • Laatst online: 12:31
jeroenk13 schreef op maandag 3 juli 2017 @ 14:04:
[...]
Kan iemand mij uitleggen waar het precies mis gaat?
Zoals farlane al aangaf, geeft sizeof de grootte van de pointer. 'sizeof msg' is dus de grootte van een byte(== 1). Je verstuurd dus denk ik maar 1 byte, in plaats van het hele bericht (msglen).

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 27-09 13:03
jeroenk13 schreef op maandag 3 juli 2017 @ 14:04:
[...]
Ik heb geen idee wat ik hier aan heb / hoe ik het moet implementeren. Ik ga er vanuit dat dit voor de Slave kant is voor het verzenden?
Ik denk dat je te makkelijk de bal hier neerlegt. Als je niet begrijpt wat daar gebeurt moet je je gaan verdiepen in de C(achtige) taal die voor de Arduino gebruikt wordt.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1