Schnäppchen

Kategorien

Bidirektionale serielle Kommunikation zwischen Arduino und PC

Für ein Projekt (nämlich unser Zimmergewächshaus) brauchte ich eine vernünftige Möglichkeit aus einem C++ Programm auf einem Linux-PC bidirektional mit dem Arduino zu kommunizieren. Eigentlich ist das natürlich kein unlösbares Problem. Trotzdem hat es etwas gedauert, bis das bei mir reibungslos funktionierte.

Die hier vorgestellte Test-Umgebung macht folgendes: Die Zeichen, die auf dem PC eintipperten wurden werden zum Arduino übertragen. Dort werden sie zeilenweise gesammelt und dann zurück zum PC übertragen.

Dieses ist ein Test
Arduino: Dieses ist ein Test

Um das Ganze etwas besser testen zu können, werden die Ziffern 1 bis 5 auf dem PC vor der Übertragung in Worte (“eins” bis “fünf”) gewandelt. Die Ziffern 6 bis 0 werden ganz normal übertragen, dann aber auf dem Arduino in die entsprechenden Worte gewandelt.

Ziffern 0 1 2 3 4 5 6 7 8 9
Arduino: Ziffern null eins zwei drei vier fünf sechs sieben acht neun

Hier zunächst der Arduino-Sketch:

// ----------------------------------------------
//  Project Arduino/PC Serial Communication  
// ----------------------------------------------
// Just some code to test serial communication
// between Arduino and PC
//
// Version
//   minicom 2014.02.06
// 
// Author:
//   Heiner Otterstedt <h.otterstedt@gmail.com>
//
// Website:
//   http://maheo.eu
//

char szInstruction[128];
int  iInstruction = 0;

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

void loop()
{ if(Serial.available())
  { while(Serial.available())
    { szInstruction[iInstruction] = Serial.read();
      if(szInstruction[iInstruction] == '6')
      { strcpy(&szInstruction[iInstruction], "sechs");
        iInstruction += 4;
      }
      else if(szInstruction[iInstruction] == '7')
      { strcpy(&szInstruction[iInstruction], "sieben");
        iInstruction += 5;
      }
      else if(szInstruction[iInstruction] == '8')
      { strcpy(&szInstruction[iInstruction], "acht");
        iInstruction += 3;
      }
      else if(szInstruction[iInstruction] == '9')
      { strcpy(&szInstruction[iInstruction], "neun");
        iInstruction += 3;
      }
      else if(szInstruction[iInstruction] == '0')
      { strcpy(&szInstruction[iInstruction], "null");
        iInstruction += 3;
      }

      if(szInstruction[iInstruction] == '\n' ||
         szInstruction[iInstruction] == 'q' ||
         ++iInstruction >= 120)
      { szInstruction[iInstruction] = 0;
        iInstruction = 0;
        doSomething(szInstruction);
      }
    }
  }
}

void doSomething(char *psz)
{ Serial.print  ("Arduino: ");
  Serial.println(szInstruction);
}

Das C++ Programm auf dem Linux-PC habe ich im Prinzip aus einem Beitrag in der Boost Mailingliste (siehe hier bei Nabble) abgeleitet. Der User Jeff Gray-3 hatte dort den Code für ein einfaches MiniCom Programm gepostet. Erwartungsgemäß verwendet das Programm Teile der Boost Bibliothek. Die entsprechenden Librarys müssen also beim Aufruf des Compilers angegeben werden:

g++ -lboost_system-mt -lboost_thread-mt -o minicom minicom.cpp

Hier mein modifizierter Code:

/* minicom.cpp
 *      A simple demonstration minicom client with Boost asio
 *
 *      Parameters: baud port
 *      To end the application, send Ctrl-C on standard input
 *
 * Info
 *   http://boost.2283326.n4.nabble.com/Simple-serial-port-demonstration-with-boost-asio-asynchronous-I-O-td2582657.html
 *
 * Compile:
 *   g++ -lboost_system-mt -lboost_thread-mt -o minicom minicom.cpp
 *
 * Author:
 *   Jeff Gray-3 (http://boost.2283326.n4.nabble.com/Simple-serial-port-demonstration-with-boost-asio-asynchronous-I-O-td2582657.html)
 *
 *   modified by Heiner Otterstedt (maheo.eu)
 */

