Samstag, 23. Juni 2012

Arduino, das Restclient Gem und bidirektionale Kommunikation

Ausgehend von meinem gestrigen Blogpost habe ich mein Rubyscript funktional erweitert. Ein puts an sich ist witzlos, mit einem Restclient kann ich aber beliebige REST APIs eventgesteuert befüttern. Das Ruby Gem rest-client bringt dafür schon alles Notwendige mit. Zudem habe ich nun die Konfiguration des seriellen Devices in ein Commandline Argument verschoben und die Übergabeparameter für den Restclient in Form der URL und des Request Bodys in ein YAML-File. Der Name dieses YAML-Files wiederum wird über ein zweites Commandline Argument übergeben.
Somit ist es möglich, im Sinne eines Plug-Ins eine ganze Reihe von YAML-Files für eine Sammlung von API-Services vorzuhalten.

Die vermutlich größte Einschränkung an dieser Stelle meines Aufbaus ist die mangelnde Implementierung einer Authentifizierungsmethode. Basic Auth lässt sich aber beim rest-client gem relativ schmerzlos in folgender Form direkt in die URL einflechten:

http(s)://username:password@urlofservice.com

Arduino-seitig habe ich nun den einfachen Zeit-Trigger um einen Button-Trigger erweitert. Zudem erfolgt die Kommunikation über den Serial-Port jetzt bidirektional: Wenn der REST-Client auf seinen Call einen Statuscode 200 vom Zielserver zurückerhält schreibt das Rubyscript eine "1" in den seriellen Port. Dies veranlasst den Arduino eine grüne LED für 5 Sekunden leuchten zu lassen. Falls es während des Calls zu einem Error kommt wird [UPDATE] dieser über ein resuce abgefangen und[/UPDATE] stattdessen eine 0 über den seriellen Port geschrieben, was Arduino-Seitig eine rote LED leuchten lässt.

tl;dr:

This example combines a ruby script to trigger REST api calls via an event driven Arduino script. The api response triggers an event inside the Arduino script, too (bidirectional communication via the serial port).


[UPDATE] Die Scripte finden sich auch auf GitHub: https://github.com/petschbot/arduino-restapi [/UPDATE]


Ruby-Script
# This script combines the serialport gem with the rest-client gem
# purpose: trigger a REST-API-Call by a serial signal generated by a Arduino
# V1.1 Update: Alternative Exception handling


require "rubygems"
require "serialport"
require 'rest-client'
require 'yaml'


# reading the config yaml
config_file = ARGV[0] # first command line argument, e.g. config.yml
raw_config = File.read(config_file)
app_config = YAML.load(raw_config)


# params for serial port
port_str = ARGV[1] # second command line argument, e.g. /dev/tty.usbmodem411
baud_rate = 9600
data_bits = 8
stop_bits = 1

parity = SerialPort::NONE

# init serial port
sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity)


# params for rest client
url = app_config[:restclient][:url]
request_body = app_config[:restclient][:request_body]

sleep 10
puts "Ready!"

while true do
  
  sp_string = sp.gets
  
  # If the arduino send an "a" via the serial port start the REST Call
  if sp_string.to_s =~ /a/ then

      response = RestClient.post(url, request_body, :content_type => 'application/xml', :accept => 'application/xml'){|response, request, result| response }
      puts response

      # if the REST Call was sucessfull ACK by the server send a "1" to the Arduino
      if response.code == 200 then
        sp.write "1"
      else
        sp.write "0"
      end

  end
end

sp.close


Arduino-Script
 // Initalize variables

 int led_red = 12;
 int led_green = 10;
 int button = 8;
 int incoming = 0;


 void setup() {                
   // initialize the digital pins as output and input.
   pinMode(led_red, OUTPUT);
   pinMode(led_green, OUTPUT);
   pinMode(button, INPUT);

   // initialize the serial port
   Serial.begin(9600);  
 }

 // the loop routine runs over and over again forever:
 void loop() {

   // turn booth leds off
   digitalWrite(led_red, LOW);
   digitalWrite(led_green, LOW);


   // if button is pushed send an a and wait two sec
   // so the button can be raised again
   if (digitalRead(button) == HIGH) {     
     Serial.print("a");
     delay(2000);
   }

   incoming = Serial.read();

   // if an "0" is received turn on the red led for 5 seconds
   if (incoming == 48) {
     digitalWrite(led_red, HIGH);
     delay(5000);
   }

   // if an "1" is received turn on the green led for 5 seconds
   if (incoming == 49) {
     digitalWrite(led_green, HIGH);
     delay(5000);
   }


 } 

Und so sieht das ganze in der Hardware-Verdrahtung aus:

Keine Kommentare:

Kommentar veröffentlichen