Передача данных с Arduino Mega на PC

Здравствуйте. С Arduino работаю недавно плата Mega 2560, пытаюсь организовать поток данных от датчиков на компьютер. На компе. Pycharm и GUI на PySide6. Плата подключена к com3, через монитор порта вижу нормальный поток информации о температуре и т.д. Но когда запускаю GUI и нажимаю кнопку подключить порт в самом начале информация идет по байтно (как в мониторе), но в какой-то момент происходит что-то, что переводит передачу в по битный режим. Подскажите, что происходит и как с этим бороться ? Благодарю.

Откуда же мы можем знать?

Ни вашего кода ардуино, ни программы на питоне никто из нас не видел

int speed = 120;                    // https://www.youtube.com/watch?v=f_rmcND-3jI
bool isLedOn = true;
float distance = 31.6;
float time = speed / distance; 
char letterA = 'f';
String chanel = "SoftWareProjer"; 
int x;
int y = 40;
String name = "Ivan";
String surname = " Smirnov";
String summ_2 = name + surname;  
char myStr1[20];        
String shh = "привет";     // вопрос с передачей кирилици 

void setup()
{
  Serial.begin(9600);
  while (!Serial)             //https://www.youtube.com/watch?v=_RN_Bpze-Jo
  {}
  //Serial.println("HelloComputerHelloComputerHelloComputerHelloComputerHelloComputer");
  //Serial.println(summ_2);     // сбой потока данных в питоне произошел в мемент передачи в порт содерживого переменной summ_2 
}

void loop() {
  int size_bufer = Serial.available();   /* Принимаемые по последовательному порту байты попадают в буфер микроконтроллера, 
                                   откуда Ваша программа может их считать. Функция возвращает количество накопленных в буфере байт. */
  Serial.println("HelloComputerHelloComputerHelloComputerHelloComputerHelloComputer");
  count++;
  x = y * 3;
  y++;
  //Serial.print("x- ");
  //Serial.println(x); 
  //Serial.print("y- ");
  //Serial.println(y); 
  //Serial.println(shh);
  Serial.print("size_bufer- ");
  Serial.println(size_bufer);
  Serial.print("count- ");
  Serial.println(count);

  dtostrf(distance,2,1,myStr1);
  Serial.println(myStr1); 

  delay(5000); ```

В Pithon код намного больше, не стал обрезать. 
функция приема данных из буфера:  
def read_bufer():    

``` import sys
from PySide6.QtCore import QDate, QDateTime              # загружаем необходимые для работы с виджетами методы
from PySide6.QtWidgets import QApplication, QMainWindow, QLCDNumber, QMessageBox, QSlider, QScrollBar, QLabel


from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo
from PySide6.QtCore import QIODevice

from Monitor_Kuzya import Ui_MainWindow    # читаем файл с эскизом GUI в формате .py. Импортируем класс Ui_MainWindow

class Arduino(QMainWindow):
    def __init__(self):
        super(Arduino,self).__init__()           # задаем место расположения окна на экране компьютера (х, у, w, h)
        self.setGeometry(10,100,619, 458)           # w и h взяты из size_convert класс Ui_MainWindow размеры самого окна MainWindow.resize(422, 425)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.dateEdit.setDisplayFormat("yyyy.MM.dd")                         # устанавливаем формат даты в котором мы хотим ее выводить в окно dateEdit
        self.ui.dateEdit.setDate(QDate.currentDate())                           # в окно dateEdit передаем текущую дату с помощью метода QDate.currentDate()
        self.ui.dateTimeEdit.setDisplayFormat("H: mm /// dd.MM.yyyy")           # устанавливаем формат времени и даты в котором мы хотим ее выводить в окно dateTimeEdit
        self.ui.dateTimeEdit.setDateTime(QDateTime.currentDateTime())           # в окно dateTimeEdit передаем текущую дату и время с помощью метода QDateTime.currentDateTime()


        # *** --- РАБОТА С com ПОРТОМ ПК --- *** #
        serial = QSerialPort()                        # создадим объект класса QSerialPort
        serial.setBaudRate(9600)                      # зададим скорость обмена данными для этого объекта
        portList = []                                 # список для хранения доступных портов ПК
        ports = QSerialPortInfo.availablePorts()      # функция возвращает object  доступных портов в системе
        for port in ports:                            # в цикле просматриваем объект
            portList.append(port.portName())          # получаем список доступных портов
            # portList.append(port.description())     # дает информацию по портам, что  к ним подключено
            print('Список доступных портов ПК', portList)

        def open_com():
            print("Кнопка OPEN нажата")
            print("Имя Порта -", self.ui.comboBox.currentText())   # выбираем из списка в comboBoxе имя нужного нам порта
            serial.setPortName(self.ui.comboBox.currentText())     # присваиваем serial имя выбранного порта
            serial.setBaudRate(9600)                               # зададим скорость обмена данными для этого объекта
            serial.open(QIODevice.ReadWrite)                       # открываем порт

        def close_com():
            serial.close()                                         # закрываем порт
            print("Порт: ", self.ui.comboBox.currentText(), "закрыт")

        def serial_send(data):          # передаем на Ar data - список int значений от датчиков
            txs = ""                    # для подачи в буфер создадим пустую строку
            for val in data:            # пройдем циклом по списку data
                txs += str(val)         # текущее значение списка переведем в строковый вид и добавим к строке для буфера
                txs += ','              # разделим элементы списка "," - требуется по нашему протоколу
            txs = txs[:-1]              # уберем из строки последнюю запятую
            txs += ';'                  # добавим в конце строки терминатор ";"
            # print(type(txs), txs)     # вид и тип подготовленного к передаче в порт пакета <class 'str'> 1,84,71,96;
            serial.write(txs.encode())  # отправим в порт подготовленную строку переведя ее в байтовый вид (encode())

        def read_bufer():                    # читаем поступившую с Ar через буфер com порта информацию
            str_in_line = serial.readLine()  # поступившая из буфера строка
            print(str_in_line)
            
        serial.readyRead.connect(read_bufer) # связывает сигнал readyRead() со слотом readData()
                                             # Этот сигнал возникает, когда последовательный порт
                                             # получает новые данные

        # *** --- Ф У Н К Ц И И --- *** #

        def ledControl(val_led):
            # print(val_led)                # контроль пришедшего сигнала
            if val_led == 2: val_led = 1;   # конвертация к цифровой единице (1 или 5В)
            print(val_led)                  # контроль отправляемого на Ar сигнала
            serial_send([0, val_led])       # передаем сигнал ledControl в функцию отправки пакета на Ar по ключу 0

        def fanControl(val_fan):
            # print(val_fan)                # контроль пришедшего сигнала
            if val_fan == 2: val_fan = 1;   # конвертация к цифровой единице (1 или 5В)
            print(val_fan)                  # контроль отправляемого на Ar сигнала
            serial_send([3, val_fan])       # передаем сигнал fanControl в функцию отправки пакета на Ar по ключу 3

        def lightControl(val_lig):
            # print(val_lig)                # контроль пришедшего сигнала
            if val_lig == 2: val_lig = 1;   # конвертация к цифровой единице (1 или 5В)
            print(val_lig)                  # контроль отправляемого на Ar сигнала
            serial_send([4, val_lig])       # передаем сигнал fanControl в функцию отправки пакета на Ar по ключу 4

        def rgb_Control():
            val_R = self.ui.r_slider.value()       # читаем состояние R-slidera
            val_G = self.ui.g_slider.value()       # читаем состояние G-slidera
            val_B = self.ui.b_slider.value()       # читаем состояние B-slidera
            print(val_R, val_G, val_B)             # контроль состояния RGB-slidera
            serial_send([1, val_R, val_G, val_B])  # передаем пакет в функцию отправки через порт на Ar

        # *** --- РАБОТА С ТЕРМИНАЛОМ И АРДУИНО --- *** #

        print(QDate.currentDate().toString('dd-MM-yyyy'))          # выводим в widget currentDate текущую дату

        self.ui.comboBox.addItems(portList)                        # передаем в comboBox список доступных портов
        self.ui.pbt_open.clicked.connect(open_com)                 # нажатием кнопки "pbt_open" открываем порт arduino
        self.ui.pbt_close.clicked.connect(close_com)               # нажатием кнопки "pbt_close" закрываем порт arduino
        self.ui.checkBox_LED.stateChanged.connect(ledControl)      # устанавливаем флаг "checkBox_LED" включаем светодиод
        self.ui.checkBox_FAN.stateChanged.connect(fanControl)      # устанавливаем флаг "checkBox_FAN" включаем вентилятор
        self.ui.checkBox_LIGHT.stateChanged.connect(lightControl)  # устанавливаем флаг "checkBox_LIGHT" включаем свет
        self.ui.r_slider.valueChanged.connect(rgb_Control)         # читаем r_slider
        self.ui.g_slider.valueChanged.connect(rgb_Control)         # читаем g_slider
        self.ui.b_slider.valueChanged.connect(rgb_Control)         # читаем b_slider


        temperatura_out = -26.2
        temperatura_in = 15.4
        vlagnost = 72

        self.ui.tempBar.setValue(temperatura_in)                  # t_комната_n в виджет QProgressBar выводится значение float, в Qt Designer задаем пределы значений.
        self.ui.temperatura_in.setValue(temperatura_out)           # Т_Погода в виджет QProgressBar выводится значение float, в Qt Designer задаем пределы значений.
        self.ui.temperatura_in_lcd.setDigitCount(5)               # зададим количество цифр в виджете, используя метод setDigitCount(5). Количество цифр в виджете будет "3" + "знак" + "точка"
        self.ui.temperatura_in_lcd.display(temperatura_out)        # выводим температуру на улице на LCD дисплей
        self.ui.label_vlagnost.setText(str(vlagnost))             # в виджет label выводится тип данных str
        self.ui.Temperatura_label.setText(str(temperatura_in))             # в виджет label выводится тип данных str

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Arduino()
    window.show()
    sys.exit(app.exec()) ``` 

Если что делаю не так, извените я здесь первый день...

Код для Arduino сократил до минимума (ошибка не изменилась)

int speed = 120;                 
float distance = 32;
float time = speed / distance; 
char myStr1[20];   

void setup()
{
  Serial.begin(9600);
  while (!Serial)             // задержка пока порт не откроется 
  {}  
}

void loop() {
  int size_bufer = Serial.available();   //  Функция возвращает количество накопленных в буфере байт 
  Serial.println("HelloComputerHelloComputerHelloComputerHelloComputerHelloComputer");
  count++;
  Serial.print("size_bufer- ");
  Serial.println(size_bufer);
  Serial.print("count- ");
  Serial.println(count);

  //dtostrf(distance,2,1,myStr1);
  dtostrf(time,2,1,myStr1);
  Serial.println(myStr1); 

  delay(5000);
} ```

Да, еще. Подобный переход передачи с байтового на битовый, происходит в не зависимости от содержания передаваемой информации. В другом скетче я передаю температуру и тек же принимаемая информация меняет вид. См. скрин.

слушайте, о чем вы? О том что сначала “31” передавалось в одну строку, а потом каждая цифра - на своей строке?

Вы понимаете что такое байты, а что биты?

А если по делу - сложно искать ошибки, когда код и вывод в Сериал не соответвует друг другу.

Пожалуйста, выложите вывод именно от того кода, что вы показали.
Ну или наоборот - выложите код, от которого этот вывод.

и

в вашем случае (плата Mega 2560) совершено не нужные строки.
человек с ютуба тоже не очень осведомлен о назначении данных строк.

и да, ваш вывод в порт отличается от того что написано в коде.

плюс еще, где объявление переменной count ?

Убрал все лишнее из скетча

int count;
int speed = 120;                 
float distance = 32;
float time = speed / distance; 
char myStr1[20];   

void setup()
{
  Serial.begin(9600);
}

void loop() {
  int size_bufer = Serial.available();   //  Функция возвращает количество накопленных в буфере байт 
  Serial.println("HelloComputerHelloComputerHelloComputerHelloComputerHelloComputer");
  count++;
  Serial.print("size_bufer- ");
  Serial.println(size_bufer);
  Serial.print("count- ");
  Serial.println(count);

  //dtostrf(distance,2,1,myStr1);
  dtostrf(time,2,1,myStr1);
  Serial.println(myStr1); 

  delay(5000);
} ```

В PyCharme  ответ теперь соответствует коду в Arduino 

![изображение|690x164](upload://lh56HLFWrWCbKl4uQm92sDCwVDK.png)

извините поторопился

Передача не всегда одинаковая после подключения порта. В этот раз картина другая , в первом цикле сообщение передано полностью.

Первая строка кода.

я не знаю питона, но сдается мне что

читает все что есть в буфере, а потом вы выводите это как одну строку
так вот, пока ваш скрипт очухывается в буфер успевает налететь что то, а после, как очухается, он просто печатает то что приходит по одному символу
вам надо накапливать строку до символов \r\n и только потом выводить ее
может я и не прав, мое предположение…

1 лайк

В вашем первом коде было такое:

serial.readyRead.connect(read_bufer) # связывает сигнал readyRead() со слотом readData()
                                     # Этот сигнал возникает, когда последовательный порт
                                     # получает новые данные

Если найти ман по функции readyRead(), там есть такое:

This signal is emitted once every time new data is available for reading from the device’s current read channel.

Я не нашел исходник, нужно бы еще покопаться, чтобы понять, что там на самом деле выполняется.

Зато я нашел похожую проблему с посимвольным выводом на stackoverflow. И там есть несколько решений, которые сводятся к написанию своей функции для буферизации данных и выводу по определенному ключу, например при получении кода переноса строки и/или возврата каретки. Я думаю, вы можете взять решение оттуда.

В 3 питоне (а про 2.7 мы давно забыли) ровно так и происходит и это видно по распечатке от “потерпевшего” - в ней не символ выводится одинокий а байтовый массив. :wink: :wink: :wink: что по букаффке b впереди видно.
Так что убирать нахрен это вывод, или превращать его в правильный. Тут уж каждый сам пи…ец своего счастья.

Всем привет. Продолжу задавать вопросы и предоставлять информацию о том, что накопал, т.к. проблему не решил. Начну со ссылки на видео, где подобная схема работает. Ардуинка шлет данные, они попадают в порт в виде байтового массива (букаффке b) и далее с ними работают. Почему у меня так не происходит ?

Вторая ссылка на видео как у меня загружается скетч в Arduino, и что я получаю в мониторе порта.
Что смущает. Это выдача старой информации в мониторе, той которая была до очередного открытия порта, которое сопровождается перезагрузкой платы. И по моему разумению поток должен начинаться с вывода фразы из
setup()
{
Serial.println(“Its frosty today”); }

Получается, что этот блок остается в буфере:
count-3
3.8
Hello Computer

А этот начинает передаваться уже в потоке, начиная с фразы в setup() и с обновленным счетчиком.
Its frosty today
count-1
3.8
Hello Computer
count-2
3.8
Hello Computer

Почему я на это обращаю внимание? Ответ в полученной информации в Pithon. Там я тоже получаю в правильном виде только ту информацию, которая храниться в буфере. Как только начинается поток, она передается в виде байтового массива (правильно замечено в одном из ответов), но по одному символу. Сейчас сделаю видео и выложу.

В этом видео, как выводится в Python информация из Arduino через com порт.
Сначала мы видим нормальный вывод того, что осталось в буфере от “предыдущего” потока. Потом идет начало подачи текущего потока, пытается собирать символы в слова, и далее сваливается в передачу символов в виде “бит” массивов. Простите за несоответствующую лексику, просто не знаю как это безобразие назвать.
Я посмотрел вчерашние рекомендации по разбору всего этого программным способом, но мне кажется, что здесь проблема аппаратного свойства. Ведь работает у Aleksa. Я ему писал, но ответа не получил.

Я с Вами согласен, именно до появления символов \n.
Вот только как, и главное где это сделать. Ардуинка своим println(), уже эти символы добавляет в пакет к тексту ( char myStr2[18] = “Hello Computer”; ).
В мониторе порта мы видим правильный вывод. Три последовательные строки с соответствующей информацией.
А вот pithon ведет себя по разному хранящуюся в буфере информацию он воспринимает правильно, а вот в потоке не ждет этого (\n) символа и все печатает по битно… Как то так, остоется придумать как это исправить…