DllCall Hilfe !

  • Hallo sehr geehrte Community !

    Ich bin gerade an einem kleinen Projekt, um mit AutoIt via Modbus/TCP eine WAGO 750-341 anzusprechen.
    ( eine WAGO 750-341 ist ein Ethernet Koppler mit dem man Digitale und Analoge Ein und Ausgänge ansprechen kann) ( geiles Teil )

    Jetzt habe ich da im Internet eine DLL gefunden namens "libmodbus.dll" ist ein OpenSource Projekt ( http://libmodbus.org/ )

    Dokumentation ist eigentlich alles da aber ich bin wohl nicht in der Lage das ding zum laufen zu kriegen.

    hier die Funktion an der ich scheitere :

    in C -> modbus_t *modbus_new_tcp(const char *ip, int port);

    bedeutet für mich :

    [autoit]


    Local $dll = DllOpen("libmodbus.dll")
    Local $ctx = DllStructCreate("STRUCT")
    Local $ip = DllStructCreate("CHAR[128]")
    DllStructSetData($ip, 1, "172.18.65.159")

    [/autoit][autoit][/autoit][autoit]

    $ctx = DllCall($dll, "ptr", "modbus_new_tcp", "ptr", DllStructGetPtr($ip), "int", 502)

    [/autoit][autoit][/autoit][autoit]

    MsgBox(0,"$ctx",$ctx)

    [/autoit][autoit][/autoit][autoit]

    EXIT

    [/autoit][autoit][/autoit][autoit][/autoit]

    das einzige was da bei raus kommt ist

    !>15:56:38 AutoIT3.exe ended.rc:-1073741819

    eine Strukturdefinierung von "modbus_t" ist in der Doku mit " typedef struct _modbus modbus_t;" beschrieben.

    FINDE den FEHLER !! ??!? ?(

    DANKE !!

    Einmal editiert, zuletzt von vivus (18. Dezember 2012 um 13:23)

  • Hallo vivus,
    für Modbus-TCP brauchst du keine DLL. Das geht mit AutoIt auch ganz einfach.

    Hier mal ein Beispiel-Code. Wird bei dir zwar so nicht laufen, aber du kannst dir daraus sicher was basteln.

    Spoiler anzeigen
    [autoit]

    #include <WinAPI.au3>

    [/autoit] [autoit][/autoit] [autoit]

    TCPStartup()

    [/autoit] [autoit][/autoit] [autoit]

    Global $ModbusTCP_Debug_Send = 1
    Global $ModbusTCP_Debug_Recv = 1

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Global $iSock = TCPConnect("192.168.0.1", 502)

    [/autoit] [autoit][/autoit] [autoit]

    If $iSock = -1 Then
    ConsoleWrite("Fehler Verbindungsaufbau" & @LF)
    Exit
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $aTest = _ModbusTCP_ReadValues($iSock, 4, 1, 4, "float") ;Functioncode 4, Register 1, Länge 4 Resiter lesen, als float zurückgeben

    [/autoit] [autoit][/autoit] [autoit]

    TCPCloseSocket($iSock)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    TCPShutdown()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _ModbusTCP_ReadValues($mainsocket, $FC, $iStart, $iNum, $sType)
    Local Static $TI = 0
    $TI += 1

    [/autoit] [autoit][/autoit] [autoit]

    Local $iBytesToSend = 6

    [/autoit] [autoit][/autoit] [autoit]

    _ModbusTCP_Send($mainsocket, "0x" & Hex($TI, 4) & "0000" & Hex($iBytesToSend, 4) & "01" & Hex($FC, 2) & Hex($iStart, 4) & Hex($iNum, 4))
    If @error Then Return SetError(1, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $sRecv

    [/autoit] [autoit][/autoit] [autoit]

    Do
    $sRecv = _ModbusTCP_Recv($mainsocket, 512)
    Until @error Or $sRecv <> ""
    If @error Then Return SetError(2, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    If Int(String(BinaryMid($sRecv, 1, 2))) <> $TI Then Return SetError(3, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $iSize = 4 ; default

    [/autoit] [autoit][/autoit] [autoit]

    Switch $sType
    Case "word"
    $iSize = 2
    Case "float"
    $iSize = 4
    Case "double"
    $iSize = 8
    EndSwitch

    [/autoit] [autoit][/autoit] [autoit]

    Local $aRet[$iNum / ($iSize / 2)], $iTemp

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($aRet) - 1
    $iTemp = BinaryMid($sRecv, 10 + $i * $iSize, $iSize)
    Switch $sType
    Case "word"
    $aRet[$i] = Int(String($iTemp))
    Case "float"
    $aRet[$i] = _WinAPI_IntToFloat(Int(String($iTemp)))
    Case "double"
    $aRet[$i] = _WinAPI_Int64ToDouble(Int(String($iTemp)))
    EndSwitch
    Next
    Return $aRet
    EndFunc ;==>_ModbusTCP_ReadValues

    [/autoit] [autoit][/autoit] [autoit]

    Func _ModbusTCP_Send($mainsocket, $data)
    If $ModbusTCP_Debug_Send Then ConsoleWrite("_ModbusTCP_Send(" & $mainsocket & ", """ & $data & """)" & @CRLF)
    Local $Send = TCPSend($mainsocket, $data)
    Return SetError(@error, 0, $Send)
    EndFunc ;==>_ModbusTCP_Send

    [/autoit] [autoit][/autoit] [autoit]

    Func _ModbusTCP_Recv($mainsocket, $maxlen = 256, $flag = 0)
    Local $sRecv = TCPRecv($mainsocket, $maxlen, $flag)
    Local $error = @error
    If $sRecv <> "" And $ModbusTCP_Debug_Recv Then ConsoleWrite("_ModbusTCP_Recv: " & $sRecv & @CRLF)
    Return SetError($error, 0, $sRecv)
    EndFunc ;==>_ModbusTCP_Recv

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name...........: _WinAPI_Int64ToDouble
    ; Description ...: Returns a 8 byte integer as a double value
    ; Syntax.........: _WinAPI_IntToFloat($iInt64)
    ; Parameters ....: $iInt64 - 8 byte Integer value (64 bit)
    ; Return values .: Success - 8 byte integer value as a double
    ; Author ........: funkey
    ; Modified.......:
    ; Remarks .......:
    ; Related .......:
    ; Link ..........:
    ; Example .......: Yes
    ; ===============================================================================================================================
    Func _WinAPI_Int64ToDouble($iInt64)
    Local $tInt64 = DllStructCreate("INT64")
    Local $tDouble = DllStructCreate("double", DllStructGetPtr($tInt64))
    DllStructSetData($tInt64, 1, $iInt64)
    Return DllStructGetData($tDouble, 1)
    EndFunc ;==>_WinAPI_Int64ToDouble

    [/autoit]
  • Hei funkey !! DANKESCHÖN !!

    Sieht heiss aus der Code ... Hast noch n bissel mehr davon ? ( du hast übrigens recht bei mir läuft der nicht .. :wacko: )

    Werde mich jetzt da wohl reindenken müssen ... :huh:

    Dachte ich komm unter Zuhilfenahme der DLL um das ganze ModBus/TCP Protokoll drum rum .. ;)

    Man muss ja das Rad nicht 2 mal erfinden .. ;)

  • NACHTRAG :

    mit der Ergänzung

    [autoit]


    $aTest = _ModbusTCP_ReadValues($iSock, 4, 0, 1, "word") ;Functioncode 4, Register 1, Länge 4 Resiter lesen, als float zurückgeben
    _ArrayDisplay($aTest)

    [/autoit]

    bekomme ich das imput Register ;):rock:

    hast du noch ne Funktion zum Schreiben ?
    Write Singel Coil ? ( 0x05 )
    und
    Write Singel Register ? ( 0x06)

  • Hab jetzt noch eine Funktion für's Lesen von Bits (Inputs - Funktionscode 2) geschrieben. Hoffe, die funktioniert bei dir.

    Spoiler anzeigen
    [autoit]

    #include <WinAPI.au3>

    [/autoit] [autoit][/autoit] [autoit]

    TCPStartup()

    [/autoit] [autoit][/autoit] [autoit]

    Global $ModbusTCP_Debug_Send = 1
    Global $ModbusTCP_Debug_Recv = 1

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Global $iSock = TCPConnect("172.18.65.159", 502)

    [/autoit] [autoit][/autoit] [autoit]

    If $iSock = -1 Then
    ConsoleWrite("Fehler Verbindungsaufbau" & @LF)
    Exit
    EndIf

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Global $iRet = _ModbusTCP_ReadDiscreteInputs($iSock, 0, 16)
    ConsoleWrite("Bits = " & $iRet & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    TCPCloseSocket($iSock)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    TCPShutdown()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _ModbusTCP_ReadDiscreteInputs($mainsocket, $iStartAddress, $iNumOfCoils)
    Local Static $TI = 0
    $TI += 1

    [/autoit] [autoit][/autoit] [autoit]

    Local $iBytesToSend = 6, $iFunctionCode = "02"

    [/autoit] [autoit][/autoit] [autoit]

    If $iNumOfCoils < 1 Then Return SetError(1, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    _ModbusTCP_Send($mainsocket, "0x" & Hex($TI, 4) & "0000" & Hex($iBytesToSend, 4) & "01" & $iFunctionCode & Hex($iStartAddress, 4) & Hex($iNumOfCoils, 4))
    If @error Then Return SetError(2, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $sRecv

    [/autoit] [autoit][/autoit] [autoit]

    Do
    $sRecv = _ModbusTCP_Recv($mainsocket, 512)
    Until @error Or $sRecv <> ""
    If @error Then Return SetError(3, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    If Int(String(BinaryMid($sRecv, 1, 2))) <> $TI Then Return SetError(4, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $iAnzahlBytes = Int(String(BinaryMid($sRecv, 9, 1)))

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite("Anzahl der erhaltenen Daten: " & $iAnzahlBytes & " Byte(s), max " & $iAnzahlBytes * 8 & " Bits." & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    Return _Convert_To_Binary(Int(String(BinaryMid($sRecv, 10, $iAnzahlBytes))))
    EndFunc ;==>_ModbusTCP_ReadDiscreteInputs

    [/autoit] [autoit][/autoit] [autoit]

    Func _ModbusTCP_Send($mainsocket, $data)
    If $ModbusTCP_Debug_Send Then ConsoleWrite("_ModbusTCP_Send(" & $mainsocket & ", """ & $data & """)" & @CRLF)
    Local $Send = TCPSend($mainsocket, $data)
    Return SetError(@error, 0, $Send)
    EndFunc ;==>_ModbusTCP_Send

    [/autoit] [autoit][/autoit] [autoit]

    Func _ModbusTCP_Recv($mainsocket, $maxlen = 256, $flag = 0)
    Local $sRecv = TCPRecv($mainsocket, $maxlen, $flag)
    Local $error = @error
    If $sRecv <> "" And $ModbusTCP_Debug_Recv Then ConsoleWrite("_ModbusTCP_Recv: " & $sRecv & @CRLF)
    Return SetError($error, 0, $sRecv)
    EndFunc ;==>_ModbusTCP_Recv

    [/autoit] [autoit][/autoit] [autoit]

    Func _Convert_To_Binary($iNumber)
    Local $sBinString = ""
    Do
    $sBinString = BitAND($iNumber, 1) & $sBinString
    $iNumber = BitShift($iNumber, 1)
    Until $iNumber <= 0
    If $iNumber < 0 Then SetError(1, 0, 0)
    Return $sBinString
    EndFunc ;==>_Convert_To_Binary

    [/autoit]
  • Hei funkey

    falls du's mal brauchst .. -> _ModbusTCP_WriteCoils ( 0x05 )

    Spoiler anzeigen
    [autoit][/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite("write" & @CRLF)
    $retval = _ModbusTCP_WriteCoils($iSock,$TI, 1, 1) ;
    ConsoleWrite("$retval ="& $retval & @CRLF)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite("clear" & @CRLF)
    $retval = _ModbusTCP_WriteCoils($iSock,$TI, 1, 0)
    ConsoleWrite("$retval ="& $retval & @CRLF)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _ModbusTCP_WriteCoils($mainsocket,ByRef $TI, $Port_adress , $Hi_Lo)
    ;Local Static $TI = 0
    $TI += 1

    [/autoit] [autoit][/autoit] [autoit]

    Local $iBytesToSend = 6, $iFunctionCode = "05"

    [/autoit] [autoit][/autoit] [autoit]

    If $Port_adress < 1 Then Return SetError(1, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    If $Hi_Lo = 1 then
    _ModbusTCP_Send($mainsocket, "0x" & Hex($TI, 4) & "0000" & Hex($iBytesToSend, 4) & "01" & $iFunctionCode & Hex($Port_adress-1, 4) & "FF00"); 65280 = FF00

    [/autoit] [autoit][/autoit] [autoit]

    ElseIf $Hi_Lo = 0 then
    _ModbusTCP_Send($mainsocket, "0x" & Hex($TI, 4) & "0000" & Hex($iBytesToSend, 4) & "01" & $iFunctionCode & Hex($Port_adress-1, 4) & "0000")

    [/autoit] [autoit][/autoit] [autoit]

    Else
    ;If $iNumOfCoils < 1 Then Return SetError(1, 0, 0)
    Return SetError(1, 0, 0)
    ;_ModbusTCP_Send($mainsocket, "0x" & Hex($TI, 4) & "0000" & Hex($iBytesToSend, 4) & "01" & $iFunctionCode & Hex($iStartAddress, 4) & Hex($iNumOfCoils, 4))

    [/autoit] [autoit][/autoit] [autoit]

    EndIf
    If @error Then Return SetError(2, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $sRecv

    [/autoit] [autoit][/autoit] [autoit]

    Do
    $sRecv = _ModbusTCP_Recv($mainsocket, 512)
    Until @error Or $sRecv <> ""
    If @error Then Return SetError(3, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    If Int(String(BinaryMid($sRecv, 1, 2))) <> $TI Then Return SetError(4, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    ;Local $iAnzahlBytes = Int(String(BinaryMid($sRecv, 9, 1)))

    [/autoit] [autoit][/autoit] [autoit]

    ;ConsoleWrite("Anzahl der erhaltenen Daten: " & $iAnzahlBytes & " Byte(s), max " & $iAnzahlBytes * 8 & " Bits." & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    Return 1 ;_Convert_To_Binary(Int(String(BinaryMid($sRecv, 10, $iAnzahlBytes))))

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>_ModbusTCP_ReadCoils

    [/autoit] [autoit][/autoit] [autoit][/autoit]
  • Danke vivus, werde ich zwar nicht brauchen, aber vllt jemand anderes. es ist immer gut, wenn man mehrere brauchbare Funktionen in einem Thread findet.
    Du könntest ja eine richtige universale MpdbisTCP-UDF daraus machen.

    BTW: Ich habe mir jetzt auch libModbus zu einer DLL kompiliert und ein paar Funktionen daraus gewrappt, um es zu testen. Läuft einwandfrei.

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $hDLL_Modbus = DllOpen("libModbus.dll")

    [/autoit] [autoit][/autoit] [autoit]

    Global $tModbus = _Modbus_New_TCP("192.168.0.1")
    If _Modbus_Connect($tModbus) = -1 Then
    ConsoleWrite("! Fehler: Es konnte keine Verbindung aufgebaut werden." & @CRLF)
    _Modbus_Free($tModbus)
    Exit
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Global $aVal = _Modbus_Read_Registers($tModbus, 65, 2)
    _ArrayDisplay($aVal)

    [/autoit] [autoit][/autoit] [autoit]

    _Modbus_Close($tModbus)
    _Modbus_Free($tModbus)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _Modbus_New_TCP($sIP, $iPort = 502)
    Local $aRet = DllCall($hDLL_Modbus, "ptr:cdecl", "modbus_new_tcp", "str", $sIP, "int", $iPort)
    Return $aRet[0]
    EndFunc ;==>_Modbus_New_TCP

    [/autoit] [autoit][/autoit] [autoit]

    Func _Modbus_Connect($tModbus)
    Local $aRet = DllCall($hDLL_Modbus, "int:cdecl", "modbus_connect", "ptr", $tModbus)
    Return $aRet[0]
    EndFunc ;==>_Modbus_Connect

    [/autoit] [autoit][/autoit] [autoit]

    Func _Modbus_Close($tModbus)
    DllCall($hDLL_Modbus, "none:cdecl", "modbus_close", "ptr", $tModbus)
    EndFunc ;==>_Modbus_Close

    [/autoit] [autoit][/autoit] [autoit]

    Func _Modbus_Free($tModbus)
    DllCall($hDLL_Modbus, "none:cdecl", "modbus_free", "ptr", $tModbus)
    EndFunc ;==>_Modbus_Free

    [/autoit] [autoit][/autoit] [autoit]

    Func _Modbus_StrError($iError)
    Local $aRet = DllCall($hDLL_Modbus, "str:cdecl", "modbus_strerror", "int", $iError)
    Return $aRet[0]
    EndFunc ;==>_Modbus_StrError

    [/autoit] [autoit][/autoit] [autoit]

    Func _Modbus_Read_Registers($tModbus, $iAddr, $iNum)
    Local $tBuffer = DllStructCreate("WORD[" & $iNum & "]")
    Local $aRet = DllCall($hDLL_Modbus, "int:cdecl", "modbus_read_registers", "ptr", $tModbus, "int", $iAddr, "int", $iNum, "struct*", $tBuffer)

    [/autoit] [autoit][/autoit] [autoit]

    Local $aRes[$iNum]
    For $i = 0 To $iNum - 1
    $aRes[$i] = DllStructGetData($tBuffer, 1, $i + 1)
    Next
    Return $aRes
    EndFunc ;==>_Modbus_Read_Registers

    [/autoit] [autoit][/autoit] [autoit][/autoit]
  • well done !!

    hast mir da auch die source ?

    hab vorhin bemerkt, dass ich ne Verzögerung von 20ms pro Befehl habe .. ( via Oszi an der Wago ) :cursing:
    ich denke Autoit wird vielleicht irgend wo die Bremse angezogen haben. ?(

    Da wäre eine C DLL echt was hübsches .. ..

    gibt n paar fertige im netz .. nmodbus/ und http://freemodbus.berlios.de/ und natürlich noch http://libmodbus.org/


    wenn wir die in AutoIt ansprechen könnten, wäre das wirklich ne runde Sache .. aber deine Möglichkeit geht natürlich auch .. müsste ich dann halt wieder erweitern ( wenn du mir die gibst )

    An dem einbauen der "fertigen dll's" bin ich ja schon gescheitert. ;(

    ps hier noch die Funktion "WriteSingleRegister" ;für den den's interessiert

    Spoiler anzeigen
    [autoit]


    Func _ModbusTCP_WriteSingleRegister($mainsocket,ByRef $TI, $iStartAddress, $iNumOfCoils)
    ;Local Static $TI = 0
    $TI += 1

    [/autoit] [autoit][/autoit] [autoit]

    Local $iBytesToSend = 6, $iFunctionCode = "06"

    [/autoit] [autoit][/autoit] [autoit]

    If $iNumOfCoils < 0 Then
    Return SetError(1, 0, 0)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    _ModbusTCP_Send($mainsocket, "0x" & Hex($TI, 4) & "0000" & Hex($iBytesToSend, 4) & "01" & $iFunctionCode & Hex($iStartAddress, 4) & Hex($iNumOfCoils, 4))
    If @error Then Return SetError(2, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $sRecv

    [/autoit] [autoit][/autoit] [autoit]

    Do
    $sRecv = _ModbusTCP_Recv($mainsocket, 512)
    Until @error Or $sRecv <> ""
    If @error Then Return SetError(3, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    If Int(String(BinaryMid($sRecv, 1, 2))) <> $TI Then Return SetError(4, 0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    ;Local $iAnzahlBytes = Int(String(BinaryMid($sRecv, 9, 1)))

    [/autoit] [autoit][/autoit] [autoit]

    ;ConsoleWrite("Anzahl der erhaltenen Daten: " & $iAnzahlBytes & " Byte(s), max " & $iAnzahlBytes * 8 & " Bits." & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    Return 1;_Convert_To_Binary(Int(String(BinaryMid($sRecv, 10, $iAnzahlBytes))))
    EndFunc ;==>_ModbusTCP_ReadCoils

    [/autoit]

    wenn ich mit allen Funktionen fertig bin, mach ich natürkich ne UDF draus .. mit Docu

  • ich korrigiere !!

    du Teufelskerl !!! :thumbup:

    du hast es ja geschafft !!! 8o

    das ist ja die original libmodbus.dll !!!!!!!!

    UNGLAUBLICH !!!

    S ganze Wochenende hab ich rum getüftelt !! und du machst des halt mal geschwind so nebenher !! :wacko:

    jetzt bin ich echt baff !!!!

    dann kann ich mir die UDF ja sparen .. bzw diese auf die DLL schreiben .. !!

    DANKE !!!!!