Помогите совокупить VB6 и Arduino/

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
bma2bma
Начинающий
Начинающий
 
Сообщения: 10
Зарегистрирован: 12.04.2014 (Сб) 5:59

Помогите совокупить VB6 и Arduino/

Сообщение bma2bma » 28.06.2018 (Чт) 18:20

Добрый день! Обращаюсь к нашим гуру, поскольку делаю что-то не так, но что - не могу понять.
Стоит задача средствами VB6 через порт прочитать строку, посланную с Ардуино, произвести с ней некие манипуляции и отправить результат обратно в Ардуино.

КодАрдуино:
Код: Выделить всё
String StringIN;
void setup()
{
    Serial.begin(9600);
}
void loop()
{
    while(Serial.available()==0)
    {
    }
    StringIN = Serial.readString();
    Serial.print(StringIN);
    Serial.println();
}


Контроллер ждет, пока содержимое порта будет не пустым, считывает находящуюся там строку, отсылает ее же в порт и снова ждет, пока в порту что-нибудь не появится. И так по кругу. На мониторе Arduino IDE напрямую эта процедура отрабатывается штатно, четко и без потерь.

Код VB6:

Код: Выделить всё
Private Sub Form_Load()

mscComm1.CommPort = 3
mscComm1.Settings = "9600,N,8,1"
mscComm1.PortOpen = True
mscComm1.RTSEnable = False
mscComm1.DTREnable = False

End Sub

Private Sub Command1_Click()

mscComm1.Output = Text1.Text
Do
    StringIn = mscComm1.Input
    If StringIn <> "" Then Exit Do
Loop
Text2.Text = StringIn

End Sub

То есть, тут я тоже по клику на кнопке посылаю в порт строку из текстового поля, затем в цикле "Do...Loop" жду, чтобы в порту что-то появилось (ответ Ардуино) и это появившееся выкидываю в другое текстовое поле. Так вот, во втором текстовом поле строка высвечивается то целиком, то фрагментами (кусок сначала, кусок с конца, куски из середины...).

Как программист я очень слабый, поэтому помогите мне решить эту проблему (насколько могу судить, я напортачил в коде Бэйсика).

Заранее спасибо!

[Хакер] :: Код надо оформлять тегом [code], а не [color]. Исправил.
Последний раз редактировалось Хакер 28.06.2018 (Чт) 22:57, всего редактировалось 1 раз.
Причина: Исправление неправильный bb-кодов.

bon818
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 267
Зарегистрирован: 29.08.2009 (Сб) 4:49
Откуда: Ташкент

Re: Помогите совокупить VB6 и Arduino/

Сообщение bon818 » 28.06.2018 (Чт) 23:40

Попробуй так:
Код: Выделить всё
Private Sub mscComm1_OnComm()
Text2.Text = mscComm1.Input
End Sub

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Помогите совокупить VB6 и Arduino/

Сообщение Хакер » 28.06.2018 (Чт) 23:58

Потому что логика работы кода неверная, что со стороны Arduino, что со стороны VB6.

Начнём с того, что точки зрения COM-порта не существует ни понятия «строка», ни понятия «команда», ни понятия «пакет», ни понятия «сообщения». COM-порт, построенный на протоколе RS-232, который в свою очередь построен на протоколе UART, делает не более, чем передачу потока байтов (бесконечного — в теории). Ни один из этих протоколов, ни сам COM-порт понятия не имеют, что эти байты означают (буквы какого-то алфавита? закодированный звук? случайным шум? двоичные команды для гусеничного робота?), а по этой причине COM-порт (и используемые протоколы) не умеют и не предполагают самостоятельно делить бесконечный и «однородный» (по мнению COM-порта) поток байтов на какие-то отдельные строки, сообщения, порции или команды.

Отсюда есть два важных следствия:
  1. Ни со стороны Ардуино метод Serial.readString() не даёт гарантии, что вернёт строку «целиком».
  2. Ни со стороны VB6 свойство mscComm1.Input не даёт гарантии, что вернёт строку «целиком»

Оно так происходит, потому что ни с той, ни с другой стороны не существует самого понятия «целиком». Оба метода возвращают столько данных, сколько есть в приёмном буфере на момент обращения к методу. Сколько байтиков там успело накопиться на момент обращения — это фактор сугубо случайный, зависящий от кучи факторов, и полагаться на него не стоит.

Что же делать?

Так вот, надо вернуться назад к осознанию того факта, что с точки зрения использования COM-порта на уровне протокола RS-232 не существует понятия «отдельной строки» или «отдельной команды», а есть просто непрерывный поток байтов, в котором COM-порт не видит никаких разделителей, разграничителей, используя которые COM-порт мог бы выделить из общего потока отдельные «сообщения»/«пакеты»/«команды»/«строки».

Вместо этого разработчик должен озаботиться этим моментом и придумать протокол обмена между двумя устройствами. Придумать единые «правила игры» для обоих устройств, которые будут отражены в коде обоих устройств. Речь идёт не о том, что надо придумывать какой-то протокол вместо RS-232, а о том, что надо придумывать протокол, который будет работать поверх RS-232.

Собственно, сам протокол и будет определять:
  • и структуру потока данных — состоит ли он из каких-то отдельных блоков, фрагментов, пакетов, сообщений, команд, и если да, то имеют ли эти сообщения фиксированную длину или нефиксированнуюю.
  • И если структура имеется — то с помощью каких средств (а они могут быть разными) осуществляется деление непрерывного потока на блоки/пакеты/команды.

Разработчик сам должен решить эти вопросы, и здесь он волен выбирать и выдумать что угодно. Например, если к компьютеру подключено устройство, имеющее в себе датчик температуры, которое шлёт через COM-порт показания 3 раза в секунду (или 30 раз в секунду) в виде одного единственного байтика, то понятно, что никаких пакетов/сообщений нет, нужно просто каждый отдельно принятый байт при получении рассматривать как отдельно взятое показание, считанное и отправленное нам.

Тот же термодатчик может слать свои показания, но кодируя каждый замер не одним байтом, а например 10-байтным блоком. Это уже случай с блоками/пакетами/сообщениями, но в этом случае мы имеем фиксированный размер блока, поэтому на получающей стороне нам нужно просто принимать и накапливать принимаемые байты, и когда число принятых байтов станет >= 10, нужно будет взять накопленные 10 байтов и обработать как принятый цельный блок и изъять из «накопителя». При этом в накопитель может разом попасть скажем 36 байт — это значит что нужно три раза последовательно брать, обрабатывать и убирать из накопителя три 10-байтных блока, после чего в блоке останется 6 байтов, а нам останется ждать, когда придут ещё 4. Впрочем, придтим могут не 4, а лишь 2 (и тогда придётся вновь ждать), либо сразу 7, и тогда в накопителе сначала окажется 13 байтов, потом первые 10 мы обработаем как пакет, останется 3, и бы опять будем ждать.

Совершенно другое дело, если данные нам приходят пакетами, но пакеты имеют не фиксированный размер (длину в байтах), а переменный. В этом случае протокол должен предусматривать какой-то способ определять, где кончается один пакет и начинается другой. Именно разработчик протокола должен придумать этот способ.

Какие это могут быть способы?
  • Например, в начале пакета может указываться его длина. В простейшем случае: первый байт пакета может означать длину всего пакета, в этом случае после приёма первого байта (пусть он равен N) останется накопить в приёмнике-накопителе ещё N-1 байтов, и когда все N окажутся в приёмнике, можно будет обработать накопленное как единый цельный пакет и выбросить из накопителя (оставив там то, что успело накопиться после целиком принятного фрагмента). В более сложном случае длина не обязательно должна быть в первом байте пакета, и не обязательно должна выражаться одним байтом. У пакета может быть заголовок постоянной длины, в составе которого может быть поле (например с 4-го по 8-байт), состоящее из скольки-то байтов (1, 2, 4, 8 или сколько угодно), в котором бы хранилась длина всего пакета целиком, или же длина только той части пакета, которая имеет переменную длину и идёт после обязательного заголовка. Этот способ имеет один недостаток — на момент начала отправки пакета отправитель должен уже знать размер пакета, а не всегда отправитель знает размер на момент начала передачи пакета, бывает что «облик» пакета окончательно формируется только после того, как часть пакета уже была передана. Ярчайший пример: набор текстового сообщения/команды с клавиатуры терминала, мы не можем в начале каждого текстового сообщения передавать его длину, если пользователь прямо сейчас пишет текст и заранее неизвестно, когда он закончит его писать.
  • Подход, который нашёл применение в терминалах, и, как следствие, используется в протоколе связи, к примеру, модемов и компьютеров. Длина пакета нигде не передаётся — вместо этого протоколом определяется (по вкусу того, кто выдумывает протокол) какой-то маркер конца сообщения. Маркером конца сообщения может быть или какой-то один байт, имеющий определённое значение, или же последовательность из нескольких байтов. В случае с терминалами, каждый байт определяет какой-то символ введённого текста, концом же «сообщения»/«пакета» считается символ перевода строки, а значит байт с кодом 13 (или с кодом 10, а может быть и не байт, а лишь последовательность из байтов 13 10 — всё из-за того, что в разных системах разный подход к тому, какой символ означает конец строки многострочного текста).

    В этом случае приёмник копит байты до тех пор, пока не встретит байт с кодом 13 (или 10, или 13-10, или что-то другое, например байт с кодом 0, и как только в приёмнике оказался такой байт, то всё что до него — считается как одно целое («пакет»/«команда»/«сообщение») и как-то обрабатывается (например, если это команда, то анализируется и выполняется), а всё что больше — продолжает лежать в накопителе до тех пор, пока там не появится следующий маркер-разделитель.

    При этом получается, что пока человек пишет команду, символы передаются по последовательному порту, стоит же ему нажать Enter, код символа перевода строки тоже передаётся через последовательный порт и принимающим устройством воспринимается как маркер конца «команды» и сигнал к действию.

    Это, пожалуй, самый распространённый вариант протокола связи, предполагающего что одним из передающих может быть человек, сидящий за клавиатурой/терминалом. Не обязательно, конечно, человек, но для человека такой механизм работы (набрал команду и нажатием Enter завершил её ввод и запустил её выполнение) удобен и естественен, а для компьютера/устройства такой протокол не сложен и почти не обременителен.
  • Совершенно другой подход: это конец команды/пакета/сообщения/порции показывать с помощью линий сигналов квитирования (RTS/CTS/DTR), которые предусмотрены протоколом RS-232.

Так вот, прежде чем вообще писать какой-то код для принимающего/передающего устройства (не важно — компьютер это или плата), нужно с осознанием того, что сам RS-232 поток байтов не делит на фрагменты/блоки/команды ни по какому принципу (кроме принципа «сколько влезло в приёмный буфер самого COM-порта, столько и отдали»), сесть и придумать/разработать протокол обмена между двумя агентами (назовём их так). Можно придумать что-то полностью своё, ни на что не похожее. Можно взять за основу существующий вариант, например тот принцип, что применён в модемно-терминальной связи.

Всё что я выше сказал, относится к принципу проектирования/программирования таких систем в целом. Безотносительно выбора языков программирования, программных и аппаратных платформ. Хоть пусть бы оно на ассемблере с обеих сторон писалось, принцип был бы тот же. Придумываем протокол (желательно его оформить в виде текста/документа, чтобы по мере работы не путаться в собственных же правилах игры, нигде не закреплённых), а потом берёмся за реализацию каждой из сторон взаимодействия.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Помогите совокупить VB6 и Arduino/

Сообщение Хакер » 29.06.2018 (Пт) 0:57

А теперь перейдём к конкретике.

Со стороны Arduino я вижу вот такое ожидание того, что что-то пришло:
Код: Выделить всё
    while(Serial.available()==0)
    {
    }

Но надо понимать, что протокол RS-232 не гарантирует, что данные придут сразу и целиком. Он вообще не подразумевает такого понятия как «целиком»: он лишь умеет передавать по одному байту друг за другом. В случае с Arduino, реальная передача идёт через USB. USB пакетный протокол, но нас это мало волнует. Данные, который были отправлены в сторону Arduino, попадают с приёмный буфер чипа FT232RL. Если буфер чипа переполнен, чип в сторону компьютера шлёт сообщения «хватит пока слать», и компьютер временно перестаёт. Вне зависимости от того, переполнился ли приёмный буфер чипа, чип попавшие туда данные начинает по очереди и с чётко установленной скоростью (в нашем случае — 9600) выдавать через UART-интерфейс, а точнее на свою ногу TX, которая подключена к ноге RX микроконтроллера.

У микроконтроллера свой маленький буфер (однобайтный!), и как только все 10 бодов UART-символа получены, в микроконтроллере срабатывает прерывание, обработчик которого должен из маленького однобайтного аппаратного буфера данные срочно забрать, чтобы микроконтроллер мог быть готовым к приёму по UART следующего символа, потому что чип FT232 не будет ждать.

В принципе (теоретически), обработчик прерывания в микроконтроллере (AVR) мог бы не освобождать аппаратный приёмный буфер максимально срочно, а «потянуть резину», а чтобы ничего не потерялось, заставить FT232 временно ничего не передавать ему (ему = МК). Сделать это можно было бы с помощью всё тех же сигналов квитирования (RTS/CTS/RTR/CTR), достаточно было бы на нужном входе квитирования чипа FT232 выставить нужный уровень, и FT232 перестал бы слать МК по UART-у новые байты, а лишь накапливал бы получаемое по USB в своём аппаратном буфере (у FT232 он больше, чем у AVR, но не сильно), а когда аппаратный буфер FT232 заполнился бы, FT232 послал бы компу по USB отмашку, что пока хватит слать, что «пробка»/«затор». МК мог бы сколько угодно тянуть резину, а когда обрабатал бы принятый байт, поменял бы уровень на линии квитирования, и FT232 тут же послал бы следующий байт из своего приёмного буфера, и всё бы повторилось. Но в Arduino этот принцип не применяется и применить его невозможно даже чисто их схемотехнических причин: входы «RTS#», «CTS#» и прочие у чипа FT232 не соединены с пинами МК и МК не имеет рычага воздействия на FT232 и не может заставить этот чип «сделать паузу».

Вместо этого обработчик прерывания Arduino переносит принятый байт из аппаратного буфера в программный и выставляет флаг, что следующий байт может быть принят аппаратно через UART-интерфейс.

Так вот, метод Serial.available() всего лишь возвращает кол-во байт, накопленных в программном приёмном буфере. Если мы послали что-то в сторону Arduino, например послали туда 20 или 2018 байтов, то конечно рано или поздно случится такое, что метод Serial.available() вернёт ненулевое значение, но нет абсолютно никакой гарантии, что он вернёт именно число 20 или число 2018. То, что мы послали в адрес Arudino, может разбиться на куски произвольного размера (для передаче по шине USB). Эти куски могут дойти до чипа с непредсказуемой задержкой, и каждый кусок может иметь непредсказуемый размер. В случае, если мы пошлём 2018 байтов разом (со стороны компа), можно гарантированно сказать, что метод никогда не вернёт число 2018. Хотя бы потому, что столько не может вместить ни программный приёмный буфер Ардуино (его размер — 64 байта), ни аппаратный буфер чипа FT232RL (у этого чипа буферы 256 и 128 байтов на приём и передачу). Поэтому к моменту, когда в буфере окажется ненулевое кол-во байтов и вышеуказанный цикл прервётся, реальное число байтов в буфере может быть любым. И один байт, и 5, и 15, и 7.

И не нужно делать никаких предположений, что когда вот этот цикл с проверкой Serial.available()==0 закончится, мы приняли всё сообщение (всю строку целиком). Никто не даёт таких гарантий, а строка вообще может быть больше, чем способен вместить буфер.

Поэтому, когда со стороны Arduino мы что-то приняли, надо принятое рассматривать как исключительно сырые данные, которые поступили частично. И на наших плечах лежит ответственность за тем, чтобы во-первых сырые фрагменты склеивать в что-то большее, а, во-вторых, чтобы в склеенном находить нечто цельное и обарабывать как цельное. Забегая вперёд скажу, что то же спреведливо и для программирования на стороне ПК.

На практике это означает, что помимо встроенного (и невидимого для нас) буфера Arduino (который всего 64 байта размером) нам скорее всего понадобится свой собственный буфер, который будет больше. В котором мы будем склеивать принимаемые куски, искать в скленном цельное сообщение, блок, команду, пакет в зависимости от способа, который мы установили нашим протоколом.

Когда мне потребовалось с компьютера управлять Arduino-устройством через терминал путём ввода команда вида COMMAND param1, param2, ..., я решил использовать типичный и традиционный протокол, где команда передаётся текстом, а конец команды знаменуется передачей символа перевода строки (т.е. нажатие клавиши Enter в терминале).

Я сделал свой собственный приёмный буфер для команды и счётчик принятых символов:
Код: Выделить всё
char CmdBuffer[64];
unsigned char cchCommand = 0;


и написал вот такой цикл, отвечающий за приём данных от компьютера:
Код: Выделить всё
void loop()
{
    char sym;
    if(Serial.available() == 0) return;
    sym = Serial.read();

    if((sym == '\r') || (sym == '\n'))
    {
        if(cchCommand <= sizeof(CmdBuffer))
        {
            DispatchCommand(CmdBuffer, cchCommand);
        }
        else
        {
            RipError(F("Too long command!"));
        }

        cchCommand = 0;
    }
    else
    {
        if(cchCommand < sizeof(CmdBuffer))
        {
           CmdBuffer[cchCommand++] = sym;
        }
        else
        {
            cchCommand = sizeof(CmdBuffer) + 1;
        }
    }
}


Из этого кода видно, что мы, если в родном приёмном буфере нет вообще ничего (Serial.available() == 0), сразу возвращаемся из функции loop, а если есть (не важно сколько), достаём 1 байт и начинаем его обрабатывать: если это не символ перевод строки, то этот символ пишем в наш собственный буфер (в котором, по мере прихода новых байтов, растёт и формируется строка, означающая команду), если же это символ перевод строки (т.е. нажат Enter и мы получили команду целиком к этому моменту), то мы вызываем функцию DispatchCommand, которая принятую целиком команду обработает, проанализирует и что-то сделает.

Причём, обращаю внимание, что предусмотрена ситуация, что символы некой длинной команды всё приходят и приходят, а символа перевода строки всё никак не поступает, и при таком сценарии мы рискуем исчерпать всё место в нашем собственном буфере, и нам будет некуда писать новые поступающие символы.

На этот случай сделаны в обоих ветках наружного if-условия проверки на переполнение: если пришёл очередной символ команды, а командный буфер уже заполнен до предела, то новый символ никуда не пишется, а просто игнорируется, а счётчик принятых символов (он равен длине принятой команды) устанавливается на величину, на 1 большую, чем размер буфера. Таким значением сигнализируется, что не только буфер заполнен до предела, но и что мы как минимум один символ команды потеряли. Если же место в буфере есть, то новый символ записывается в буфер (приклеивается в конец строки, которая там уже успела накопиться) и счётчик принятых символов увеличивается на 1 (этот же счётчик служит как указатель позиции записи в буфер).

Если же принятый символ соответствует символу перевода строки, мы опять же анализируем: к этому моменту произошло ли переполнение буфера (а значит в буфере мы имеем только начальную часть команды, концовка была вынужденно проигнорирована нами, потому что её некуда было писать), и если так, то тупо жалуемся, что команда была слишком длинной. Если всё нормально, то вызываем функцию, которая уже разделывается с командой.

Хочу заметить, что размер буфера CmdBuffer[64]; в 64 байта никак не связан с тем, что встроенный приёмный буфер имет размер 64 байта. Мой собственный приёмный буфер мог бы быть любого размера, например 220 байт. Просто я сделал его таким, каким сделал, из экономии SRAM и исходя из того, что длинных команд у меня не было.

Также обращаю внимание, что вне зависимости от того, смогла ли функцию DispatchCommand разделаться с командой и выполнить её, и независимо от того, дошло ли вообще дело до вызова этой функции или мы упёрлись в ограничение по размеру буфера для команд, после того, как был принят символ перевода строки и была сделана какая-то реакция на это, мы обнуляем счётчик принятых байтов (он же указатель на позицию следующего свободного символа в приёмном буфере) и после этого наш собственный буфер готов к приёму следующей команды (которая, возможно, в этот момент уже давно лежит в штатном приёмном буфере Arduino или буфере чипа).

Не имеет никакого отношения к вопросу, но всё-таки покажу, как выглядит функция DispatchCommand —она полностью абстрагирована от того, по какому алгоритму происходит приём команд (и вообще, абстрагирована даже от того факта, что команды приходят по UART-у, ибо она принимает на вход уже готовые цельные команды) и занимается синтаксическим разбором команды (либо выводит сообщение «Неизвестная команда»):

Код: Выделить всё
void DispatchCommand(const char* Command, unsigned char cmdLength)
{
   //
   // Skipping space characters
   //

   SkipSpaces(&Command, &cmdLength);

   if(cmdLength == 0) return;

   for(unsigned int i = 0; i < sizeof(rgCommands) / sizeof(rgCommands[0]); i++)
   {
      if(ProbeToken(&Command,
                    &cmdLength,
                    rgCommands[i].pszCmd,
                    rgCommands[i].cchCmd))
      {
         rgCommands[i].Dispatcher(Command, cmdLength);
         return;
      }
   }
   
   RipError(F("Unknown command"));
}


_______________________


Абсолютно аналогичным образом обстоят дела со стороны компьютера и VB6.

Никто (имеется в виду ни протокол RS-232, ни компонент MSComm) не даёт гарантии, что в свойстве Input окажется что-то цельное и законченное, а не какой-то жалкий фрагмент, потому что с точки зрения протокола и компонента (и COM-порта) нет никаких критериев цельности и законченности, есть просто непонятный поток байтов, и байты могут приходить с любыми произвольными задержками между любыми двумя отдельными байтами.

Поэтому в свойстве Input может оказаться сколько угодно байтов, в том числе и 1, а может и 4, даже если со стороны Arduino была отправка строки HELLO WORLD разом (то есть одним вызовом). Всё зависит от скоростей работы устройств, от скоростей передачи, от размеров буферов, от загруженности контроллеров и промежуточных шин (в нашем случае USB). Картина, в целом, непредсказуемая.

Поэтоу правильный подход — это со стороны VB6 точно так же иметь свой собственный буфер. По мере прихода новых порций данных добавлять принятые порции в этот буфер, и в зависимости от того, как протокол устанавливает границы пакетов/фрагментов/сообщений, искать в собственном приёмном буфере где кончается один пакет и начинается другой (либо по маркеру конца пакета/команды/сообщения, либо по длине пакета, указанной в начале пакета — тут всё зависит от протокола).

Поделюсь, опять же, примером. В одном проекте Arudino была в роли сниффера, которая мониторила некую засекреченную шину (типа IIC/TWI) и нужные пакеты отправляла на компьютер, обернув содежимое пакетов в «собственную обёртку». Со стороны компьютера нужно было принимать пакеты уже от Arduino и выводить захваченные сниффером данные/события.

Для этой цели использовался VB6 и MSComm, как наиболее быстрое и простое решение.

  1. Во-первых, в форме, где лежал принимающий MSComm, был сделан приёмный буфер:
    Код: Выделить всё
    Private m_InputBuffer As String
  2. Момент, когда что-то приходило в наш COM-порт — отслеживался, и пришедшее (сколько бы байтов там ни было, пусть даже 1) добавлялся в собственный приёмный буфер (m_InputBuffer). Отслеживался этот момент, конечно, не таким уродским способом, как у тебя, а с помощью события OnComm:
    Код: Выделить всё
    Private Sub ComPort_OnComm()
        m_InputBuffer = m_InputBuffer + ComPort.Input
        CheckIncomingQueue
    End Sub

    Использование события позволяет не крутить бешеный бесконечный цикл Do/Loop, у которого 9999 из 10000 итераций проходят бестолку и который грузит ядро процессора на 100%, а «просыпаться» только тогда, когда есть на что реагировать. Принятое сначала складывается в приёмный буфер, а затем вызывается CheckIncomingQueue, которая проверяет, а не накопилось ли в приёмном буфере (с учётом возможности поступления данных по байту в секунду) достаточной длины сообщения, чтобы как-то всё это можно было обработать.
  3. Сама процедура устроена довольно просто:
    Код: Выделить всё
    Private Sub CheckIncomingQueue()
        Dim pos As Long
       
        Do
            pos = InStr(1, m_InputBuffer, vbNewLine)
            If pos = 0 Then Exit Do
           
            ProcessIncomingMessage Left$(m_InputBuffer, pos - 1)
            m_InputBuffer = Mid$(m_InputBuffer, pos + 2)
        Loop
    End Sub

    Она пытается в принятом найти символ переноса строки, и если он там есть, то всё, что стоит слева от него — считается за целиком принятое сообщение и передаётся в процедуру ProcessIncomingMessage которая уже имеет дело с отдельно взятыми сообщениями (ей ничего не нужно ни склеивать, ни разрезать). Причём, с учётом того, что байты могут идти сначала в час по чайной ложке, а потом привалит целый килобайт данных одним мигом, процедура CheckIncomingQueue делает проверку на наличие символа перевода строки в цикле, исходя из того, что в приёмном буфере разом может оказаться сразу несколько сообщений, например сразу 4 штуки, плюс первые символы пятой (незавершённой). Так что она крутит цикл и по очереди обрабатывает каждое принятое сообщение, пока в буфере не останется ни одного целого сообщения (там может остаться кусок сообщения либо пустота).


Мораль — для правильной работы нужно:
  1. Рабобраться с протоколом (придумать его, если нужно) и чётко установить его
  2. Использовать подход с буфером (работающим как очередь), в конец которого принятые фрагменты (любой длины) дописываются, а из начала которого принятые фрагменты (цельные блоки, сообщения, команды) извлекаются, как только они оказываются полностью сформированными в буфере. Как опредеть момент, когда нечто цельное накопилось в приёмном буфере — всецело зависит от протокола.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

bma2bma
Начинающий
Начинающий
 
Сообщения: 10
Зарегистрирован: 12.04.2014 (Сб) 5:59

Re: Помогите совокупить VB6 и Arduino/

Сообщение bma2bma » 29.06.2018 (Пт) 6:14

bon818 писал(а):Попробуй так

Спасибо большое!

bma2bma
Начинающий
Начинающий
 
Сообщения: 10
Зарегистрирован: 12.04.2014 (Сб) 5:59

Re: Помогите совокупить VB6 и Arduino/

Сообщение bma2bma » 29.06.2018 (Пт) 6:21

Хакер, Вы даже не представляете себе глубину моей благодарности! Ни доли иронии! Уже с первых строк в башке что-то щелкнуло и посветлело. Буду разбираться. Это не быстро. Если что-то недопойму - оставлю за собой возможность вновь обратиться. Еще раз, большое спасибо!

bma2bma
Начинающий
Начинающий
 
Сообщения: 10
Зарегистрирован: 12.04.2014 (Сб) 5:59

Отослал личное сообщение, но в кабинете от не отразилось.

Сообщение bma2bma » 02.07.2018 (Пн) 9:07

Уважаемый Хакер! По-видимому, я пока не имею права на личные сообщения. Отослал Вам его, но никаких следов (чтобы продублировать его здесь) найти не могу. Сделайте мне ОДНО (!) исключение: если возможно, все-таки, посмотрите его.

ger_kar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1957
Зарегистрирован: 19.05.2011 (Чт) 19:23
Откуда: Кыргызстан, Иссык-Куль, г. Каракол

Re: Помогите совокупить VB6 и Arduino/

Сообщение ger_kar » 02.07.2018 (Пн) 19:17

Да, очень полезная информация, которой Хакер так любезно поделился. Я хоть и не озадачен в данный момент такими вопросами, но прочитал с интересом и после прочтения белых пятен в голове по этой тематике стало значительно меньше. Спасибо.
Бороться и искать, найти и перепрятать

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Помогите совокупить VB6 и Arduino/

Сообщение Хакер » 02.07.2018 (Пн) 21:42

bma2bma писал(а):По-видимому, я пока не имею права на личные сообщения.

Нет, все имеют.

Отослал Вам его, но никаких следов (чтобы продублировать его здесь) найти не могу.

Ничего не пришло, а значит ничего не было и отправлено. И вообще: на надо было писать личные сообщения, надо продолжать обсуждение здесь.

bma2bma писал(а):Сделайте мне ОДНО (!) исключение: если возможно, все-таки, посмотрите его.

Ничего не пришло...
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

bma2bma
Начинающий
Начинающий
 
Сообщения: 10
Зарегистрирован: 12.04.2014 (Сб) 5:59

Re: Помогите совокупить VB6 и Arduino/

Сообщение bma2bma » 03.07.2018 (Вт) 8:20

Странно: было сообщение, что отправлено.... Ну ладно, после работы - напишу тут. Еще раз извините.

bma2bma
Начинающий
Начинающий
 
Сообщения: 10
Зарегистрирован: 12.04.2014 (Сб) 5:59

Re: Помогите совокупить VB6 и Arduino/

Сообщение bma2bma » 04.07.2018 (Ср) 7:43

Уважаемый Хакер, по Вашей рекомендации пишу не в личку, а тут.
С приложенным кодом VB я разобрался. Спасибо, Вы предложили именно то, что я примерно и предполагал сделать после штудирования предыдущего роскошного поста. А вот программирование Ардуино я пока крепко в руках не держу, к сожалению. Хоть и читаю и пытаюсь разобраться, но успешно до тонкостей дойти не позволяет, видимо, мой седьмой десяток :D . Теперь конкретика.
Уточню кое-какие позиции хотелок, возможно, это немного конкретизирует и упростит мою задачу. От ПК в Ардуино предполагаю посылать информационную строку в формате «S12345-1-0-1-0F». Длина ее будет постоянной — 15 символов. Для уточнения:
1. S — знак начала команды.
2. 12345 — пятизначное число от 00000 до 10000 (количество микросекунд задержки включения триака после перехода сетевого напряжения через 0). Или это скважность ШИМа (пока не решил, что буду использовать, провожу эксперименты с силовой частью). Но это сейчас не важно.
3. Последующие четыре цифры — 1 или 0 (команды подать соответствующие логические уровни на пины Ардуино).
4. F- знак конца команды.
Понимаю, что тире можно опустить, но так мне будет проще избежать опечаток при формировании команды в VB. Понимаю также, что и "S" в начале строки можно не прописывать (просто отсчитывать слева от "F" нужное количество символов), но причина ее использования та же. Мне кажется, что эти лишние символы буфер не перегрузят.
Насколько я уразумел, нужно из всего вороха появившегося в буфере циклически вычленять эту последовательность и записывать ее в переменную, если такая последовательность найдется. После чего очищать буфер (или не надо очищать?). Далее — выгрызать из переменной соответствующие части и соответственно заставлять Ардуино выполнять нужные действия. С последней задачей (используя оператор substring, наверное?), кодами VB6 и реализацией «распоряжений» на пинах Ардуино я справлюсь. А вот процесс от накопления в буфере содержимого порта, вычленения нужной строки из буфера Ардуино и до записи в переменную — пока для меня не совсем темный, но лес. Ковырялся пару дней, но как-то не получается. Не поможете ли мне именно с этим фрагментом? Только, умоляю, с комментариями: мне будет значительно проще разобраться в идеологии этого фрагмента на Вашем примере кода.
Заранее спасибо!

bma2bma
Начинающий
Начинающий
 
Сообщения: 10
Зарегистрирован: 12.04.2014 (Сб) 5:59

Re: Помогите совокупить VB6 и Arduino/

Сообщение bma2bma » 17.07.2018 (Вт) 12:21

Спасибо всем, особенно Хакеру. задача выполнена, система работает.


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: Yandex-бот и гости: 14

    TopList