Ciao Candaluar,
grazie per la risposta... sto cercando di "capire" bene la logica dell'invio del messaggio, la risposta del dispositivo slave e del calcolo CRC.
Al momento ho compilato il mio programma test che potete vedere di seguito, solo che non funziona.
Non ho ben capito come devo gestire il risultato restituito dal dispositivo che vado ad interrogare; nel manuale del dispositivo molti valori sono in "Float" e altri in "Uint32" o "Uint16"... immagino che debba convertire il risultato a seconda del formato specificato, ma non so come.
Per di più ho visto che bisogna considerare anche l'ordine dei byte! Il mio dispositivo utilizza l'ordine "ABCD" (poi ne ho un altro che utilizza il CDAB)... cosa devo impostare per dialogare in questi due diversi modi?
Imports System.IO.Ports
Imports System.Threading
Public Class ReadHoldingRegistersForm
'dichiarazione della porta seriale
Dim serialPort as SerialPort = Nothing
'Al caricamento della Form, definisco le impostazioni della porta
Private Sub ReadHoldingRegistersForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
serialPort = New SerialPort("COM4", 19200, Parity.Even, 8, StopBits.One)
serialPort.Open() 'Open COM4
Catch ex As Exception
MessageBox.Show(Me, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
'--> ECCO LA SUB che secondo me mi da l'errore!
'Alla pressione di un pulsante definisco l'indirizzo dello slave, il codice funzione 03 (relativo alla lettura), l'indirizzo modbus da puntare e il numero di punti da leggere (perché 16*2???)
Private Sub btnReadHoldingRegisters_Click(sender As Object, e As EventArgs) Handles btnReadHoldingRegisters.Click
Try
Dim slaveAddress As Byte = 1
Dim functionCode As Byte = 3
Dim startAddress As UShort = 41269
Dim numberOfPoints = 16 * 2 ' Read 4 Float.
Dim frame As Byte() = Me.ReadHoldingRegisters(slaveAddress, functionCode, startAddress, numberOfPoints)
txtSendMsg.Text = Me.DisplayValue(frame) ' Diplays frame: send.
serialPort.Write(frame, 0, frame.Length) ' Send frame to modbus slave.
Thread.Sleep(100) ' Delay 100ms.
If serialPort.BytesToRead >= 5 Then ' --> perché >=5???
Dim buffRecei As Byte() = New Byte(serialPort.BytesToRead) {}
serialPort.Read(buffRecei, 0, buffRecei.Length) ' Read data from modbus slave.
txtReceiMsg.Text = Me.DisplayValue(buffRecei) ' Display frame: received. ']--> il messaggio che ricevo è sempre uguale, non cambia se utilizzo un altro registro modbus! perché?
Dim data As Byte() = New Byte(buffRecei.Length - 5) {}
Array.Copy(buffRecei, 3, data, 0, data.Length)
'Convert byte array to word array
Dim result As '...... ????? in che formato devo gestirlo il risultato???
'Display Result
txtResult.Text = String.Empty
For Each item As Single In result
txtResult.Text += String.Format("{0}/ ", item)
Next
End If
Catch ex As Exception
MessageBox.Show(Me, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
'Definisco una funzione per creare il messaggio in un array di byte
Private Function ReadHoldingRegisters(slaveAddress As Byte, functionCode As Byte, startAddress As UShort, numberOfPoints As UShort) As Byte()
Dim frame As Byte() = New Byte(7) {} ' Total 8 Bytes '--> perchè un array da 8 Bytes??
frame(0) = slaveAddress ' Slave Address
frame(1) = functionCode 'Function
frame(2) = CByte(startAddress / 256) 'Starting Address Hi. '--> perché diviso 256?
frame(3) = CByte(startAddress Mod 256) 'Starting Address Lo. '--> perché ho bisogno di restituire il resto?
frame(4) = CByte(numberOfPoints / 256) ' Quantity of Registers Hi.
frame(5) = CByte(numberOfPoints Mod 256) ' Quantity of Registers Lo.
Dim crc As Byte() = Me.CRC(frame) ' Call CRC Calculate.
frame(6) = crc(0) ' Error Check Lo
frame(7) = crc(1) ' Error Check Hi
Return frame '
End Function
' Modbus CRC calculation in VB
'--> Questo codice l'ho trovato su internet e l'ho copiato senza modificare nulla; non so se è giusto e se vale per ogni dispositivo ModBUS in RS485... secondo voi?
Private Function CRC(data As Byte()) As Byte()
Dim CRCFull As UShort = &HFFFF
Dim CRCHigh As Byte = &HF, CRCLow As Byte = &HFF
Dim CRCLSB As Char
Dim result As Byte() = New Byte(1) {}
For i As Integer = 0 To (data.Length) - 3
CRCFull = CUShort(CRCFull Xor data(i))
For j As Integer = 0 To 7
CRCLSB = ChrW(CRCFull And &H1)
CRCFull = CUShort((CRCFull >> 1) And &H7FFF)
If Convert.ToInt32(CRCLSB) = 1 Then
CRCFull = CUShort(CRCFull Xor &HA001)
End If
Next
Next
CRCHigh = CByte((CRCFull >> 8) And &HFF)
CRCLow = CByte(CRCFull And &HFF)
Return New Byte(1) {CRCLow, CRCHigh}
End Function
'Display frame
'--> Converto l'array di byte in stringa
Private Function DisplayValue(values As Byte()) As String
Dim result As String = String.Empty
For Each item As Byte In values
result += String.Format("{0:X2} ", item)
Next
Return result
End Function
End Class
Allegati: