Tag Archives: com port
Intéraction entre Dotnet et Arduino

Pour mon projet EasyLight, je vais devoir faire interagir un programme développé en DotNet avec un Arduino Uno.
Après avoir parcouru plusieurs forums, j’ai noté qu’il existait plusieurs possibilités:
- utiliser FirmData pour faire communiquer Arduino et votre programme DotNet
- faire du code pour connecter votre logiciel DotNet au port COM de votre Arduino
J’ai donc décidé de partir sur cette seconde option en développant 2 parties de code:
- une classe DotNet
- un gabarit pour Arduino
Le code s’inspire de pas mal de samples trouvés ici et là sur le net. Je l’ai commenté et il est relativement simple à comprendre.
Voici la classe DotNet Arduino:
Public Class Arduino
#Region "Properties"
Private WithEvents CurrentSerialPort As System.IO.Ports.SerialPort
Private Property ComPort As String = "COM8"
Private Property BaudRate As Integer = 9600
Private WithEvents WatchDogTimer As New System.Timers.Timer(5000) With {.Enabled = True}
Private Receiving As Boolean
Private BufferPointer As Integer
Private CBuffer(30) As Byte
#End Region
#Region "Event"
Public Event WatchdogReceived() 'Fires when a watchdog message (heartbeat) is received
Public Event ConnectionLost() 'Fires when there has not been a watchdog message for 5 seconds
Public Event LogMessageReceived(ByVal Message As String) 'Gives a system message from the Arduino
#End Region
#Region "Constructeur"
Public Sub New(ByVal _comport As String, ByVal _baudrate As Integer)
Me.ComPort = Trim(_comport)
Me.BaudRate = _baudrate
End Sub
#End Region
#Region "Connection / Start / Stop"
'Fonction qui démarre la connection
Public Sub Start()
Call StartCommunication()
End Sub
'Fonction qui stoppe la connection
Public Sub [Stop]()
CurrentSerialPort.Close()
WatchDogTimer.Stop()
End Sub
'Fonction de connection au arduino
Private Function StartCommunication() As Boolean
Dim ret As Boolean = False
Try
Dim components As System.ComponentModel.IContainer = New System.ComponentModel.Container()
CurrentSerialPort = New System.IO.Ports.SerialPort(components)
CurrentSerialPort.PortName = _ComPort
CurrentSerialPort.BaudRate = _BaudRate
CurrentSerialPort.ReceivedBytesThreshold = 1
CurrentSerialPort.Open()
If Not CurrentSerialPort.IsOpen Then
WriteLog("[ERROR]" & vbTab & "[" & System.Reflection.MethodBase.GetCurrentMethod.Name & "]" & vbTab & "Unable to open port com...")
Return ret
Else
CurrentSerialPort.DtrEnable = True
WriteLog("[INFO]" & vbTab & "[" & System.Reflection.MethodBase.GetCurrentMethod.Name & "]" & vbTab & "Serial port is open")
System.Threading.Thread.Sleep(1000)
'on envoie une commande pour nettoyer le buffer du arduino
Dim Command As Byte() = {40, 0, 0, 0, 41, 0}
Me.SendCommand(Command)
Me.SendCommand(Command)
WatchDogTimer.Start()
ret = True
End If
'On ajoute un handler pour récupérer les infos envoyées par le arduino
AddHandler CurrentSerialPort.DataReceived, AddressOf OnReceived
Catch ex As Exception
WriteLog("[ERROR]" & vbTab & "[" & System.Reflection.MethodBase.GetCurrentMethod.Name & "]" & vbTab & "Error opening port com...")
ret = False
End Try
Return ret
End Function
#End Region
#Region "Reception d'une commande"
'Fonction de traitements des données reçues (données émises par le arduino
Private Sub OnReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs)
If Receiving = False Then
Receiving = True
Try
Dim BytesRead As Integer
While CurrentSerialPort.BytesToRead > 0
BytesRead = CurrentSerialPort.Read(CBuffer, BufferPointer, CurrentSerialPort.BytesToRead)
BufferPointer += BytesRead
While BufferPointer > 0
'on recherche le début de commande (
Dim CommandStart As Integer = -1
For i As Integer = 0 To BufferPointer
If CBuffer(i) = CByte(40) Then
CommandStart = i
Exit For
End If
Next
'si aucun debut de commande n'a été trouvé, on nettoie le buffer car la commande est invalide
If CommandStart = -1 Then
ClearCBuffer()
End If
'si la commande ne commence pas au premier byte alors on supprime les bytes avant la commande
If CommandStart > 0 Then
LeftShiftCBuffer(CommandStart)
End If
'a partir d'ici le buffer est clean
'on recherche la fin de commande )
Dim Commandend As Integer = 0
For i As Integer = 0 To BufferPointer
If CBuffer(i) = CByte(41) Then
Commandend = i
Exit For
End If
Next
'si la fin de commande a été trouvée alors on execute la commande
If Commandend > 0 Then
Dim CommandBytes(Commandend) As Byte
For i As Integer = 0 To Commandend
CommandBytes(i) = CBuffer(i)
Next
'on execute la commande
ProcessCommand(CommandBytes)
'on reset le buffer et le pointer
LeftShiftCBuffer(Commandend + 1)
Else
Exit While
End If
End While
End While
Catch ex As Exception
End Try
Receiving = False
End If
End Sub
'Fonction qui nettoie le buffer de réception
Private Sub ClearCBuffer()
For i As Integer = 0 To CBuffer.Length - 1
CBuffer(i) = 0
Next
BufferPointer = 0
End Sub
'Fonction qui shift le buffer vers la gauche
Private Sub LeftShiftCBuffer(ByVal NrOfPlaces As Integer)
For i As Integer = NrOfPlaces To CBuffer.Length - 1
CBuffer(i - NrOfPlaces) = CBuffer(i)
Next
For i As Integer = CBuffer.Length - NrOfPlaces To CBuffer.Length - 1
CBuffer(i) = 0
Next
BufferPointer -= NrOfPlaces
End Sub
'Fonction de traitement des commandes reçues
Private Sub ProcessCommand(ByVal CommandBytes As Byte())
Try
If ((CommandBytes(0) = CByte(40)) And (CommandBytes(CommandBytes.Length - 1) = CByte(41))) Then 'toutes les commandes sont au format (cmd)
Dim PType As Char = ChrW(CommandBytes(1))
Select Case PType
Case CChar("S") 'Arduino send it started
RaiseEvent WatchdogReceived()
If Not IsNothing(WatchDogTimer) Then
WatchDogTimer.Stop()
End If
WatchDogTimer.Start()
Case CChar("W") 'Arduino send watchdog
RaiseEvent WatchdogReceived()
If Not IsNothing(WatchDogTimer) Then
WatchDogTimer.Stop()
End If
WatchDogTimer.Start()
Case Else
Dim CommandString As String = String.Empty
For i As Integer = 0 To CommandBytes.Length - 1
CommandString += CommandBytes(i).ToString + " "
Next
WriteLog("[MSG]" & vbTab & "[" & System.Reflection.MethodBase.GetCurrentMethod.Name & "]" & vbTab & CommandString)
End Select
Else
Dim CommandString As String = String.Empty
For i As Integer = 1 To CommandBytes.Length - 1
CommandString += CommandBytes(i).ToString
Next
WriteLog("[ERROR]" & vbTab & "[" & System.Reflection.MethodBase.GetCurrentMethod.Name & "]" & vbTab & "Bad command format received: " & CommandString)
End If
Catch ex As Exception
WriteLog("[ERROR]" & vbTab & "[" & System.Reflection.MethodBase.GetCurrentMethod.Name & "]" & vbTab & "Bad command format received")
End Try
End Sub
#End Region
#Region "Emission d'une commande"
'Fonction qui envoie une commande
Public Sub SendCommand(ByVal Command As Byte())
Try
If Command.Length > 0 Then
If CurrentSerialPort.IsOpen Then
CurrentSerialPort.Write(Command, 0, Command.Length - 1)
End If
End If
Catch ex As Exception
End Try
End Sub
#End Region
#Region "Fonctions Diverses"
'Fonction qui permet de savoir lorsqu'on n'a pas reçu de signal du arduino depuis 5 secondes
Private Sub WatchdogTimerElaped(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles WatchDogTimer.Elapsed
WriteLog("[ERROR]" & vbTab & "[" & System.Reflection.MethodBase.GetCurrentMethod.Name & "]" & vbTab & "Connection lost...")
RaiseEvent ConnectionLost()
If Not IsNothing(WatchDogTimer) Then
WatchDogTimer.Stop()
End If
End Sub
'Fonction de notification de message
Private Sub WriteLog(ByVal Message As String)
RaiseEvent LogMessageReceived(Message)
End Sub
#End Region
End Class
Et voici le gabarit de sketch Arduino qu’il vous faudra compléter avec votre code :
// Interaction entre Arduino et DotNet
// (Ce code fonctionne avec la classe DotNet "Arduino" )
// (Ce code s'inspire de plusieurs projets libres)
// www.zem.fr
int pt = 0; //Pointeur courant
boolean cfound = false; //Fin de commande trouvée
byte currentCmd[200]; //Buffer de commande
long previousMillis = 0; //Interval de verification du watchdog
long interval = 1000; //Interval d'émission entre 2 watchdogs
#define SOC 40 //Byte de debut de commande
#define EOC 41 //Byte de fin de commande
void setup() {
Serial.flush();
Serial.begin(9600);
delay(500);
Serial.print("(S)"); //On envoie une commande pour notifier que l'arduino est démarré
}
void loop() {
//STEP1: Lecture du Buffer
int sa;
byte bt;
sa = Serial.available(); //On recupere les données du port serie
if (sa > 0) { //On ecrit le buffer dans une variable jusqu'à ce que l'on rencontre le caractère de fin de commande
for (int i=0; i < sa; i++){
bt = Serial.read();
currentCmd[pt] = bt;
pt++;
if (bt == EOC) {
cfound = true;
break; //Le caractere de fin a été trouvé, on pourra proceder à l'execution de la commande
}
}
}
//STEP2: Execution d'une commande
if (cfound) {
if (int(stringIn[0]) == SOC) { //On verifie que le premier caractère est le caractere de debut de commande
RunCommand();
}
ResetCurrentCmd();
cfound = false;
}
//STEP3: Divers traitements
//Vous pouvez ajouter des traitements ici
//STEP4: Déclenchement du WatchDog
if (millis() - previousMillis > interval) {
previousMillis = millis();
Serial.print("(W)");
}
}
//Fonction qui reset la commande courante
void ResetCurrentCmd(void) {
for (int i=0; i<=200; i++) {
currentCmd[i] = 0;
pt = 0;
}
}
//Fonction qui distribue les differentes commandes
void RunCommand(void) {
char c = stringIn[1]; //Type de commande
switch (c) {
case 'D': //Mode Dynamic
break;
case 'F': //Mode Static
break;
break;
}
}