Rete arduino con Ds1307 e sensore temperatura

ciao, sto cercando di realizzare una rete di arduino formata da un MEGA (master) e molti UNO (slave).

Ad ogni slave c'è collegato un sensore di temperatura (sto provando sia con un ds1820 1-wire, sia con un ds1629 i2c) che deve leggere e, a richiesta del Master, passare la temperatura letta.

Al Master c'è collegato un DS1307 e un sensore di temperatura. Il Master ciclicamente interroga tutti gli slave per sapere la loro temperatura.

Ho buttato giù un po' di codice ma ho dei problemi con la comunicazione.

Utilizzando i ds1820 1-wire e la comunicazione master-slave su I2C, gli slave si bloccano.
Utilizzando i ds1629 I2C e la comunicazione master-slave su I2C, il tutto funziona ma noto che il master nello stampare sul display da 3,2'' data e ora del ds1307 riceve molte interferenze, penso dovute al fatto che anche lo slave usa il bus per leggere il ds1629, e stampa anche altri valori.

Ad ogni slave c'è installato questo display; https://www.sparkfun.com/products/9363
Al master invece c'è installato questo: SainSmart 3 2" Touch Screen LCD & SD Slot Expansion Shield For Arduino De. | Acquisti Online su eBay

Allego il codice.

tutte le righe commentate all'interno del file SLAVE sono relative al sensore ds1820 o alla comunicazione tra master e slave.

Grazie per l'aiuto che potrete darmi.

MASTER.rtf (1.92 KB)

SLAVE.rtf (9.2 KB)

Quanto è lungo il cavo master-slave?
Con I2C non conviene superare i 10-20cm.
L'I2C ha all'interno un ciclo while senza timeout, se lo slave non risponde, il master aspetta.. aspetta.. aspetta.. aspetta... all'infinito.

EDIT: Hai tutti i sensori sullo stesso bus, e anche lo slave si comporta da master verso i sensori.
C'è un po' di traffico. :cold_sweat:
Dovresti usare un altro bus per il master-slave. Perché cosi non ha molto senso (considerando che non puoi andare lontano), tanto vale eliminare lo slave e collegare tutto ad un unico Arduino.
Se, al contrario, devi mettere i due Arduino molto distanti allora potresti orientarti verso la RS485.

EDIT2: La Mega ha 4 seriali hardware, potresti usare queste.

Non esagerare; I2C funziona anche fin o 1 metro se le resistenze pullup sono piccole (1,5Kohm) e il cavo ha poca capacitá.

Ciao Uwe

Mi serve una comunicazione tra master e slave che possa coprire minimo una 30ina di metri.
Il master dovrà interrogare tutti gli slave che installerò e farsi dare la temperatura rilevata, la temperatura impostata per il riscaldamento, orari inizio/fine, ecc...

Quale tipo di comunicazione mi consigliate?

rs485

Ho visto che ci sono varie librerie in giro per internet.

Io posseggo delle schede rs485 della mikroe con chip adm485 come QUESTA, vanno bene? quale libreria potrei usare compatibile con Arduino 1.0.1?

Non servono tante schedine, bastano gli SN75176, che costano una miseria.
L'RS485 è in pratica una seriale estesa, puoi trasmetterci direttamente con i metodi della seriale. Se vuoi farti un protocollo, puoi implementarlo velocemente. Altrimenti puoi usarne uno già pronto: molti consigliano ModBus, io personalmente non l'ho mai usato.

Se le hai già, meglio sfruttare quelle.

ho collegato le schedine in mio possesso ai due arduino e ho caricato questi codici:

/* YourDuino RS485 Master Node S Example
 terry@yourduino.com */

/*-----( Declare Variables )-----*/
int EN = 4;

void setup() /****** SETUP: RUNS ONCE ******/
{
  pinMode(EN, OUTPUT );
  Serial.begin (9600);
}//--(end setup )---

void loop()    /****** LOOP: RUNS CONSTANTLY ******/
{
  // Send Data
  digitalWrite(EN, HIGH ); // enable send
  Serial.print ( 'A' );
  delay(1000);
  Serial.print ( 'B' );
  delay (1000);
}//--(end main loop )---
/* YourDuino RS485 Slave Node A Example
 terry@yourduino.com */

/*-----( Declare Variables )-----*/
int ledPin=12;
int EN = 4;
int Val;

void setup()/****** SETUP: RUNS ONCE ******/
{
  pinMode(ledPin, OUTPUT );
  pinMode(EN, OUTPUT );
  Serial.begin (9600);
}//--(end setup )---

void loop()  /****** LOOP: RUNS CONSTANTLY ******/
{
  // receive Data
  digitalWrite (EN, LOW ); // enable receive
  Val = Serial.read ();
  if (-1 != Val) 
  {
    if ( 'A' == Val) 
    {
      digitalWrite (ledPin, HIGH );
      delay (500);
      digitalWrite (ledPin, LOW );
      delay (500);
    }
  }

}//--(end main loop )---

Ho seguito questa pagina: https://arduino-info.wikispaces.com/RS485-Brick

però ho dovuto cambiare la velocità perchè a 19200 non andava.
Eppure nell'esempio della mikroe porta come velocità proprio 19200...
Come mai a tale velocità non funziona secondo voi?

Per il resto posso utilizzare questo codice per la 485?

Se funziona... si.

Tempo permettendo ho potuto continuare lo sviluppo della comunicazione rs485 tra arduino.
Sto riscontrando alcuni problemi.
Il Master "ARDUINO MEGA 2560" collegato sulla Seriale 1 hardware al chip adm485 RX1-19 TX1-18 e pinControl-9, il master invia alcuni valori agli slave che controllano i dati ricevuti e se corretti inviano altri dati al master che verifica questi dati e se corretti, per il momento, attiva un led posto sul pin 12.

Gli slave "ARDUINO UNO SMD" collegati sulla Seriale hardware al chip adm485 RX-0 TX-1 e pinControl-4.

Il problema è che vedo gli slave che ricevono e inviano i dati, ma il master non sempre riceve e quindi non sempre attiva il led

CODICE MASTER

#if ARDUINO >= 100
  #include <Arduino.h>
  #include <SoftwareSerial.h>

  //SoftwareSerial cameraconnection = SoftwareSerial(8, 9);
  SoftwareSerial rs485(10, 11); // receive & transmit pins
#else
  #include <WConstants.h>
  #include <NewSoftSerial.h>

  //NewSoftSerial cameraconnection = NewSoftSerial(8, 9);
  NewSoftSerial rs485(10, 11); // receive pin & transmit pins
#endif

int  pinCONTROL = 9;
int  pinMLED = 13;
int  pinLED = 12;
int  myaddress = 0;
int  slave = 1;
char msg1[] = {'2', '1', '3', '9'};
char msg2[] = {'2', '2', '3', '9'};
char msg3[] = {'2', '3', '3', '9'};
char msg4[] = {'2', '4', '3', '9'};
char msg5[] = {'2', '5', '3', '9'};
char msg6[] = {'2', '6', '3', '9'};
char msg7[] = {'2', '7', '3', '9'};
char msg8[] = {'2', '8', '3', '9'};

byte  byte_receive;
char  data[4];
byte  state=0;
byte  INDEX=0;
byte  pronto=0;

unsigned long previousMillis = 0; // last time update
long interval = 10000;           // interval at which to blink (milliseconds)


void setup() {

   pinMode(pinLED, OUTPUT);
   pinMode(pinMLED, OUTPUT);
   pinMode(pinCONTROL, OUTPUT);
   digitalWrite(pinCONTROL, LOW);
   Serial.begin(9600);
   Serial1.begin(9600);

}

void loop() 
{
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
     previousMillis = currentMillis;
     
      slave = 1;
      for (slave = 1; slave <= 8; slave++)
      {
          interroga_slave(slave, INDEX);
      }
      
      if (slave > 8)
        slave = 1;
  }  
}

void interroga_slave(int num_slave, int INDEX)
{
   digitalWrite(pinMLED, LOW);
   digitalWrite(pinLED, LOW);
   
   digitalWrite(pinCONTROL, HIGH);
   
   delay(250); // Still some more waiting
   if ( num_slave == 1 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg1[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   if ( num_slave == 2 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg2[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   if ( num_slave == 3 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg3[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   if ( num_slave == 4 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg4[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   if ( num_slave == 5 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg5[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   if ( num_slave == 6 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg6[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   if ( num_slave == 7 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg7[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   if ( num_slave == 8 ) {
     digitalWrite(pinMLED, HIGH);
     for (int i = 0; i<=4; i++) {
       Serial1.print(msg8[i]);
     }
     digitalWrite(pinMLED, LOW);
   }
   
   Serial1.flush();
   digitalWrite(pinCONTROL, LOW);
   Serial1.end();
   delay(10);
   Serial1.begin(9600);
   
   delay(100);
   
   if ( Serial1.available() ) {
    // Serial.println("A");
     
        for (int INDEX=0; INDEX<4; INDEX++) {
          data[INDEX] = Serial1.read();
          
      }
      
      Serial.print(data[0]);
      Serial.print(data[1]);
      Serial.print(data[2]);
      Serial.println(data[3]);
      
      if (data[1] == '1') {
        digitalWrite(pinMLED, HIGH);
        delay(200);
      }
      if (data[2] == '2') {
        digitalWrite(pinLED, HIGH);
        delay(200);
      }
      
    }
}

CODICE SLAVE

#if ARDUINO >= 100
  #include <Arduino.h>
  //#include <SoftwareSerial.h>

  //SoftwareSerial cameraconnection = SoftwareSerial(8, 9);
  //SoftwareSerial rs485(2, 3); // receive & transmit pins
#else
  #include <WConstants.h>
  //#include <NewSoftSerial.h>

  //NewSoftSerial cameraconnection = NewSoftSerial(8, 9);
  //NewSoftSerial rs485(2, 3); // receive pin & transmit pins
#endif

#define  pinCONTROL    4
#define  pinMLED       13
#define  pinLED        12
#define  myaddress     1

char msg[] = {'2', '1', '2', '8'};
  
byte  byte_receive[4];
char  data[4];
byte  state=0;
byte  INDEX=0;
byte  pronto=0;

void setup() {

   pinMode(pinLED,OUTPUT);
   pinMode(pinMLED,OUTPUT);
   pinMode(pinCONTROL,OUTPUT);
   digitalWrite(pinCONTROL,LOW);
   Serial.begin(9600);
   //rs485.begin(9600);

}

void loop()
{
  delay(100);
  digitalWrite(pinMLED,LOW);
  digitalWrite(pinLED,LOW);
  digitalWrite(pinCONTROL,LOW);
  delay(150);
  
  if (Serial.available()) {
        for (int INDEX=0; INDEX<4; INDEX++) {
          data[INDEX] = Serial.read(); 
        }
  }
  
  //Serial.println(data);
  if (data[0] == '2')        //Se il primo dato ricevuto è 2 allora siamo sicuri che il master ci sta interrogando e abilitiamo l'invio
       pronto=1;
   
  if (pronto == 1 && data[1] == '1') {
       digitalWrite(pinCONTROL,HIGH);
       digitalWrite(pinMLED,HIGH);
       delay(250);
      
       for (int i = 0; i<=4; i++) {
         Serial.print(msg[i]);
       }
      
       while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmission to complete
       digitalWrite(pinCONTROL,LOW);
  
       if (data[1] == 1) {
           digitalWrite(pinMLED,HIGH);
           delay(200);
       }
       if (data[2] == 3) {
           digitalWrite(pinLED,HIGH);
           delay(200);
       }
       pronto = 0;
       data[0] = 0;
       data[1] = 0;
       data[2] = 0;
       data[3] = 0;
   }
}

byte hex2num(byte x){

  byte result;
  
  if (x>=48 && x<=57){
    result=x-48;  
  }else if (x>=65 && x<=70){
    switch(x){
      case 65:
        result=10;
        break;
      case 66:
        result=11;
        break;
      case 67:
        result=12;
        break;
      case 68:
        result=13;
        break;
      case 69:
        result=14;
        break;
      case 70:
        result=15;
        break;    
    }  
  }
  return result;  
}

La prima parte del codice la puoi eliminare

#if ARDUINO >= 100
  #include <Arduino.h>
  #include <SoftwareSerial.h>

  //SoftwareSerial cameraconnection = SoftwareSerial(8, 9);
  SoftwareSerial rs485(10, 11); // receive & transmit pins
#else
  #include <WConstants.h>
  #include <NewSoftSerial.h>

  //NewSoftSerial cameraconnection = NewSoftSerial(8, 9);
  NewSoftSerial rs485(10, 11); // receive pin & transmit pins
#endif

Se usi la seriale hardware non serve utilizzare la software serial.
Inoltre se usi l'IDE 1.0.3 (l'ultimo disponibile) e non hai intenzione di tornare a versione precedenti la 1.0 il doppio include con l'if non serve.

Poi c'è un po di casino con la variabile INDEX. La definisci globale, poi la passi come parametro di funzione e infine come indice di un ciclo for. Sono tre variabili diverse con lo stesso nome. Il compilatore ci capisce, ma chi legge il codice no. :astonished:

Poi c'è un dalay di 250 ms. In quel lasso di tempo il micro perde tutti i dati in arrivo.
Inoltre potresti usare uno switch case al posto della valanga di if.

MASTER

int  pinCONTROL = 9;
int  pinMLED = 13;
int  pinLED = 12;
int  myaddress = 0;
int  slave = 1;
char msg1[] = {
  '2', '1', '3', '9'};
char msg2[] = {
  '2', '2', '3', '9'};
char msg3[] = {
  '2', '3', '3', '9'};
char msg4[] = {
  '2', '4', '3', '9'};
char msg5[] = {
  '2', '5', '3', '9'};
char msg6[] = {
  '2', '6', '3', '9'};
char msg7[] = {
  '2', '7', '3', '9'};
char msg8[] = {
  '2', '8', '3', '9'};

byte  byte_receive;
char  data[4];
byte  state=0;
byte  INDEX=0;
byte  pronto=0;

unsigned long previousMillis = 0; // last time update
long interval = 10000;           // interval at which to blink (milliseconds)


void setup() {

  pinMode(pinLED, OUTPUT);
  pinMode(pinMLED, OUTPUT);
  pinMode(pinCONTROL, OUTPUT);
  digitalWrite(pinCONTROL, LOW);
  Serial.begin(9600);
  Serial1.begin(9600);

}

void loop() 
{
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;

    slave = 1;
    for (slave = 1; slave <= 8; slave++)
    {
      interroga_slave(slave, INDEX);
    }

    if (slave > 8)
      slave = 1;
  }  
}

void interroga_slave(int num_slave, int INDEX)
{
  digitalWrite(pinMLED, LOW);
  digitalWrite(pinLED, LOW);

  digitalWrite(pinCONTROL, HIGH);

  delay(250); // Still some more waiting
  switch(num_slave){
  case 1:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg1[i]);
    digitalWrite(pinMLED, LOW);
    break;

  case 2:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg2[i]);
    digitalWrite(pinMLED, LOW);
    break;

  case 3:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg3[i]);
    digitalWrite(pinMLED, LOW);
    break;

  case 4:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg3[i]);
    digitalWrite(pinMLED, LOW);
    break;

  case 5:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg5[i]);
    digitalWrite(pinMLED, LOW);
    break;

  case 6:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg6[i]);
    digitalWrite(pinMLED, LOW);
    break;

  case 7:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg7[i]);
    digitalWrite(pinMLED, LOW);
    break;

  case 8:
    digitalWrite(pinMLED, HIGH);
    for (int i = 0; i<=4; i++) Serial1.print(msg2[i]);
    digitalWrite(pinMLED, LOW);
    break;

  default:
    break;
  }

  Serial1.flush();
  digitalWrite(pinCONTROL, LOW);
  Serial1.end();
  delay(10);
  Serial1.begin(9600);

  delay(100);

  if ( Serial1.available() ) {
    // Serial.println("A");
    for (int x=0; x<4; x++) data[x] = Serial1.read();

    Serial.print(data[0]);
    Serial.print(data[1]);
    Serial.print(data[2]);
    Serial.println(data[3]);

    if (data[1] == '1') {
      digitalWrite(pinMLED, HIGH);
      delay(200);
    }
    if (data[2] == '2') {
      digitalWrite(pinLED, HIGH);
      delay(200);
    }
  }
}

Non ho capito perché termini e poi reinizializzi la seriale.
Potresti eliminare lo switch case se crei una matrice bidimensionale.
Inoltre la variabile INDEX non è utilizzata.
Quella del ciclo for l'ho rinominata per non creare confusione. Non era la stessa variabile.

ora provo col codice che hai modificato.
il Serial1.end() e Serial1.begin(9600) l'ho utilizzati per azzerare il buffer dato che col debug oltre ai dati giusti noto anche altri simboli...inserendo l'end e il nuovo begin invece non noto altro che i dati giusti...(questo perchè ho letto che il flush, con l'IDE 1.0.0, non pulisce il buffer ma attende che tutti i dati siano stati inviati...o sbaglio?)

dici di eliminare il delay(250) che sta all'inizio del void interroga_slave?

EDIT:
Ho inserito il codice che mi hai dato ma continuano ad esserci problemi.
Ho notato che lo slave invia il dato, giustamente, quando riceve e controlla quello che gli ha inviato il master e noto che quando lo slave finisce di inviare, il master continua e porta al termine l'invio verso gli altri slave...può essere che il problema sia dovuto a collisioni? forse dovrei includere in ogni switchcase oltre all'invio dei dati anche la ricezione invece di inviare tutto e poi leggere? non so se mi sono spiegato bene...

questo è ciò che il debug mi stampa:

2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2ÿÿÿ
ÿÿ
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
21ÿÿ
øÿÿÿ
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128
2128

No, è meglio interrogare uno slave alla volta; ma devi prevedere un timeout per la risposta dello slave.
Purtroppo usi un solo bus, quindi devi prevedere una gestione delle collisioni, ovvero dati di 2 slave che si sovrappongono.

il problema è che al momento sto provando con un solo slave collegato al bus, eppure ci sono già problemi...
come si può notare del debug, ogni tanto il master, invece di riceve 2128 riceve ÿ.

altra cosa che non mi spiego:
se scrivo " if (Serial1.available() > 0) " non funziona, se invece scrivo " if (Serial1.available()) " funziona...

Viste le distanze che vuoi coprire, hai mai pensato ai moduli RF a 433MHz? Tipo questi: http://www.ebay.it/itm/433Mhz-RF-transmitter-and-receiver-module-Alarm-Set-for-Arduino-ARM-MCU-WL-/330854868895?pt=LH_DefaultDomain_0&hash=item4d087ca79f

Costano due lire e funzionano discretamente bene. Io un po' li ho usati, potenze basse ma consentono di generare un buon network distribuito senza dover cablare nulla.

nextor:
come si può notare del debug, ogni tanto il master, invece di riceve 2128 riceve ÿ.

A tal riguardo puoi leggere questo post: Serial communication with nslu2 - #3 by system - Interfacing - Arduino Forum

Probabilmente è anche legato al tuo "if (Serial.available())" che non fa un check esatto.