Created
July 6, 2024 04:34
-
-
Save h4tr3d/143cc4b3767e3ebc6221ef70c982e63e to your computer and use it in GitHub Desktop.
SerialSend (2024)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [SerialSend.c] | |
// Эта программа посылает текст или файл через последовательный порт | |
// Автор Ted Burke, последнее обновление 8-4-2015 | |
// | |
// Обновление: | |
// 2024-06-07: Alexander Drozdoff <[email protected]>: | |
// - Добавлено отправка данных из файла as-is | |
// - Добавлен режим /read_back для вычитывания после | |
// отправки данных из порта. Полезно для синхронных | |
// устройство которые отсылают эхо, но которые блокируются | |
// если выходной буфер заполнен. Все вычитанные данные | |
// отбрасываются. | |
// | |
// Текст для отправки указывается в аргументах командной строки. | |
// По умолчанию используется COM-порт с самым большим найденным номером. | |
// Используемая по умолчанию скорость 38400 baud. | |
// | |
// Как компилировать с помощью установленной системы MinGW: | |
// | |
// gcc -o SerialSend.exe SerialSend.c | |
// | |
// Как компилировать компилятором cl компании Microsoft: | |
// | |
// cl SerialSend.c | |
// | |
// Пример запуска (отправка последовательности символов S365 E120): | |
// | |
// SerialSend.exe "S356 E120" | |
#include <windows.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
static DWORD write_serial_port(HANDLE hSerial, const void *ptr, size_t size, const char* dev_name, int read_back); | |
int main(int argc, char *argv[]) | |
{ | |
// Декларация переменных и структур: | |
int m, n; | |
unsigned char buffer[MAX_PATH]; | |
unsigned char text_to_send[MAX_PATH]; | |
unsigned char digits[MAX_PATH]; | |
unsigned char *file_data = NULL; | |
unsigned int file_size = 0; | |
int baudrate = 38400; | |
int dev_num = 50; | |
int parse_hex_bytes = 0; | |
int close_delay = 0; | |
int read_back = 0; | |
char dev_name[MAX_PATH] = {0}; | |
char file_name[MAX_PATH] = {0}; | |
HANDLE hSerial; | |
DCB dcbSerialParams = {0}; | |
COMMTIMEOUTS timeouts = {0}; | |
// Вывод приветственного сообщения: | |
fprintf(stderr, "SerialSend (last updated 06-07-2024)\n"); | |
fprintf(stderr, "See http://batchloaf.com for more information\n"); | |
// Парсинг аргументов командной строки: | |
int argn = 1; | |
strcpy(buffer, ""); | |
while(argn < argc) | |
{ | |
if (strcmp(argv[argn], "/baudrate") == 0) | |
{ | |
// Parse baud rate | |
if (++argn < argc && ((baudrate = atoi(argv[argn])) > 0)) | |
{ | |
fprintf(stderr, "%d baud specified\n", baudrate); | |
} | |
else | |
{ | |
fprintf(stderr, "Baud rate error\n"); | |
return 1; | |
} | |
} | |
else if (strcmp(argv[argn], "/devnum") == 0) | |
{ | |
// Обработка номера устройства. SerialSend начинает поиск доступных | |
// COM-портов с этого номера, постепенно уменьшая его до нуля. | |
if (++argn < argc) | |
{ | |
dev_num = atoi(argv[argn]); | |
fprintf(stderr, "Device number %d specified\n", dev_num); | |
} | |
else | |
{ | |
fprintf(stderr, "Device number error\n"); | |
return 1; | |
} | |
} | |
else if (strcmp(argv[argn], "/closedelay") == 0) | |
{ | |
// Обработка задержки перед закрытием порта. После передачи | |
// указанного текста SerialSend выполнит задержку на это | |
// количество миллисекунд, после чего закроет COM-порт. | |
if (++argn < argc) | |
{ | |
close_delay = atoi(argv[argn]); | |
fprintf(stderr, "Delay of %d ms specified before closing COM port\n", close_delay); | |
} | |
else | |
{ | |
fprintf(stderr, "Close delay error\n"); | |
return 1; | |
} | |
} | |
else if (strcmp(argv[argn], "/hex") == 0) | |
{ | |
// Обработка hex-флага для передаваемого байта. | |
// Если этот флаг установлен, то в строку передачи можно | |
// добавить произвольный байт, указывая его через нотацию '\x'. | |
// Например, команда "SerialSend /hex Hello\x0D" | |
// отправит 6 байт, где последним будет байт перевода строки CR | |
// (символ '\r', у которого код 0x0D). | |
parse_hex_bytes = 1; | |
} | |
else if (strcmp(argv[argn], "/file") == 0) | |
{ | |
// Будем читать данные из файла, сохраняем его имя, | |
// любой другой текст отправим после отправки файла | |
if (++argn < argc) | |
{ | |
strncpy(file_name, argv[argn], sizeof(file_name)); | |
fprintf(stderr, "File name %s specified\n", file_name); | |
} | |
else | |
{ | |
fprintf(stderr, "File name error\n"); | |
return 1; | |
} | |
} | |
else if (strcmp(argv[argn], "/read_back") == 0) | |
{ | |
// Инициируем вычитывания данных из порта после записи. | |
// Иногда нужно для некоторых устройств, которые синхронно шлют эхо | |
// и нужно его вычитать, иначе другие команды не принимаются | |
read_back = 1; | |
} | |
else | |
{ | |
// Этот аргумент командной строки будет отправляемым текстом: | |
strcpy(buffer, argv[argn]); | |
} | |
// Переход к следующему аргументу командной строки: | |
argn++; | |
} | |
// Проверка, предоставлен ли для передачи какой-нибудь текст: | |
if (strlen(buffer) == 0 && strlen(file_name) == 0) | |
{ | |
fprintf(stderr, "Usage:\n\n\tSerialSend [/baudrate BAUDRATE] "); | |
fprintf(stderr, "[/devnum DEVICE_NUMBER] [/file FILE_NAME] [/read_back] [/hex] \"TEXT_TO_SEND\"\n"); | |
return 1; | |
} | |
// Если hex-парсинг разрешен, то текст для передачи модифицируется: | |
n = 0; m = 0; | |
while(n < strlen(buffer)) | |
{ | |
if (parse_hex_bytes && buffer[n] == '\\') | |
{ | |
n++; | |
if (buffer[n] == '\\') text_to_send[m] = '\\'; | |
else if (buffer[n] == 'n') text_to_send[m] = '\n'; | |
else if (buffer[n] == 'r') text_to_send[m] = '\r'; | |
else if (buffer[n] == 'x') | |
{ | |
digits[0] = buffer[++n]; | |
digits[1] = buffer[++n]; | |
digits[2] = '\0'; | |
text_to_send[m] = strtol(digits, NULL, 16); | |
} | |
} | |
else | |
{ | |
text_to_send[m] = buffer[n]; | |
} | |
m++; n++; | |
} | |
text_to_send[m] = '\0'; // Null-символ, завершающий строку. | |
// Прочитаем файл в память | |
if (strlen(file_name)) | |
{ | |
FILE *fp = fopen(file_name, "rb+"); | |
size_t readed = 0; | |
if (!fp) | |
{ | |
fprintf(stderr, "Can't open file %s\n", file_name); | |
return 1; | |
} | |
// Классический "наивный" способ узнать размер файла | |
fseek(fp, 0, SEEK_END); | |
file_size = ftell(fp); | |
fseek(fp, 0, SEEK_SET); | |
file_data = (unsigned char*)malloc(file_size); | |
if (!file_data) | |
{ | |
fprintf(stderr, "Can't allocate memory for file reading\n"); | |
return 1; | |
} | |
if (fread(file_data, 1, file_size, fp) != file_size) | |
{ | |
fprintf(stderr, "Error during file reading: %d, %s\n", errno, strerror(errno)); | |
return 1; | |
} | |
fclose(fp); | |
} | |
// Открыть доступный порт с самым большим номером: | |
fprintf(stderr, "Searching serial ports...\n"); | |
while(dev_num >= 0) | |
{ | |
fprintf(stderr, "\r "); | |
fprintf(stderr, "\rTrying COM%d...", dev_num); | |
sprintf(dev_name, "\\\\.\\COM%d", dev_num); | |
hSerial = CreateFile( | |
dev_name, GENERIC_READ|GENERIC_WRITE, 0, NULL, | |
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); | |
if (hSerial == INVALID_HANDLE_VALUE) dev_num--; | |
else break; | |
} | |
if (dev_num < 0) | |
{ | |
fprintf(stderr, "No serial port available\n"); | |
return 1; | |
} | |
fprintf(stderr, "OK\n"); | |
// Установка параметров устройства (38400 baud, 1 start bit, | |
// 1 stop bit, no parity) | |
dcbSerialParams.DCBlength = sizeof(dcbSerialParams); | |
if (GetCommState(hSerial, &dcbSerialParams) == 0) | |
{ | |
fprintf(stderr, "Error getting device state\n"); | |
CloseHandle(hSerial); | |
return 1; | |
} | |
//dcbSerialParams.BaudRate = CBR_38400; | |
dcbSerialParams.BaudRate = baudrate; | |
dcbSerialParams.ByteSize = 8; | |
dcbSerialParams.StopBits = ONESTOPBIT; | |
dcbSerialParams.Parity = NOPARITY; | |
if(SetCommState(hSerial, &dcbSerialParams) == 0) | |
{ | |
fprintf(stderr, "Error setting device parameters\n"); | |
CloseHandle(hSerial); | |
return 1; | |
} | |
// Настройки таймаута COM-порта: | |
timeouts.ReadIntervalTimeout = 1; | |
timeouts.ReadTotalTimeoutConstant = 1; | |
timeouts.ReadTotalTimeoutMultiplier = 1; | |
timeouts.WriteTotalTimeoutConstant = 50; | |
timeouts.WriteTotalTimeoutMultiplier = 10; | |
if(SetCommTimeouts(hSerial, &timeouts) == 0) | |
{ | |
fprintf(stderr, "Error setting timeouts\n"); | |
CloseHandle(hSerial); | |
return 1; | |
} | |
// Отправка файла | |
if (file_data) | |
{ | |
DWORD bytes_written = 0; | |
fprintf(stderr, "Sending file... "); | |
bytes_written = write_serial_port(hSerial, file_data, file_size, dev_name, read_back); | |
fprintf(stderr, "\n%d bytes written to %s\n", bytes_written, dev_name); | |
} | |
// Отправка указанного текста: | |
if (m > 0) | |
{ | |
DWORD bytes_written = 0; | |
fprintf(stderr, "Sending text... "); | |
bytes_written = write_serial_port(hSerial, text_to_send, m, dev_name, read_back); | |
fprintf(stderr, "\n%d bytes written to %s\n", bytes_written, dev_name); | |
} | |
// Сброс (Flush) буфера передачи перед закрытием последовательного порта: | |
FlushFileBuffers(hSerial); | |
if (close_delay > 0) | |
{ | |
fprintf(stderr, "Delaying for %d ms before closing COM port... ", close_delay); | |
Sleep(close_delay); | |
fprintf(stderr, "OK\n"); | |
} | |
// Закрытие последовательного порта: | |
fprintf(stderr, "Closing serial port..."); | |
if (CloseHandle(hSerial) == 0) | |
{ | |
fprintf(stderr, "Error\n", dev_name); | |
return 1; | |
} | |
fprintf(stderr, "OK\n"); | |
// Нормальный выход: | |
return 0; | |
} | |
static DWORD write_serial_port(HANDLE hSerial, const void *ptr, size_t size, const char* dev_name, int read_back) | |
{ | |
unsigned char *text_to_send = (unsigned char*)ptr; | |
DWORD bytes_written, total_bytes_written = 0; | |
while(total_bytes_written < size) | |
{ | |
if(!WriteFile(hSerial, text_to_send + total_bytes_written, | |
size - total_bytes_written, &bytes_written, NULL)) | |
{ | |
fprintf(stderr, "Error writing text to %s\n", dev_name); | |
CloseHandle(hSerial); | |
exit(1); | |
} | |
// Wait for data back | |
if (read_back) | |
{ | |
char buf[512]; | |
DWORD readed = 0; | |
while (ReadFile(hSerial, buf, sizeof(buf), &readed, NULL)) | |
{ | |
if (readed == 0) | |
break; | |
} | |
} | |
total_bytes_written += bytes_written; | |
} | |
return total_bytes_written; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment