diff --git a/examples/win32/comm.c b/examples/win32/comm.c new file mode 100644 index 0000000..0b2f6fc --- /dev/null +++ b/examples/win32/comm.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +UINT WriteToCommPort(HANDLE hComm, uint8_t* data_ptr, int data_length) { + + int actual_length; + + PurgeComm(hComm, PURGE_RXCLEAR | PURGE_TXCLEAR); + + bool Status = WriteFile(hComm, // Handle to the Serialport + data_ptr, // Data to be written to the port + data_length, + &actual_length, // No of bytes written to the port + NULL); + + if (!Status) + printf("\nWriteFile() failed with error %d.\n", GetLastError()); + + if (data_length != actual_length) + printf("\nWriteFile() failed to write all bytes.\n"); + + return data_length; +} + +bool SetLocalBaudRate(HANDLE hComm, DWORD baudrate) { + bool Status; + DCB dcb = {0}; + + // Setting the Parameters for the SerialPort + // but first grab the current settings + dcb.DCBlength = sizeof(dcb); + + Status = GetCommState(hComm, &dcb); + if (!Status) + return false; + + dcb.BaudRate = baudrate; + dcb.ByteSize = 8; // ByteSize = 8 + dcb.StopBits = ONESTOPBIT; // StopBits = 1 + dcb.Parity = NOPARITY; // Parity = None + + Status = SetCommState(hComm, &dcb); + + return Status; +} + +bool InitCommPort(HANDLE* hComm, int PortNumber) { + uint8_t SerialBuffer[2048] = {0}; + COMMTIMEOUTS timeouts = {0}; + bool Status; + char commName[50] = {0}; + int serialPort = 9; + + // special syntax needed for comm ports > 10, but syntax also works for comm ports < 10 + sprintf_s(commName, sizeof(commName), "\\\\.\\COM%d", PortNumber); + + // Open the serial com port + *hComm = CreateFileA(commName, + GENERIC_READ | GENERIC_WRITE, // Read/Write Access + 0, // No Sharing, ports cant be shared + NULL, // No Security + OPEN_EXISTING, // Open existing port only + 0, + NULL); // Null for Comm Devices + + if (*hComm == INVALID_HANDLE_VALUE) + return false; + + Status = SetLocalBaudRate(*hComm, 9600); + if (!Status) + return false; + + // setup the timeouts for the SerialPort + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + + timeouts.WriteTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + + if (!SetCommTimeouts(*hComm, &timeouts)) + return false; + + Status = PurgeComm(*hComm, PURGE_RXCLEAR | PURGE_TXCLEAR); // clear the buffers before we start + if (!Status) + return false; + + return true; +} + +bool CloseCommPort(HANDLE* hComm) { + if (hComm != INVALID_HANDLE_VALUE) + CloseHandle(hComm); + else + return false; + + return true; +} + +int32_t ReadCommPort(HANDLE hComm, uint8_t* buf, uint16_t count, int32_t byte_timeout_ms) { + int TotalBytesRead = 0; + bool Status = false; + bool TimedOut = false; + ULONGLONG StartTime = 0; + uint8_t b; + int tmpByteCount; + + StartTime = GetTickCount64(); + + do { + // read one byte + Status = ReadFile(hComm, &b, 1, &tmpByteCount, NULL); + + // can't read from port at all?? + if (!Status) + return false; + + // put one byte into our buffer + if (tmpByteCount == 1) { + buf[TotalBytesRead++] = b; + } + + // did we time out yet?? + if (GetTickCount64() - StartTime > byte_timeout_ms) { + TimedOut = true; + break; + } + + } while (TotalBytesRead < count); + + return TotalBytesRead; +} \ No newline at end of file diff --git a/examples/win32/comm.h b/examples/win32/comm.h new file mode 100644 index 0000000..37eb161 --- /dev/null +++ b/examples/win32/comm.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +// function prototypes +bool SetLocalBaudRate(HANDLE hComm, DWORD baudrate); +bool InitCommPort(HANDLE* hComm, int PortNumber); +bool CloseCommPort(HANDLE* hComm); +int32_t WriteToCommPort(HANDLE hComm, uint8_t* data_ptr, int data_length); +int32_t ReadCommPort(HANDLE hComm, uint8_t* buf, uint16_t count, int32_t byte_timeout_ms); \ No newline at end of file diff --git a/examples/win32/modbus_cli.c b/examples/win32/modbus_cli.c new file mode 100644 index 0000000..1ae4532 --- /dev/null +++ b/examples/win32/modbus_cli.c @@ -0,0 +1,87 @@ +/* + * This example application uses the win32 API to read a single modbus + * register from a server. + */ + +#include +#include +#include +#include + +#define NMBS_LITTLE_ENDIAN 1 + +#include "..\..\nanomodbus.h" +#include "comm.h" + +#define NMBS_DEBUG 1 +#define RTU_SERVER_ADDRESS 1 + +HANDLE hComm; +int reg_to_read; +int commPort_In; + +void parseCmdLine(int argc, char** argv) { + + if (argc > 1) { + commPort_In = atoi(argv[1]); + reg_to_read = atoi(argv[2]); + } + + if (reg_to_read == 0 || commPort_In == 0) { + printf("please specify both a comm port and a register to read\n"); + exit(0); + } +} + +int32_t read_serial(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) { + return ReadCommPort(hComm, buf, count, byte_timeout_ms); +} + +int32_t write_serial(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) { + return WriteToCommPort(hComm, buf, count); +} + +void onError(nmbs_error err) { + printf("error: %d\n", err); + exit(0); +} + +void ReadRegister(uint16_t reg) { + + nmbs_platform_conf platform_conf; + platform_conf.transport = NMBS_TRANSPORT_RTU; + platform_conf.read = read_serial; + platform_conf.write = write_serial; + + nmbs_t nmbs; + nmbs_error err = nmbs_client_create(&nmbs, &platform_conf); + if (err != NMBS_ERROR_NONE) + onError(err); + + nmbs_set_read_timeout(&nmbs, 1000); + nmbs_set_byte_timeout(&nmbs, 100); + + nmbs_set_destination_rtu_address(&nmbs, RTU_SERVER_ADDRESS); + + uint16_t r_regs[2]; + err = nmbs_read_holding_registers(&nmbs, reg, 1, r_regs); + if (err != NMBS_ERROR_NONE) + onError(err); + + printf("register %d is set to: %d\n", reg, r_regs[0]); +} + +int main(int argc, char** argv) { + + printf("modbus_cli - CLI to read modbus registers\n"); + printf("Usage: modbus_cli comport register\n\n"); + + parseCmdLine(argc, argv); + + if (!InitCommPort(&hComm, commPort_In)) { + printf("error opening output comm port %d\n", commPort_In); + exit(0); + } + + ReadRegister(reg_to_read); +} \ No newline at end of file diff --git a/examples/win32/vs2022/.gitignore b/examples/win32/vs2022/.gitignore new file mode 100644 index 0000000..334bacc --- /dev/null +++ b/examples/win32/vs2022/.gitignore @@ -0,0 +1,3 @@ +x64/ +Debug/ +.vs/ diff --git a/examples/win32/vs2022/modbus_cli.sln b/examples/win32/vs2022/modbus_cli.sln new file mode 100644 index 0000000..edd4114 --- /dev/null +++ b/examples/win32/vs2022/modbus_cli.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.32916.344 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "modbus_cli", "modbus_cli.vcxproj", "{CBA78147-81C5-4DED-B716-F6363421298B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CBA78147-81C5-4DED-B716-F6363421298B}.Debug|x64.ActiveCfg = Debug|x64 + {CBA78147-81C5-4DED-B716-F6363421298B}.Debug|x64.Build.0 = Debug|x64 + {CBA78147-81C5-4DED-B716-F6363421298B}.Debug|x86.ActiveCfg = Debug|Win32 + {CBA78147-81C5-4DED-B716-F6363421298B}.Debug|x86.Build.0 = Debug|Win32 + {CBA78147-81C5-4DED-B716-F6363421298B}.Release|x64.ActiveCfg = Release|x64 + {CBA78147-81C5-4DED-B716-F6363421298B}.Release|x64.Build.0 = Release|x64 + {CBA78147-81C5-4DED-B716-F6363421298B}.Release|x86.ActiveCfg = Release|Win32 + {CBA78147-81C5-4DED-B716-F6363421298B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8835A584-0C6B-42CA-B3F8-0CEAB23D83A7} + EndGlobalSection +EndGlobal diff --git a/examples/win32/vs2022/modbus_cli.vcxproj b/examples/win32/vs2022/modbus_cli.vcxproj new file mode 100644 index 0000000..e549f45 --- /dev/null +++ b/examples/win32/vs2022/modbus_cli.vcxproj @@ -0,0 +1,141 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + 16.0 + Win32Proj + {cba78147-81c5-4ded-b716-f6363421298b} + modbuscli + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + NMBS_BIG_ENDIAN=1WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + NMBS_LITTLE_ENDIAN=1;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + true + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + \ No newline at end of file diff --git a/examples/win32/vs2022/modbus_cli.vcxproj.filters b/examples/win32/vs2022/modbus_cli.vcxproj.filters new file mode 100644 index 0000000..7a499c3 --- /dev/null +++ b/examples/win32/vs2022/modbus_cli.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/examples/win32/vs2022/modbus_cli.vcxproj.user b/examples/win32/vs2022/modbus_cli.vcxproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/examples/win32/vs2022/modbus_cli.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file