Problema comunicazione seriale VB.NET - Arduino

Ciao a tutti,
un mio progetto prevede un continuo scambio di dati tra Arduino e un software su pc; per far questo utilizzo una comunicazione di tipo seriale.

In particolare il computer invia un semplice carattere di riconoscimento (“A”), dopodiché, una volta riconosciuto il carattere, Arduino invia dei byte al computer, il quale li elabora ed invia ad Arduino due byte.
Questi due byte sono fondamentali, in quanto ogni bit rappresenta lo stato logico che un determinato pin digitale deve assumere, ma sono proprio il mio problema… Sono certo che i byte in questione vengano costruiti correttamente (ho verificato i valori nelle diverse situazioni), ma Arduino non li gestisce correttamente.

Appena il programma parte i due byte vengono impostati a 255 in decimale ed Arduino li riceve correttamente (per verificare ho fatto sì che Arduino rispedisse i due byte al computer, in quale li stampa a schermo), ma quando durante l’utilizzo il valore dei due byte viene cambiato Arduino continua ad inviare al computer due byte di valore 255. Cosa potrebbe essere?

I due codici non sono particolarmente efficaci, ho cercato di fare in modo che Arduino e il computer inviassero dei byte di conferma nelle zone sensibili, ma ciò ha portato ad un totale blocco del programma. Sotto trovate i due codici.

Codice di Arduino:

#include <Wire.h>

// PINS
// Current Sensors
const unsigned int PanelCS = 0;
const unsigned int ToEnelCS = 1;
const unsigned int RequiredCS = 2;
const unsigned int FromEnelToDevCS = 3;
const unsigned int FromEnelToBatteryCS = 4;

// Voltage dividers
const unsigned int PanelVD = 5;
const unsigned int BatteryVD = 6;

// Smart devices
const unsigned int Dev1 = 22;
const unsigned int Dev2 = 23;
const unsigned int Dev3 = 24;
const unsigned int Dev4 = 25;
const unsigned int Dev5 = 26;
const unsigned int Dev6 = 27;
const unsigned int Dev7 = 28;
const unsigned int Dev8 = 29;
const unsigned int Dev9 = 30;
const unsigned int Dev10 = 31;
const unsigned int Dev11 = 32;
const unsigned int Dev12 = 33;
const unsigned int Dev13 = 34;
const unsigned int Dev14 = 35;
const unsigned int Dev15 = 36;
const unsigned int Dev16 = 37;

// Variables
// Currents
int PanelCurrent;
int ToEnelCurrent;
int RequiredCurrent;
int FromEnelToDevCurrent;
int FromEnelToBatteryCurrent;
// Voltages
int PanelVoltage;
int BatteryVoltage;
// Powers
byte ProducedPower;
byte ConsumedPower;
int BoughtPower;
int SoldPower;
// Panel angle
byte PanelAngle;
// Bytes from PC
byte BytesFromPc[2];


void setup(){
  pinMode(Dev1,OUTPUT);
  pinMode(Dev2,OUTPUT);
  pinMode(Dev3,OUTPUT);
  pinMode(Dev4,OUTPUT);
  pinMode(Dev5,OUTPUT);
  pinMode(Dev6,OUTPUT);
  pinMode(Dev7,OUTPUT);
  pinMode(Dev8,OUTPUT);
  pinMode(Dev9,OUTPUT);
  pinMode(Dev10,OUTPUT);
  pinMode(Dev11,OUTPUT);
  pinMode(Dev12,OUTPUT);
  pinMode(Dev13,OUTPUT);
  pinMode(Dev14,OUTPUT);
  pinMode(Dev15,OUTPUT);
  pinMode(Dev16,OUTPUT);
  
  Serial.begin(9600);
  Wire.begin();
}

void loop(){

  ProducedPower = 1;
  ConsumedPower = 2;
  BatteryVoltage = 3;
  
  //------------------------------------------------------------- 
  
  Wire.requestFrom(2, 1);    
  while(Wire.available()){
   PanelAngle = Wire.read();
  }
  
  if(Serial.available()){    
    if(Serial.read()==0x41){
      delay(50); //Is it useful?
      Serial.write(ProducedPower);
      Serial.write(ConsumedPower);
      Serial.write(BatteryVoltage);
      Serial.write(PanelAngle);
 
        for(int i=0; i<2; i++){
          BytesFromPc[i] = Serial.read(); 
        }
        for(int j=0; j<2; j++){
          Serial.write(BytesFromPc[j]);
        }
      }
    }
  
                                                      
      digitalWrite(Dev1, bitRead(BytesFromPc[1],0));
      digitalWrite(Dev2, bitRead(BytesFromPc[1],1));
      digitalWrite(Dev3, bitRead(BytesFromPc[1],2));
      digitalWrite(Dev4, bitRead(BytesFromPc[1],3));
      digitalWrite(Dev5, bitRead(BytesFromPc[1],4));
      digitalWrite(Dev6, bitRead(BytesFromPc[1],5));
      digitalWrite(Dev7, bitRead(BytesFromPc[1],6));
      digitalWrite(Dev8, bitRead(BytesFromPc[1],7));
      digitalWrite(Dev9, bitRead(BytesFromPc[0],0));
      digitalWrite(Dev10, bitRead(BytesFromPc[0],1));
      digitalWrite(Dev11, bitRead(BytesFromPc[0],2));
      digitalWrite(Dev12, bitRead(BytesFromPc[0],3));
      digitalWrite(Dev13, bitRead(BytesFromPc[0],4));
      digitalWrite(Dev14, bitRead(BytesFromPc[0],5));
      digitalWrite(Dev15, bitRead(BytesFromPc[0],6));
      digitalWrite(Dev16, bitRead(BytesFromPc[0],7));
    
}

Codice VB.NET:

'Open serial communication and send a starting code
        SerialPort1.Open()
        SerialPort1.Write("A")

        'Read bytes form Arduino Mega
        ProducedPower = SerialPort1.ReadByte
        ConsumedPower = SerialPort1.ReadByte
        AccumulatorCharge = SerialPort1.ReadByte
        PanelAngle = SerialPort1.ReadByte

        [...]

        SerialPort1.Write(Dev16To9)
        SerialPort1.Write(Dev8To1)

        Label6.Text = SerialPort1.ReadByte()
        Label4.Text = Dev16To9
        Label8.Text = SerialPort1.ReadByte()
        Label10.Text = Dev8To1

        SerialPort1.Close()

Grazie mille :slight_smile:

Scusa ma tu prima mandi dei dati da Arduino al PC, e poi leggi (penso) una risposta dal PC senza verificare ci siano dati ?

         Serial.write(PanelAngle);
// nessuna attesa con Serial.available() ???
         for(int i=0; i<2; i++){
          BytesFromPc[i] = Serial.read(); 
        }

Può sembrare assurdo ma è così; prima controllavo se fossero presenti byte sul buffer ogni volta che dovevo fare una lettura, ma paradossalmente i Serial.available() mi creano problemi.

La versione teoricamente più corretta del programma, in cui pc ed Arduino inviavano anche dei byte puramente indicativi per informare dell'avvenuta ricezione dei dati e controllavano la presenza di nuovi byte sul buffer prima di leggere, non funzionava.

Sai che la Serial.read() ritorna -1 ovvero 255 come byte (unsigned char) se sul buffer non ha nulla da leggere ?
Quindi questo semplice while aggiunto di attesa non ti funziona ? Non ci credo. :fearful:

...
Serial.write(PanelAngle);
  while( ! Serial.available()) ;   // attesa con Serial.available() 
  for(int i=0; i<2; i++) {
    BytesFromPc[i] = Serial.read(); 
  }
...

Non in questo modo. Avevo messo degli if(Serial.available()). Comunque ora sto lavorando ad un codice ben più solido; una volta terminato lo proverò e lo pubblicherò.

Anche io ho pensato di mettere dei while, probabilmente le if una volta risultate false facevano saltare il resto del programma.

Grazie per l'aiuto!

E’ meglio evitare questo apri e chiudi della porta seriale da VB.
Associa il serial open al form load ed il serial close al form close.
Molto meglio.

Ecco i nuovi codici; ora son decisamente migliori, ma Arduino sembra leggere 53 e 50 invece che 255 nei due byte ricevuti alla fine.

Codice di Arduino:

#include <Wire.h>

// PINS
// Current Sensors
const unsigned int PanelCS = 0;
const unsigned int ToEnelCS = 1;
const unsigned int RequiredCS = 2;
const unsigned int FromEnelToDevCS = 3;
const unsigned int FromEnelToBatteryCS = 4;

// Voltage dividers
const unsigned int PanelVD = 5;
const unsigned int BatteryVD = 6;

// Smart devices
const unsigned int Dev1 = 22;
const unsigned int Dev2 = 23;
const unsigned int Dev3 = 24;
const unsigned int Dev4 = 25;
const unsigned int Dev5 = 26;
const unsigned int Dev6 = 27;
const unsigned int Dev7 = 28;
const unsigned int Dev8 = 29;
const unsigned int Dev9 = 30;
const unsigned int Dev10 = 31;
const unsigned int Dev11 = 32;
const unsigned int Dev12 = 33;
const unsigned int Dev13 = 34;
const unsigned int Dev14 = 35;
const unsigned int Dev15 = 36;
const unsigned int Dev16 = 37;

// Variables
// Currents
int PanelCurrent;
int ToEnelCurrent;
int RequiredCurrent;
int FromEnelToDevCurrent;
int FromEnelToBatteryCurrent;
// Voltages
int PanelVoltage;
int BatteryVoltage;
// Powers
byte ProducedPower;
byte ConsumedPower;
int BoughtPower;
int SoldPower;
// Panel angle
byte PanelAngle;
// Bytes from PC
byte BytesFromPc[2];

// Boolean variables for functions
boolean StartingCode;
boolean ComputerIsReady;


void setup(){
  pinMode(Dev1,OUTPUT);
  pinMode(Dev2,OUTPUT);
  pinMode(Dev3,OUTPUT);
  pinMode(Dev4,OUTPUT);
  pinMode(Dev5,OUTPUT);
  pinMode(Dev6,OUTPUT);
  pinMode(Dev7,OUTPUT);
  pinMode(Dev8,OUTPUT);
  pinMode(Dev9,OUTPUT);
  pinMode(Dev10,OUTPUT);
  pinMode(Dev11,OUTPUT);
  pinMode(Dev12,OUTPUT);
  pinMode(Dev13,OUTPUT);
  pinMode(Dev14,OUTPUT);
  pinMode(Dev15,OUTPUT);
  pinMode(Dev16,OUTPUT);
  
  Serial.begin(9600);
  Wire.begin();
}

void loop(){
  GetPanelAngle();
  ReadAnalogPins();
  ReadStartingCode();
  SendMeasuredValues();
  CheckComputerStatus();
  ReadyToReceive();
  ReceiveBytes();
  SetPins();
  SendEndCode();
}

// Function that reads the starting code "A"
void ReadStartingCode(){
  while(Serial.available()!=1);
    if(Serial.read()==0x41) StartingCode = true;
    else StartingCode = false;   
}


// Function that reads the PanelAngle byte from the second Arduino through I2C
void GetPanelAngle(){
  Wire.requestFrom(2, 1);    
  while(Wire.available()){
   PanelAngle = Wire.read();
  }  
}


// Function that reads currents and voltages values (NOW JUST EMULATED)
void ReadAnalogPins(){
  ProducedPower = 1;
  ConsumedPower = 2;
  BatteryVoltage = 3;
}

// Function that sends the measured values to the computer
void SendMeasuredValues(){
  if(StartingCode==true){
    Serial.write(ProducedPower);
    Serial.write(ConsumedPower);
    Serial.write(BatteryVoltage);
    Serial.write(PanelAngle);
  }
}


// Function that checks if the computer is ready to send the two bytes
void CheckComputerStatus(){
  while(Serial.available()!=1);
    if(Serial.read()==0x42) ComputerIsReady = true;
    else ComputerIsReady = false;   
}


