Форум программистов, компьютерный форум, киберфорум
Visual Basic .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342

Почему пропадают/искажаются сообщения SerialPort?

06.03.2025, 17:09. Показов 7647. Ответов 61

Студворк — интернет-сервис помощи студентам
Программа должна обмениваться данными с несколькими платами Arduino(сейчас подключаю 3 шт., планируется больше, но проблемы появляются и ни одной шт., и на двух…).
В тестовом режиме режиме я отправляю “99/0/0/0” – плата считывает это сообщение и отправляет обратно. В мониторе порта программы Arduino все работает четко и стабильно, без каких-либо сбоев (во всяком случае, мне не удалось такое обнаружить).
Но вот когда подключаю написанную программу – начинаются проблемы:
часто вместо отправленного сообщения программа получает не отображаемые символы, в кодировке Asc номера: 13, 10. Иногда программа вообще не получает никакого ответа.
В программе использованы библиотеки:
VB.NET
1
2
3
4
5
6
Imports System.IO.Ports
Imports System.Threading
Imports System.Runtime.InteropServices
Imports System.Windows.Forms.Design
Imports System.IO
Imports System.Text
Программа формирует коллекцию COM-портов:
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
Public oNameCol As New Dictionary(Of [String], [String])() ' массив СОМ-портов с номерами ответов плат
Public oNunbCol As New Dictionary(Of [String], [String])() ' массив номеров плат с СОМ-портами
Public oNPlat As Integer = 0 '  счетчик количества подключенных плат 
Public oComPort As New List(Of SerialPort) ' коллекция СОМ-портов
Public oComSpeed As Integer = 250000 ' скорость сомпорта
Public oReadCom As String ' переменная для чтения ответа из COM-порта
Dim WithEvents mySerialPort As SerialPort
 
    Private Sub ButtR_Click(sender As System.Object, e As System.EventArgs) Handles ButtR.Click
        ReadNameCom()
     End Sub
 
    Private Sub ReadNameCom()
        Dim oPorts As String() = SerialPort.GetPortNames()
        oNPlat = 0
        oNameCol.Clear()
        oNunbCol.Clear()
        If oComPort.Count > 0 Then
            Try
                For i = 0 To oComPort.Count - 1
                    oComPort(i).Close()
                Next
            Catch ex As Exception
            End Try
        End If
        oComPort.Clear()
        For Each oName In oPorts
            If oName <> "COM1" Then
                Try
                    Try
                        Try
                            mySerialPort.Close()
                        Catch ex3 As Exception
                        End Try
                        mySerialPort = New SerialPort(oName)
                        With mySerialPort
                            .BaudRate = oComSpeed '57600 '9600
                            .Parity = Parity.None
                            .StopBits = StopBits.One
                            .DataBits = 8
                            .Handshake = Handshake.None
                            .RtsEnable = True
                            .Open()
                        End With
                    Catch ex As Exception
                    End Try
                    oReadCom = ""
                    mySerialPort.WriteLine("97/0/0/0")
                    Thread.Sleep(90)
                    oReadCom = mySerialPort.ReadExisting()
                    If oReadCom = "" Then
                        Thread.Sleep(50)
                        oReadCom = mySerialPort.ReadExisting()
                    End If
                    mySerialPort.Close()
                Catch ex2 As Exception
                End Try
                If oReadCom <> "" Then
                    oNameCol.Add(oName, oReadCom) 
                    oNunbCol.Add(oReadCom, oName) 
                    oNPlat = oNPlat + 1
                End If
            End If
        Next
        Try
            If oNunbCol.Count > 0 Then
                For i = 1 To oNunbCol.Count
                    If oNunbCol.ContainsKey(i.ToString) Then
                        ListBox1.Items.Add(i.ToString & "  -  " & oNunbCol(i.ToString))
                        oComPort.Add(New SerialPort(oNunbCol(i.ToString)))
                        With oComPort(i - 1) ' нумерация от 0
                            .BaudRate = oComSpeed '57600
                            .Parity = Parity.None
                            .StopBits = StopBits.One
                            .DataBits = 8
                            .Handshake = Handshake.None
                            .RtsEnable = True
                        End With
                    Else
                    End If
                Next
            End If
        Catch ex As Exception
            ButtR.BackColor = Color.Red
        End Try
    End Sub
Провожу тест в цикле:
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    Private Sub ButStart_Click(sender As System.Object, e As System.EventArgs) Handles ButStart.Click
            For i = 1 To 100
                    SubStart()
            Next
    End Sub
 
    Private Sub SubStart()
        Dim oText As String = ""
        oText = ComsRead() ' ОТПРАВКА ЗАПРОСА
        Debug.Print(oText)
    End Sub
 
    Public oActivText As String ' переменная содержит последний ответ полученный в событии COM-порта
 
    Function ComsRead() As String ' функция отправки и получения ответа из COM-порта
        ComsRead = ""
        Try
            Dim oCallP2 As Integer = 1 ' номер платы чтения сигнала
            Dim oMess1 As String ' тест сообщения в COM-порт подачи сигнала
            oActivText = ""
            AddHandler oComPort(oCallP2).DataReceived, AddressOf DataReceivedHandler ' включение события 
            If oCallP2 = 1 Then
                oMess1 = "99/0/0/0"
                oComPort(oCallP2).WriteLine(oMess1)
            End If
            Dim oText As String = ""
            Thread.Sleep(5)
            For i = 1 To 50
                Thread.Sleep(10)
                oText = oActivText
                If oText <> "" Then
                    i = 999
                End If
            Next i
            RemoveHandler oComPort(oCallP2).DataReceived, AddressOf DataReceivedHandler 
            oComPort(oCallP2).DiscardInBuffer()
            If InStr(oText, "99") = 0 Then
                If oText <> "" Then ' просмотр кодов нештатных ответов
                    TextPin.Text = ""
                    For i = 1 To oText.Length
                        TextPin.Text = TextPin.Text & "  " & Asc(Mid(oText, i, 1))
                        Debug.Print(i.ToString & "=" & Asc(Mid(oText, i, 1)))
                    Next
                End If
            If InStr(oText, "99") = 0 Then
                oText = ""
                oComPort(oCallP2).Close()
                oComPort(oCallP2).Open()
                If oCallP2 = 1 Then
                    oMess1 = "99/0/0/0" 
                    oComPort(oCallP2).WriteLine(oMess1)
                End If
                Thread.Sleep(100) 
                oText = oComPort(oCallP2).ReadExisting()
                If oText = "" Then
                    Thread.Sleep(50) 
                    oText = oComPort(oCallP2).ReadExisting()
                End If
            End If
            ComsRead = oText
        Catch ex As Exception
        End Try
    End Function
 
    Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs)
        Try
            Dim sp As SerialPort = CType(sender, SerialPort)
            Dim indata As String = sp.ReadExisting()
            oActivText = indata
        Catch ex As Exception
            oActivText = "Error DataReceivedHandler"
        End Try
    End Sub
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
06.03.2025, 17:09
Ответы с готовыми решениями:

Visual studio 2019. Компонент SerialPort, почему не активен, не добавляется?
Добрый день. Установил Visual studio 2019. Пишу программу для работы с последовательным портом. В примерах, ктр видел, люди перетаскивают...

Visual studio 2019. Элемент serialport, почему нет метода getportnames?
В общем установил я вижуал студио 2019 и решил накидать простую программку для работы с серийным портом но оказалось что метод с помощью...

Почему пропадают элементы в конструкторе формы?
Добрый день. Не могу понять почему пропадают элементы в конструкторе формы. Что делал до этого: У меня есть форма с необходимыми...

61
Модератор
1245 / 676 / 292
Регистрация: 10.11.2019
Сообщений: 1,406
19.04.2025, 17:40
Лучший ответ Сообщение было отмечено Yury Komar как решение

Решение

Студворк — интернет-сервис помощи студентам
С кодировками действительно оказалась проблема - по умолчанию стоит ASCII и все русские символы
превращаются в знаки вопроса '?' Вот какой рабочий пример получился (на форме два TextBox, SerialPort,Timer)

VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Imports System
Imports System.Text
Imports Microsoft.VisualBasic
 
