Как точно получить значения потенциометра через USB-порт?

Я хочу прочитать аналоговые значения моего ardunio uno в отдельную программу на c++. На Arduino у меня есть последовательный порт, инициализированный для 9600 бод:

int sensorPin = A0;    // select the input pin for the potentiometer

int sensorValue = 0;  // variable to store the value coming from the sensor

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

void loop() {
  // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
  Serial.println(sensorValue);
} 

Выходное значение должно быть около 390, учитывая текущие настройки. Мой код чтения последовательного порта, кажется, нормально считывает порт, но иногда отображает только некоторые символы.... иногда он считывает значение нормально. в других случаях я получаю одного или двух символов, таких как 3 или 39.

#include <stdio.h>      // standard input / output functions
#include <stdlib.h>
#include <string.h>     // string function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <termios.h>    // POSIX terminal control definitions
#include <iostream>     
using namespace std;

int main(){
    const char* device ="/dev/ttyACM0";
/* Open File Descriptor */
//int USB = open( device, O_RDWR| O_NONBLOCK | O_NDELAY );
int USB = open( device, O_RDWR| O_NOCTTY );

/* Error Handling */
if ( USB < 0 ){
cout << "Error " << errno << " opening " << device << ": " << strerror (errno) << endl;
}

/* *** Configure Port *** */
struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 ) {
   cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << endl;
}

/* Save old tty parameters */
tty_old = tty;

/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;

tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  1;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout
tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

/* Make raw */
cfmakeraw(&tty);

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {
   cout << "Error " << errno << " from tcsetattr" << endl;
}


if ( tcsetattr ( USB, TCSANOW, &tty ) != 0){
cout << "Error " << errno << " from tcsetattr" << endl;
}


/* Allocate memory for read buffer */
char buf [1024];
memset (&buf, '\0', sizeof buf);

/* *** READ *** */
int n = read( USB, &buf , sizeof buf );
cout << "buffer: " << &buf << endl; 
/* Error Handling */
if (n < 0)
{
     cout << "Error reading: " << strerror(errno) << endl;
}

/* Print what I read... */
cout << "Read: " << buf << endl;

close(USB);
 return 0;
}

Как я могу считывать правильное значение, отправляемое каждый раз?

, 👍2

Обсуждение

https://www.arduino.cc/en/tutorial/potentiometer, @Majenko

Странно, что вас не волнует " Serial.print ()", поскольку это то, что дает Arduino для передачи данных по последовательному соединению. Вы упомянули "стабильное соединение"; означает ли это, что вы обнаружили, что ваше последовательное соединение нестабильно?, @timemage

Я знаю, как настроить arduino на отправку данных на последовательный порт, его получение в другом месте, с которым я борюсь. Моя интерпретация результатов, которые я получаю, наводит меня на мысль, что существует проблема стабильности., @j0h

Будет ли выполняться захват вывода Serial.print() с помощью эмулятора терминала, поддерживающего вывод журнала, а затем последующая обработка этого файла? По крайней мере, это подтвердило бы (или нет), что отправляемые данные верны (или нет). Если он отправляется правильно, ваша проблема не в Arduino или его коде., @JRobert

Я проверяю с помощью последовательного монитора, я бы хотел избежать загрузки файлов ввода-вывода и перенаправления. Я думаю, что, возможно, я приближаюсь, set_blocking может быть моей последней проблемой ... тогда мне придется попробовать прочитать несколько горшков., @j0h

Итак, после полного переписывания вашего (изначально очень плохо написанного) вопроса вы теперь просите о чем-то совершенно другом: помочь с программой на C++, написанной для вашего компьютера, а не помочь с вашим Arduino....?, @Majenko

да, да, возможно, плохо написано, но трудно понять, когда ваш проект не работает, подходите ли вы к нему вообще правильно. Я электрик, а не программист, не инженер, у меня нет ученой степени, и я не знаю, как надежно передавать данные с моего Arduino в мою компьютерную программу. Проблема та же самая, вопрос тот же самый. Как мне получить эти данные? пожалуйста, опубликуйте свое решение @Majenko, @j0h

Ваша основная проблема заключается в том, как вы используете чтение, как отмечает Эдгар. Вы должны прочитать строку, а не просто случайное число байтов., @Majenko


2 ответа


Лучший ответ:

4

