14

Arduinoと通信するための本当にシンプルなC++アプリケーションを書こうとしています。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);
}

GNU screenを使用してArduinoと簡単に通信できるので、基本的な通信ですべてが正常に機能していることがわかります。

$ screen /dev/tty.usbmodem641 9600

私が持っている(壊れた)C ++コードは次のようになります:

#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を開く

どこが間違っているのですか?

4

3 に答える 3

11

次の 3 つのポイントがあります。

まず、Linux 側でシリアル ポート (TTY) を初期化しません。どのような状態かは誰にもわかりません。

プログラムでこれを行うには、 と を使用する必要がありtcgetattr(3)ますtcsetattr(3)。このサイト、Arduino サイト、または Google でこれらのキーワードを使用して、必要なパラメーターを見つけることができます。ただし、簡単なテストのために、独自のコマンドを呼び出す前に、このコマンドを発行することを提案します。

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

特に、欠落しているclocal場合は、TTY を開くことができない場合があります。

2 つ目:デバイスが開いているときは、何かを送信する前に少し待つ必要があります。デフォルトでは、シリアルラインが開いたり閉じたりするとArduinoがリセットされます。これを考慮に入れる必要があります。

デバイスは、ほとんどの場合、この-hupclリセットを防ぎます。-hupclただし、TTY が既に開いていて、その時点で Arduino が既にリセット信号を受信して​​いる場合にのみ設定できるため、少なくとも 1 つのリセットが常に必要です。したがって-hupcl、将来のリセットを「のみ」防止します。

3 番目:コードにはエラー処理がありません。TTY の各 IO 操作の後にコードを追加してください。このコードは、エラーをチェックし、最も重要な部分として、perror(3)または同様の関数を使用して役立つエラー メッセージを出力します。

于 2012-07-29T17:05:24.160 に答える
4

を使用して単純なミニコムタイプのクライアントを作成する方法のJeffGrayによる良い例を見つけましたboost::asio。元のコードリストは、boostユーザーグループにあります。これにより、元の投稿で説明したGNU Screenの例のように、Arduinoとの接続と通信が可能になります。

コード例(以下)は、次のリンカーフラグとリンクする必要があります

-lboost_system-mt -lboost_thread-mt

...しかし、少し調整することで、ブーストへの依存の一部を新しいC++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's 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; 
}
于 2012-12-05T01:28:58.483 に答える
1

にアクセスできるかどうかを確認する必要があります/dev/tty.usbmodem641。Linux での通常の方法は、ユーザーを適切なグループに追加することadduserです。

ところで、シリアル ポートにアクセスするには、/dev/ttyS0(COM1 用に) /dev/ttyS3. たとえば、 C のこの例を参照してください。

于 2012-07-27T07:21:52.933 に答える