Public Class Form1
    Dim txt As String, ready As Boolean
 
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim d As Integer
        If Not SerialPort1.IsOpen Then Exit Sub
        While SerialPort1.BytesToRead > 0
            d = SerialPort1.ReadByte ' принять байт из порта
            If d <> 10 Then ' если это не конец строки
                txt = txt + Chr(d)
            Else ' если конец строки
                TextBox2.Text = txt : ready = True
            End If
        End While
    End Sub
 
    Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
        If e.KeyChar = Chr(13) Then ' если нажата Enter
            Timer1.Enabled = False
            TextBox2.Text = "" : txt = ""
            ready = False
            Timer1.Enabled = True ' включить таймер для принятия данных
            e.Handled = True
            ' для правильной работы Write необходимо установить Encoding (см ниже)
            SerialPort1.Write(TextBox1.Text + Chr(10)) ' выдать в порт текст и конец строки
            TextBox1.Text = ""
            While Not ready ' ожидать получения ответа из порта
                My.Application.DoEvents()
            End While
            Timer1.Enabled = False
        End If
    End Sub
 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' без этой установки считает рабочей 7-битную ASCII и вместо русских букв
        ' посылает и возвращает символы '?'
        ' 1251 - русские 8битные буквы в Windows
        Dim encod As Encoding = Encoding.GetEncoding(1251)
        SerialPort1.Encoding = encod
        SerialPort1.Open() ' открыть порт
    End Sub
End Class
Программа для Arduino (текст для OLED дисплея можно убрать или заменить на вывод на другой дисплей)

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// библиотека для работы I²C
#include <Wire.h>
#include <iarduino_OLED_txt.h> // Подключаем библиотеку iarduino_OLED.
iarduino_OLED_txt myOLED(0x3C); 
 
char sbuf[9];
 
void setup()
{
Serial.begin(9600); // Start serial communication at 9600 bps 
myOLED.begin(&Wire); // &Wire1, &Wire2 ...
myOLED.setFont(MediumFontRus);
myOLED.setCursor(8,1);myOLED.print("++");
}
 
 
void loop()
{unsigned char d;
while (Serial.available()) { // If data is available to read,
    d = Serial.read(); // read it and store it in d 
    Serial.write(d);
    myOLED.setCursor(8,1);
    sprintf(sbuf,"%02X",d);myOLED.print(sbuf);}
delay(100);}
Добавлено через 6 минут
Пересылку всех произвольных байт с кодами >=128 пока не проверял, может какие-то байты опять не работают.
1
Модератор
1245 / 676 / 292
Регистрация: 10.11.2019
Сообщений: 1,406
20.04.2025, 13:14
Проверил, при использовании команд

VB.NET
1
2
Dim encod As Encoding = Encoding.GetEncoding(1251)
SerialPort1.Encoding = encod
все байты от 0 до 255 передаются и принимаются нормально.
Но тогда возникает вопрос: а как работать с нерусскими версиями Windows ?
Или как правильно использовать .Write при передаче байт, а не текста ?
0
Модератор
1245 / 676 / 292
Регистрация: 10.11.2019
Сообщений: 1,406
08.08.2025, 17:38
Случайно нашёл ответ на этот вопрос. Достаточно написать:

VB.NET
1
2
Dim encod As Encoding = Encoding.Default
SerialPort1.Encoding = encod
1
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
16.08.2025, 08:33  [ТС]
Цитата Сообщение от qbfan Посмотреть сообщение
С кодировками действительно оказалась проблема - по умолчанию стоит ASCII и все русские символы
превращаются в знаки вопроса '?' Вот какой рабочий пример получился (на форме два TextBox, SerialPort,Timer)
Представленный вариант кода достаточно стабилен, но для понимания начинающих, следует отметить, что в Form1_Load для SerialPort1 стоит указывать наименование порта и другие параметры...
Отмечу тот момент, что время процедуры запрос-ответ, в данном варианте кода, у меня выдавал 105...115 мс (при скорости 250000, понятно что на эту величину влияет размер сообщения).
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
17.08.2025, 07:50  [ТС]
К чему я в предыдущем посте привел цифры времени: привожу пример кода, к которому пришел я. Не утверждаю что в нем нет изъянов, и не претендую на исключительную правильность, при тех же исходных данных этот код дает затрату времени на оберацию порядка 40...65 мс:

VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    Private _serialPort As SerialPort ' для активного порта, который должен получить ответ
    Public oReadPort As String ' переменная для чтения ответа из COM-порта
    Private bErr As Boolean = False ' глобальный маркер ошибки в раб проги  False - норм, True - ошибка
    Private bContinue As Boolean ' переменная  для повторение попыток получения ответа из потока
    Private readThread As Thread ' переменная для отдельного потока, в котором будет работать считыватель ответов Com-порта
 
    <DllImport("user32.dll")> Shared Function GetAsyncKeyState(ByVal vKey As System.Windows.Forms.Keys) As Short   ' получение событий клавиатуры в процессе выполнения основного потока!
    End Function  
 
    Sub...
    ... объявление порта и отрытие
                    Dim t1 = Now.TimeOfDay ' время начала оберации
                        oReadPort = "" ' обнуляем переменную для чтения ответа из Com-порта
                        Dim oWritePort As String = "32/54/76" ' & vbCrLf ' текст сообщения
                        bContinue = True
                        readThread = New Thread(AddressOf Read)
                        readThread.Start()
                        _serialPort.WriteLine(oWritePort) 
                        Thread.Sleep(3) 
                        While bContinue
                            If InStr(oReadPort, "32/") > 0  Then
                                bContinue = False
                            End If
                            System.Windows.Forms.Application.DoEvents() 
                            Thread.Sleep(1)
                            If GetAsyncKeyState(Keys.Escape) <> 0 Then ' при нажатии на клавиатуре Esc - этот цикл прерывается
                                bContinue = False
                            End If
                        End While
                        ListBox1.Items.Add( "___" & oReadPort) ' Выводим в ListBox отправленную команду и ответ из порта
                    Dim t2 = Now.TimeOfDay ' время конца оберации
                    Dim t3 = t2.Subtract(t1)  ' продолжительность оберации
    ... прочий код обработки ответа
    End Sub
 
 
    Private Sub Read() ' метод получения ответа от Com-порта
        While bContinue
            Try
                Dim NewMess As String = ""
                NewMess = _serialPort.ReadLine()
                If NewMess <> "" Then
                    If Me.InvokeRequired Then
                        Me.Invoke(New Action(Of String)(AddressOf MessAdd), NewMess)
                    Else
                        oReadPort = NewMess
                    End If
                End If
                If GetAsyncKeyState(Keys.Escape) <> 0 Then ' при нажатии на клавиатуре Esc - этот цикл прерывается
                    bContinue = False
                End If
            Catch ex As Exception
                Debug.Print(ex.ToString)
            End Try
        End While
    End Sub
 
    Private Sub MessAdd(newValue As String)
        If Me.InvokeRequired Then ' Проверка, нужно ли использовать Invoke
            Me.Invoke(New Action(Of String)(AddressOf MessAdd), newValue)
        Else
            bContinue = False
            oReadPort = newValue
        End If
    End Sub
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
17.08.2025, 11:54
Цитата Сообщение от filat18 Посмотреть сообщение
If Me.InvokeRequired Then
Такая проверка дважды совершенно не нужна.
Запись и чтение из порта без "арбитра", причём ещё и из разных потоков, теоретически ведёт к проблемам. Да и наличие Thread.Sleep и DoEvents как бы намекает на проблемы.
Для стабильности принятия данных из порта нужно чтобы обратные вызовы были не блокирующими поток. Для реализации такого можно использовать SynchronizationContext и его метод Post. Метод _serialPort.WriteLine следует обернуть в методы "арбитра", который будет разруливать очерёдность работы с портом (чтение/запись по очереди) если протокол обмена запрос-ответ. Он же (арбитр) может следить за целостностью данных. Например, если ответ не принят полностью, то ожидать принятия данных и блокировать запись в порт. Как только данные получены полностью или не получены за установленный тайм-аут разрешать запись в порт. Если данные получены не полностью и истёк тайм-аут, то убивать "сломанный" пакет данных и поднимать соответствующий флаг.

Не по теме:

Работы не початый край.