Здесь есть несколько проблем. Во-первых, Arduino сбрасывается каждый раз, когда вы открываете последовательное соединение на стороне ПК. Вы можете предотвратить это, поместив конденсатор емкостью 1 мкФ (или более) между 5 В и СБРОСОМ. Вторая проблема заключается в том, что драйвер tty может хранить старые данные в своем буфере, и вы получаете эти данные при открытии соединения. Третья проблема заключается в том, что функция read() возвращается, как только она способна читать некоторые данные: он не ждет, пока у него будет байт буфера размера.

Чтобы устранить эти проблемы, я предлагаю:

  1. Есть какая-то форма рукопожатия, когда ПК отправляет запрос на Arduino, а затем Arduino подтверждает и начинает отправлять данные. Затем программа для ПК откажется от всего, что предшествовало подтверждению.
  2. На ПК несколько раз прочитайте (), пока не получите полную строку (которая заканчивается на CRLF). Возможно, вы захотите использовать линейно-ориентированный поток, если хотите , чтобы libc справился с этим за вас: см. fdopen(3) setlinebuf(3).

Обновление: Очень простой пример рукопожатия: ПК отправляет S (для “запуска”), затем Arduino отправляет OK, затем начинает отправлять данные. На стороне Arduino:

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

    // Wait for the 'S' (start) command from the computer.
    while (Serial.read() != 'S')
        /* wait */ ;

    // Send acknowledgement.
    Serial.println("OK");
}

Более длинная команда “пуск” была бы более надежной, но немного более сложной для работы с Arduino. ПК должен немного подождать, прежде чем отправлять команду (в случае, если Arduino перезапускается), или, возможно, отправить ее несколько раз, пока не получит ответ.

,

у вас есть какой-нибудь хороший материал для этого?, @j0h

@j0h: За что? Что касается буферизации строк, то обозначение “fdopen(3), setlinebuf(3)” является сокращением для “см. man 3 fdopen 3 setlinebuf., @Edgar Bonet

что касается рукопожатия, я не совсем понимаю. это функция Serial.read (), считывающая данные с последовательного порта на компьютер или с Arduino? Я думаю, может быть, я могу отправить символ на arduino, ПК, скажем, эй, ардуино, ты там отвечаешь на запрос 42. arduino прослушивает символ 42, получает его и отвечает строкой значений или чем-то еще, и отправляет знак отбоя, и возвращается к прослушиванию следующего ответа с ПК. ... или что-то в этом роде, вот как я себе это представляю, @j0h


1

Подключение по USB на самом деле очень сложное. И последовательная связь (или класс USB CDC) - это только 1 из более чем десятка протоколов, используемых через USB. Большинство дешевых встроенных процессоров экономят деньги, не используя эту функцию. Большинство Arduino используют дешевые встроенные процессоры. Например, классическому Arduino Uno нужен второй чип (поиск нового USB по этой ссылке), чтобы даже разговаривать последовательно через USB-порт. К счастью, это то, что делает Serial.print (). Он позаботится о настройке последовательного протокола через USB - порт для вас. И отправит ваши данные по этому соединению.

То, как вы получаете эти данные на другом конце USB-соединения, обычно выходит за рамки обсуждения на этом сайте обмена стеками. Но если вы находитесь на компьютере с Windows, это, вероятно, будет один из COM-портов. А в Linux это, вероятно, будет что-то вроде /dev/ttyUSB0 или 1.

,

Вам это не обязательно, но комментарии приветствуются, когда когда-либо проводится голосование. В любом направлении. Цель состоит в том, чтобы отредактировать и улучшить как вопросы, так и ответы., @st2000

Возможно, мне придется перепостить это, так как сейчас оно закрыто. Но я ценю ответ., @j0h

Вы можете отредактировать этот вопрос, чтобы он был более сфокусированным. Это должно вызвать сообщение для человека, который закрыл его, чтобы он мог пересмотреть отредактированную версию нового более сфокусированного вопроса. Подсказка: Всегда хорошо сначала попробовать что-то самостоятельно, а затем спросить, когда что-то идет не так, как ожидалось., @st2000

да, я отредактировал его, но он все еще закрыт. каждый голос за повторное открытие помогает. подсказка подсказка, @j0h

Я понизил голос, потому что это было, а теперь еще больше не является ответом на вопрос, @Juraj

Спасибо @Juraj за ответ. Я понимаю вашу точку зрения. Но поскольку @ Edgar Bonet дал такой полный ответ, я не вижу смысла повторять даже часть того, что он сказал. Чтобы смягчить путаницу, я собираюсь оставить все как есть., @st2000