WiringOSC \ Learning \ Wiring ALPHA 1.0
Author: Hernando Barragán
Description: Wiring is an electronic sketchbook and hardware electronics for developing ideas. It is a context for learning fundamentals of computer programming and prototyping with electronics within the context of the electronic arts.
Keywords: Wiring, Processing, Interactive Media, Electronic Arts, Programming, C, C++, Hernando Barragán

Search wiring.org.co:

This example is for Wiring version 0023+. If you have a previous version, use the examples included with your software. If you see any errors or have comments, please let us know.

WIRING_OSC 0004
Firmware to send OSC messages from a Wiring board to a PC and to receive OSC messages sent from the PC.
Right now, only messages with a single integer argument are supported in either direction.
Uses the following serial OSC format: 0xBE MSG_LENGTH OSC_MESSAGE CHECKSUM ---- ---------- ---/.../-- -------- 1b 1byte 4*n bytes 1byte
PROTOCOL DETAILS Digital pins 0..39, analog inputs 0..7 are supported. PWM output on PWM pins 0..5 is supported as well. Below, the notation [0..39] means: any number from 0 to 39. The notation [0|1] means: either 0 or 1. Pin numbers are always part of the OSC address. The single integer argument for each OSC message represents either HIGH/LOW, or an 8bit analog value.
PC->WIRING MESSAGE FORMAT /pinmode/[0..39] [0|1] - set a pin to input or output mode
/report/adc [0|1] - turn all analog pin reporting on/off (Default: off) /report/adc/[0..7] [0|1] - set analog pin reporting for one pin on/off /report/in [0|1] - turn digital pin reporting on/off (default: on)
/out/[0..39] [0|1] - set a digital pin to [low|high] /pwm/[0..5] [0..1023] - set duty on a pwm-enabled pin
ARDUINO->PC PROTOCOL /in/[0..39] [0|1] - a digital input pin changed to [high|low] /adc/[0..7] [0..255] - analog input value changed to [0..255] NOTE: input pins use pull-up resistors and are HIGH by default. Therefore, 0 means HIGH, 1 means LOW (pulled to ground).
EXAMPLES: PC->ARDUINO /pinmode/5 0 - set pin 5 to INPUT /pinmode/9 1 - set pin 9 to OUTPUT /out/9 0 - set pin 9 to LOW /out/12 1 - set pin 12 to HIGH /pwm/4 512 - set PWM duty on PWM pin 4 to 512 (50%) /report/in 1 - turn digital input pin reporting on /report/adc/4 0 - turn reporting of analog input 4 off
EXAMPLES: WIRING->PC /in/4 1 - digital input pin 4 pulled to ground /adc/2 512 - analog input pin2 read 512 (=2.5V)
DEFAULT STARTUP CONFIGURATION - Pins 0-39 are all set to input, digital reporting enabled (change variable reportDigital to False to disable by default) - Analog reporting is disabled (change variable reportAnalog to 0xFF to enable by default)
NOTES: - Pins 32,33 cannot be used (needed for Rx/Tx) - Resolution on analog in and out is 10 bit. - So far, not much error checking for bounds done - In testing with pd and [serialIO], message bytes tend to get "stuck" in a buffer either on PC or on Wiring board side when sending to hardware, with the effect that an OSC message doesn't get processed on the board until the *next* message arrives, or until a few 0x00 characters are sent. This does not appear to happen wir Arduino_Osc. - On the tested board, pin Analog In 0 corresponds to analogRead(7) and pins 1..7 correspond to analogRead(0)..analogRead(6).
MIT License: Copyright (c) 2008 Bjoern Hartmann, Stanford HCI Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
bjoern@cs.stanford.edu 11/23/2008

    #define VERSION 4

    #define MIN_A2D_DIFF 4  // threshold for reporting a2d changes
    #define MAX_LENGTH 24   // size of buffer for building OSC msgs
    #define SERIAL_SPEED 38400

    #define FIRST_DIGITAL_PIN 0
    #define LAST_DIGITAL_PIN 39
    #define NUM_DIGITAL_PINS 40
    #define NUM_PORTS 5

    #define FIRST_ANALOG_PIN 0
    #define LAST_ANALOG_PIN 7
    #define NUM_ANALOG_PINS 8
    #define RX_PIN 32
    #define TX_PIN 33

    // define state constants for parsing FSM
    #define OSC_RXOP_WAITFORSTART 0
    #define OSC_RXOP_READSIZE 1
    #define OSC_RXOP_READADDR 2
    #define OSC_RXOP_READTAG 3
    #define OSC_RXOP_READARG 4
    #define OSC_RXOP_READCHECKSUM 5
    //removed READMSG
    #define OSC_RXOP_SKIPMSG 7
    #define OSC_RXOP_READARG1BYTE1 8
    #define OSC_RXOP_READARG1BYTE2 9
    #define OSC_RXOP_READARG1BYTE3 10
    #define OSC_RXOP_READARG1BYTE4 11
    #define OSC_RXOP_READTAGBYTE1 12
    #define OSC_RXOP_READTAGBYTE2 13
    #define OSC_RXOP_READTAGBYTE3 14
    #define OSC_RXOP_READTAGBYTE4 15
    #define OSC_MAX_RX_MSG_SIZE 32

    int incomingByte = 0;	// for incoming serial data
    int k = FIRST_ANALOG_PIN;

    byte inputBuffer[NUM_PORTS] = {0xFF}; // holds previous values of PORTB and PORTD (pins 0..7); start all high because of pull-ups
    int a2dBuffer[NUM_ANALOG_PINS] = {0x00};   // holds previous A2D conversion values
    char oscBuffer[MAX_LENGTH]={0x00}; // holds outgoing OSC message

    byte pinDir[NUM_PORTS] = {0x00}; //buffer that saves pin directions 0=input; 1=output; default: all in

    char prefixReport[] = "/report/";
    char prefixPinmode[] = "/pinmode/";
    char prefixOut[] = "/out/";
    char prefixPwm[] = "/pwm/";
    char prefixIn[]="/in/";
    char prefixA2d[]="/adc/";
    char prefixReset[]="/reset"; //TODO: implement

    char oscOutAddress[10]={0x00}; //string that holds outgoing osc message address

    char* numbers[] = {"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16",
                       "17","18","19","20","21","22","23","24","25","26","27","28","29","30",
                     "31","32","33","34","35","36","37","38","39"};
    byte pwmPinMap[6] = {37, 36, 35, 31, 30, 29};
    // which values should be reported?
    byte reportAnalog = 0x00; //bitmask - 0=off, 1=on - default:all off
    boolean reportDigital = true; //no per-pin reporting for analog

    //////parser variables////////
    static byte oscRxNextOp = OSC_RXOP_WAITFORSTART; //keeps track of current state
    // space for buffer in RAM
    static char oscRxData[OSC_MAX_RX_MSG_SIZE];

    byte oscRxMsgSize; // size of incoming msg
    byte oscRxReadBytes; //number of bytes read
    byte *oscRxAddr;
    unsigned long oscRxIntArg1; //int argument of message
    byte oscRxChecksum;

    /***********************************************
     * SETUP - open serial comm and initialize pins
     ***********************************************/
    void setup() {
      int i;

      reportAnalog=0x00;
      reportDigital=true;

      // set all pins as inputs
      for(i=FIRST_DIGITAL_PIN; i<=LAST_DIGITAL_PIN; i++) {
        if((i!=RX_PIN) && (i != TX_PIN)) {
          pinMode(i,INPUT);
          digitalWrite(i,HIGH); // use pull-ups
        }
      }

      Serial.begin(SERIAL_SPEED);

    }

    /***********************************************
     * LOOP - poll pin values and read incoming
     * serial communication
     ***********************************************/
    void loop() {

      // check all digital inputs
      if(reportDigital) {
        checkDiscreteInputs();
      }

      // check one analog input per loop
      if(reportAnalog & (1<<k)) {
        checkAnalogInput(k);
      }
      k=(k+1)%NUM_ANALOG_PINS;

      // handle all received serial bytes
      while (Serial.available() > 0) {
        incomingByte = Serial.read() & 0xFF;// read byte - truncate to 8bits to be safe
        oscRxHandler(incomingByte); // hand to message parser
      }

    }

    /***********************************************
     * Check all digital inputs and call
     * oscSendMessageInt() if values has changed
     ***********************************************/
    void checkDiscreteInputs() {
      int i;
      byte state[NUM_PORTS] = {0x00};
      //WIRING: read all ports into buffer
      for(i=0; i<NUM_PORTS; i++) {
        state[i]= portRead(i);
      }

      // if the state of a pin has changed since last time,
      // and that pin is an input pin, send a message
      for(i=FIRST_DIGITAL_PIN;i<=LAST_DIGITAL_PIN;i++) {
        if(!(pinDir[i/8] & (1<<(i%8))) && (i!=RX_PIN) && (i!=TX_PIN)) { //if pin is input and not RX/TX
          if  ((state[i/8] & (1<<(i%8))) != (inputBuffer[i/8] &(1<<(i%8)))) { //does this shift work?
            strcpy(oscOutAddress,prefixIn);
            strcat(oscOutAddress,numbers[i]);
            oscSendMessageInt(oscOutAddress, !(state[i/8] & (1<<(i%8))));
          }
        }
      }

      //save current state to buffer
      for(i=0; i<NUM_PORTS; i++) {
        inputBuffer[i]=state[i];
      }
    }

    /***********************************************
     * Check one analog input channel and call
     * oscSendMessageInt() if its value has changed
     ***********************************************/
    void checkAnalogInput(byte channel) {
      int result;
      int diff;
      // read a2d
      result = analogRead(channel); // >>2 on arduino

      // compare to last reading - if delta big enough,
      // send message
      //diff = result - a2dBuffer[channel];
      //if(diff>MIN_A2D_DIFF || diff<(int)((-1)*MIN_A2D_DIFF)) {
      if(result!=a2dBuffer[channel]) {
        a2dBuffer[channel]=result;
        strcpy(oscOutAddress,prefixA2d);
        strcat(oscOutAddress,numbers[channel]);
        oscSendMessageInt(oscOutAddress, result);
      }
    }

    /***********************************************
     * Send an OSC message with the passed in
     * address and a single integer argument
     ***********************************************/
    void oscSendMessageInt(char * address, unsigned long value){
      byte offset=0;
      byte i=0;

      // clear buffer
      for(i=0; i<MAX_LENGTH; i++) {
        oscBuffer[i]=0x00;
      }

      //write packet header
      oscBuffer[offset++]=0xBE;

      //compute message length
      //first compute address string length and padd if necessary
      byte addrlen = strlen(address);
      if(addrlen&0x03)
        addrlen += 4-(addrlen&0x03);

      //then add type-tag length and arg length (both 4 for a simple int message)
      byte typetaglen=4;
      byte arglen = 4;

      //final length is sum of the three
      byte len = addrlen+typetaglen+arglen;

      //write message length
      oscBuffer[offset++]=(unsigned char)len;

      //write address
      strcpy(oscBuffer+offset,address);
      offset+=addrlen;

      //write typetag
      oscBuffer[offset++]=',';
      oscBuffer[offset++]='i';
      oscBuffer[offset++]=0x00;
      oscBuffer[offset++]=0x00;

      //write argument
      oscBuffer[offset++]=*(((unsigned char *)(&value))+3);
      oscBuffer[offset++]=*(((unsigned char *)(&value))+2);
      oscBuffer[offset++]=*(((unsigned char *)(&value))+1);
      oscBuffer[offset++]=*(((unsigned char *)(&value))+0);

      //compute + write checksum
      byte checksum=0;
      for(i=2; i<offset; i++) {
        checksum+=oscBuffer[i];
      }
      oscBuffer[offset++]=checksum;

      //send message byte-by-byte
      for(i=0;i<offset;i++) {
        Serial.print(oscBuffer[i],BYTE);
      }
    }

    /***********************************************
     * Handle a received OSC message
     ***********************************************/
    void oscReceiveMessageInt(char * msg, unsigned long value)
    {
      int i;
      int outPin;

      //uncomment to echo message back for debugging
      oscSendMessageInt(msg,value);

      // check if this is an output message, i.e., starts with "/out/"
      if(strncmp(msg,prefixOut,strlen(prefixOut))==0) {
        //if so, find which pin
        outPin = atoi(msg+strlen(prefixOut));
        if(outPin>=FIRST_DIGITAL_PIN && outPin<=LAST_DIGITAL_PIN && outPin!=RX_PIN && outPin!=TX_PIN) { //sanity check
          //change its value
          //note: pin can be set to input - this enables/disables pullups
          digitalWrite(outPin,(byte)(value & 0x01));
        }
        return;
      }

      // check if this is a pwm message, i.e., starts with "/pwm/"
      if(strncmp(msg,prefixPwm,strlen(prefixPwm))==0) {
        outPin = atoi(msg+strlen(prefixPwm));
        if(outPin>=FIRST_DIGITAL_PIN && outPin<=LAST_DIGITAL_PIN  && outPin!=RX_PIN && outPin!=TX_PIN) { //sanity check
          //make sure we turn pin into output in our pinDir first
          //so we don't generate lots of extraneous messages

          //may need to map from 0..5 to 29..37 range as well
          if(0<=outPin && 5 >= outPin) {
            pinDir[pwmPinMap[outPin]/8] =
              pinDir[pwmPinMap[outPin]/8] |  (1<<(pwmPinMap[outPin]%8));
          } else {
            pinDir[outPin/8] = pinDir[outPin/8] | (1<<(outPin%8));
          }

          //set pwm
          analogWrite(outPin,value&1023);
        }
        return;
      }

      // check if this is a "report" message which starts with "/report/"
      // changes which pins get reported
      if(strncmp(msg,prefixReport,strlen(prefixReport))==0) {
        //if it continues with "/in"

        if(strncmp(msg+strlen(prefixReport)-1,prefixIn,strlen(prefixIn)-1)==0) {
          reportDigital = (value!=0);
          return;
        }

        //else if it continues with "/adc/"
        else if(strncmp(msg+strlen(prefixReport)-1,prefixA2d,strlen(prefixA2d))==0) {
          //extract which analog pin we're talking about
          outPin = atoi(msg+strlen(prefixReport)-1+strlen(prefixA2d));
          //flip the bit in reportAnalog:
          if(outPin>=FIRST_ANALOG_PIN && outPin<=LAST_ANALOG_PIN) { //sanity check
            if(value==0) {
              reportAnalog = reportAnalog & ~(1<<outPin);
            }
            else {
              reportAnalog = reportAnalog | (1<<outPin);
            }
          }
          return;
        }

        //else if it continues with "/adc" (no final slash)
        else if(strncmp(msg+strlen(prefixReport)-1,prefixA2d,strlen(prefixA2d)-1)==0) {
          //turn reporting for all analog pins on or off
          if(value==0) {
            reportAnalog=0x00;
          }
          else {
            reportAnalog=0xFF;
          }
          return;
        }

      }

      //finally, this could be a "/pinmode/ message"
      if(strncmp(msg,prefixPinmode,strlen(prefixPinmode))==0) {
        outPin = atoi(msg+strlen(prefixPinmode));
        if(outPin>=FIRST_DIGITAL_PIN && outPin<=LAST_DIGITAL_PIN) { //sanity check
          if(value==0) {
            pinDir[outPin/8] = pinDir[outPin/8] & ~(1<<(outPin%8)); //turn bit in our own direction buffer to off = input
            pinMode(outPin,INPUT); //set DDR register bit to input
            digitalWrite(outPin,HIGH); //reenable pull-up
          }
          else {
            pinDir[outPin/8] = pinDir[outPin/8] | (1<<(outPin%8)); //turn bit on
            pinMode(outPin,OUTPUT); // turn DDR bit to output
          }
        }
        return;
      }
      //is this a reset message? if so, reinitialize.
      if(strncmp(msg,prefixReset,strlen(prefixReset))==0) {
        oscRxNextOp = OSC_RXOP_WAITFORSTART;
        setup();
      }
    }

    /***********************************
     * PARSING OF MESSAGES BELOW
     ***********************************/

    /***********************************
     * PARSER
     ***********************************/
    void oscRxHandler(unsigned char c) {
      byte i;
      switch(oscRxNextOp) {
      case OSC_RXOP_WAITFORSTART:
        if(c==0xBE) { //0xBE is the magic start byte
          oscRxNextOp = OSC_RXOP_READSIZE;
        }
        break;

      case OSC_RXOP_READSIZE:
        oscRxMsgSize = c; // read message size
        oscRxReadBytes = 0; //reset index into message buffer
        if(oscRxMsgSize < (OSC_MAX_RX_MSG_SIZE-1)) {
          oscRxNextOp = OSC_RXOP_READADDR;
        }
        else {
          oscRxNextOp = OSC_RXOP_SKIPMSG; //Msg is too long
        }
        break;

      case OSC_RXOP_READADDR:
        if(c!=0 && c!=',') {
          oscRxData[oscRxReadBytes++] = c; //copy normal addr byte
        }
        else if (c==',') {
          //if we went straight from addr to to tag string, convert "," of tag string to 0x00
          // so our address string is properly zero-terminated
          //and jump ahead in the state machine to read second tag byte next
          oscRxData[oscRxReadBytes++]=0x00;
          oscRxNextOp = OSC_RXOP_READTAGBYTE2;
        }
        else {
          oscRxData[oscRxReadBytes++] = 0;
          if(!((oscRxReadBytes)&0x03)) { //skip 0s until we hit byte boundary
            oscRxNextOp = OSC_RXOP_READTAGBYTE1;
          }
        }
        break;

        // read type tag string bytes 1-4
      case OSC_RXOP_READTAGBYTE1:
        oscRxData[oscRxReadBytes++] = c;
        if(c==',') {
          // valid type tag start character found
          oscRxNextOp = OSC_RXOP_READTAGBYTE2;
        }
        else {
          // no type tag start char present
          // assume one int arg and read its first byte
          oscRxIntArg1 = 0;
          oscRxIntArg1 |= ((unsigned long)(c) << 0x18);
          oscRxNextOp = OSC_RXOP_READARG1BYTE2;
        }
        break;
      case OSC_RXOP_READTAGBYTE2:
        oscRxData[oscRxReadBytes++] = c;
        switch (c) {
        case 'i': //int32 argument
          oscRxNextOp = OSC_RXOP_READTAGBYTE3;
          break;
          // all other types besides int are not supported yet
          // so skip remainder of message
        case 'f': //float arg
        case 's': //string arg
        default:
          oscRxNextOp = OSC_RXOP_SKIPMSG;
        }
        break;
      case OSC_RXOP_READTAGBYTE3:
        oscRxData[oscRxReadBytes++] = c;
        switch (c) {
          // null - message has only one arg - we're ok
        case 0:
          oscRxNextOp = OSC_RXOP_READTAGBYTE4;
          break;
          // all others: not yet supported; skip rest of message
        default:
          oscRxNextOp = OSC_RXOP_SKIPMSG;
          break;
        }
        break;
      case OSC_RXOP_READTAGBYTE4:
        oscRxData[oscRxReadBytes++] = c;
        switch (c) {
          // null - message has only one arg - we're ok
        case 0:
          oscRxNextOp = OSC_RXOP_READARG1BYTE1;
          break;
          // all others: not yet supported; skip rest of message
        default:
          oscRxNextOp = OSC_RXOP_SKIPMSG;
          break;
        }
        break;

        // read argument bytes 1-4
      case OSC_RXOP_READARG1BYTE1:
        oscRxData[oscRxReadBytes++] = c;
        oscRxIntArg1 = 0;
        oscRxIntArg1 |= ((unsigned long)(c) << 0x18);
        oscRxNextOp = OSC_RXOP_READARG1BYTE2;
        break;
      case OSC_RXOP_READARG1BYTE2:
        oscRxData[oscRxReadBytes++] = c;
        oscRxIntArg1 |= ((unsigned long)(c) << 0x10);
        oscRxNextOp = OSC_RXOP_READARG1BYTE3;
        break;
      case OSC_RXOP_READARG1BYTE3:
        oscRxData[oscRxReadBytes++] = c;
        oscRxIntArg1 |= ((unsigned long)(c) << 0x08);
        oscRxNextOp = OSC_RXOP_READARG1BYTE4;
        break;
      case OSC_RXOP_READARG1BYTE4:
        oscRxData[oscRxReadBytes++] = c;
        oscRxIntArg1 |= (unsigned long)(c);
        oscRxNextOp = OSC_RXOP_READCHECKSUM;
        break;

        // read checksum byte; check msg integrity; fire off user function
      case OSC_RXOP_READCHECKSUM:
        // check that we read the right number of bytes
        if (oscRxReadBytes != oscRxMsgSize) {
          oscSendMessageInt("/error/msgsize/",oscRxReadBytes);
        }
        else {
          oscRxChecksum = 0;
          for (i=0; i<oscRxMsgSize; i++) {
            oscRxChecksum+=oscRxData[i];
          }
          if(oscRxChecksum == c) {
            // checksum matched and we're done with this msg
            // -> fire off user function
            oscReceiveMessageInt(oscRxData,oscRxIntArg1);

          }
          else {
            // mismatch - throw this message away
            oscSendMessageInt("/error/checksum",oscRxChecksum);
          }
        }
        // wait for next message header
        oscRxNextOp = OSC_RXOP_WAITFORSTART;
        break;

        //skip rest of message - called if an error was detected in the current
        //incoming message
      case OSC_RXOP_SKIPMSG:
        if(++oscRxReadBytes == (oscRxMsgSize+1)) {
          oscRxNextOp = OSC_RXOP_WAITFORSTART;
        }
        break;
      default:
        oscRxNextOp = OSC_RXOP_WAITFORSTART;
      }
    }