2012-07-26 87 views
11

我正在嘗試編寫一個非常簡單的C++應用程序來與Arduino進行通信。我想發送Arduino一個它立即發回的字符。我從一個教程了Arduino的代碼如下所示:通過串行連接進行雙向C++通信

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屏幕與Arduino的溝通,所以我知道一切正常的基本通信:

$屏幕/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的

我要去哪裏錯了?

+2

嘗試用升壓[ASIO串行端口(http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/serial_port.html)。如果你的驅動程序是FTDI,你需要設置[波特率](http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/serial_port_base__baud_rate.html)。 – Arpegius 2012-07-26 22:15:42

+0

謝謝。我發現了一個我將在明天看到的演示。 。 。 http://www.college-code.com/blog/2008/boost-asio-serial_port-demo – learnvst 2012-07-26 23:48:41

+3

您的代碼正在等待Arduino使用硬件流量控制。我敢打賭你的Arduino不會做硬件流量控制。你需要控制串行端口,你不能讓標準I/O庫這樣做,因爲它不知道如何並且只是假設它就像一個終端。 – 2012-07-27 20:01:17

回答

0

您應該檢查您是否有權訪問/dev/tty.usbmodem641。 Linux中常用的方法是將用戶添加到adduser的適當組中。

順便說一句,我知道要訪問串口,需要打開/dev/ttyS0(用於COM1),直到/dev/ttyS3。例如參見this example in C

2

當你打開串口時,Arduino會重置。對於Arduino Uno,在復位和接地之間添加一個10  μF電容可防止出現這種情況。詳情請參閱this。請注意,每次需要重新編程芯片時都必須移除電容。

您還必須正確配置串口,但首先需要解決硬件問題。

+0

如何通過簡單的控制檯訪問正常工作?例如'$ screen /dev/tty.usbmodem641 9600' – learnvst 2012-07-28 21:41:56

8

有三點:

第一:你不會在Linux中初始化串口(TTY)。沒有人知道它是什麼狀態。

在您的程序中執行此操作,您必須使用tcgetattr(3)tcsetattr(3)。您可以在此網站,Arduino網站或Google上使用這些關鍵字來查找所需的參數。但只是爲了快速測試我建議你打電話給自己的命令之前發出以下命令:

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

尤其是缺少clocal可能會阻止您打開TTY。

第二個:當設備打開時,您應該稍等一會,然後再發送任何內容。默認情況下,當串行線路打開或關閉時,Arduino會重置。你必須考慮到這一點。

-hupcl部分將會阻止大部分時間重置。但至少需要一次復位,因爲-hupcl只有在TTY已經打開並且此時Arduino已經收到復位信號時才能設置。所以-hupcl將「僅」阻止未來的重置。

第三:沒有錯誤處理在您的代碼。請在TTY上的每個IO操作之後添加代碼以檢查錯誤,並且 - 最重要的部分 - 使用perror(3)或類似功能打印有用的錯誤消息。

2

我發現了Jeff Gray如何使用boost::asio製作簡單的minicom類型客戶端的一個很好的例子。原來的code listing can be found on the boost user group。這允許與原始帖子中提到的GNU屏幕示例中的Arduino進行連接和通信。

的代碼示例(下面)需要被用下面的連接標誌

-lboost_system-MT -lboost_thread-MT

聯...但具有一些調整,一些對boost的依賴性可以用新的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; 
}