1
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
22.08.2025, 11:28  [ТС]
Цитата Сообщение от Uswer Посмотреть сообщение
использовать SynchronizationContext и его метод Post. Метод _serialPort.WriteLine следует обернуть в методы "арбитра", который будет разруливать очерёдность работы с портом (чтение/запись по очереди) если протокол обмена запрос-ответ.
к сожалению я полностью не понимаю что такое SynchronizationContext Как "оборачивание" WriteLine влияет на ReadLine?
Цитата Сообщение от Uswer Посмотреть сообщение
Он же (арбитр) может следить за целостностью данных. Например, если ответ не принят полностью, то ожидать принятия данных и блокировать запись в порт. Как только данные получены полностью или не получены за установленный тайм-аут разрешать запись в порт.
вот это тоже не понятно - как реализуется методом Post (про оборачивание - понятно. не понятно как этот метод влияет на описанные процессы)?
0
 Аватар для Andrey-MSK
3390 / 2276 / 388
Регистрация: 14.08.2018
Сообщений: 7,696
Записей в блоге: 4
22.08.2025, 12:12
Цитата Сообщение от Uswer Посмотреть сообщение
Для реализации такого можно использовать SynchronizationContext и его метод Post.
Если следующая строчка кода зависит от выполнения синхронизированного кода, то лучше использовать синхронный метод Send(), тогда будет гарантия выполнения задания и последующий код будет работать с реальными данными. Метод Post() асинхронный и после его вызова следующая строка выполняется сразу, не ожидая выполнения синхронизированного кода, а это чревато трудноуловимыми ошибками...
Использование метода Send() не вешает основной поток, всё происходит как нужно при асинхронных операциях, но гарантировано даёт согласованность действий.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
22.08.2025, 12:38  [ТС]
Стоит еще обратить внимание на такой момент: приведенный вариант кода, с прежними исходными данными, дает скорость на одну оберацию 5...7 мс:
VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
Imports System.IO
Imports System.IO.Ports
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading
Imports System.Windows.Forms.VisualStyles.VisualStyleElement
 
 
Public Class Form1
 
    WithEvents _serialPort As SerialPort ' для активного порта, который должен получить ответ
    Public oReadPort As String ' переменная для чтения ответа из COM-порта
    Private bErr As Boolean = False ' глобальный маркер ошибки в раб проги  False - норм, True - ошибка
    Private bContinue As Boolean ' переменная  для повторение попыток получения ответа из потока
    Private readThread As Thread ' переменная для отдельного потока, в котором будет работатьсчитыватель ответов Com-порта
 
    <DllImport("user32.dll")> Shared Function GetAsyncKeyState(ByVal vKey As System.Windows.Forms.Keys) As Short   ' получение событий клавиатуры в процессе выполнения основного потока!
    End Function    'в саб If GetAsyncKeyState(Keys.Down) <> 0 Then ...
 
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Button2.Enabled = False
        Button3.Enabled = False
        Dim oPorts As String() = SerialPort.GetPortNames()
        For Each oName In oPorts
            If oName <> "COM1" Then
                ComboBox1.Items.Add(oName)
            End If
        Next
    End Sub
    Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
        bContinue = False
        Timer1.Stop()
        Try
            Button2.Enabled = True
            If Not _serialPort Is Nothing Then
                _serialPort.Dispose()
            End If
            _serialPort = Nothing
            _serialPort = New SerialPort(ComboBox1.Text)
            With _serialPort
                .BaudRate = 250000
                .Parity = Parity.None
                .StopBits = StopBits.One
                .DataBits = 8
                .Handshake = Handshake.None
                .RtsEnable = True
                AddHandler .DataReceived, AddressOf _serialPort_DataReceived
                .Open()
            End With
            Dim encod As Encoding = Encoding.Default
            _serialPort.Encoding = encod
            Button4.Enabled = True
            If _serialPort.IsOpen = False Then
                ListBox1.Items.Add("порт " & ComboBox1.Text & " не удалось открыть")
                '_serialPort.Open()
            End If
        Catch ex As Exception
            Button2.Enabled = False
        End Try
    End Sub
 
    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Label4.Text = ""
        ListBox1.Items.Clear()
        Button4.Enabled = False
        If _serialPort IsNot Nothing Then
            If _serialPort.IsOpen = True Then
                Try
                    Dim t1 = Now.TimeOfDay
                    For i = 1 To CInt(TextBox1.Text)
                        Timer1.Interval = 100
                        Timer1.Start()
                        oReadPort = "" ' обнуляем переменную для чтения ответа из Com-порта
                        Dim oWritePort As String = "32/54/76" ' & vbCrLf ' Команда для отправки в порт
                        bContinue = True
                        _serialPort.WriteLine(oWritePort) '
                        While bContinue
                            System.Windows.Forms.Application.DoEvents() ' позволяет таймеру срабатывать в пределах циклв While
                            If InStr(oReadPort, "/_ v") > 0 Or InStr(oReadPort, "N") > 0 Or InStr(oReadPort, "Y") > 0 Then ' If InStr(oTextd, "3") > 0 Then
                                bContinue = False
                            End If
                            If Timer1.Enabled = False Then
                                bContinue = False
                            End If
                            If GetAsyncKeyState(Keys.Escape) <> 0 Then ' при нажатии на клавиатуре Esc - этот цикл прерывается
                                bContinue = False
                            End If
                        End While
                        ListBox1.Items.Add(i.ToString & "___" & oReadPort) ' Выводим в ListBox отправленную команду и ответ из порта
                    Next
                    Dim t2 = Now.TimeOfDay
                    If bContinue = False Then
                        Dim t3 = t2.Subtract(t1)
                        Label4.Text = t3.ToString
                    Else
                        Label4.Text = "Нет ответа"
                    End If
                Catch ex As Exception
                    bErr = True
                    Label4.Text = "Ошибка: " & ex.Message
                End Try
            Else
                Label4.Text = "Порт не открыт"
            End If
            '_serialPort.Dispose() ' освобождаем ресурсы, если они были ранее инициализированы
        Else
            Label4.Text = "Порт не инициализирован"
        End If
        Timer1.Stop()
        Button4.Enabled = True
    End Sub
 
    Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick ' ограничивает время цикла запросов
        ListBox1.Items.Add("Timer Tick")
        Timer1.Stop()
    End Sub
 
    Private Sub _serialPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles _serialPort.DataReceived
        Try
            Dim sp As SerialPort = CType(sender, SerialPort)
            Dim indata As String = sp.ReadExisting()
            'Debug.Print(indata)
            Me.Invoke(Sub() If oReadPort = "" Then oReadPort = indata) ' Example for updating UI from another thread
        Catch ex As Exception
            Me.Invoke(Sub() oReadPort = "err")
        End Try
    End Sub
 
    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        Try
            If _serialPort IsNot Nothing Then
                If _serialPort.IsOpen = True Then
                    _serialPort.Close()
                End If
            End If
        Catch ex As Exception
            ' Обработка исключения, если необходимо
        End Try
    End Sub
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Try
            If _serialPort IsNot Nothing Then
                If _serialPort.IsOpen = True Then
                    _serialPort.Close()
                End If
            End If
        Catch ex As Exception
            ' Обработка исключения, если необходимо
        End Try
    End Sub
P.S. скорость оберации получена без учета возможного влияния (если подключено несколько портов) одного порта на другие....

Добавлено через 24 минуты
На данный момент у меня получается, что я разрушил все фундаменты, на которых собирался построит программу

Предполагался следующий алгоритм действий для реализации:
1. Программа работает с динамическим количеством портов SerialPort, доступным при запуске программы и сообщившем ей уникальный идентификатор для общения.
2. Программа из файла Excel считывает таблицу адресов отправки сообщений.
3. Программа, в зависимости от адреса, общается с SerialPort в двух вариантах: в один порт отправляет сообщение и из него получает ответ. Второй вариант сообщения в 2 порта: 1 - подать сигнал; 2 - проверить получение сигнала и отправить соответствующее сообщение о результате в программу.
4. Наверное, нужен отдельный поток по обработке полученного результата и его выводе на форму и в Excel. Допускаю, что этот поток может отставать от процесса общения с портами...
Как это всё разбросать по переменным/ классам/потокам/...? чтоб все работало стабильно и быстро?
0
Модератор
Эксперт .NET
 Аватар для Yury Komar
4372 / 3442 / 512
Регистрация: 27.01.2014
Сообщений: 6,266
22.08.2025, 14:40
filat18, Я делал как-то прием всех сообщений (а это непрерывный поток) в отдельном потоке, этот поток тупо их собирал в очередь последовательно, а уже другой поток, при наличии в очереди тех самых сообщений, начинал эту очередь перебирал последовательно и обрабатывал.
Почему так делал? Потому, что скорость приема была быстрее, чем обработка данных с отрисовкой результатов. Разнича была примерно в одну секунду, и за эту секунду, без очереди принятых сообщений, часть данных просто терялась, пока обрабатывалось предыдущее, 2/3 сообщения пролетали мимо, и поэтому решил собирать ВСЁ и обрабатывать уже ВСЕ принятые ненапрямую из порта, а из очереди... Очень, на мой взгляд, удачное решение было для моего случая.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
23.08.2025, 07:28  [ТС]
Цитата Сообщение от Yury Komar Посмотреть сообщение
прием всех сообщений (а это непрерывный поток) в отдельном потоке, этот поток тупо их собирал в очередь последовательно,
Можно подробней описать реализацию этого решения? Сообщения уходили в порты из этого же потока?

Добавлено через 33 минуты
У меня пока не складывается общая конфигурация алгоритма увязки коллекции портов с механизмом подключения события чтения? Если можно, добавьте пожалуйста максимум подробностей: на какой этапе и какие элементы должны появляться, и куда передаваться?
0
Модератор
Эксперт .NET
 Аватар для Yury Komar
4372 / 3442 / 512
Регистрация: 27.01.2014
Сообщений: 6,266
23.08.2025, 11:39
filat18, Ну вот то, что вы сейчас делаете, тоесть по получению данных из порта - выполняете что-то (в подробности вашего кода не заглядывал, поэтому незнаю, что именно у вас там происходит) и после завершения выполнения процедуры, обрабатывающей принятые данные, снова слушаете порт и так далее по кругу...я так понимаю.

Так вот поставьте один поток непрерывно слушать порт, без остановок собирая приходящие сообщения в очередь (можно взять готовый класс в составе . NET "queue", а уже другой поток запускайте, ктороый будет поочереди в порядке получения брать каждое сообщение и обрабатывать (слать в другие порты или же пользователю показывать результат).
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
23.08.2025, 13:26
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Если следующая строчка кода зависит от выполнения синхронизированного кода, то лучше использовать синхронный метод Send()...
Так-то оно так, но я имел ввиду другое. А именно то, что подробно описал Yury Komar. То есть для работы с сом-портом (чтения/записи в порт) нужно выделить отдельный поток и, чтобы данный поток не пропускал данные, методы "выгрузки" данных должны быть асинхронными.
Как я вижу алгоритм оооочень приблизительно.
1. Создаётся поток чтения сом-порта.
2. В бесконечном цикле (естественно с проверкой CancellationToken или чего-то подобного) ведётся чтение порта, но только после запроса в порт.
3. После получения порции данных они либо сразу отправляются в потокобезопасную коллекцию, либо собираются во вспомогательной коллекции. Это нужно для контроля целостности данных. После получения полного ответа (для второго случая) он помещается в потокобезопасную коллекцию. И вот именно тут нужен неблокирующий вызов, т.к. коллекция может быть занята на чтение.
4. Далее поток переходит к ожиданию или отправке, если он уже есть, запроса в порт.
Для организации очереди запросов, хранения ответов, арбитража запрос/ответ и контроля целостности данных нужен класс инкапсулирующий всю эту логику. Именно такой класс я имел ввиду называя его "арбитр" в сообщении выше.

Тоже самое на пальцах:
Мы поток работы с сом-портом.
- ожидаем разрешающий сигнал (например от семафора) на чтение запроса из очереди запросов.
- получив сигнал выгружаем запрос из очереди. При необходимости преобразуем его в пакет данных понятный устройству на линии сом-порта.
- отправляем запрос в порт.
- разрешаем чтение из порта просигналив соответствующим семафором.
- метод чтения из порта блокирует отправку запросов в порт, например семафором.
- в цикле читаем порт и складываем данные в переменную или свойство "арбитра". Одновременно с этим проверяем получение пакета данных и отсчитываем тайм-аут.
- если пакет получен полностью завершаем чтение из порта при этом создаём разрешающий сигнал на чтение запроса из очереди запросов. Если истекает тайм-аут, а ответ не получен полностью, то генерируем либо исключение, либо событие об ошибке, а далее завершаем чтение точно также как в предыдущем предложении.
При получении пакета данных он может быть преобразован в ответ "высокого уровня". Таким образом у нас есть два протокола: один низкоуровневый (на уровне сом-порта), другой высокоуровневый на уровне нашей программы. Причём стандартизируются как запросы так и ответы.
1
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
24.08.2025, 22:58
Накидал пример "арбитра", в котором реализованы попеременная запись команд и приём данных. В примере два варианта обработки полученных данных. Первый через асинхронный вызов делегата, второй при помощи события (с блокировкой потока чтения). Второй вариант закомментирован. Оба варианта потокобезопасны.
В примере нет:
- самих чтения и записи данных в порт (только имитация);
- какой либо обработки команд и ответов (парсинг, контроль целостности и т.п.);
- слежения за тайм-аутом ответа;
Вложения
Тип файла: zip ProtokolBroker.zip (320.2 Кб, 12 просмотров)
2
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
25.08.2025, 09:58
Взглянув со свежей головой на код я думаю, что совершенно излишне я разделил чтение и запись по потокам. Так как протокол обмена "запрос-ответ", то процедуры записи и чтения можно запихнуть в один поток. То есть код можно значительно упростить и сократить.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
29.08.2025, 13:12  [ТС]
Чуть подправил, добавил строку закрытия порта:
VB.NET
1
2
3
4
                If ExitSignal Then
                    _Port.Close()
                    Exit While
                End If
При подключении на реальный порт, у меня получается, что есть случаи, когда ответ не приходит, а добавляются ответы подряд... Получается, я не могу определить, на какой запрос не был получен ответ? Как соотнести на какой запрос пришел ответ? Заставить микроконтроллер возвращать с ответом и текст запроса на который дан ответ?
0
Эксперт .NET
 Аватар для Rius
13685 / 7865 / 1702
Регистрация: 25.05.2015
Сообщений: 23,880
Записей в блоге: 14
29.08.2025, 13:26
Послать запрос и дождаться ответа в течение определённого интервала времени.
Если ответ пришёл спустя интервал, когда хочет, то исправлять программу в МК.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
29.08.2025, 13:45  [ТС]
И еще столкнулся с моментом когда процесс отправки стопорится (ни отправка, ни чтение не работают). Окно программы закрыть можно, но отдельный поток продолжает висеть... А в окне вывода пишет: "Программа "[12732] ProtokolBroker.exe" завершилась с кодом 4294967295 (0xffffffff)."
0
 Аватар для Andrey-MSK
3390 / 2276 / 388
Регистрация: 14.08.2018
Сообщений: 7,696
Записей в блоге: 4
29.08.2025, 13:51
Цитата Сообщение от filat18 Посмотреть сообщение
Окно программы закрыть можно, но отдельный поток продолжает висеть...
Обрабатывайте закрытие окна и в обработчике ожидайте завершения потока, либо останавливайте его с освобождением ресурсов, если результат не важен.
0
8 / 7 / 4
Регистрация: 03.12.2020
Сообщений: 342
29.08.2025, 14:35  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Обрабатывайте закрытие окна...
VB.NET
1
broker.Dispose()
не завершает поток, хотя отлично это делает при нормальной работе программы.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
29.08.2025, 14:35

Почему пропадают созданные ячейки таблицы?
Проблемка в удалении данных из контролла (к примеру из таблицы) при обновлении страницы (например по нажатии кнопки). Подскажите как...

Serialport.write и serialport.basestream.write - в чем разница
в чем разница между следующими способами отправки данных на ком-порт?: serPort.BaseStream.Write(data,0,datalen); ...

C# SerialPort Неполный прием сообщений
Добрый день. Долго искал решение своей проблемы, но поиски оказались тщетны. Задача: организовать обмен сообщениями между железом и...

Почему не приходит сообщение группе SignalR?
Методы из хаба. public void UserConnect(string message) { Clients.Group(&quot;managers&quot;).notifyManager(Context.ConnectionId, message);...

Почему в отправляемом почтовом сообщении появляются вопросики?
Все русские символы в отправляемом почтовом сообщении почему-то заменены на вопросики? Может надо указывать кодировку письма? Если да, то...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru