Arduino Leonardo + C#

Buongiorno a tutti

sto cercando di far comunicare il mio Arduino Leonardo con il PC. Ho pensato quindi di usare la porta USB e una applicazioncina C# (perché l'idea è quella di costruire in seguito una applicazione con interfaccia grafica Windows Form), ma ho qualche problema con la lettura dei dati provenienti da Arduino.

Ho caricato sul mio Leonardo lo sketch di esempio Event Serial

/*
  Serial Event example

 When new serial data arrives, this sketch adds it to a String.
 When a newline is received, the loop prints the string and 
 clears it.

 A good test for this is to try it with a GPS receiver 
 that sends out NMEA 0183 sentences. 

 Created 9 May 2011
 by Tom Igoe

 This example code is in the public domain.

 http://www.arduino.cc/en/Tutorial/SerialEvent

 */
#define LED 13            // LED collegato al pin digitale 13
String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete

void setup() {
  pinMode(LED, OUTPUT);     // imposta il pin digitale come output  
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
    digitalWrite(LED, HIGH);  // accende il LED  
  }
  
  digitalWrite(LED, LOW);  // accende il LED  
}

void loop() {
  // print the string when a newline arrives:

  if (stringComplete) {
    Serial.println(inputString); 

   
    // clear the string:
    inputString = "";
    stringComplete = false;
    
   
  }
  
  if(Serial.available() > 0)
    serialEvent();
}


void serialEvent() {
  while (Serial.available()) {
    digitalWrite(LED, HIGH);
    // get the new byte:
    char inChar = (char)Serial.read(); 
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      stringComplete = true;
      digitalWrite(LED, LOW);
    } 
  }
}

Innanzi tutto ho scoperto che, contrariamente a quanto dice la documentazione la funzione serialEvents()non viene chiamata automaticamente al termine di ogni chiamata a loop(). Poco male... ho messo io la chiamata all'interno di loop.

A questo punto utilizzando il monito seriale dell'IDE ho avuto la controprova che lo sketch funziona: se invio la stringa 'Ciao' a Leonardo lui mi risponde con 'Ciao'.

Grande successo!!! :D :D :D

Passando alla parte C# ho scritto questa classe seguendo le indicazioni di Microsoft:

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;

namespace SimpleUsbWriter2
{
    class Program
    {
       
       

        static void Main(string[] args)
        {
            bool _continue = true;
            SerialPort mySerialPort = new SerialPort("COM4");

            mySerialPort.BaudRate = 9600;
            mySerialPort.Parity = Parity.None;
            mySerialPort.StopBits = StopBits.One;
            mySerialPort.DataBits = 8;
            mySerialPort.Handshake = Handshake.None;
            mySerialPort.NewLine = "\n";
            
            mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

            mySerialPort.Open();

            
            while (_continue)
            {
                String s = Console.ReadLine();
                if (s.Equals("exit"))
                {
                    _continue = false;
                    break;
                }
                mySerialPort.WriteLine(s);
            }
            mySerialPort.Close();
        }

        private static void DataReceivedHandler(
                         object sender,
                         SerialDataReceivedEventArgs e)
        {
            Console.Write("DataReceivedHandler");
            SerialPort sp = (SerialPort)sender;
            Console.WriteLine("Data Received: " + sp.BytesToRead);
            string indata = sp.ReadExisting();

            
            Console.Write(indata);
        }

    }
}

e qui nasce il problema. I dati vengono inviati ad Arduino (o almeno così pare, perché il LED RX blinka) ma non tornano indietro: il metodo DataReceivedHandler() non viene mai invocato.

Dove sto sbagliando? Qualcuno ha qualche suggerimento?

Grazie a tutti

Ps: C'è un modo per fare debug del codice che gira in arduino? Inserire breack point e far avanzare le istruzioni step by step?

Ti invitiamo a presentarti (dicci quali conoscenze hai di elettronica e di programmazione) qui: Presentazioni
e a leggere il regolamento: Regolamento

sandros:
Ps: C’è un modo per fare debug del codice che gira in arduino? Inserire breack point e far avanzare le istruzioni step by step?

Non con l’IDE di Arduino. Qualcosa usando Atmel Studio di Atmel. Ma poi devi usare o un plugin a pagamento oppure un programmatore hw tipo Avr Dragon.

Non so se c'entra col tuo problema né so se lo hai considerato ma le schede Arduino sono progettate in modo che ogni volta che si apre il canale seriale esse si autoresettino. In più, c'è da dire che la Leonardo è una scheda particolare perché integra un controller USB nel chip stesso: se da un lato questo è un vantaggio (meno componenti e comunicazione diretta dal chip al computer) dall'altra ha uno svantaggio, ossia che ad ogni reset la scheda rinegozia la connesione quindi le viene riassegnata una nuova porta. Non ho guardato il tuo programma quindi non so come funziona, ma se lo hai strutturato in modo che la lettura avvenga con una nuova riapertura della connessione seriale, allora il problema potrebbe essere questo.

In realtà no... la connessione viene aperta una sola volta viene aggiunto all'oggetto C# rappresenta la porta USB un event listner per l'evento DataReceived.

Proprio mentre sto scrivendo sto pernsando che dato che provando con Java usando DLL rxtxSerial.dll dell'IDE di Arduino la comunicazione avviene correttamente, forse usando quella stella dll riesco a farlo funzionare.

Vi tengo aggionati

Niente da fare. E' una dll nativa e non .NET; quindi faccio la mia applicazione in java con swing

La Leonardo non ri resetta alla semplice apertura della Seriale ma necessita della apertura e chiusura della seriale alla velocità di 1200 baud. Puoi tranquillamente aprire la seriale quanto volte vuoi a velocità superiori. Rimuoverei invece il !Serial che blocca l'esecuzione del codice finché non rileva attività sulla seriale.

Hai provato a vedere la libreria Firmata. Carichi lo sketch sulla Arduino e i moduli su PC. --> http://firmata.org/wiki/Download

PaoloP: La Leonardo non ri resetta alla semplice apertura della Seriale ma necessita della apertura e chiusura della seriale alla velocità di 1200 baud.

E' vero, hai ragione. Non mi ricordavo dei 1200 bps.

Se ho capito bene hai suggerito di modificare la velocità della USB a 1200 baud. Ho apportato la modifica sia al codice C# che allo sketch, ma non succede niente.

Inoltre, quando uso il monitor seriale la comunicazione funziona anche alla velocità di 9600.

Risolto mer....coledì!!! Bisogna impostare a true i flag DtrEnable e RtsEnable.

Posto il codice completo della clase C# funzionante.

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;

namespace SimpleUsbWriter2
{
    class Program
    {
       
       

        static void Main(string[] args)
        {
            bool _continue = true;
            SerialPort mySerialPort = new SerialPort("COM4");

            mySerialPort.BaudRate = 9600;
            mySerialPort.Parity = Parity.None;
            mySerialPort.StopBits = StopBits.Two;
            mySerialPort.DataBits = 8;
            mySerialPort.Handshake = Handshake.None;
            mySerialPort.DtrEnable = true;
            mySerialPort.RtsEnable = true;
            mySerialPort.NewLine = "\n";
            
            mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
            mySerialPort.ErrorReceived += new SerialErrorReceivedEventHandler(ErrorReceivedHandler);
            mySerialPort.Open();
            
            
            while (_continue)
            {
                String s = Console.ReadLine();
                if (s.Equals("exit"))
                {
                    _continue = false;
                    break;
                }
                mySerialPort.WriteLine(s);
            }
            mySerialPort.Close();
        }

        private static void DataReceivedHandler(
                         object sender,
                         SerialDataReceivedEventArgs e)
        {
            Console.Write("DataReceivedHandler");
            SerialPort sp = (SerialPort)sender;
            Console.WriteLine("Data Received: " + sp.BytesToRead);
            string indata = sp.ReadExisting();

            
            Console.Write(indata);
        }

        private static void ErrorReceivedHandler(
                         object sender,
                         SerialErrorReceivedEventArgs e)
        {
            Console.Write("DataReceivedHandler");
            SerialPort sp = (SerialPort)sender;
            Console.WriteLine("Data Received: " + sp.BytesToRead);
            string indata = sp.ReadExisting();


            Console.Write(indata);
        }

    }
}

Adesso ho tutti gli ingredienti per costruire un cannoncino controllato da PC tramite USB per sparare ai colleghi!!! :D :D :D :D

Grazie a tutti