// Function with which Arduino tells the computer it's ready to receive data
void ReadyToReceive(){
  if(ComputerIsReady==true) Serial.write("C");
}


// Function that reads the bytes from the computer
void ReceiveBytes(){
  while(Serial.available()!=2);
  for(int i=0; i<2; i++){
    BytesFromPc[i] = Serial.read(); 
  }
}


// Function that sets the digital pins according to the received bytes
void SetPins(){
  digitalWrite(Dev1, bitRead(BytesFromPc[1],0));
  digitalWrite(Dev2, bitRead(BytesFromPc[1],1));
  digitalWrite(Dev3, bitRead(BytesFromPc[1],2));
  digitalWrite(Dev4, bitRead(BytesFromPc[1],3));
  digitalWrite(Dev5, bitRead(BytesFromPc[1],4));
  digitalWrite(Dev6, bitRead(BytesFromPc[1],5));
  digitalWrite(Dev7, bitRead(BytesFromPc[1],6));
  digitalWrite(Dev8, bitRead(BytesFromPc[1],7));
  digitalWrite(Dev9, bitRead(BytesFromPc[0],0));
  digitalWrite(Dev10, bitRead(BytesFromPc[0],1));
  digitalWrite(Dev11, bitRead(BytesFromPc[0],2));
  digitalWrite(Dev12, bitRead(BytesFromPc[0],3));
  digitalWrite(Dev13, bitRead(BytesFromPc[0],4));
  digitalWrite(Dev14, bitRead(BytesFromPc[0],5));
  digitalWrite(Dev15, bitRead(BytesFromPc[0],6));
  digitalWrite(Dev16, bitRead(BytesFromPc[0],7));
}


// Function that sends the end code
void SendEndCode(){
  //Serial.write("D");
  Serial.write(BytesFromPc[1]);   // Now for debugging it sends one of the two received bytes
}

Codice VB.NET:

  Public Class Home  
    Public Sub SendStartingCode()
        'Open serial communication and send a starting code
        If CommunicationEnded = True Then
            SerialPort1.Open()
            SerialPort1.Write("A")
            CommunicationEnded = False
        End If
    End Sub

    Public Sub ReadValues()
        While SerialPort1.BytesToRead <> 4
        End While
        'Read bytes form Arduino Mega
        ProducedPower = SerialPort1.ReadByte
        ConsumedPower = SerialPort1.ReadByte
        AccumulatorCharge = SerialPort1.ReadByte
        PanelAngle = SerialPort1.ReadByte

        'Write new values to labels
        ProducedPowerLabel.Text = ProducedPower
        ConsumedPowerLabel.Text = ConsumedPower
        AccuChargeLabel.Text = AccumulatorCharge
        PanelAngleLabel.Text = CStr(PanelAngle) + "°"
    End Sub

    Public Sub CheckIfSmartDevNeeded()
        [...]
    End Sub

    Public Sub BuildBytes()
        [...]
    End Sub

    Public Sub ComputerIsReady()
        SerialPort1.Write("B")
    End Sub

    Public Sub ArduinoIsReady()
        While SerialPort1.BytesToRead <> 1
        End While
        If SerialPort1.ReadByte = 67 Then   '67 is the decimal value of "C"
            ArduinoReadyToReceive = True
        Else : ArduinoReadyToReceive = False
        End If
    End Sub

    Public Sub SendBytes()
        If ArduinoReadyToReceive = True Then
            SerialPort1.Write(Dev16To9)
            SerialPort1.Write(Dev8To1)
        End If
        
    End Sub

    Public Sub EndCommunication()
        While SerialPort1.BytesToRead <> 1
        End While
        'If SerialPort1.ReadByte = 68 Then
        Label4.Text = SerialPort1.ReadByte
        SerialPort1.Close()
        CommunicationEnded = True
        'End If
    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        SendStartingCode()
        ReadValues()
        CheckIfSmartDevNeeded()
        BuildBytes()
        ComputerIsReady()
        ArduinoIsReady()
        SendBytes()
        EndCommunication()
    End Sub
End Class