#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 write(const string s)
  { int i;

    for(i=0; i<s.length(); i++)
      { write((const char)s.at(i));
    }

  }

  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;

      // Testing strings...
      if(ch == '1')      { string s = "eins"; c.write(s); }
      else if(ch == '2') { string s = "zwei"; c.write(s); }
      else if(ch == '3') { string s = "drei"; c.write(s); }
      else if(ch == '4') { string s = "vier"; c.write(s); }
      else if(ch == '5') { string s = "fünf"; c.write(s); }
      else               {                    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;
}

Das fertige Programm wird mit den Parametern Baudrate und Port aufgerufen, also zum Beispiel

minicom 9600 /dev/ttyUSB0

für einen Arduino Duemilanove oder

minicom 9600 /dev/ttyACM0

für einen Arduino Uno.

Erweiterbare Adsense-Anzeigen ausschalten

Adsense Overlay-Ad

Auf unserem Gartenblog erscheinen plötzlich riesige Overlay-Ads, wenn man über bestimmte Anzeigen hoovert. Echt furchtbar sowas!

Um das abzuschalten musste ich ziemlich lange suchen. Deshalb hier eine kurze Anleizung:

  • Im Adsense Kontrollzentrum geht man auf “Anzeigen zulassen und blockieren“.
  • Anschließend wählt man oben den Punkt “Anzeigenschaltung” auf.
  • dort kann man unten die Option “Erweiterbare Anzeigen – Schaltung von Anzeigen, die durch eine Nutzeraktion über die Größe des Anzeigenblocks hinaus erweitert werden können” ausschalten.

Wieso das versehentliche überqueren einer Anzeige schon eine “Nutzerreaktion” ist, bleibt wohl das Geheimnis von Google.

Debian: IP-Adresse ändern

Um die IP-Adresse eines Debian-Rechners zu ändern wird die neue Adresse zunächst in /etc/network/interfaces eingetragen. Zum Beispiel so:

address 192.168.42.12

Anschließend wird das Netzwerk neu gestartet:

/etc/init.d/networking restart

Vorsicht, wenn man das per ssh macht, ist Verbindung nun logischerweise weg ;-)

Owncloud – Google Kalender und Googe Mail Kontakte importieren

Ich glaube ich bin nicht der einzige, der in letzter Zeit das Vertrauen in kostenlose Internet-Dienste verloren hat. Nach der Ankündigung, dass der Googel-Reader eingestellt wird, habe ich mich nun endgültig entschieden auf eine Lösung zu setzen, die ich voll unter Kontrolle habe. Bei mir heißt diese Lösung Owncloud.

Die Frage lautet, wie transferiert man die Termine aus dem Google Kalender und die Kontakte aus Google Mail hinüber in die eigene Cloud? Wenn man erst einmal weiß wie es geht, dann ist es ganz einfach:

Daten aus Google Kalender exportieren

Ich habe meine Kalender einzeln exportiert, damit ich sie als getrennte Kalender in Owncloud importieren kann. Soweit ich es verstanden habe kann man aber auch alle Kalender gemeinsam exportieren

  • In der linken Sidebar unter “Meine Kalender” das Menü des gewünschten Kalenders aufklappen
  • Menüpunkt “Kalender-Einstellungen” aufrufen
  • auf der folgenden Seite (etwas weiter unten bei “Privatadresse“) den grünen Button “iCal” auswählen
  • ICal-Daten herunterladen

Kalenderdaten nach Owncloud importieren

  • links “Dateien” auswählen
  • Die heruntergeladenen iCal-Daten als Dateien hochladen
  • Datei im Webinterface von Owncloud anklicken
  • gewünschten Kalender auswählen bzw. neuen Kalender anlegen
  • Importieren” auswählen

Kontakte aus Google Mail exportieren

  • In Google Mail zu den Kontakten wechseln (oben links das Menü “Gmail” aufklappen)
  • über den Kontakten das Menü “Mehr” aufklappen und “Exportieren” auswählen
  • Kontakte gruppenweise oder “alle” im vCard-Format exportieren
    (Vorsicht, wenn man Google Mail schon länger benutzt, dann kann “alle” ganz schön groß sein, bei mir knapp 5000)

Kontakte nach Owncloud importieren

  • links “Kontakte” auswählen
  • bei den Einstellungen (Rädchen unten links) “Importieren” aufklappen
  • Dateien für den Import auswählen” anklicken
  • gewünschtest Adressbuch auswählen oder neues Adressbuch anlegen
  • Dateinamen angeben und öffnen

 

Linux Kernel 3.0-rc1

Ein neues Linux-Kernel ist draußen und es heißt – trara – 3.0.

Kein besonderer Anlass, kein technologischer Quantensprung, einfach zur Abwechslung mal eine neue Zahl. Linus schreibt dazu:

I decided to just bite the bullet, and call the next version 3.0. It will get released close enough to the 20-year mark, which is excuse enough for me, although honestly, the real reason is just that I can no longe rcomfortably count as high as 40.

Social Krimskrams

An anderer Stelle hatte ich vor kurzem einen Überblick über die (meiner Ansicht nach) wichtigsten Social-Sonstwas-Dienste gegeben. Nun ist bei mir auch noch Google+ dazu gekommen. Wer meine Google-plus-Aktivitäten verfolgen möchte: siehe hier…

Wenn das dort allerdings ebenso weiter geht wie bisher bei Twitter oder Facebook, dann sollte man dort nicht zu viel erwarten.

Backdoor in WordPress-Plugins

Nur ein kleiner Hinweis: Unbekannte haben sich Zugriff auf das offizielle Pluginverzeichnis verschafft und die drei populären Plugins AddThis, WPtouch und W3 Total Cache mit einer Backdoor ausgestattet.

Installation aborted by User

Gerade lief hier ein automatisches Update meiner OpenSuse Installation. Normalerweise ist das etwas, dass ich nur am Rande verfolge, weil es völlig problemlos im Hintergrund abläuft. Heute war das anders. Die Fehlermeldung lautete:

Installation aborted by User

Huch, bin ich da vielleicht versehentlich auf irgendeine falsche Taste gefallen? Gleich noch mal …

Installation aborted by User

Sehr merkwürdig! Nach etwas Kopfkratzen und Logs-Lesen kam ich dann dahinter. Offenbar hatte sich in die RPM-Datenbank ein Fehler eingeschlichen. Den kann man leicht beheben, indem man als root

rpm --rebuilddb

ausführt. Das war’s, anschließend funktionierte der Systemupdate wieder ohne Probleme.

Wo ist das Feed-Icon in Firefox 4.0?

Ich persönlich fand das Feed-Icon in der Adressleiste des Firefox-Browsers ziemlich hilfreich. Als Grund für sein Verschwinden gibt Alexander Limi an:

Even among our very advanced users, this button has the lowest percentage use of all our buttons, and is not used anywhere near enough to justify being in the main UI.

Sollte mich jemand fragen, was ich davon halte – die Entscheidung dieses Icon zu entfernen halte ich für einen Rückschritt und totalen Blödsinn! OK, zumindest ist es möglich das Feed-Icon zurück zu holen. Dazu klickt man auf die Navigationsleite (nicht auf das Eingabefeld, sondern auf den Hintergrund) und wählt “Anpassen …” aus. Aus den Optionen im folgenden Dialog wählt man das Feed-Icon aus und zieht es in die Navigationsleiste.

X-Forwarding über ssh

Wenn man sich in einem unsicheren Netzwerk eine X-Session von einem entfernten Rechner holen möchte, dann sollte die Verbindung tunlichst verschlüsselt sein. Mit ssh/sshd ist das ganz einfach. Hier eine kleine Anleitung, die ich unter openSuse 11.4 und Debian Lenny getestet habe:

  • Auf dem Server muss xauth  installiert sein. Falls das nicht der Fall ist, muss es nachgeholt werden.
  • Eine normale SSH-Verbindung vom Client zum Server sollte funktionieren. Dafür müssen natürlich der SSH-Client und der SSH-Daemon (sshd) installiert sein und die Firewall muss entsprechende Verbindungen durchlassen.
  • Sowohl unter openSuse als auch unter Debian war der SSH-Daemon nach der Installation bereits richtig konfiguriert. Sollte das nicht der Fall sein, muss die Variable X11Forwarding in /etc/ssh/sshd_config auf yes gesetzt werden. Anschließend muss der SSH-Daemon neu gestartet werden (/etc/init.d/ssh restart)
  • In der Konfigurationsdatei des SSH-Client (/etc/ssh/ssh_config) müssen die Variablen ForwardX11 und  ForwardX11Trusted auf yes gestzt werden.
  • Die Option -X gibt dem SSH-Client eingeschränkte Rechte für das eigene Display. Die Option -Y gibt ihm vollen Zugriff auf das Display. Meist reicht es eine SSH-Verbindung mit der Option -X zum Server aufzunehmen (ssh -X zielrechnername).

Programme, die nun in diesem Terminal auf dem Server gestartet werden, werden nun auf dem Client angezeigt. Versuche es einfach mal, indem Du glxgears eingibst.

Weblinks: