In diesem kleinen Artikel wollen wir euch zeigen wir ihr eurem Arduino das Empfangen von OSC-Nachrichten beibringen könnt um einige Dinge zu steuern

Bei unserem Beispiel steuern wir eine RGB LED (BlinkM) an.

Zunächst muss man sich die library Z_OSC installieren, welche man auf folgender Seite findet: http://arduino.cc/playground/Interfacing/MaxMSP (ganz unten)

Ausserdem solltet ihr natürlich das Ethernet Shield verbunden haben und die Ethernet library installiert haben (standardmäßig installiert)

 

Arduino-Code

#include <Ethernet.h>
#include <SPI.h>
#include <Z_OSC.h>
#include "Wire.h"
#include "BlinkM_funcs.h"

// Mac: DE:AD:BE:EF:FE:ED
// IP: 172.168.0.30
// Port: 10000

byte myMac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte myIp[]  = { 172, 168, 0, 30 };
int  serverPort  = 10000;

// Variablen fuer RGB LED

int val;
int red;
int green;
int blue;

// BlinkM-Adresse

#define blinkm_addr 0x00

// Initialisierung des OSC-Servers

Z_OSCServer server;
Z_OSCMessage *rcvMes;

void setup(){ 
 // BlinkM starten
 BlinkM_beginWithPower();
 BlinkM_stopScript(blinkm_addr);
 // Netzwerk-Verbindung starten
 Ethernet.begin(myMac ,myIp);
 // OSC-Socket oeffnen 
 server.sockOpen(serverPort);
}

void loop(){
 // Wenn eine Nachricht am OSC-Server verfuegbar ist..
 if(server.available()){
   // ...wird diese ausgelesen...
   rcvMes=server.getMessage();
   // ...und in der Funktion 'doSomething()' verarbeitet.
   doSomething(rcvMes->getZ_OSCAddress(),rcvMes->getFloat(0));  
 }
}

void doSomething(String text,float val){
  // Diese Funktion bekommt im ersten Parameter 'text' den Pfad zur OSC-Variable und im zweiten Parameter 'val' den Wert dieser Variable uebergeben
  // Hier findet zunaechst nur ein Vergleich des Variablen-Namens mit ein paar vordefinierten Namen statt
  if(text=="/1/red"){
    red=val;
  }else if(text=="/1/green"){
    green=val;
  }else if(text=="/1/blue"){
    blue=val;
  }
  // Zu guter letzt wird beim verarbeiten natuerlich alles noch ans BlinkM gesendet
  BlinkM_setRGB(blinkm_addr,red,green,blue);
}

// Habe Fertig.

Zusätzlich wird die Datei BlinkM_funcs.h angelegt oder aus den BlinkM-Beispielen kopiert:

/*
 * BlinkM_funcs.h -- Arduino 'library' to control BlinkM
 * --------------
 *
 *
 * Note: original version of this file lives with the BlinkMTester sketch
 *
 * Note: all the functions are declared 'static' because 
 *       it saves about 1.5 kbyte in code space in final compiled sketch.  
 *       A C++ library of this costs a 1kB more.
 *
 * 2007-8, Tod E. Kurt, ThingM, http://thingm.com/
 *
 * version: 20081101
 *
 * history:
 *  20080101 - initial release
 *  20080203 - added setStartupParam(), bugfix receiveBytes() from Dan Julio
 *  20081101 - fixed to work with Arduino-0012, added MaxM commands,
 *             added test script read/write functions, cleaned up some functions
 *  20090121 - added I2C bus scan functions, has dependencies on private 
 *             functions inside Wire library, so might break in the future
 *  20100420 - added BlinkM_startPower and _stopPower
 *
 */

#include "WProgram.h"
#include "wiring.h"
#include "Wire.h"

extern "C" { 
#include "utility/twi.h"  // from Wire library, so we can do bus scanning
}

// format of light script lines: duration, command, arg1,arg2,arg3
typedef struct _blinkm_script_line {
  uint8_t dur;
  uint8_t cmd[4];    // cmd,arg1,arg2,arg3
} blinkm_script_line;

// Call this first (when powering BlinkM from a power supply)
static void BlinkM_begin()
{
  Wire.begin();                // join i2c bus (address optional for master)
}

/*
 * actually can't do this either, because twi_init() has THREE callocs in it too
 *
static void BlinkM_reset()
{
  twi_init();  // can't just call Wire.begin() again because of calloc()s there
}
*/

//
// each call to twi_writeTo() should return 0 if device is there
// or other value (usually 2) if nothing is at that address
// 
static void BlinkM_scanI2CBus(byte from, byte to, 
                              void(*callback)(byte add, byte result) ) 
{
  byte rc;
  byte data = 0; // not used, just an address to feed to twi_writeTo()
  for( byte addr = from; addr <= to; addr++ ) {
    rc = twi_writeTo(addr, &data, 0, 1);
    callback( addr, rc );
  }
}

//
//
static int8_t BlinkM_findFirstI2CDevice() 
{
  byte rc;
  byte data = 0; // not used, just an address to feed to twi_writeTo()
  for( byte addr=1; addr < 120; addr++ ) {  // only scan addrs 1-120
    rc = twi_writeTo(addr, &data, 0, 1);
    if( rc == 0 ) return addr; // found an address
  }
  return -1; // no device found in range given
}

// FIXME: make this more Arduino-like
static void BlinkM_startPowerWithPins(byte pwrpin, byte gndpin)
{
  DDRC |= _BV(pwrpin) | _BV(gndpin);  // make outputs
  PORTC &=~ _BV(gndpin);
  PORTC |=  _BV(pwrpin);
}

// FIXME: make this more Arduino-like
static void BlinkM_stopPowerWithPins(byte pwrpin, byte gndpin)
{
  DDRC &=~ (_BV(pwrpin) | _BV(gndpin));
}

//
static void BlinkM_startPower()
{
  BlinkM_startPowerWithPins( PORTC3, PORTC2 );
}

//
static void BlinkM_stopPower()
{
  BlinkM_stopPowerWithPins( PORTC3, PORTC2 );
}

// General version of BlinkM_beginWithPower().
// Call this first when BlinkM is plugged directly into Arduino
static void BlinkM_beginWithPowerPins(byte pwrpin, byte gndpin)
{
  BlinkM_startPowerWithPins(pwrpin,gndpin);
  delay(100);  // wait for things to stabilize
  Wire.begin();
}

// Call this first when BlinkM is plugged directly into Arduino
// FIXME: make this more Arduino-like
static void BlinkM_beginWithPower()
{
  BlinkM_beginWithPowerPins( PORTC3, PORTC2 );
}

// sends a generic command
static void BlinkM_sendCmd(byte addr, byte* cmd, int cmdlen)
{
  Wire.beginTransmission(addr);
  for( byte i=0; i<cmdlen; i++) 
    Wire.send(cmd[i]);
  Wire.endTransmission();
}

// receives generic data
// returns 0 on success, and -1 if no data available
// note: responsiblity of caller to know how many bytes to expect
static int BlinkM_receiveBytes(byte addr, byte* resp, byte len)
{
  Wire.requestFrom(addr, len);
  if( Wire.available() ) {
    for( int i=0; i<len; i++) 
      resp[i] = Wire.receive();
    return 0;
  }
  return -1;
}

// Sets the I2C address of the BlinkM.  
// Uses "general call" broadcast address
static void BlinkM_setAddress(byte newaddress)
{
  Wire.beginTransmission(0x00);  // general call (broadcast address)
  Wire.send('A');
  Wire.send(newaddress);
  Wire.send(0xD0);
  Wire.send(0x0D);  // dood!
  Wire.send(newaddress);
  Wire.endTransmission();
  delay(50); // just in case
}

// Gets the I2C address of the BlinKM
// Kind of redundant when sent to a specific address
// but uses to verify BlinkM communication
static int BlinkM_getAddress(byte addr)
{
  Wire.beginTransmission(addr);
  Wire.send('a');
  Wire.endTransmission();
  Wire.requestFrom(addr, (byte)1);  // general call
  if( Wire.available() ) {
    byte b = Wire.receive();
    return b;
  }
  return -1;
}

// Gets the BlinkM firmware version
static int BlinkM_getVersion(byte addr)
{
  Wire.beginTransmission(addr);
  Wire.send('Z');
  Wire.endTransmission();
  Wire.requestFrom(addr, (byte)2);
  if( Wire.available() ) {
    byte major_ver = Wire.receive();
    byte minor_ver = Wire.receive();
    return (major_ver<<8) + minor_ver;
  }
  return -1;
}

// Demonstrates how to verify you're talking to a BlinkM 
// and that you know its address
static int BlinkM_checkAddress(byte addr)
{
  //Serial.print("Checking BlinkM address...");
  int b = BlinkM_getAddress(addr);
  if( b==-1 ) {
    //Serial.println("No response, that's not good");
    return -1;  // no response
  } 
  //Serial.print("received addr: 0x");
  //Serial.print(b,HEX);
  if( b != addr )
    return 1; // error, addr mismatch 
  else 
    return 0; // match, everything okay
}

// Sets the speed of fading between colors.  
// Higher numbers means faster fading, 255 == instantaneous fading
static void BlinkM_setFadeSpeed(byte addr, byte fadespeed)
{
  Wire.beginTransmission(addr);
  Wire.send('f');
  Wire.send(fadespeed);
  Wire.endTransmission();  
}

// Sets the light script playback time adjust
// The timeadj argument is signed, and is an additive value to all
// durations in a light script. Set to zero to turn off time adjust.
static void BlinkM_setTimeAdj(byte addr, byte timeadj)
{
  Wire.beginTransmission(addr);
  Wire.send('t');
  Wire.send(timeadj);
  Wire.endTransmission();  
}

// Fades to an RGB color
static void BlinkM_fadeToRGB(byte addr, byte red, byte grn, byte blu)
{
  Wire.beginTransmission(addr);
  Wire.send('c');
  Wire.send(red);
  Wire.send(grn);
  Wire.send(blu);
  Wire.endTransmission();
}

// Fades to an HSB color
static void BlinkM_fadeToHSB(byte addr, byte hue, byte saturation, byte brightness)
{
  Wire.beginTransmission(addr);
  Wire.send('h');
  Wire.send(hue);
  Wire.send(saturation);
  Wire.send(brightness);
  Wire.endTransmission();
}

// Sets an RGB color immediately
static void BlinkM_setRGB(byte addr, byte red, byte grn, byte blu)
{
  Wire.beginTransmission(addr);
  Wire.send('n');
  Wire.send(red);
  Wire.send(grn);
  Wire.send(blu);
  Wire.endTransmission();
}

// Fades to a random RGB color
static void BlinkM_fadeToRandomRGB(byte addr, byte rrnd, byte grnd, byte brnd)
{
  Wire.beginTransmission(addr);
  Wire.send('C');
  Wire.send(rrnd);
  Wire.send(grnd);
  Wire.send(brnd);
  Wire.endTransmission();
}
// Fades to a random HSB color
static void BlinkM_fadeToRandomHSB(byte addr, byte hrnd, byte srnd, byte brnd)
{
  Wire.beginTransmission(addr);
  Wire.send('H');
  Wire.send(hrnd);
  Wire.send(srnd);
  Wire.send(brnd);
  Wire.endTransmission();
}

//
static void BlinkM_getRGBColor(byte addr, byte* r, byte* g, byte* b)
{
  Wire.beginTransmission(addr);
  Wire.send('g');
  Wire.endTransmission();
  Wire.requestFrom(addr, (byte)3);
  if( Wire.available() ) {
    *r = Wire.receive();
    *g = Wire.receive();
    *b = Wire.receive();
  }
}

//
static void BlinkM_playScript(byte addr, byte script_id, byte reps, byte pos)
{
  Wire.beginTransmission(addr);
  Wire.send('p');
  Wire.send(script_id);
  Wire.send(reps);
  Wire.send(pos);
  Wire.endTransmission();
}

//
static void BlinkM_stopScript(byte addr)
{
  Wire.beginTransmission(addr);
  Wire.send('o');
  Wire.endTransmission();
}

//
static void BlinkM_setScriptLengthReps(byte addr, byte script_id, 
                                       byte len, byte reps)
{
  Wire.beginTransmission(addr);
  Wire.send('L');
  Wire.send(script_id);
  Wire.send(len);
  Wire.send(reps);
  Wire.endTransmission();
}

// Fill up script_line with data from a script line
// currently only script_id = 0 works (eeprom script)
static void BlinkM_readScriptLine(byte addr, byte script_id, 
                                  byte pos, blinkm_script_line* script_line)
{
  Wire.beginTransmission(addr);
  Wire.send('R');
  Wire.send(script_id);
  Wire.send(pos);
  Wire.endTransmission();
  Wire.requestFrom(addr, (byte)5);
  while( Wire.available() < 5 ) ; // FIXME: wait until we get 7 bytes
  script_line->dur    = Wire.receive();
  script_line->cmd[0] = Wire.receive();
  script_line->cmd[1] = Wire.receive();
  script_line->cmd[2] = Wire.receive();
  script_line->cmd[3] = Wire.receive();
}

//
static void BlinkM_writeScriptLine(byte addr, byte script_id, 
                                   byte pos, byte dur,
                                   byte cmd, byte arg1, byte arg2, byte arg3)
{
#ifdef BLINKM_FUNCS_DEBUG
  Serial.print("writing line:");  Serial.print(pos,DEC);
  Serial.print(" with cmd:"); Serial.print(cmd); 
  Serial.print(" arg1:"); Serial.println(arg1,HEX);
#endif
  Wire.beginTransmission(addr);
  Wire.send('W');
  Wire.send(script_id);
  Wire.send(pos);
  Wire.send(dur);
  Wire.send(cmd);
  Wire.send(arg1);
  Wire.send(arg2);
  Wire.send(arg3);
  Wire.endTransmission();

}

//
static void BlinkM_writeScript(byte addr, byte script_id, 
                               byte len, byte reps,
                               blinkm_script_line* lines)
{
#ifdef BLINKM_FUNCS_DEBUG
  Serial.print("writing script to addr:"); Serial.print(addr,DEC);
  Serial.print(", script_id:"); Serial.println(script_id,DEC);
#endif
  for(byte i=0; i < len; i++) {
    blinkm_script_line l = lines[i];
    BlinkM_writeScriptLine( addr, script_id, i, l.dur,
                            l.cmd[0], l.cmd[1], l.cmd[2], l.cmd[3]);
    delay(20); // must wait for EEPROM to be programmed
  }
  BlinkM_setScriptLengthReps(addr, script_id, len, reps);
}

//
static void BlinkM_setStartupParams(byte addr, byte mode, byte script_id,
                                    byte reps, byte fadespeed, byte timeadj)
{
  Wire.beginTransmission(addr);
  Wire.send('B');
  Wire.send(mode);             // default 0x01 == Play script
  Wire.send(script_id);        // default 0x00 == script #0
  Wire.send(reps);             // default 0x00 == repeat infinitely
  Wire.send(fadespeed);        // default 0x08 == usually overridden by sketch 
  Wire.send(timeadj);          // default 0x00 == sometimes overridden by sketch
  Wire.endTransmission();
} 

// Gets digital inputs of the BlinkM
// returns -1 on failure
static int BlinkM_getInputsO(byte addr)
{
  Wire.beginTransmission(addr);
  Wire.send('i');
  Wire.endTransmission();
  Wire.requestFrom(addr, (byte)1);
  if( Wire.available() ) {
    byte b = Wire.receive();
    return b; 
  }
  return -1;
}

// Gets digital inputs of the BlinkM
// stores them in passed in array
// returns -1 on failure
static int BlinkM_getInputs(byte addr, byte inputs[])
{
  Wire.beginTransmission(addr);
  Wire.send('i');
  Wire.endTransmission();
  Wire.requestFrom(addr, (byte)4);
  while( Wire.available() < 4 ) ; // FIXME: wait until we get 4 bytes

  inputs[0] = Wire.receive();
  inputs[1] = Wire.receive();
  inputs[2] = Wire.receive();
  inputs[3] = Wire.receive();

  return 0;
}

 

Client

Jetzt benötigt man einen OSC Client. Wir haben bei unserem Test TouchOSC auf einem iPhone genutzt. Dazu haben wir im TouchOSC Editor 3 Slider angelegt mit den Namen „red“, „green“ und „blue“, welche sich auch so im Arduino-Code widerspiegeln. Es ist hilfreich sich per Serial ausgeben zu lassen welchen Pfad ein OSC-Client denn nun wirklich aufruft.

 

Sonstiges

Eigentlich ist es nicht wirklich nötig aber wir haben unser Ethernet-Shield mit einem Kühler und einem Lüfter ausgerüstet da uns der Netzwerk-Chip doch merkwürdig warm vorgekommen ist.