Подтвердить что ты не робот

Двунаправленная связь на С++ по последовательному соединению

Я пытаюсь написать очень простое приложение на С++ для общения с Arduino. Я хотел бы отправить Arduino персонажу, который он отправит сразу. Код Arduino, который я взял из учебника, выглядит следующим образом:

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

void loop()
{
    //Have the Arduino wait to receive input
    while (Serial.available()==0);

    //Read the input
    char val = Serial.read();

    //Echo
    Serial.println(val);
}

Я могу общаться с Arduino легко с помощью экрана GNU, поэтому я знаю, что все работает нормально с базовым сообщением:

$screen/dev/tty.usbmodem641 9600

(сломанный) код С++, который у меня выглядит следующим образом:

#include <fstream>
#include <iostream>
int main()
{
    std::cout << "Opening fstream" << std::endl;
    std::fstream file("/dev/tty.usbmodem641");
    std::cout << "Sending integer" << std::endl;
    file << 5 << std::endl; // endl does flush, which may be important
    std::cout << "Data Sent" << std::endl;
    std::cout << "Awaiting response" << std::endl;
    std::string response;
    file >> response;
    std::cout << "Response: " << response << std::endl;

    return 0;
}

Он компилируется отлично, но при его запуске на Arduino мигают некоторые огни, и терминал просто зависает:

Открытие fstream

Где я иду не так?

4b9b3361

Ответ 1

Есть три точки:

Сначала:. Вы не инициализируете последовательный порт (TTY) на стороне Linux. Никто не знает, в каком состоянии это.

Выполняя это в своей программе, вы должны использовать tcgetattr(3) и tcsetattr(3). Вы можете найти необходимые параметры, используя эти ключевые слова на этом сайте, на сайте Arduino или в Google. Но для быстрого тестирования я предлагаю выпустить эту команду, прежде чем вы вызовете свою собственную команду:

stty -F /dev/tty.usbmodem641 sane raw pass8 -echo -hupcl clocal 9600

В частности, отсутствующий clocal может помешать вам открыть TTY.

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

Часть -hupcl будет предотвращать эту reset большую часть времени. Но по крайней мере один reset всегда необходим, потому что -hupcl может быть установлен только тогда, когда TTY уже открыт, и в это время Arduino уже получил сигнал reset. Таким образом, -hupcl будет "только" предотвращать будущие сбрасывания.

Третий: В вашем коде отсутствует обработка ошибок. Пожалуйста, добавьте код после каждой операции ввода-вывода в TTY, которая проверяет наличие ошибок и - самую важную часть - выводит полезные сообщения об ошибках с помощью perror(3) или подобных функций.

Ответ 2

Когда вы открываете последовательный порт, Arduino сбрасывается. Для Arduino Uno добавление 10 μF конденсатора между reset и заземлением предотвращает это. Подробнее см. этот. Обратите внимание, что каждый раз, когда вам нужно перепрограммировать чип, вы должны удалить конденсатор.

Вы также должны правильно настроить последовательный порт, но сначала необходимо решить проблему с оборудованием.

Ответ 3

Я нашел хороший пример Джеффа Грея о том, как создать простой клиент типа minicom, используя boost::asio. Исходный код можно найти в группе повышенных пользователей. Это позволяет подключать и поддерживать связь с Arduino, как в примере с GNU Screen, упомянутом в исходном сообщении.

Пример кода (ниже) должен быть связан со следующими флагами компоновщика

-lboost_system-mt -lboost_thread-mt

... но с небольшим количеством настроек, некоторые из зависимостей от boost могут быть заменены новыми стандартными функциями С++ 11. Я отправляю исправленные версии, когда и когда я общаюсь с ним. На данный момент это компилируется и является прочной основой.

/* minicom.cpp 
        A simple demonstration minicom client with Boost asio 

        Parameters: 
                baud rate 
                serial port (eg /dev/ttyS0 or COM1) 

        To end the application, send Ctrl-C on standard input 
*/ 

#include <deque> 
#include <iostream> 
#include <boost/bind.hpp> 
#include <boost/asio.hpp> 
#include <boost/asio/serial_port.hpp> 
#include <boost/thread.hpp> 
#include <boost/lexical_cast.hpp> 
#include <boost/date_time/posix_time/posix_time_types.hpp> 

#ifdef POSIX 
#include <termios.h> 
#endif 

using namespace std; 

class minicom_client 
{ 
public: 
        minicom_client(boost::asio::io_service& io_service, unsigned int baud, const string& device) 
                : active_(true), 
                  io_service_(io_service), 
                  serialPort(io_service, device) 
        { 
                if (!serialPort.is_open()) 
                { 
                        cerr << "Failed to open serial port\n"; 
                        return; 
                } 
                boost::asio::serial_port_base::baud_rate baud_option(baud); 
                serialPort.set_option(baud_option); // set the baud rate after the port has been opened 
                read_start(); 
        } 

        void write(const char msg) // pass the write data to the do_write function via the io service in the other thread 
        { 
                io_service_.post(boost::bind(&minicom_client::do_write, this, msg)); 
        } 

        void close() // call the do_close function via the io service in the other thread 
        { 
                io_service_.post(boost::bind(&minicom_client::do_close, this, boost::system::error_code())); 
        } 

        bool active() // return true if the socket is still active 
        { 
                return active_; 
        } 

private: 

        static const int max_read_length = 512; // maximum amount of data to read in one operation 

        void read_start(void) 
        { // Start an asynchronous read and call read_complete when it completes or fails 
                serialPort.async_read_some(boost::asio::buffer(read_msg_, max_read_length), 
                        boost::bind(&minicom_client::read_complete, 
                                this, 
                                boost::asio::placeholders::error, 
                                boost::asio::placeholders::bytes_transferred)); 
        } 

        void read_complete(const boost::system::error_code& error, size_t bytes_transferred) 
        { // the asynchronous read operation has now completed or failed and returned an error 
                if (!error) 
                { // read completed, so process the data 
                        cout.write(read_msg_, bytes_transferred); // echo to standard output 
                        read_start(); // start waiting for another asynchronous read again 
                } 
                else 
                        do_close(error); 
        } 

        void do_write(const char msg) 
        { // callback to handle write call from outside this class 
                bool write_in_progress = !write_msgs_.empty(); // is there anything currently being written? 
                write_msgs_.push_back(msg); // store in write buffer 
                if (!write_in_progress) // if nothing is currently being written, then start 
                        write_start(); 
        } 

        void write_start(void) 
        { // Start an asynchronous write and call write_complete when it completes or fails 
                boost::asio::async_write(serialPort, 
                        boost::asio::buffer(&write_msgs_.front(), 1), 
                        boost::bind(&minicom_client::write_complete, 
                                this, 
                                boost::asio::placeholders::error)); 
        } 

        void write_complete(const boost::system::error_code& error) 
        { // the asynchronous read operation has now completed or failed and returned an error 
                if (!error) 
                { // write completed, so send next write data 
                        write_msgs_.pop_front(); // remove the completed data 
                        if (!write_msgs_.empty()) // if there is anthing left to be written 
                                write_start(); // then start sending the next item in the buffer 
                } 
                else 
                        do_close(error); 
        } 

        void do_close(const boost::system::error_code& error) 
        { // something has gone wrong, so close the socket & make this object inactive 
                if (error == boost::asio::error::operation_aborted) // if this call is the result of a timer cancel() 
                        return; // ignore it because the connection cancelled the timer 
                if (error) 
                        cerr << "Error: " << error.message() << endl; // show the error message 
                else 
                        cout << "Error: Connection did not succeed.\n"; 
                cout << "Press Enter to exit\n"; 
                serialPort.close(); 
                active_ = false; 
        } 

private: 
        bool active_; // remains true while this object is still operating 
        boost::asio::io_service& io_service_; // the main IO service that runs this connection 
        boost::asio::serial_port serialPort; // the serial port this instance is connected to 
        char read_msg_[max_read_length]; // data read from the socket 
        deque<char> write_msgs_; // buffered write data 
}; 

int main(int argc, char* argv[]) 
{ 
// on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress 
// On other systems, you'll need to look for an equivalent 
#ifdef POSIX 
        termios stored_settings; 
        tcgetattr(0, &stored_settings); 
        termios new_settings = stored_settings; 
        new_settings.c_lflag &= (~ICANON); 
        new_settings.c_lflag &= (~ISIG); // don't automatically handle control-C 
        tcsetattr(0, TCSANOW, &new_settings); 
#endif 
        try 
        { 
                if (argc != 3) 
                { 
                        cerr << "Usage: minicom <baud> <device>\n"; 
                        return 1; 
                } 
                boost::asio::io_service io_service; 
                // define an instance of the main class of this program 
                minicom_client c(io_service, boost::lexical_cast<unsigned int>(argv[1]), argv[2]); 
                // run the IO service as a separate thread, so the main thread can block on standard input 
                boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); 
                while (c.active()) // check the internal state of the connection to make sure it still running 
                { 
                        char ch; 
                        cin.get(ch); // blocking wait for standard input 
                        if (ch == 3) // ctrl-C to end program 
                                break; 
                        c.write(ch); 
                } 
                c.close(); // close the minicom client connection 
                t.join(); // wait for the IO service thread to close 
        } 
        catch (exception& e) 
        { 
                cerr << "Exception: " << e.what() << "\n"; 
        } 
#ifdef POSIX // restore default buffering of standard input 
        tcsetattr(0, TCSANOW, &stored_settings); 
#endif 
        return 0; 
}

Ответ 4

Вы должны проверить, есть ли у вас доступ к /dev/tty.usbmodem641. Обычный способ в Linux состоит в том, чтобы добавить пользователя в нужную группу с помощью adduser.

Кстати, я знаю, что для доступа к последовательному порту нужно открыть /dev/ttyS0 (для COM1), пока /dev/ttyS3. См. Например этот пример в C.