Salve a tutti, sono nuovo nella community e da poco tempo a questa parte mi dilettando nello sviluppo di un sistema di controllo tramite Arduino
Essendo inesperto sono subito inciampato in un problema, che spero di riuscire a risolvere grazie all'aiuto di qualcuno che ha un'esperienza maggiore della mia con Arduino, VB e il datalogging :D.
Sto sviluppando un data logger in VB che salverà all'interno di un file TXT dei dati mandati sulla seriale dal mio Arduino UNO. Per quanto riguarda lo sketch sulla scheda funziona alla perfezione e inoltre riesco anche ad "ascoltare" quello che invio sulla seriale (lato PC) tramite la Serial.println
Il programma di per se quindi è già completo se non fosse per un problema con la funzione DataReceived di Visual Basic che mi crea dei log con dei dati ripetuti se acquisiti inseriti tramite variale o funzione anziché dalla sola seriale.
Mi spiego: quello che vorrei come risultato è un log del tipo
17/11/2014 12:52:25 Macchina_On
Quello che ottengo invece è un qualcosa del genere:
In breve mi duplica la data ottenuta dalla funzione Now sballandomi il log. Lo stesso accade anche se inserisco io manualmente una stringa al posto della funzione.
#Macc#hina#_On#
Se invece mi limito a salvarmi il solo dato inviato dalla scheda sulla seriale non ho problemi.
Ho provato diverse vie, ma non essendo molto pratico di seriali e VB sto trovando difficoltà. Penso Il codice che ho usato per leggere sulla seriale comunque è il seguente, vorrei capire come dovrei modificarlo per risolvere il mio problema:
Imports System.IO
Module Module1
Private WithEvents objSerial As New System.IO.Ports.SerialPort("COM4")
Dim percorso As String = "C:\Seriale\SerialTest.txt"
Sub Main()
If Not File.Exists(percorso) Then
Dim creaFile As FileStream
creaFile = File.Create(percorso)
creaFile.Close()
End If
objSerial.BaudRate = 9600
objSerial.Open()
Console.ReadKey()
objSerial.Close()
End Sub
Private Sub objSerial_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles objSerial.DataReceived
ScriviFile(objSerial.ReadExisting)
End Sub
Private Sub ScriviFile(log As String)
Console.Write(Now & " " & log)
File.AppendAllText(percorso, Now & " " & log)
End Sub
End Module
Seconda cosa
Se facendo così, il txt spezza ancora la riga "now() + log" allora mi viene da pensare che, essendo il txt un file aperto ma non "lockato", c'è una sovrascrittura.
Cioè, il DataReceived potrebbe scattare 2 volte, e forse la seconda volta non ha alcun testo "log" da dare (c'è solo il now(), ma quello lo metti tu). Questo secondo evento potrebbe contenere altri caratteri (p.es. di fine riga, CR, ecc.) che a prima vista non si leggono.
Prova anche con File.WriteAllText e verifica il contenuto del txt: potresti trovare solo il now(), segno che è il file è stato aperto e riscritto 2 volte e la seconda non aveva un log da scrivere. In tal caso sarebbe da indagare il motivo del secondo evento.
Mentre per quanto riguarda l'utilizzare la File.WriteAllText, non fa altro che cancellare tutto ciò che c'era prima nel file anziché aggiungere in coda, cosa che a me serve per questo uso la AppendAllText, ma comunque il problema persiste.
@nid69ita: utilizzando la console anziché il file l'errore non cambia, quindi suppongo che il problema non sia nella scrittura su file ma nella funzione DataReceived.
Comunque pensandoci un po' su, il problema potrebbe stare nel fatto che arduino spedisce la stringa in più pacchetti e non in uno singolo e quindi è per questo che mi si presenta questo problema. Se capissi come identificare il carattere che identifica la fine della stringa magari potrei risolvere così, oppure trovando un modo per inviare tutto insieme. Qualcuno ha idea?
A me di solito funziona, ma io l'open ed il close della seriale li uso diversamente.
Faccio un open ad inizio programma ed un close alla fine.
Altra prova che puoi fare: invece di quelle due scritture consecutive su console e su file dove ripeti
Now & " " & log
creati prima una variabile k e le assegni k = Now & " " & log e poi scrivi k.
Potrebbe derivare anche dal fatto che leggi la seriale più velocemente di quanto sia necessario per completare l'invio.
Se ricordo bene, il readexisting legge quello che c'è al momento, senza attendere la fine dei dati.
Prova a leggere più lentamente, metti una piccola pausa nel programma VB, oppure lega la lettura ad un timer e metti un tick opportuno. Se così si risolve, basta modificare il metodo di lettura.
Ah... quasi dimenticavo... il carattere da cercare per identificare il fineriga si chiama vbcrlf (ho dimenticato quali lettere sono maiuscole, ma vedi col completamento automatico... ora ho il VB lontano).
Non mi pare sia emerso nella discussione, @JoeSarro io non so come lavora il tuo programma o VB ma ricordati che ogni volta che apri la seriale la scheda Arduino viene resettata quindi se quelle sono le prime stringhe spedite e ti arrivano doppie, è probabile che possa anche essere questo il problema. La scheda inizia la trasmissione, arriva il segnale di reset, e questa ricomincia a spedire i dati.
@leo72: succede solo con la seriale sull'USB di Arduino?
Sto cercando in questi giorni un convertitore TTL per staccarmi dal connettore USB e far girare Arduino con una propria alimentazione, mantenendo comunque le comunicazioni. Allora mi chiedevo se anche in questo caso c'è il reset, pur usando SoftwareSerial
Succede con qualunque seriale che apri verso l'Arduino: l'Arduino è programmato affinché questo provochi il reset del microcontrollore. La caratteristica è stata inserita per un motivo: per poter scrivere un nuovo sketch nella flash del chip deve essere avviato un particolare programma che risiede in un'area di memoria protetta detto "bootloader". Questo programma parte solo al reset, quindi per facilitare l'upload si è fatto in modo che l'apertura della seriale provochi il reset.
Puoi ovviare a questo o inserendo un condensatore elettrolitico da 10 uF tra i pin RESET e GND della scheda (soluzione temporanea) oppure tagliando la pista RESET-EN sulla schedina (soluzione permanente: devi ripristinarla poi con una goccia di stagno).
se "seriale verso Arduino" significa "qualsiasi COM del PC", allora la mia domanda era espressa male e mi scuso.
Se rendo Arduino stand-alone con una propria alimentazione esterna (non i 5V dell'USB), già programmato e funzionante, e se voglio comunicare col PC tramite una SoftwareSerial su qualche pin a scelta (non via connettore USB di Arduino), ho ancora il reset quando da un sw su PC apro la COM a cui faccio arrivare il convertitore TTL/USB?
No, in questo caso no: non hai il reset. Perché del reset si occupa il chippino Atmega16U2 che gestisce la comunicazione tra PC e Arduino. Se frapponi un convertitore seriale, basta non collegare anche il pin di reset del chip al convertitore.
Scusate se vi rispondo in ritardo comunque alla fine ho risolto. Il problema stava nel fatto che arduino spediva i messaggi che inviavo via seriale in più pacchetti e non in uno; ho quindi definito un fine stringa (identificato col punto e virgola) e "riassemblato" la stringa, scrivendo su console e su file solo quando individuava il ";"
Posto qui comunque il codice della mia scrivi file
Private Sub ScriviFile(serialMsg As String)
evento = evento & serialMsg ' Dichiarato a inizio modulo
Dim i As Integer = evento.Length ' Lunghezza stringa assemblata
If GetChar(evento, i) = ";" Then
Dim log As String = Now & " " & evento
Console.Write(log) ' Visualizza l'evento nella console
File.AppendAllText(percorso, log)
evento = "" ' Pulizia della stringa per preparazione al prossimo evento
End If
End Sub
Ottimo.
Comunque resta valido il discorso sull'apertura e chiusura della porta com.
Conviene aprirla all'avvio del programma e chiuderla all'uscita, senza bisogno di fare altre cose, mettere condensatori o tagliare piste.
JoeSarro:
Scusate se vi rispondo in ritardo comunque alla fine ho risolto. Il problema stava nel fatto che arduino spediva i messaggi che inviavo via seriale in più pacchetti e non in uno;
Ogni volta che usi Serial.println Arduino spedisce il carattere di nuova riga. Potresti provare a sostituire le stampe con Serial.print e mettere Serial.println solo alla fine .