C: Нет связи с подключенным последовательным портом?
Я пытаюсь подключиться к устройству Arduino Mega At2560, подключенному через последовательный порт USB, на моем ПК с ОС Linux. Используя код C, я пытаюсь отправлять и получать простые текстовые строки, просто я могу отправлять и получать обе стороны.
На ардуино:
int incomingByte = 0; // для входящих последовательных данных
void setup() {
Serial.begin(19200); // открывает последовательный порт, устанавливает скорость передачи данных 9600 бит/с
}
void loop() {
// отправлять данные только при получении данных:
if (Serial.available() > 0) {
// прочитать входящий байт:
incomingByte = Serial.read();
// скажите, что у вас есть:
Serial.print((char)incomingByte);
}
}
по сути, это просто цикл, который проверяет, есть ли последовательные данные, и если да, то считывает их и печатает обратно. есть (символьное) преобразование, поэтому я сразу увижу, что отправленные данные - это то, что я получил обратно (сторона Linux)
Для кода Linux я использую довольно стандартный код для открытия порта, который я нашел здесь
Я называю Arduino «столом», так как в конечном итоге он будет управлять движущимся столом с помощью команд USB.
C-файл:
#include "TableFunctions.h"
bool connected=false;
int fd;
char *portname;
int set_interface_attribs (int fd, int speed, int parity)
{
connected=false;
struct termios tty;
struct termios tty_old;
memset(&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
printf("PC: Error %d from tcgetattr \n", errno);
return -1;
}
tty_old = tty;
cfsetospeed (&tty, (speed_t)B19200);
cfsetispeed (&tty, (speed_t)B19200);
//tty.c_cflag |= B19200;
tty.c_cflag &= ~PARENB; // Делаем 8n1
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS; // без управления потоком
tty.c_cc[VMIN] = 1; // чтение не блокируется
tty.c_cc[VTIME] = 5; //
tty.c_cflag |= CREAD | CLOCAL; // включаем READ & игнорировать строки ctrl
cfmakeraw(&tty);
tcflush( fd, TCIFLUSH );
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
printf("PC: Error %d from tcsetattr \n", errno);
return -1;
}
return 0;
}
void set_blocking(int fd, int should_block)
{
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
printf("PC: Error %d from tggetattr \n", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 10; // тайм-аут чтения 0,5 секунды
if (tcsetattr (fd, TCSANOW, &tty) != 0)
printf("PC: Error %d setting term attributes \n", errno);
}
void OpenSerialPort()
{
char *portname = "/dev/ttyACM0";
printf("PC: Opening port to table \n");
int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
usleep(2000000);
if (fd < 0)
{
printf("PC: Error %d opening %s: %s \n", errno, portname, strerror(errno));
return;
}
set_interface_attribs(fd, B19200, 0); // устанавливаем скорость 19 200 бит/с, 8n1 (без четности)
set_blocking(fd, 0); // не ставим блокировку
printf("PC: Connected\n");
connected = true;
}
void PrepareWriteCommand(int numberOfCommands, const char *commands[numberOfCommands])
{
if(connected) //проверить, подключена ли ардуино
{
for(int i = 0; i<numberOfCommands; i++) //перебираем команды
{
int bufferSize = strlen(commands[i]); // получаем размер буфера, необходимый для этой команды
char charArray[bufferSize]; //вспомогательный массив символов
memcpy(charArray,commands[i],bufferSize);//копируем команду в массив символов
charArray[bufferSize]=0; //убедитесь, что в конце есть символ остановки
WriteSerialPort(charArray); //команда готова к отправке, отправляем.
}
}
}
int WriteSerialPort(const char *buffer)
{
printf("PC: Now writing: ");
int n_written = 0; //сколько байт было записано
n_written = write(fd, buffer, strlen(buffer)); // пишем команду и возвращаем сколько байт было записано
printf("\n");
//проверка отправленных и возвращенных байтов (ошибка, ничего или x отправленных байтов)
if(n_written<0)
{
printf("PC: Error %d from %s \n",errno, strerror(errno));
}
else if(n_written == 0)
{
printf("PC: Nothing was written \n");
}
else
{
printf("PC: Written %i bytes \n", n_written);
}
}
int ReadSerialPort(char *buffer, unsigned int buff_size)
{
//проверяем, подключена ли еще ардуино
if(connected)
{
//читаем серийные данные
if(read(fd,buffer,buff_size))
return sizeof(buffer); //возвращаем сколько байт было прочитано
else
{
//иначе вывести ничего не получено
printf("PC: Arduino not Connected (ReadSerialPort) \n");
}
}
}
в OpenSerialPort я в основном открываю новый fd и вызываю функции, которые устанавливают параметры связи. Я использую PrepareWriteCommand, чтобы убедиться, что команды, вводимые пользователями, отмечены символом остановки, и отправить их в WriteSerialPort, где я использую write() и печатаю, сколько байтов было отправлено.
H-файл:
#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#define MAX_DATA_LENGTH 256
int set_interface_attribs (int fd, int speed, int parity);
void set_blocking(int fd, int should_block);
void OpenSerialPort();
void PrepareWriteCommand(int numberOfCommands, const char *commands[numberOfCommands]);
int WriteSerialPort(const char *buffer);
int ReadSerialPort(char *buffer, unsigned int buff_size);
Главное:
#include "TableFunctions.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char output[MAX_DATA_LENGTH]; //массив символов для хранения вывода arduino
int received = 0; // проверяем, получил ли read() какие-либо байты
const char* commands[]={"test"}; // тестовая команда
OpenSerialPort(); // открываем последовательный порт
PrepareWriteCommand((sizeof(commands)/sizeof(commands[1])),commands); //подготавливаем команду к отправке
usleep(500000); // ждем ответа ардуино
while(true)
{
ReadSerialPort(output,MAX_DATA_LENGTH);//проверяем последовательный порт на наличие ответа
if(strlen(output)>0) //если что-то получено
{
printf("PC: received %i\n",received); //если да, то сколько байт
printf("PC: %s\n",output); //и что было получено
usleep(500000);
}
else if(strlen(output)<=0) //иначе ничего не выводить
{
printf("PC: Nothing received\n");
}
}
return 0; //заканчиваем программу
}
Я безуспешно пытался изменить настройки флагов tty. Каждый раз, когда я запускаю код C, я вижу, что Arduino перезагружается (и после открытия нового fd я даю ему некоторое время для загрузки), но связь между ними не отправляется.
Вывод:
PC: Opening port to table
PC: Connected
testPC: Now writing:
PC: Written 4 bytes
PC: received 0
PC:
Мы будем очень признательны за любую помощь в понимании того, почему код C не может взаимодействовать с Arduino!
@Jordan, 👍1
Обсуждение1 ответ
Проблема была в OpenSerialPort(). Я устанавливаю другую переменную fd, которая является локальной, поэтому я получаю приоритет. затем, когда OpenSerialPort() выполняется, он удаляется, а глобальный fd остается неустановленным. Вот почему я не могу ничего отправить с компьютера или на него.
Спасибо.
- Чтение последовательных данных с Arduino с помощью кода C
- Никакие данные не передаются через последовательный порт в Linux С++
- Последовательная связь между ПК и Arduino с использованием C
- Как разделить входящую строку?
- Какова максимальная длина провода для последовательной связи между двумя Arduino?
- Последовательная связь между двумя Arduino (запрос и получение)
- Не нашел датчик отпечатков пальцев :( Arduino Mega 2560 Adafruit Fingerprint Sensor
- Модуль SIM808: команда определения местоположения GSM (AT+CIPGSMLOC=1,1) дает неверное значение после выполнения команды отправки сообщения (AT+CMGS=+91xxxxxxxx)
Вы ждете, пока загрузчик завершит выполнение, прежде чем пытаться отправить ...?, @Majenko
@Majenko да: int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); спать(2000000); мне сказали, что я больше никогда не назначаю полученный в ReadSerialPort(), поэтому я исправил это, и теперь я получаю: ПК: получено 8 ПК: Я отправляю только 4 байта, поэтому arduino может отправлять вдвое больше данных за указанное время, пока ПК не прочитает их. Это не имеет значения, сейчас ПК по-прежнему ничего не показывает в буфере после того, как я вызываю read(), @Jordan
Я могу ошибаться, и 8 байтов - это размер указателя (sizeof(buf)), @Jordan
Это не имеет смысла:
void PrepareWriteCommand(int numberOfCommands, const char *commands[numberOfCommands])
, @Majenko@Majenko Функция получает массив команд и циклически перебирает его. В основном просто подготавливает каждую команду (убедившись, что есть символ остановки) и отправляет для записи через USB одну за другой., @Jordan
Да, но вы не можете использовать параметр для установки размера параметра. Это не имеет никакого смысла. Вы должны использовать
const char **commands
илиconst char *commands[]
, так как вы используете массив указателей., @Majenkoотносительно:
Serial.begin(19200);
как насчет количества стоповых битов? что с паритетом? как насчет количества бит данных? Все это нужно установить, @user3629249относительно:
ReadSerialPort (выход, MAX_DATA_LENGTH);
Я сомневаюсь, что функция:ReadSerialPort()
завершает ввод байтом NUL, поэтому любые последующие вызовы, ожидающие массива символов с завершением NUL, не будут работать. Я ожидаю, что эта функция возвращает количество прочитанных символов, которые можно использовать для вставки байта NUL в массивoutput[]
, @user3629249относительно:
ReadSerialPort (выход, MAX_DATA_LENGTH);
Опубликованный код не проверяет возвращаемое значение изReadSerialPort()
И фактическая функция:ReadSerialPort()
не возвращает статус ошибки, когда (например) порт Не подключен, @user3629249относительно:
printf("ПК: Сейчас пишет: ");
эти данные будут оставаться в выходном потоке до тех пор, пока что-то не произойдет (например, другой вызов printf(), который (правильно) имеет '\n' как часть данных ( желательно вывод последнего символа) заставляет данные передаваться на терминал, @user3629249вызов
read()
сообщит коду, есть ли что-нибудь для чтения. Ожидание произвольного времени не является хорошим методом. Гораздо лучше продолжать вызыватьReadSerialPort()
, пока не будут видны некоторые данные., @user3629249OT: относительно:
printf("ПК: Ошибка %d при открытии %s: %s \n", errno, portname, strerror(errno));
Сообщения об ошибках должны выводиться вstderr
, а неstdout
. Предложить: `fprintf( stderr, "ПК: Ошибка %d при открытии %s: %s \n", errno, имя порта, strerror(errno));, @user3629249функции в коде, которые обрабатывают обмен данными ввода-вывода, не могут сообщить вызывающему абоненту, когда возникает ошибка, поэтому вызывающий абонент будет (в блаженстве) продолжать, как будто все хорошо, @user3629249