├── .gitignore ├── README ├── TODO ├── distribution ├── arduino-osc-1.zip ├── arduino-osc-beta.zip └── pack.sh ├── examples ├── serialBasics │ └── serialBasics.ino └── udpBasics │ └── udpBasics.ino ├── extras └── processing │ ├── README │ ├── build.xml │ ├── examples │ ├── Processing_SerialBasics │ │ └── Processing_SerialBasics.pde │ └── Processing_udpBasics │ │ └── Processing_udpBasics.pde │ ├── library │ └── OscSerial.jar │ ├── libs │ ├── RXTXcomm.jar │ ├── core.jar │ ├── oscp5.jar │ └── serial.jar │ └── src │ └── oscP5 │ └── OscSerial.java ├── keywords.txt ├── library.properties ├── src ├── OSCData.cpp ├── OSCData.h ├── OSCMatch.c ├── OSCMatch.h ├── OSCMessage.cpp ├── OSCMessage.h ├── OscSerial.cpp ├── OscSerial.h ├── OscUDP.cpp ├── OscUDP.h ├── SLIPEncodedSerial.cpp └── SLIPEncodedSerial.h └── temp ├── OSCBundle.cpp ├── OSCBundle.h ├── README ├── SLIPEncodedUSBSerial.cpp └── SLIPEncodedUSBSerial.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | OSCuino is an Arduino and Teensy library implementation of the OSC encoding (http://opensoundcontrol.org) 2 | It was developed primarily by Yotam Mann at CNMAT where OSC was invented. 3 | It benefits from contributions from Adrian Freed, John MacCallum, Matt Wright and Andy Schmeder 4 | 5 | This release: 6 | 7 | - Supports the four basic OSC data types (32-bit integers, 32-bit floats, strings, and blobs - arbitrary length byte sequences) 8 | - Sends and receives OSC packets over any transport layer that implements the Arduino Stream - Class such as Serial, EthernetUdp, MIDI, and more. 9 | - Tested on Arduino Ethernet, Arduino Leonardo, Teensy 10 | - Address pattern matching 11 | - Dynamic memory allocation 12 | 13 | Features for next time: 14 | - nested bundles 15 | - TCP/IP Examples 16 | - deadline scheduling of OSC 64-bit timetags 17 | - Due support 18 | - ADK support 19 | 20 | 21 | API Documentation and examples are available at http://cnmat.berkeley.edu/oscuino 22 | 23 | //////////////////////////////////////////////////////////////////////// 24 | SLIPSerial 25 | 26 | The library includes extensions of the USB serial and Hardware serial functions of the Arduino which sends and receives data using the SLIP encoding This makes Max/MSP and PDintegration very simple using CNMAT's slipOSC object connected to the Serial object. The SLIPSerial library implements the same methods as the Serial object except for an additional "endPacket" method which transmits for marking the end of teach packet in a serial stream. 27 | 28 | //////////////////////////////////////////////////////////////////////// 29 | Installation 30 | 31 | Make sure you're using Arduino 1.0.4 or greater. 32 | Download the zip from github. Unzip it and remove text after the "-" in the name (i.e. -master) because 33 | Arduino doesn't allow some characters in library names. 34 | Move the OSCuino folder into your "libraries" folder in your Arduino Sketch search path. 35 | Now you will see OSCuino examples under the Examples menu of Arduino. 36 | 37 | 38 | The Applications folder contains examples for Max/MSP and PD that work with the example sketches. This will be expanded to include other applications like TouchOSC and Processing 39 | For the Max/MSP examples you will need to download the CNMAT max externals package that includes the odot objects available at http://cnmat.berkeley.edu/downloads 40 | //////////////////////////////////////////////////////////////////////// 41 | Guide 42 | 43 | As well as many small examples illustrating the API, there is a larger application called "oscuino" 44 | which illustrates how to use OSC to simplify situations Firmata and Maxuino are typically used in. 45 | //////////////////////////////////////////////////////////////////////// 46 | Performance 47 | 48 | Currently best performance is achieved with Arduinos with built-in USB Serial, i.e. Teensy 3.0, Teensy 2.0 and 2.0++ and Leanardo variants (12Mbps max) 49 | 50 | This is because the Wiznet 5100 used in the Ethernet Arduino and shields uses really slow SPI (0.3Mbps) 51 | This will change as people retool to use the much faster Wiznet 5200 which has been measured with the Due at 6Mbps 52 | http://forum.pjrc.com/threads/17951-WIZ820io-Ethernet-and-2nd-power-supply-with-teensy-3-0 53 | http://arduino.cc/forum/index.php?topic=139147.0 54 | 55 | The oscuino serial examples use 9600 baud serial which is reliable on most of the FTDI based Arduinos. 56 | The slow rate is required for Arduino's without clock chips such as the TinyLili. 57 | Once you have established that things work at 9600 baud try increasing the rate. 58 | e.g. Serial.begin(345600); // !! 115200, 230400, 345600, 460800 X 59 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Rename classes to match OscP5 capitalization 2 | Rename "dispatch" to "plug", move to Osc main class 3 | What to do when oscEvent is not present -------------------------------------------------------------------------------- /distribution/arduino-osc-1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbouchard/arduino-osc/bcd93963d3d7bdc20f2f4f9caaecd14f38e38845/distribution/arduino-osc-1.zip -------------------------------------------------------------------------------- /distribution/arduino-osc-beta.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbouchard/arduino-osc/bcd93963d3d7bdc20f2f4f9caaecd14f38e38845/distribution/arduino-osc-beta.zip -------------------------------------------------------------------------------- /distribution/pack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir osc 3 | rm -v osc/* 4 | cp ../*.cpp osc/ 5 | cp ../*.h osc/ 6 | cp ../*.c osc/ 7 | cp ../README osc/ 8 | cp ../keywords.txt osc/ 9 | cp -r ../examples osc/ 10 | 11 | rm arduino-osc-$1.zip 12 | zip -r arduino-osc-$1.zip osc 13 | -------------------------------------------------------------------------------- /examples/serialBasics/serialBasics.ino: -------------------------------------------------------------------------------- 1 | 2 | // MESSAGE PROTOCOL OBJECT 3 | #include 4 | 5 | // tile support libraries 6 | #include 7 | #include 8 | 9 | OscSerial oscSerial; 10 | long timer; 11 | 12 | void setup() { 13 | Serial.begin(9600); 14 | oscSerial.begin(Serial); 15 | pinMode(13, OUTPUT); 16 | } 17 | 18 | void loop() { 19 | // send a message every 100 ms 20 | // avoid using delay() since it just blocks everything 21 | long now = millis(); 22 | if (now-timer > 100) { 23 | 24 | OscMessage msg("/helloFromArduino"); 25 | msg.add(0); // <-- this could be any data 26 | oscSerial.send(msg); 27 | timer = now; 28 | } 29 | 30 | // important! 31 | oscSerial.listen(); 32 | 33 | } 34 | 35 | void oscEvent(OscMessage &m) { // *note the & before msg 36 | // receive a message 37 | m.plug("/led", myFunction); 38 | } 39 | 40 | void myFunction(OscMessage &m) { // *note the & before msg 41 | // getting to the message data 42 | int value = m.getInt(0); 43 | if (value == 0) digitalWrite(13, LOW); 44 | if (value == 1) digitalWrite(13, HIGH); 45 | } 46 | -------------------------------------------------------------------------------- /examples/udpBasics/udpBasics.ino: -------------------------------------------------------------------------------- 1 | 2 | // MESSAGE PROTOCOL OBJECT 3 | #include 4 | 5 | 6 | // hardware libraries to access use the shield 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | // arduino needs us to start ethernet, then start UDP 13 | // http://arduino.cc/en/Reference/EthernetBegin 14 | 15 | 16 | // Enter a MAC address and IP address for your SHIELD. 17 | // The IP address will be dependent on your local network 18 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 19 | 20 | // listeningIP == SHIELD initialization IP 21 | IPAddress listeningIP(141,117,45,176); // you need to set this 22 | 23 | // listening -- port 24 | unsigned int listeningPort = 12001; // local port to listen on 25 | 26 | 27 | // speaking 28 | // set up our destination NODE 29 | NetAddress destination; 30 | IPAddress destinationIP( 141,117,45,82 ); 31 | int destinationPort = 12000; 32 | 33 | 34 | 35 | 36 | // setup a UDP object 37 | EthernetUDP UDP; 38 | 39 | // OUR OSC MESSAGE OBJECT 40 | OscUDP etherOSC; 41 | 42 | // timer variable 43 | long timer; 44 | 45 | 46 | 47 | void setup() { 48 | 49 | // some local debug -- we can see this in serial port 50 | Serial.begin(9600); 51 | 52 | // start ethernet on the shield 53 | Ethernet.begin(mac,listeningIP); 54 | 55 | // print Arduino's IP 56 | Serial.println(Ethernet.localIP()); 57 | 58 | // start UDP object, listening on port listeningPort 59 | UDP.begin(listeningPort); 60 | 61 | // set up our communication protocol 62 | // pass the UDP object to OSC 63 | etherOSC.begin(UDP); 64 | 65 | // define our destination location 66 | destination.set(destinationIP, destinationPort); 67 | 68 | // local hardware setup 69 | pinMode(2, OUTPUT); // note can't use 13 for UDP becasue of shield 70 | Serial.println("starting"); 71 | 72 | } 73 | 74 | void loop() { 75 | // send a message every 100 ms 76 | 77 | // avoid using delay() since it just blocks everything 78 | // so here is a simple timer that controls how often we send 79 | long now = millis(); 80 | if (now-timer > 100) { 81 | // build a message, start with address pattern 82 | OscMessage msg("/arduinoEthernetSays"); 83 | 84 | // add a data point (can add many) 85 | msg.add(digitalRead(2)); // <-- this could be any data -- its our LED state 86 | 87 | // actually do the sending 88 | etherOSC.send(msg, destination); 89 | 90 | // reset the timer 91 | timer = now; 92 | 93 | // local debug -- you can get rid of this 94 | Serial.println("send one"); 95 | } // end if 96 | 97 | 98 | // important! non-blocking listen routine 99 | etherOSC.listen(); // if there is data waiting, this will trigger OSC EVENT 100 | 101 | } 102 | 103 | 104 | void oscEvent(OscMessage &m) { // *note the & before msg 105 | // receive a message 106 | Serial.println("got a message"); 107 | // in arduino, we plug events in the EVENT method 108 | m.plug("/led", myFunction); 109 | 110 | } 111 | 112 | 113 | void myFunction(OscMessage &m) { // *note the & before msg 114 | // getting to the message data 115 | Serial.println("led"); 116 | 117 | // getType takes position as a parameter 118 | int value = m.getInt(0); 119 | if (value == 0) digitalWrite(2, LOW); 120 | if (value == 1) digitalWrite(2, HIGH); 121 | 122 | } 123 | -------------------------------------------------------------------------------- /extras/processing/README: -------------------------------------------------------------------------------- 1 | Processing class to send/receive OSC over SLIP encoded serial. 2 | 3 | Uses the OscMessage object from the OscP5 library and follows the same 4 | usage pattern. 5 | 6 | -------------------------------------------------------------------------------- /extras/processing/build.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /extras/processing/examples/Processing_SerialBasics/Processing_SerialBasics.pde: -------------------------------------------------------------------------------- 1 | import oscP5.*; 2 | import netP5.Logger; 3 | import processing.serial.*; 4 | 5 | Serial port; 6 | OscSerial osc; 7 | 8 | int ledSTATE = 0; 9 | 10 | 11 | void setup() { 12 | println(Serial.list()); 13 | port = new Serial(this, Serial.list()[8], 9600); 14 | osc = new OscSerial(this, port); 15 | 16 | osc.plug(this,"plugTest", "/helloFromArduino"); 17 | } 18 | 19 | void draw() { 20 | background(ledSTATE*255); 21 | } 22 | 23 | void mousePressed() { 24 | OscMessage msg = new OscMessage("/led"); 25 | ledSTATE = 1 - ledSTATE; 26 | msg.add(ledSTATE); 27 | osc.send(msg); 28 | 29 | } 30 | 31 | void plugTest(int value) { 32 | println("Plugged from /helloFromArduino: " + value); 33 | } 34 | 35 | void oscEvent(OscMessage theMessage) { 36 | println("Message: " + theMessage + ", " + theMessage.isPlugged()); 37 | } 38 | -------------------------------------------------------------------------------- /extras/processing/examples/Processing_udpBasics/Processing_udpBasics.pde: -------------------------------------------------------------------------------- 1 | /* 2 | * oscP5sendreceive by andreas schlegel 3 | * example shows how to send and receive osc messages. 4 | * oscP5 website at http://www.sojamo.de/oscP5 5 | */ 6 | 7 | // YOU WILL NEED TO CONNECT YOUR LAPTOP VIA ETHERNET TO RECEIVE IN PROCESSING 8 | 9 | 10 | import oscP5.*; 11 | import netP5.*; 12 | 13 | OscP5 oscP5; 14 | 15 | // listening 16 | int iNet_myListeningPort = 12000; // port I am listening on 17 | 18 | // speaking 19 | NetAddress iNet_DestinationAddress; // someone to talk to 20 | String iNet_DestinationIP = "141.117.45.176"; 21 | int iNet_DestinationPort = 12001; 22 | 23 | 24 | int ledState = 0; 25 | 26 | void setup() { 27 | size(400,400); 28 | frameRate(25); 29 | /* start oscP5, listening for incoming messages at port 12000 */ 30 | oscP5 = new OscP5(this,iNet_myListeningPort); 31 | 32 | /* myRemoteLocation is a NetAddress. a NetAddress takes 2 parameters, 33 | * an ip address and a port number. myRemoteLocation is used as parameter in 34 | * oscP5.send() when sending osc packets to another computer, device, 35 | * application. usage see below. for testing purposes the listening port 36 | * and the port of the remote location address are the same, hence you will 37 | * send messages back to this sketch. 38 | */ 39 | iNet_DestinationAddress = new NetAddress( iNet_DestinationIP, iNet_DestinationPort ); 40 | } 41 | 42 | 43 | void draw() { 44 | background(ledState *255 ); 45 | } 46 | 47 | void mousePressed() { 48 | /* in the following different ways of creating osc messages are shown by example */ 49 | OscMessage myMessage = new OscMessage("/led"); 50 | ledState = 1 - ledState; 51 | myMessage.add(ledState); /* add an int to the osc message */ 52 | 53 | /* send the message */ 54 | oscP5.send(myMessage, iNet_DestinationAddress); 55 | } 56 | 57 | 58 | /* incoming osc message are forwarded to the oscEvent method. */ 59 | void oscEvent(OscMessage theOscMessage) { 60 | /* print the address pattern and the typetag of the received OscMessage */ 61 | print("### received an osc message."); 62 | print(" addrpattern: "+theOscMessage.addrPattern()); 63 | println(" typetag: "+theOscMessage.typetag()); 64 | 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /extras/processing/library/OscSerial.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbouchard/arduino-osc/bcd93963d3d7bdc20f2f4f9caaecd14f38e38845/extras/processing/library/OscSerial.jar -------------------------------------------------------------------------------- /extras/processing/libs/RXTXcomm.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbouchard/arduino-osc/bcd93963d3d7bdc20f2f4f9caaecd14f38e38845/extras/processing/libs/RXTXcomm.jar -------------------------------------------------------------------------------- /extras/processing/libs/core.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbouchard/arduino-osc/bcd93963d3d7bdc20f2f4f9caaecd14f38e38845/extras/processing/libs/core.jar -------------------------------------------------------------------------------- /extras/processing/libs/oscp5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbouchard/arduino-osc/bcd93963d3d7bdc20f2f4f9caaecd14f38e38845/extras/processing/libs/oscp5.jar -------------------------------------------------------------------------------- /extras/processing/libs/serial.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidbouchard/arduino-osc/bcd93963d3d7bdc20f2f4f9caaecd14f38e38845/extras/processing/libs/serial.jar -------------------------------------------------------------------------------- /extras/processing/src/oscP5/OscSerial.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A library adding OSC over Serial support for the Processing 3 | * environment. 4 | * 5 | * This library is built on Andreas Schlegel's OscP5. 6 | * http://www.sojamo.de/libraries/oscP5/ 7 | * 8 | * SLIP code based on: 9 | * https://gist.github.com/sepal/1512585 10 | * 11 | * 12 | * This library is free software; you can redistribute it and/or 13 | * modify it under the terms of the GNU Lesser General Public 14 | * License as published by the Free Software Foundation; either 15 | * version 2.1 of the License, or (at your option) any later version. 16 | * 17 | * This library is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | * Lesser General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Lesser General 23 | * Public License along with this library; if not, write to the 24 | * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 25 | * Boston, MA 02111-1307 USA 26 | */ 27 | 28 | 29 | 30 | // required to be able to use some protected members of the original 31 | // library 32 | package oscP5; 33 | 34 | import java.lang.reflect.InvocationTargetException; 35 | import java.lang.reflect.Method; 36 | import java.nio.*; 37 | import java.util.*; 38 | 39 | import netP5.Logger; 40 | import processing.core.*; 41 | import processing.serial.*; 42 | import oscP5.*; 43 | 44 | public class OscSerial { 45 | 46 | // SLIP Serial chars 47 | final int END = 0300; 48 | final int ESC = 0333; 49 | final int ESC_END = 0334; 50 | final int ESC_ESC = 0335; 51 | 52 | protected ArrayList serialBuffer; 53 | protected int count = 0; 54 | 55 | protected Serial serial; 56 | protected HashMap> _myOscPlugMap = new HashMap>(); 57 | 58 | // from OscProperties. Bypassing that object since it mostly has to do with 59 | // net config stuff 60 | protected final Vector listeners; 61 | 62 | private Class _myParentClass; 63 | private Method _myEventMethod; 64 | private Class _myEventClass = OscMessage.class; 65 | private boolean isEventMethod; 66 | 67 | protected final Object parent; 68 | 69 | // -------------------------------------------------------------------------- 70 | public OscSerial(PApplet parent, Serial serial) { 71 | this.parent = parent; 72 | this.serial = serial; 73 | serialBuffer = new ArrayList(); 74 | listeners = new Vector(); 75 | isEventMethod = checkEventMethod(); 76 | // register pre() event for automatic listen on the serial port 77 | parent.registerMethod("pre", this); 78 | } 79 | 80 | public void pre() { 81 | listen(); 82 | } 83 | 84 | // -------------------------------------------------------------------------- 85 | // Listen to data on the Serial port and assemble packets 86 | // this is called automatically by the post() event method 87 | protected void listen() { 88 | try { 89 | while (serial.available() > 0) { 90 | int buffer = serial.read(); 91 | switch (buffer) { 92 | case END: 93 | if (count > 0) { 94 | count = 0; 95 | oscPacketReady(); // <-- process the packet 96 | serial.clear(); 97 | serialBuffer.clear(); 98 | return; 99 | } 100 | break; 101 | case ESC: 102 | buffer = serial.read(); // <-- immediately read the next char 103 | switch (buffer) { 104 | case ESC_END: 105 | buffer = END; 106 | break; 107 | case ESC_ESC: 108 | buffer = ESC; 109 | break; 110 | } 111 | default: 112 | serialBuffer.add(buffer); 113 | count++; 114 | } 115 | } 116 | } 117 | catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | // -------------------------------------------------------------------------- 123 | public void send(OscMessage msg) { 124 | sendSLIP(msg.getBytes()); 125 | } 126 | 127 | // -------------------------------------------------------------------------- 128 | // TODO: needs to handle Bundles Vs Message 129 | // right now, this assumes we are always getting Messages 130 | // that's OK because on the Arduino side there is only the Message option 131 | 132 | protected void oscPacketReady() { 133 | 134 | String address = ""; 135 | int pos = 0; 136 | int b = (Integer) serialBuffer.get(pos++); 137 | ArrayList argumentTypes = new ArrayList(); 138 | while (b != 0) { 139 | address += (char) b; 140 | b = (Integer) serialBuffer.get(pos++); 141 | } 142 | 143 | // Skip address zeros and the comma for the parameters 144 | pos += 4 - ((address.length()) % 4); 145 | b = (Integer) serialBuffer.get(pos++); 146 | while (b != 0) { 147 | argumentTypes.add((char) b); 148 | b = (Integer) serialBuffer.get(pos++); 149 | } 150 | 151 | // println(address); 152 | // println(pos); 153 | 154 | // Skip parameter zeros 155 | pos--; 156 | pos += 4 - ((argumentTypes.size() + 1) % 4); 157 | 158 | OscMessage oscMsg = new OscMessage(address); 159 | int data = -1; 160 | for (int i = 0; i < argumentTypes.size(); i++) { 161 | char type = (Character) argumentTypes.get(i); 162 | switch (type) { 163 | case 'i': 164 | int[] intArr = new int[4]; 165 | intArr[0] = (Integer) serialBuffer.get(pos++); 166 | intArr[1] = (Integer) serialBuffer.get(pos++); 167 | intArr[2] = (Integer) serialBuffer.get(pos++); 168 | intArr[3] = (Integer) serialBuffer.get(pos++); 169 | data = byteArrayToInt(intArr, 0); 170 | oscMsg.add(data); 171 | break; 172 | case 'f': 173 | byte[] byteArr = new byte[4]; 174 | byteArr[0] = PApplet.parseByte((Integer) serialBuffer.get(pos++)); 175 | byteArr[1] = PApplet.parseByte((Integer) serialBuffer.get(pos++)); 176 | byteArr[2] = PApplet.parseByte((Integer) serialBuffer.get(pos++)); 177 | byteArr[3] = PApplet.parseByte((Integer) serialBuffer.get(pos++)); 178 | float f = arr2float(byteArr, 0); 179 | oscMsg.add(f); 180 | break; 181 | case 's': 182 | String str = ""; 183 | char c = PApplet.parseChar((Integer)serialBuffer.get(pos++)); 184 | while (c != 0x00) { 185 | str += c; 186 | c = PApplet.parseChar((Integer)serialBuffer.get(pos++)); 187 | } 188 | 189 | int zeros = 4 - (str.length() % 4); 190 | // Skip zeros 191 | for (int j = 0; j < zeros - 1; j++) { 192 | pos++; 193 | } 194 | oscMsg.add(str); 195 | break; 196 | } 197 | } 198 | 199 | // pass the message along 200 | callMethod(oscMsg); 201 | } 202 | 203 | // -------------------------------------------------------------------------- 204 | protected int byteArrayToInt(int[] b, int offset) { 205 | int value = 0; 206 | for (int i = 0; i < 4; i++) { 207 | int shift = (4 - 1 - i) * 8; 208 | value += (b[i + offset] & 0x000000FF) << shift; 209 | } 210 | return value; 211 | } 212 | 213 | // -------------------------------------------------------------------------- 214 | protected float arr2float(byte[] buf, int pos) { 215 | ByteBuffer bb = ByteBuffer.allocate(4); 216 | bb.put(buf, pos, 4); 217 | return bb.getFloat(0); 218 | } 219 | 220 | // -------------------------------------------------------------------------- 221 | protected void sendSLIP(byte[] packet) { 222 | int len = packet.length; 223 | serial.write(END); 224 | for (int i = 0; i < packet.length; i++) { 225 | switch (packet[i]) { 226 | case (byte) END: 227 | serial.write(ESC); 228 | serial.write(ESC_END); 229 | break; 230 | case (byte) ESC: 231 | serial.write(ESC); 232 | serial.write(ESC_ESC); 233 | break; 234 | default: 235 | serial.write(packet[i]); 236 | } 237 | } 238 | serial.write(END); 239 | } 240 | 241 | // ************************************************************************* 242 | // The following functions are from the OscP5 class, with some modifications 243 | // ************************************************************************* 244 | 245 | public void addListener(OscEventListener theListener) { 246 | listeners.add(theListener); 247 | } 248 | 249 | public void removeListener(OscEventListener theListener) { 250 | listeners.remove(theListener); 251 | } 252 | 253 | public Vector listeners() { 254 | return listeners; 255 | } 256 | 257 | /** 258 | * osc messages can be automatically forwarded to a specific method of an 259 | * object. the plug method can be used to by-pass parsing raw osc messages - 260 | * this job is done for you with the plug mechanism. you can also use the 261 | * following array-types int[], float[], String[]. (but only as on single 262 | * parameter e.g. somemethod(int[] theArray) {} ). 263 | * 264 | * @param theObject 265 | * Object, can be any Object 266 | * @param theMethodName 267 | * String, the method name an osc message should be forwarded to 268 | * @param theAddrPattern 269 | * String, the address pattern of the osc message 270 | * @param theTypeTag 271 | * String 272 | * @example oscP5plug 273 | * @usage Application 274 | */ 275 | public void plug(final Object theObject, final String theMethodName, 276 | final String theAddrPattern, final String theTypeTag) { 277 | final OscPlug myOscPlug = new OscPlug(); 278 | myOscPlug.plug(theObject, theMethodName, theAddrPattern, theTypeTag); 279 | // _myOscPlugList.add(myOscPlug); 280 | if (_myOscPlugMap.containsKey(theAddrPattern)) { 281 | _myOscPlugMap.get(theAddrPattern).add(myOscPlug); 282 | } else { 283 | ArrayList myOscPlugList = new ArrayList(); 284 | myOscPlugList.add(myOscPlug); 285 | _myOscPlugMap.put(theAddrPattern, myOscPlugList); 286 | } 287 | } 288 | 289 | /** 290 | * @param theObject 291 | * Object, can be any Object 292 | * @param theMethodName 293 | * String, the method name an osc message should be forwarded to 294 | * @param theAddrPattern 295 | * String, the address pattern of the osc message 296 | * @example oscP5plug 297 | * @usage Application 298 | */ 299 | public void plug(final Object theObject, final String theMethodName, 300 | final String theAddrPattern) { 301 | final Class myClass = theObject.getClass(); 302 | final Method[] myMethods = myClass.getDeclaredMethods(); 303 | Class[] myParams = null; 304 | for (int i = 0; i < myMethods.length; i++) { 305 | String myTypetag = ""; 306 | try { 307 | myMethods[i].setAccessible(true); 308 | } catch (Exception e) { 309 | } 310 | if ((myMethods[i].getName()).equals(theMethodName)) { 311 | myParams = myMethods[i].getParameterTypes(); 312 | OscPlug myOscPlug = new OscPlug(); 313 | for (int j = 0; j < myParams.length; j++) { 314 | myTypetag += myOscPlug.checkType(myParams[j].getName()); 315 | } 316 | 317 | myOscPlug.plug(theObject, theMethodName, theAddrPattern, 318 | myTypetag); 319 | // _myOscPlugList.add(myOscPlug); 320 | if (_myOscPlugMap.containsKey(theAddrPattern)) { 321 | _myOscPlugMap.get(theAddrPattern).add(myOscPlug); 322 | } else { 323 | ArrayList myOscPlugList = new ArrayList(); 324 | myOscPlugList.add(myOscPlug); 325 | _myOscPlugMap.put(theAddrPattern, myOscPlugList); 326 | } 327 | 328 | } 329 | } 330 | } 331 | 332 | private void callMethod(final OscMessage theOscMessage) { 333 | //forward the message to all OscEventListeners 334 | for (int i = listeners().size() - 1; i >= 0; i--) { 335 | ((OscEventListener) listeners().get(i)).oscEvent(theOscMessage); 336 | } 337 | 338 | /* check if the arguments can be forwarded as array */ 339 | if (theOscMessage.isArray) { 340 | if (_myOscPlugMap.containsKey(theOscMessage.addrPattern())) { 341 | ArrayList myOscPlugList = _myOscPlugMap 342 | .get(theOscMessage.addrPattern()); 343 | for (int i = 0; i < myOscPlugList.size(); i++) { 344 | OscPlug myPlug = (OscPlug) myOscPlugList.get(i); 345 | if (myPlug.isArray 346 | && myPlug.checkMethod(theOscMessage, true)) { 347 | // Should we set the following here? The old code did 348 | // not: 349 | // theOscMessage.isPlugged = true; 350 | invoke(myPlug.getObject(), myPlug.getMethod(), 351 | theOscMessage.argsAsArray()); 352 | } 353 | } 354 | } 355 | 356 | } 357 | 358 | if (_myOscPlugMap.containsKey(theOscMessage.addrPattern())) { 359 | ArrayList myOscPlugList = _myOscPlugMap.get(theOscMessage 360 | .addrPattern()); 361 | for (int i = 0; i < myOscPlugList.size(); i++) { 362 | OscPlug myPlug = (OscPlug) myOscPlugList.get(i); 363 | if (!myPlug.isArray && myPlug.checkMethod(theOscMessage, false)) { 364 | theOscMessage.isPlugged = true; 365 | invoke(myPlug.getObject(), myPlug.getMethod(), 366 | theOscMessage.arguments()); 367 | } 368 | } 369 | } 370 | 371 | /* if no plug method was detected, then use the default oscEvent mehtod */ 372 | Logger.printDebug("OscP5.callMethod ", "" + isEventMethod); 373 | if (isEventMethod) { 374 | try { 375 | invoke(parent, _myEventMethod, new Object[] { theOscMessage }); 376 | Logger.printDebug("OscP5.callMethod ", "invoking OscMessage " 377 | + isEventMethod); 378 | } catch (ClassCastException e) { 379 | Logger.printError("OscHandler.callMethod", 380 | " ClassCastException." + e); 381 | } 382 | } 383 | } 384 | 385 | private void invoke(final Object theObject, final Method theMethod, 386 | final Object[] theArgs) { 387 | try { 388 | theMethod.invoke(theObject, theArgs); 389 | } catch (IllegalArgumentException e) { 390 | e.printStackTrace(); 391 | } catch (IllegalAccessException e) { 392 | e.printStackTrace(); 393 | } catch (InvocationTargetException e) { 394 | Logger.printError( 395 | "OscP5", 396 | "ERROR. an error occured while forwarding an OscMessage\n " 397 | + "to a method in your program. please check your code for any \n" 398 | + "possible errors that might occur in the method where incoming\n " 399 | + "OscMessages are parsed e.g. check for casting errors, possible\n " 400 | + "nullpointers, array overflows ... .\n" 401 | + "method in charge : " + theMethod.getName() 402 | + " " + e); 403 | } 404 | } 405 | 406 | private boolean checkEventMethod() { 407 | _myParentClass = parent.getClass(); 408 | try { 409 | Method[] myMethods = _myParentClass.getDeclaredMethods(); 410 | for (int i = 0; i < myMethods.length; i++) { 411 | if (myMethods[i].getName().indexOf("oscEvent") != -1) { 412 | Class[] myClasses = myMethods[i].getParameterTypes(); 413 | if (myClasses.length == 1) { 414 | _myEventClass = myClasses[0]; 415 | break; 416 | } 417 | } 418 | } 419 | 420 | } catch (Throwable e) { 421 | System.err.println(e); 422 | } 423 | 424 | String tMethod = "oscEvent"; 425 | if (tMethod != null) { 426 | try { 427 | Class[] tClass = { _myEventClass }; 428 | _myEventMethod = _myParentClass.getDeclaredMethod(tMethod, 429 | tClass); 430 | _myEventMethod.setAccessible(true); 431 | return true; 432 | } catch (SecurityException e1) { 433 | // e1.printStackTrace(); 434 | Logger.printWarning( 435 | "OscP5.plug", 436 | "### security issues in OscP5.checkEventMethod(). (this occures when running in applet mode)"); 437 | } catch (NoSuchMethodException e1) { 438 | } 439 | } 440 | // online fix, since an applet throws a security exception when calling 441 | // setAccessible(true); 442 | if (_myEventMethod != null) { 443 | return true; 444 | } 445 | return false; 446 | } 447 | 448 | } 449 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | getOSCMessage KEYWORD1 2 | fill KEYWORD1 3 | send KEYWORD1 4 | dispatch KEYWORD1 5 | route KEYWORD1 6 | setTimetag KEYWORD1 7 | getTimetag KEYWORD1 8 | hasError KEYWORD1 9 | add KEYWORD1 10 | match KEYWORD1 11 | fullMatch KEYWORD1 12 | getInt KEYWORD1 13 | getFloat KEYWORD1 14 | getBlob KEYWORD1 15 | getString KEYWORD1 16 | getAddress KEYWORD1 17 | getDataLength KEYWORD1 18 | isInt KEYWORD1 19 | isFloat KEYWORD1 20 | isBlob KEYWORD1 21 | isString KEYWORD1 22 | empty KEYWORD1 23 | OSCBundle KEYWORD1 24 | OSCMessage KEYWORD1 25 | OSCMatch KEYWORD1 26 | OSCData KEYWORD1 27 | endTransmission KEYWORD1 28 | endofTransmission KEYWORD1 29 | SLIPEncodedSerial KEYWORD1 30 | SLIPEncodedUSBSerial KEYWORD1 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino-OSC 2 | version=1.1 3 | author=David Bouchard, Steve Daniels 4 | maintainer=David Bouchard, Steve Daniels 5 | sentence=A fork of CNMAT's OSC library 6 | paragraph=A fork of CNMAT's OSC library 7 | category=Communication 8 | url=http://github.com/davidbouchard/arduino-osc -------------------------------------------------------------------------------- /src/OSCData.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "OSCData.h" 3 | 4 | /*============================================================================= 5 | CONSTRUCTORS 6 | 7 | overloaded methods for each of the types which will 8 | set the type flag, the size (in bytes), and the data 9 | =============================================================================*/ 10 | 11 | 12 | OSCData::OSCData(const char * s){ 13 | error = OSC_OK; 14 | type = 's'; 15 | bytes = (strlen(s) + 1); 16 | //own the data 17 | char * mem = (char *) malloc(bytes); 18 | if (mem == NULL){ 19 | error = ALLOCFAILED; 20 | } else { 21 | strcpy(mem, s); 22 | data.s = mem; 23 | } 24 | } 25 | 26 | OSCData::OSCData(int i){ 27 | error = OSC_OK; 28 | type = 'i'; 29 | //cast it to an int32 30 | bytes = sizeof(int32_t); 31 | int32_t i32 = (int32_t) i; 32 | data.i = i32; 33 | } 34 | 35 | OSCData::OSCData(int32_t i){ 36 | error = OSC_OK; 37 | type = 'i'; 38 | bytes = sizeof(i); 39 | data.i = i; 40 | } 41 | 42 | 43 | OSCData::OSCData(float f){ 44 | error = OSC_OK; 45 | type = 'f'; 46 | bytes = sizeof(float); 47 | data.f = f; 48 | } 49 | 50 | 51 | OSCData::OSCData(double d){ 52 | error = OSC_OK; 53 | bytes = sizeof(double); 54 | //if it's not 8 bits it's not a true double 55 | if (bytes == 8){ 56 | type = 'd'; 57 | data.d = d; 58 | } else { 59 | type = 'f'; 60 | data.f = d; 61 | } 62 | } 63 | 64 | OSCData::OSCData(uint8_t * b, int len){ 65 | error = OSC_OK; 66 | type = 'b'; 67 | bytes = len + 4; 68 | //add the size to the front of the blob 69 | uint32_t len32 = (uint32_t) len; 70 | //make sure the length is endian-safe 71 | len32 = BigEndian(len32); 72 | uint8_t * lenPtr = (uint8_t *) (& len32); 73 | //own the data 74 | uint8_t * mem = (uint8_t * ) malloc(bytes); 75 | if (mem == NULL){ 76 | error = ALLOCFAILED; 77 | } else { 78 | //copy over the blob length 79 | memcpy(mem, lenPtr, 4); 80 | //copy over the blob data 81 | memcpy(mem + 4, b, len); 82 | data.b = mem; 83 | } 84 | } 85 | 86 | OSCData::OSCData (OSCData * datum){ 87 | error = OSC_OK; 88 | type = datum->type; 89 | bytes = datum->bytes; 90 | if (type == 'i' || type == 'f' || type == 'd'){ 91 | data = datum->data; 92 | } else if (type == 's' || type == 'b'){ 93 | //allocate a new peice of memory 94 | uint8_t * mem = (uint8_t * ) malloc(bytes); 95 | if (mem == NULL){ 96 | error = ALLOCFAILED; 97 | } else { 98 | //copy over the blob length 99 | memcpy(mem, datum->data.b, bytes); 100 | data.b = mem; 101 | } 102 | } 103 | } 104 | 105 | //DESTRUCTOR 106 | OSCData::~OSCData(){ 107 | //if there are no bytes, there is nothing to free 108 | if (bytes>0){ 109 | //if the data is of type 's' or 'b', need to free that memory 110 | if (type == 's'){ 111 | free(data.s); 112 | }else if( type == 'b'){ 113 | free(data.b); 114 | } 115 | } 116 | } 117 | 118 | //sets just the type as a message placeholder 119 | //no data 120 | OSCData::OSCData(char t){ 121 | error = INVALID_OSC; 122 | type = t; 123 | bytes = 0; 124 | } 125 | 126 | /*============================================================================= 127 | GETTERS 128 | 129 | perform a safety check to make sure the data type matches the request 130 | otherwise returns NULL 131 | =============================================================================*/ 132 | 133 | int32_t OSCData::getInt(){ 134 | if (type == 'i'){ 135 | return data.i; 136 | } else { 137 | return NULL; 138 | } 139 | } 140 | 141 | float OSCData::getFloat(){ 142 | if (type == 'f'){ 143 | return data.f; 144 | } else { 145 | return NULL; 146 | } 147 | } 148 | 149 | double OSCData::getDouble(){ 150 | if (type == 'd'){ 151 | return data.d; 152 | } else { 153 | return NULL; 154 | } 155 | } 156 | 157 | int OSCData::getString(char * strBuffer, int length){ 158 | if (type == 's' && bytes <= length){ 159 | strncpy(strBuffer, data.s, bytes); 160 | return bytes; 161 | } else { 162 | return NULL; 163 | } 164 | } 165 | 166 | int OSCData::getBlob(uint8_t * blobBuffer, int length){ 167 | if (type == 'b' && bytes <= length){ 168 | memcpy(blobBuffer, data.b, bytes); 169 | return bytes; 170 | } else { 171 | return NULL; 172 | } 173 | } -------------------------------------------------------------------------------- /src/OSCData.h: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Yotam Mann, The Center for New Music and Audio Technologies, 3 | University of California, Berkeley. Copyright (c) 2013, The Regents of 4 | the University of California (Regents). 5 | 6 | Permission to use, copy, modify, distribute, and distribute modified versions 7 | of this software and its documentation without fee and without a signed 8 | licensing agreement, is hereby granted, provided that the above copyright 9 | notice, this paragraph and the following two paragraphs appear in all copies, 10 | modifications, and distributions. 11 | 12 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 13 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 14 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 15 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | 17 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 20 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 21 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 22 | 23 | For bug reports and feature requests please email me at yotam@cnmat.berkeley.edu 24 | */ 25 | 26 | #ifndef OSCDATA_h 27 | #define OSCDATA_h 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | //ERRORS///////////////////////////////////////////////// 36 | typedef enum { OSC_OK = 0, 37 | BUFFER_FULL, INVALID_OSC, ALLOCFAILED, INDEX_OUT_OF_BOUNDS 38 | } OSCErrorCode; 39 | 40 | class OSCData 41 | { 42 | 43 | private: 44 | 45 | //friends 46 | friend class OscMessage; 47 | 48 | //should only be used while decoding 49 | //leaves an invalid OSCMessage with a type, but no data 50 | OSCData(char t); 51 | 52 | public: 53 | 54 | //an error flag 55 | OSCErrorCode error; 56 | 57 | //the size (in bytes) of the data 58 | int bytes; 59 | 60 | //the type of the data 61 | int type; 62 | 63 | //the data 64 | union { 65 | char * s; //string 66 | int32_t i; //int 67 | float f; //float 68 | double d; //double 69 | uint64_t l; //long 70 | uint8_t * b; //blob 71 | } data; 72 | 73 | //overload the constructor to account for all the types and sizes 74 | OSCData(const char * s); 75 | OSCData (int); 76 | OSCData (int32_t); 77 | OSCData (float); 78 | OSCData (double); 79 | OSCData (uint8_t *, int); 80 | //accepts another OSCData objects and clones it 81 | OSCData (OSCData *); 82 | 83 | //destructor 84 | ~OSCData(); 85 | 86 | //GETTERS 87 | int32_t getInt(); 88 | float getFloat(); 89 | double getDouble(); 90 | int getString(char *, int); 91 | int getBlob(uint8_t *, int); 92 | 93 | //constructor from byte array with type and length 94 | OSCData(char, uint8_t *, int); 95 | //fill the passed in buffer with the data 96 | //uint8_t * asByteArray(); 97 | 98 | }; 99 | 100 | /* 101 | based on http://stackoverflow.com/questions/809902/64-bit-ntohl-in-c 102 | 103 | if the system is little endian, it will flip the bits 104 | if the system is big endian, it'll do nothing 105 | */ 106 | template 107 | static inline T BigEndian(const T& x) 108 | { 109 | const int one = 1; 110 | const char sig = *(char*)&one; 111 | if (sig == 0) return x; // for big endian machine just return the input 112 | T ret; 113 | int size = sizeof(T); 114 | char* src = (char*)&x + sizeof(T) - 1; 115 | char* dst = (char*)&ret; 116 | while (size-- > 0){ 117 | *dst++ = *src--; 118 | } 119 | return ret; 120 | } 121 | 122 | 123 | #endif -------------------------------------------------------------------------------- /src/OSCMatch.c: -------------------------------------------------------------------------------- 1 | #define OSC_MATCH_ENABLE_2STARS 1 2 | #define OSC_MATCH_ENABLE_NSTARS 1 3 | /* 4 | Written by John MacCallum, The Center for New Music and Audio Technologies, 5 | University of California, Berkeley. Copyright (c) 2009, The Regents of 6 | the University of California (Regents). 7 | Permission to use, copy, modify, distribute, and distribute modified versions 8 | of this software and its documentation without fee and without a signed 9 | licensing agreement, is hereby granted, provided that the above copyright 10 | notice, this paragraph and the following two paragraphs appear in all copies, 11 | modifications, and distributions. 12 | 13 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 14 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 15 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 16 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 17 | 18 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 21 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 22 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 | */ 24 | #include 25 | #include "OSCMatch.h" 26 | 27 | static int osc_match_star(const char *pattern, const char *address); 28 | static int osc_match_star_r(const char *pattern, const char *address); 29 | static int osc_match_single_char(const char *pattern, const char *address); 30 | static int osc_match_bracket(const char *pattern, const char *address); 31 | static int osc_match_curly_brace(const char *pattern, const char *address); 32 | 33 | int osc_match(const char *pattern, const char *address, int *pattern_offset, int *address_offset) 34 | { 35 | if(!strcmp(pattern, address)){ 36 | *pattern_offset = strlen(pattern); 37 | *address_offset = strlen(address); 38 | return OSC_MATCH_ADDRESS_COMPLETE | OSC_MATCH_PATTERN_COMPLETE; 39 | } 40 | 41 | const char *pattern_start; 42 | const char *address_start; 43 | 44 | pattern_start = pattern; 45 | address_start = address; 46 | 47 | *pattern_offset = 0; 48 | *address_offset = 0; 49 | 50 | while(*address != '\0' && *pattern != '\0'){ 51 | if(*pattern == '*'){ 52 | if(!osc_match_star(pattern, address)){ 53 | return 0; 54 | } 55 | while(*pattern != '/' && *pattern != '\0'){ 56 | pattern++; 57 | } 58 | while(*address != '/' && *address != '\0'){ 59 | address++; 60 | } 61 | }else if(*address == '*'){ 62 | while(*pattern != '/' && *pattern != '\0'){ 63 | pattern++; 64 | } 65 | while(*address != '/' && *address != '\0'){ 66 | address++; 67 | } 68 | }else{ 69 | int n = 0; 70 | if(!(n = osc_match_single_char(pattern, address))){ 71 | return 0; 72 | } 73 | if(*pattern == '['){ 74 | while(*pattern != ']'){ 75 | pattern++; 76 | } 77 | pattern++; 78 | address++; 79 | }else if(*pattern == '{'){ 80 | while(*pattern != '}'){ 81 | pattern++; 82 | } 83 | pattern++; 84 | address += n; 85 | }else{ 86 | pattern++; 87 | address++; 88 | } 89 | } 90 | } 91 | 92 | *pattern_offset = pattern - pattern_start; 93 | *address_offset = address - address_start; 94 | 95 | int r = 0; 96 | 97 | if(*address == '\0') { 98 | r |= OSC_MATCH_ADDRESS_COMPLETE; 99 | } 100 | 101 | if(*pattern == '\0') { 102 | r |= OSC_MATCH_PATTERN_COMPLETE; 103 | } 104 | 105 | return r; 106 | } 107 | 108 | static int osc_match_star(const char *pattern, const char *address) 109 | { 110 | const char *address_start = address; 111 | const char *pattern_start = pattern; 112 | int num_stars = 0; 113 | if(*address == '\0') { return 0; } 114 | while(*address != '/' && *address != '\0'){ 115 | address++; 116 | } 117 | while(*pattern != '/' && *pattern != '\0'){ 118 | if(*pattern == '*'){ 119 | num_stars++; 120 | } 121 | pattern++; 122 | } 123 | pattern--; 124 | address--; 125 | switch(num_stars){ 126 | case 1: 127 | { 128 | const char *pp = pattern, *aa = address; 129 | while(*pp != '*'){ 130 | if(!(osc_match_single_char(pp, aa))){ 131 | return 0; 132 | } 133 | if(*pp == ']' || *pp == '}'){ 134 | while(*pp != '[' && *pp != '{'){ 135 | pp--; 136 | } 137 | } 138 | pp--; 139 | aa--; 140 | } 141 | } 142 | break; 143 | case 2: 144 | #if (OSC_MATCH_ENABLE_2STARS == 1) 145 | { 146 | const char *pp = pattern, *aa = address; 147 | while(*pp != '*'){ 148 | if(!(osc_match_single_char(pp, aa))){ 149 | return 0; 150 | } 151 | if(*pp == ']' || *pp == '}'){ 152 | while(*pp != '[' && *pp != '{'){ 153 | pp--; 154 | } 155 | } 156 | pp--; 157 | aa--; 158 | } 159 | aa++; // we want to start one character forward to allow the star to match nothing 160 | const char *star2 = pp; 161 | const char *test = aa; 162 | int i = 0; 163 | while(test > address_start){ 164 | pp = star2 - 1; 165 | aa = test - 1; 166 | i++; 167 | while(*pp != '*'){ 168 | if(!osc_match_single_char(pp, aa)){ 169 | break; 170 | } 171 | if(*pp == ']' || *pp == '}'){ 172 | while(*pp != '[' && *pp != '{'){ 173 | pp--; 174 | } 175 | } 176 | pp--; 177 | aa--; 178 | } 179 | if(pp == pattern_start){ 180 | return 1; 181 | } 182 | test--; 183 | } 184 | return 0; 185 | } 186 | break; 187 | #else 188 | return 0; 189 | #endif 190 | default: 191 | #if (OSC_MATCH_ENABLE_NSTARS == 1) 192 | return osc_match_star_r(pattern_start, address_start); 193 | break; 194 | #else 195 | return 0; 196 | #endif 197 | } 198 | return 1; 199 | } 200 | 201 | #if (OSC_MATCH_ENABLE_NSTARS == 1) 202 | static int osc_match_star_r(const char *pattern, const char *address) 203 | { 204 | if(*address == '/' || *address == '\0'){ 205 | if(*pattern == '/' || *pattern == '\0' || (*pattern == '*' && ((*(pattern + 1) == '/') || *(pattern + 1) == '\0'))){ 206 | return 1; 207 | }else{ 208 | return 0; 209 | } 210 | } 211 | if(*pattern == '*'){ 212 | if(osc_match_star_r(pattern + 1, address)){ 213 | return 1; 214 | }else{ 215 | return osc_match_star_r(pattern, address + 1); 216 | } 217 | }else{ 218 | if(!osc_match_single_char(pattern, address)){ 219 | return 0; 220 | } 221 | if(*pattern == '[' || *pattern == '{'){ 222 | while(*pattern != ']' && *pattern != '}'){ 223 | pattern++; 224 | } 225 | } 226 | return osc_match_star_r(pattern + 1, address + 1); 227 | } 228 | } 229 | #endif 230 | 231 | static int osc_match_single_char(const char *pattern, const char *address) 232 | { 233 | switch(*pattern){ 234 | case '[': 235 | return osc_match_bracket(pattern, address); 236 | case ']': 237 | while(*pattern != '['){ 238 | pattern--; 239 | } 240 | return osc_match_bracket(pattern, address); 241 | case '{': 242 | return osc_match_curly_brace(pattern, address); 243 | case '}': 244 | while(*pattern != '{'){ 245 | pattern--; 246 | } 247 | return osc_match_curly_brace(pattern, address); 248 | case '?': 249 | return 1; 250 | default: 251 | if(*pattern == *address){ 252 | return 1; 253 | }else{ 254 | return 0; 255 | } 256 | } 257 | return 0; 258 | } 259 | 260 | static int osc_match_bracket(const char *pattern, const char *address) 261 | { 262 | pattern++; 263 | int val = 1; 264 | if(*pattern == '!'){ 265 | pattern++; 266 | val = 0; 267 | } 268 | int matched = !val; 269 | while(*pattern != ']' && *pattern != '\0'){ 270 | // the character we're on now is the beginning of a range 271 | if(*(pattern + 1) == '-'){ 272 | if(*address >= *pattern && *address <= *(pattern + 2)){ 273 | matched = val; 274 | break; 275 | }else{ 276 | pattern += 3; 277 | } 278 | }else{ 279 | // just test the character 280 | if(*pattern == *address){ 281 | matched = val; 282 | break; 283 | } 284 | pattern++; 285 | } 286 | } 287 | return matched; 288 | } 289 | 290 | static int osc_match_curly_brace(const char *pattern, const char *address) 291 | { 292 | pattern++; 293 | const char *ptr = pattern; 294 | while(*ptr != '}' && *ptr != '\0' && *ptr != '/'){ 295 | while(*ptr != '}' && *ptr != '\0' && *ptr != '/' && *ptr != ','){ 296 | ptr++; 297 | } 298 | int n = ptr - pattern; 299 | if(!strncmp(pattern, address, n)){ 300 | return n; 301 | }else{ 302 | ptr++; 303 | pattern = ptr; 304 | } 305 | } 306 | return 0; 307 | } -------------------------------------------------------------------------------- /src/OSCMatch.h: -------------------------------------------------------------------------------- 1 | /* 2 | Written by John MacCallum, The Center for New Music and Audio Technologies, 3 | University of California, Berkeley. Copyright (c) 2009, The Regents of 4 | the University of California (Regents). 5 | Permission to use, copy, modify, distribute, and distribute modified versions 6 | of this software and its documentation without fee and without a signed 7 | licensing agreement, is hereby granted, provided that the above copyright 8 | notice, this paragraph and the following two paragraphs appear in all copies, 9 | modifications, and distributions. 10 | 11 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 12 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 13 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 14 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | 16 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 17 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 19 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 20 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 21 | */ 22 | 23 | #ifndef __OSC_MATCH_H__ 24 | #define __OSC_MATCH_H__ 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | /** 31 | * Switch this off to disable matching against a pattern with 2 stars 32 | */ 33 | //#define OSC_MATCH_ENABLE_2STARS 1 34 | /** 35 | * Switch this off to disable matching against a pattern with more than 2 stars which will 36 | * be done recursively. 37 | */ 38 | //#define OSC_MATCH_ENABLE_NSTARS 1 39 | 40 | /** 41 | * Return code for osc_match() that indicates that the entire address was successfully matched 42 | */ 43 | #define OSC_MATCH_ADDRESS_COMPLETE 1 44 | 45 | /** 46 | * Return code for osc_match() that indicates that the entire pattern was successfully matched 47 | */ 48 | #define OSC_MATCH_PATTERN_COMPLETE 2 49 | /* 50 | typedef struct _osc_callback { 51 | const char* address; // Address 52 | struct _osc_callback *child; // RAM 53 | struct _osc_callback *sibling; // RAM 54 | struct _osc_callback *parent; // RAM 55 | int callback; // ROM 56 | } osc_callback; 57 | */ 58 | 59 | /** 60 | * Match a pattern against an address. In the case of a partial match, pattern_offset 61 | * and address_offset will contain the number of bytes into their respective strings 62 | * where the match failed. 63 | * 64 | * @param pattern The pattern to match 65 | * @param address The address to match 66 | * @param pattern_offset The number of bytes into the pattern that were matched successfully 67 | * @param address_offset The number of bytes into the address that were matched successfully 68 | * @return 0 if the match failed altogether, or an or'd combination of OSC_MATCH_ADDRESS_COMPLETE and 69 | * OSC_MATCH_PATTERN_COMPLETE. 70 | */ 71 | int osc_match(const char *pattern, const char *address, int *pattern_offset, int *address_offset); 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif // __OSC_MATCH_H__ 78 | 79 | -------------------------------------------------------------------------------- /src/OSCMessage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Yotam Mann, The Center for New Music and Audio Technologies, 3 | University of California, Berkeley. Copyright (c) 2012, The Regents of 4 | the University of California (Regents). 5 | 6 | Permission to use, copy, modify, distribute, and distribute modified versions 7 | of this software and its documentation without fee and without a signed 8 | licensing agreement, is hereby granted, provided that the above copyright 9 | notice, this paragraph and the following two paragraphs appear in all copies, 10 | modifications, and distributions. 11 | 12 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 13 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 14 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 15 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | 17 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 20 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 21 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 22 | 23 | For bug reports and feature requests please email me at yotam@cnmat.berkeley.edu 24 | */ 25 | 26 | #include "OSCMessage.h" 27 | #include "OSCMatch.h" 28 | 29 | extern void oscEvent(OscMessage&); 30 | 31 | /*============================================================================= 32 | CONSTRUCTORS / DESTRUCTOR 33 | =============================================================================*/ 34 | 35 | //constructor with address 36 | OscMessage::OscMessage(const char * _address){ 37 | setupMessage(); 38 | setAddress(_address); 39 | } 40 | 41 | //constructor with nothing 42 | //just a placeholder since the message is invalid 43 | OscMessage::OscMessage(){ 44 | setupMessage(); 45 | error = INVALID_OSC; 46 | } 47 | 48 | //variable length constructor 49 | //for example OSCMessage msg("/address", "isf", 1, "two", 3.0); 50 | /* 51 | OSCMessage::OSCMessage(const char * _address, char * types, ... ){ 52 | setupMessage(_address); 53 | } 54 | */ 55 | 56 | //sets up a new message 57 | void OscMessage::setupMessage(){ 58 | address = NULL; 59 | //setup the attributes 60 | dataCount = 0; 61 | error = OSC_OK; 62 | //setup the space for data 63 | data = NULL; 64 | //setup for filling the message 65 | incomingBuffer = NULL; 66 | incomingBufferSize = 0; 67 | //set the decode state 68 | decodeState = STANDBY; 69 | } 70 | 71 | //DESTRUCTOR 72 | OscMessage::~OscMessage(){ 73 | //free everything that needs to be freed 74 | //free the address 75 | free(address); 76 | //free the data 77 | empty(); 78 | //free the filling buffer 79 | free(incomingBuffer); 80 | } 81 | 82 | void OscMessage::empty(){ 83 | error = OSC_OK; 84 | //free each of hte data in the array 85 | for (int i = 0; i < dataCount; i++){ 86 | OSCData * datum = getOSCData(i); 87 | //explicitly destruct the data 88 | //datum->~OSCData(); 89 | delete datum; 90 | } 91 | //and free the array 92 | free(data); 93 | data = NULL; 94 | dataCount = 0; 95 | } 96 | 97 | // ****** NEW ADDITION *** davidb 98 | void OscMessage::reset() { 99 | free(address); 100 | empty(); 101 | free(incomingBuffer); 102 | setupMessage(); 103 | error = INVALID_OSC; 104 | } 105 | 106 | //COPY 107 | OscMessage::OscMessage(OscMessage * msg){ 108 | //start with a message with the same address 109 | setupMessage(); 110 | setAddress(msg->address); 111 | //add each of the data to the other message 112 | for (int i = 0; i < msg->dataCount; i++){ 113 | add(msg->data[i]); 114 | } 115 | } 116 | 117 | /*============================================================================= 118 | GETTING DATA 119 | =============================================================================*/ 120 | 121 | OSCData * OscMessage::getOSCData(int position){ 122 | if (position < dataCount){ 123 | OSCData * datum = data[position]; 124 | return datum; 125 | } else { 126 | error = INDEX_OUT_OF_BOUNDS; 127 | return NULL; 128 | } 129 | } 130 | 131 | int32_t OscMessage::getInt(int position){ 132 | OSCData * datum = getOSCData(position); 133 | if (!hasError()){ 134 | return datum->getInt(); 135 | } else { 136 | return NULL; 137 | } 138 | } 139 | 140 | float OscMessage::getFloat(int position){ 141 | OSCData * datum = getOSCData(position); 142 | if (!hasError()){ 143 | return datum->getFloat(); 144 | } else { 145 | return NULL; 146 | } 147 | } 148 | 149 | double OscMessage::getDouble(int position){ 150 | OSCData * datum = getOSCData(position); 151 | if (!hasError()){ 152 | return datum->getDouble(); 153 | } else { 154 | return NULL; 155 | } 156 | } 157 | 158 | int OscMessage::getString(int position, char * buffer, int bufferSize){ 159 | OSCData * datum = getOSCData(position); 160 | if (!hasError()){ 161 | //the number of bytes to copy is the smaller between the buffer size and the datum's byte length 162 | int copyBytes = bufferSize < datum->bytes? bufferSize : datum->bytes; 163 | return datum->getString(buffer, copyBytes); 164 | } else { 165 | return NULL; 166 | } 167 | } 168 | 169 | int OscMessage::getBlob(int position, uint8_t * buffer, int bufferSize){ 170 | OSCData * datum = getOSCData(position); 171 | if (!hasError()){ 172 | //the number of bytes to copy is the smaller between the buffer size and the datum's byte length 173 | int copyBytes = bufferSize < datum->bytes? bufferSize : datum->bytes; 174 | return datum->getBlob(buffer, copyBytes); 175 | } else { 176 | return NULL; 177 | } 178 | } 179 | 180 | char OscMessage::getType(int position){ 181 | OSCData * datum = getOSCData(position); 182 | if (!hasError()){ 183 | return datum->type; 184 | } else { 185 | return NULL; 186 | } 187 | } 188 | 189 | int OscMessage::getDataLength(int position){ 190 | OSCData * datum = getOSCData(position); 191 | if (!hasError()){ 192 | return datum->bytes; 193 | } else { 194 | return 0; 195 | } 196 | } 197 | 198 | /*============================================================================= 199 | TESTING DATA 200 | =============================================================================*/ 201 | 202 | bool OscMessage::testType(int position, char type){ 203 | OSCData * datum = getOSCData(position); 204 | if (!hasError()){ 205 | return datum->type == type; 206 | } else { 207 | return false; 208 | } 209 | } 210 | 211 | bool OscMessage::isInt(int position){ 212 | return testType(position, 'i'); 213 | } 214 | 215 | bool OscMessage::isFloat(int position){ 216 | return testType(position, 'f'); 217 | } 218 | 219 | bool OscMessage::isBlob(int position){ 220 | return testType(position, 'b'); 221 | } 222 | 223 | bool OscMessage::isChar(int position){ 224 | return testType(position, 'c'); 225 | } 226 | 227 | bool OscMessage::isString(int position){ 228 | return testType(position, 's'); 229 | } 230 | 231 | bool OscMessage::isDouble(int position){ 232 | return testType(position, 'd'); 233 | } 234 | 235 | 236 | /*============================================================================= 237 | PATTERN MATCHING 238 | =============================================================================*/ 239 | 240 | int OscMessage::match(const char * pattern, int addr_offset){ 241 | int pattern_offset; 242 | int address_offset; 243 | int ret = osc_match(address + addr_offset, pattern, &pattern_offset, &address_offset); 244 | char * next = (char *) (address + addr_offset + pattern_offset); 245 | if (ret==3){ 246 | return pattern_offset; 247 | } else if (pattern_offset > 0 && *next == '/'){ 248 | return pattern_offset; 249 | } else { 250 | return 0; 251 | } 252 | } 253 | 254 | bool OscMessage::fullMatch( const char * pattern, int addr_offset){ 255 | int pattern_offset; 256 | int address_offset; 257 | int ret = osc_match(address + addr_offset, pattern, &address_offset, &pattern_offset); 258 | return (ret==3); 259 | } 260 | 261 | bool OscMessage::plug(const char * pattern, void (*callback)(OscMessage &), int addr_offset){ 262 | if (fullMatch(pattern, addr_offset)){ 263 | callback(*this); 264 | return true; 265 | } else { 266 | return false; 267 | } 268 | } 269 | 270 | bool OscMessage::route(const char * pattern, void (*callback)(OscMessage &, int), int initial_offset){ 271 | int match_offset = match(pattern, initial_offset); 272 | if (match_offset>0){ 273 | callback(*this, match_offset + initial_offset); 274 | return true; 275 | } else { 276 | return false; 277 | } 278 | } 279 | 280 | /*============================================================================= 281 | ADDRESS 282 | =============================================================================*/ 283 | 284 | int OscMessage::getAddress(char * buffer, int offset){ 285 | strcpy(buffer, address+offset); 286 | return strlen(buffer); 287 | } 288 | 289 | int OscMessage::getAddress(char * buffer, int offset, int len){ 290 | strncpy(buffer, address+offset, len); 291 | return strlen(buffer); 292 | } 293 | 294 | void OscMessage::setAddress(const char * _address){ 295 | //free the previous address 296 | free(address); 297 | //copy the address 298 | char * addressMemory = (char *) malloc( (strlen(_address) + 1) * sizeof(char) ); 299 | if (addressMemory == NULL){ 300 | error = ALLOCFAILED; 301 | address = NULL; 302 | } else { 303 | strcpy(addressMemory, _address); 304 | address = addressMemory; 305 | } 306 | } 307 | 308 | /*============================================================================= 309 | SIZE 310 | =============================================================================*/ 311 | 312 | int OscMessage::padSize(int _bytes){ 313 | int space = (_bytes + 3) / 4; 314 | space *= 4; 315 | return space - _bytes; 316 | } 317 | 318 | //returns the number of OSCData in the OSCMessage 319 | int OscMessage::size(){ 320 | return dataCount; 321 | } 322 | 323 | int OscMessage::bytes(){ 324 | int messageSize = 0; 325 | //send the address 326 | int addrLen = strlen(address) + 1; 327 | messageSize += addrLen; 328 | //padding amount 329 | int addrPad = padSize(addrLen); 330 | messageSize += addrPad; 331 | //add the comma seperator 332 | messageSize += 1; 333 | //add the types 334 | messageSize += dataCount; 335 | //pad the types 336 | int typePad = padSize(dataCount + 1); 337 | if (typePad == 0){ 338 | typePad = 4; 339 | } 340 | messageSize+=typePad; 341 | //then the data 342 | for (int i = 0; i < dataCount; i++){ 343 | OSCData * datum = getOSCData(i); 344 | messageSize+=datum->bytes; 345 | messageSize += padSize(datum->bytes); 346 | } 347 | return messageSize; 348 | } 349 | 350 | /*============================================================================= 351 | ERROR HANDLING 352 | =============================================================================*/ 353 | 354 | bool OscMessage::hasError(){ 355 | bool retError = error != OSC_OK; 356 | //test each of the data 357 | for (int i = 0; i < dataCount; i++){ 358 | OSCData * datum = getOSCData(i); 359 | retError |= datum->error != OSC_OK; 360 | } 361 | return retError; 362 | } 363 | 364 | OSCErrorCode OscMessage::getError(){ 365 | return error; 366 | } 367 | 368 | /*============================================================================= 369 | SENDING 370 | =============================================================================*/ 371 | 372 | void OscMessage::send(Print &p){ 373 | //don't send a message with errors 374 | if (hasError()){ 375 | return; 376 | } 377 | uint8_t nullChar = '\0'; 378 | //send the address 379 | int addrLen = strlen(address) + 1; 380 | //padding amount 381 | int addrPad = padSize(addrLen); 382 | //write it to the stream 383 | p.write((uint8_t *) address, addrLen); 384 | //add the padding 385 | while(addrPad--){ 386 | p.write(nullChar); 387 | } 388 | //add the comma seperator 389 | p.write((uint8_t) ','); 390 | //add the types 391 | for (int i = 0; i < dataCount; i++){ 392 | p.write((uint8_t) getType(i)); 393 | } 394 | //pad the types 395 | int typePad = padSize(dataCount + 1); 396 | if (typePad == 0){ 397 | typePad = 4; 398 | } 399 | while(typePad--){ 400 | p.write(nullChar); 401 | } 402 | //write the data 403 | for (int i = 0; i < dataCount; i++){ 404 | OSCData * datum = getOSCData(i); 405 | if (datum->type == 's'){ 406 | p.write(datum->data.b, datum->bytes); 407 | int dataPad = padSize(datum->bytes); 408 | while(dataPad--){ 409 | p.write(nullChar); 410 | } 411 | } else if(datum->type == 'b'){ 412 | p.write(datum->data.b, datum->bytes); 413 | int dataPad = padSize(datum->bytes); 414 | while(dataPad--){ 415 | p.write(nullChar); 416 | } 417 | } else if (datum->type == 'd'){ 418 | double d = BigEndian(datum->data.d); 419 | uint8_t * ptr = (uint8_t *) &d; 420 | p.write(ptr, 8); 421 | } else { 422 | uint32_t i = BigEndian(datum->data.i); 423 | uint8_t * ptr = (uint8_t *) &i; 424 | p.write(ptr, datum->bytes); 425 | } 426 | } 427 | } 428 | 429 | /*============================================================================= 430 | FILLING 431 | =============================================================================*/ 432 | 433 | void OscMessage::fill(uint8_t incomingByte){ 434 | decode(incomingByte); 435 | } 436 | 437 | void OscMessage::fill(uint8_t * incomingBytes, int length){ 438 | while (length--){ 439 | decode(*incomingBytes++); 440 | } 441 | } 442 | 443 | /*============================================================================= 444 | DECODING 445 | =============================================================================*/ 446 | 447 | void OscMessage::decodeAddress(){ 448 | setAddress((char *) incomingBuffer); 449 | //change the error from invalide message 450 | error = OSC_OK; 451 | clearIncomingBuffer(); 452 | } 453 | 454 | void OscMessage::decodeType(uint8_t incomingByte){ 455 | char type = incomingByte; 456 | add(type); 457 | } 458 | 459 | void OscMessage::decodeData(uint8_t incomingByte){ 460 | //get the first OSCData to re-set 461 | for (int i = 0; i < dataCount; i++){ 462 | OSCData * datum = getOSCData(i); 463 | if (datum->error == INVALID_OSC){ 464 | //set the contents of datum with the data received 465 | switch (datum->type){ 466 | case 'i': 467 | if (incomingBufferSize == 4){ 468 | //parse the buffer as an int 469 | union { 470 | uint32_t i; 471 | uint8_t b[4]; 472 | } u; 473 | memcpy(u.b, incomingBuffer, 4); 474 | int dataVal = BigEndian(u.i); 475 | set(i, dataVal); 476 | clearIncomingBuffer(); 477 | } 478 | break; 479 | case 'f': 480 | if (incomingBufferSize == 4){ 481 | //parse the buffer as an int 482 | union { 483 | float f; 484 | uint8_t b[4]; 485 | } u; 486 | memcpy(u.b, incomingBuffer, 4); 487 | float dataVal = BigEndian(u.f); 488 | set(i, dataVal); 489 | clearIncomingBuffer(); 490 | } 491 | break; 492 | case 'd': 493 | if (incomingBufferSize == 8){ 494 | //parse the buffer as an int 495 | union { 496 | double d; 497 | uint8_t b[8]; 498 | } u; 499 | memcpy(u.b, incomingBuffer, 8); 500 | double dataVal = BigEndian(u.d); 501 | set(i, dataVal); 502 | clearIncomingBuffer(); 503 | } 504 | break; 505 | case 's': 506 | if (incomingByte == 0){ 507 | char * str = (char *) incomingBuffer; 508 | set(i, str); 509 | clearIncomingBuffer(); 510 | decodeState = DATA_PADDING; 511 | } 512 | break; 513 | case 'b': 514 | if (incomingBufferSize > 4){ 515 | //compute the expected blob size 516 | union { 517 | uint32_t i; 518 | uint8_t b[4]; 519 | } u; 520 | memcpy(u.b, incomingBuffer, 4); 521 | int blobLength = BigEndian(u.i); 522 | if (incomingBufferSize == blobLength + 4){ 523 | set(i, incomingBuffer + 4, blobLength); 524 | clearIncomingBuffer(); 525 | decodeState = DATA_PADDING; 526 | } 527 | 528 | } 529 | break; 530 | } 531 | //break out of the for loop once we've selected the first invalid message 532 | break; 533 | } 534 | } 535 | } 536 | 537 | //does not validate the incoming OSC for correctness 538 | void OscMessage::decode(uint8_t incomingByte){ 539 | addToIncomingBuffer(incomingByte); 540 | switch (decodeState){ 541 | case STANDBY: 542 | if (incomingByte == '/'){ 543 | decodeState = ADDRESS; 544 | } 545 | break; 546 | case ADDRESS: 547 | if (incomingByte == 0){ 548 | //end of the address 549 | //decode the address 550 | decodeAddress(); 551 | //next state 552 | decodeState = ADDRESS_PADDING; 553 | } 554 | break; 555 | case ADDRESS_PADDING: 556 | //it does not count the padding 557 | if (incomingByte==','){ 558 | //next state 559 | decodeState = TYPES; 560 | clearIncomingBuffer(); 561 | } 562 | break; 563 | case TYPES: 564 | if (incomingByte != 0){ 565 | //next state 566 | decodeType(incomingByte); 567 | } else { 568 | decodeState = TYPES_PADDING; 569 | } 570 | //FALL THROUGH to test if it should go to the data state 571 | case TYPES_PADDING: { 572 | //compute the padding size for the types 573 | //to determine the start of the data section 574 | int typePad = padSize(dataCount + 1); 575 | if (typePad == 0){ 576 | typePad = 4; 577 | } 578 | if (incomingBufferSize == (typePad + dataCount)){ 579 | clearIncomingBuffer(); 580 | decodeState = DATA; 581 | } 582 | } 583 | break; 584 | case DATA: 585 | decodeData(incomingByte); 586 | break; 587 | case DATA_PADDING:{ 588 | //get hte last valid data 589 | for (int i = dataCount - 1; i >= 0; i--){ 590 | OSCData * datum = getOSCData(i); 591 | if (datum->error == OSC_OK){ 592 | //compute the padding size for the data 593 | int dataPad = padSize(datum->bytes); 594 | if (incomingBufferSize == dataPad){ 595 | clearIncomingBuffer(); 596 | decodeState = DATA; 597 | } 598 | break; 599 | } 600 | } 601 | } 602 | break; 603 | 604 | } 605 | } 606 | 607 | 608 | /*============================================================================= 609 | INCOMING BUFFER MANAGEMENT 610 | =============================================================================*/ 611 | 612 | void OscMessage::addToIncomingBuffer(uint8_t incomingByte){ 613 | //realloc some space for the new byte and stick it on the end 614 | incomingBuffer = (uint8_t *) realloc ( incomingBuffer, incomingBufferSize + 1); 615 | if (incomingBuffer != NULL){ 616 | incomingBuffer[incomingBufferSize++] = incomingByte; 617 | } else { 618 | error = ALLOCFAILED; 619 | } 620 | } 621 | 622 | void OscMessage::clearIncomingBuffer(){ 623 | incomingBufferSize = 0; 624 | free(incomingBuffer); 625 | incomingBuffer = NULL; 626 | } 627 | -------------------------------------------------------------------------------- /src/OSCMessage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Yotam Mann, The Center for New Music and Audio Technologies, 3 | University of California, Berkeley. Copyright (c) 2012, The Regents of 4 | the University of California (Regents). 5 | 6 | Permission to use, copy, modify, distribute, and distribute modified versions 7 | of this software and its documentation without fee and without a signed 8 | licensing agreement, is hereby granted, provided that the above copyright 9 | notice, this paragraph and the following two paragraphs appear in all copies, 10 | modifications, and distributions. 11 | 12 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 13 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 14 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 15 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | 17 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 20 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 21 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 22 | 23 | For bug reports and feature requests please email me at yotam@cnmat.berkeley.edu 24 | */ 25 | 26 | #ifndef OSCMESSAGE_h 27 | #define OSCMESSAGE_h 28 | 29 | #include "OSCData.h" 30 | 31 | #include 32 | 33 | class OscMessage 34 | { 35 | 36 | private: 37 | 38 | //friends 39 | friend class OSCBundle; 40 | 41 | 42 | /*============================================================================= 43 | PRIVATE VARIABLES 44 | =============================================================================*/ 45 | 46 | //the address 47 | char * address; 48 | 49 | //the data 50 | OSCData ** data; 51 | 52 | //the number of OSCData in the data array 53 | int dataCount; 54 | 55 | //error codes for potential runtime problems 56 | OSCErrorCode error; 57 | 58 | /*============================================================================= 59 | DECODING INCOMING BYTES 60 | =============================================================================*/ 61 | 62 | //the decoding states for incoming bytes 63 | enum DecodeState { 64 | STANDBY, 65 | ADDRESS, 66 | ADDRESS_PADDING, 67 | TYPES, 68 | TYPES_PADDING, 69 | DATA, 70 | DATA_PADDING, 71 | DONE, 72 | } decodeState; 73 | 74 | //stores incoming bytes until they can be decoded 75 | uint8_t * incomingBuffer; 76 | int incomingBufferSize; 77 | 78 | //adds a byte to the buffer 79 | void addToIncomingBuffer(uint8_t); 80 | //clears the incoming buffer 81 | void clearIncomingBuffer(); 82 | 83 | //decoding function 84 | void decode(uint8_t); 85 | void decodeAddress(); 86 | void decodeType(uint8_t); 87 | void decodeData(uint8_t); 88 | 89 | /*============================================================================= 90 | HELPER FUNCTIONS 91 | =============================================================================*/ 92 | 93 | void setupMessage(); 94 | 95 | //compares the OSCData's type char to a test char 96 | bool testType(int position, char type); 97 | 98 | //returns the number of bytes to pad to make it 4-bit aligned 99 | int padSize(int bytes); 100 | 101 | public: 102 | 103 | //returns the OSCData at that position 104 | OSCData * getOSCData(int); 105 | 106 | /*============================================================================= 107 | CONSTRUCTORS / DESTRUCTOR 108 | =============================================================================*/ 109 | 110 | //new constructor needs an address 111 | OscMessage (const char * _address); 112 | //no address 113 | //placeholder since it's invalide OSC 114 | OscMessage(); 115 | 116 | //can optionally accept all of the data after the address 117 | //OSCMessage(const char * _address, char * types, ... ); 118 | //created from another OSCMessage 119 | OscMessage (OscMessage *); 120 | 121 | //DESTRUCTOR 122 | ~OscMessage(); 123 | 124 | //empties all of the data 125 | void empty(); 126 | 127 | void reset(); 128 | 129 | /*============================================================================= 130 | SETTING DATA 131 | =============================================================================*/ 132 | 133 | //returns the OSCMessage so that multiple 'add's can be strung together 134 | template 135 | OscMessage& add(T datum){ 136 | //make a piece of data 137 | OSCData * d = new OSCData(datum); 138 | //check if it has any errors 139 | if (d->error == ALLOCFAILED){ 140 | error = ALLOCFAILED; 141 | } else { 142 | //resize the data array 143 | OSCData ** dataMem = (OSCData **) realloc(data, sizeof(OSCData *) * (dataCount + 1)); 144 | if (dataMem == NULL){ 145 | error = ALLOCFAILED; 146 | } else { 147 | data = dataMem; 148 | //add data to the end of the array 149 | data[dataCount] = d; 150 | //increment the data size 151 | dataCount++; 152 | } 153 | } 154 | return *this; 155 | } 156 | 157 | //blob specific add 158 | OscMessage& add(uint8_t * blob, int length){ 159 | //make a piece of data 160 | OSCData * d = new OSCData(blob, length); 161 | //check if it has any errors 162 | if (d->error == ALLOCFAILED){ 163 | error = ALLOCFAILED; 164 | } else { 165 | //resize the data array 166 | OSCData ** dataMem = (OSCData **) realloc(data, sizeof(OSCData *) * (dataCount + 1)); 167 | if (dataMem == NULL){ 168 | error = ALLOCFAILED; 169 | } else { 170 | data = dataMem; 171 | //add data to the end of the array 172 | data[dataCount] = d; 173 | //increment the data size 174 | dataCount++; 175 | } 176 | } 177 | return *this; 178 | } 179 | 180 | //sets the data at a position 181 | template 182 | void set(int position, T datum){ 183 | if (position < dataCount){ 184 | //replace the OSCData with a new one 185 | OSCData * oldDatum = getOSCData(position); 186 | //destroy the old one 187 | delete oldDatum; 188 | //make a new one 189 | OSCData * newDatum = new OSCData(datum); 190 | //test if there was an error 191 | if (newDatum->error == ALLOCFAILED){ 192 | error = ALLOCFAILED; 193 | } else { 194 | //otherwise, put it in the data array 195 | data[position] = newDatum; 196 | } 197 | } else if (position == (dataCount)){ 198 | //add the data to the end 199 | add(datum); 200 | } else { 201 | //else out of bounds error 202 | error = INDEX_OUT_OF_BOUNDS; 203 | } 204 | } 205 | 206 | //blob specific setter 207 | void set(int position, uint8_t * blob, int length){ 208 | if (position < dataCount){ 209 | //replace the OSCData with a new one 210 | OSCData * oldDatum = getOSCData(position); 211 | //destroy the old one 212 | delete oldDatum; 213 | //make a new one 214 | OSCData * newDatum = new OSCData(blob, length); 215 | //test if there was an error 216 | if (newDatum->error == ALLOCFAILED){ 217 | error = ALLOCFAILED; 218 | } else { 219 | //otherwise, put it in the data array 220 | data[position] = newDatum; 221 | } 222 | } else if (position == (dataCount)){ 223 | //add the data to the end 224 | add(blob, length); 225 | } else { 226 | //else out of bounds error 227 | error = INDEX_OUT_OF_BOUNDS; 228 | } 229 | } 230 | 231 | void setAddress(const char *); 232 | 233 | /*============================================================================= 234 | GETTING DATA 235 | 236 | getters take a position as an argument 237 | =============================================================================*/ 238 | 239 | int32_t getInt(int); 240 | float getFloat(int); 241 | double getDouble(int); 242 | 243 | //return the copied string's length 244 | int getString(int, char *, int); 245 | //returns the number of unsigned int8's copied into the buffer 246 | int getBlob(int, uint8_t *, int); 247 | 248 | //returns the number of bytes of the data at that position 249 | int getDataLength(int); 250 | 251 | //returns the type at the position 252 | char getType(int); 253 | 254 | //put the address in the buffer 255 | int getAddress(char * buffer, int offset = 0); 256 | int getAddress(char * buffer, int offset, int len); 257 | 258 | // TODO: int getAddressLength(int offset = 0); 259 | 260 | 261 | /*============================================================================= 262 | TESTING DATA 263 | 264 | testers take a position as an argument 265 | =============================================================================*/ 266 | 267 | bool isInt(int); 268 | bool isFloat(int); 269 | bool isBlob(int); 270 | bool isChar(int); 271 | bool isString(int); 272 | bool isDouble(int); 273 | 274 | /*============================================================================= 275 | PATTERN MATCHING 276 | =============================================================================*/ 277 | 278 | //match the pattern against the address 279 | //returns true only for a complete match 280 | bool fullMatch( const char * pattern, int = 0); 281 | 282 | //returns the number of characters matched in the address 283 | int match( const char * pattern, int = 0); 284 | 285 | //calls the function with the message as the arg if it was a full match 286 | bool plug(const char * pattern, void (*callback)(OscMessage &), int = 0); 287 | 288 | //like dispatch, but allows for partial matches 289 | //the address match offset is sent as an argument to the callback 290 | //also room for an option address offset to allow for multiple nested routes 291 | bool route(const char * pattern, void (*callback)(OscMessage &, int), int = 0); 292 | 293 | 294 | 295 | /*============================================================================= 296 | SIZE 297 | =============================================================================*/ 298 | 299 | //the number of data that the message contains 300 | int size(); 301 | 302 | //computes the number of bytes the OSCMessage occupies if everything is 32-bit aligned 303 | int bytes(); 304 | 305 | /*============================================================================= 306 | TRANSMISSION 307 | =============================================================================*/ 308 | 309 | //send the message 310 | void send(Print &p); 311 | 312 | //fill the message from a byte stream 313 | void fill(uint8_t); 314 | void fill(uint8_t *, int); 315 | 316 | /*============================================================================= 317 | ERROR 318 | =============================================================================*/ 319 | 320 | bool hasError(); 321 | 322 | OSCErrorCode getError(); 323 | 324 | }; 325 | 326 | #endif 327 | -------------------------------------------------------------------------------- /src/OscSerial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | 5 | 6 | void oscEvent(OscMessage &); 7 | 8 | //============================================================================= 9 | // OSC_Serial Wrapper 10 | //============================================================================= 11 | 12 | /* 13 | // old wrapper constructor 14 | OscSerial::OscSerial(HardwareSerial &s) { 15 | slip = new SLIPEncodedSerial(s); 16 | } 17 | */ 18 | 19 | OscSerial::OscSerial() { 20 | ; 21 | } 22 | 23 | 24 | void OscSerial::begin(HardwareSerial &s) { 25 | slip = new SLIPEncodedSerial(s); 26 | } 27 | 28 | 29 | 30 | // Non-blocking version 31 | void OscSerial::listen() { 32 | int size; 33 | if( (size = slip->available()) > 0) { 34 | while(size--) msgIN.fill(slip->read()); 35 | } 36 | if (!slip->endofPacket()) return; 37 | 38 | if (!msgIN.hasError()) { 39 | oscEvent(msgIN); 40 | } 41 | 42 | msgIN.reset(); 43 | } 44 | 45 | /* 46 | // Blocking Version -- do not use, left there for reference 47 | void OscSerial::listen() { 48 | int size; 49 | while(!slip->endofPacket()) { 50 | if( (size = slip->available()) > 0) { 51 | while(size--) msgIN.fill(slip->read()); 52 | } 53 | } 54 | 55 | if(!msgIN.hasError()) { 56 | oscEvent(msgIN); 57 | } 58 | 59 | // get the OSC message ready for the next one 60 | msgIN.reset(); 61 | } 62 | */ 63 | 64 | void OscSerial::send(OscMessage &msg) { 65 | msg.send(*slip); 66 | slip->endPacket(); 67 | msg.empty(); 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/OscSerial.h: -------------------------------------------------------------------------------- 1 | #ifndef OscSerial_h 2 | #define OscSerial_h 3 | 4 | #include "OSCMessage.h" 5 | 6 | #include "SLIPEncodedSerial.h" 7 | #include 8 | 9 | //============================================================================= 10 | class OscSerial { 11 | 12 | 13 | private: 14 | OscMessage msgIN; 15 | SLIPEncodedSerial *slip; 16 | 17 | public: 18 | // constructor 19 | OscSerial( ); 20 | 21 | void begin( HardwareSerial & ); 22 | void send( OscMessage & ); 23 | void listen( ); 24 | 25 | }; 26 | 27 | 28 | 29 | 30 | 31 | #endif -------------------------------------------------------------------------------- /src/OscUDP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | void oscEvent(OscMessage &); 5 | 6 | 7 | //============================================================================= 8 | // OSC_UDP Wrapper 9 | //============================================================================= 10 | 11 | // TODO: write similar wrapper class using the UDP interface 12 | // waiting to solve the blocking kink on the Serial one first 13 | 14 | OscUDP::OscUDP( ){ 15 | 16 | ; 17 | 18 | } 19 | 20 | void OscUDP::begin(EthernetUDP &u){ 21 | 22 | udp = &u; // &u ? u 23 | 24 | } 25 | 26 | 27 | void OscUDP::send(OscMessage &msg, NetAddress &na){ 28 | OscUDP::send (msg, na.getIP(), na.getPort() ); 29 | } 30 | 31 | 32 | void OscUDP::send(OscMessage &msg, IPAddress outIp, int outPort) { 33 | 34 | // SEND BASED ON THIS : 35 | // http://cnmat.berkeley.edu/library/oscuino/omessage 36 | 37 | // we need to do some magic here 38 | udp->beginPacket(outIp, outPort); 39 | msg.send(*udp); // send the bytes to the SLIP stream 40 | udp->endPacket(); // mark the end of the OSC Packet 41 | msg.empty(); // free space occupied by message 42 | 43 | } 44 | 45 | void OscUDP::listen() { 46 | 47 | // need a non-blocking method to read bytes from the UDP stream 48 | // parsePacket us analogous to available 49 | int UDPpacketSize; 50 | 51 | if( (UDPpacketSize = udp->parsePacket()) > 0) { 52 | while(UDPpacketSize--) msgIN.fill(udp->read()); 53 | } else { 54 | return; // i am not sure that works but lets see 55 | // i think it says if packet is <= 0 return 56 | } 57 | 58 | 59 | 60 | if (!msgIN.hasError()) { 61 | oscEvent(msgIN); 62 | } 63 | 64 | msgIN.reset(); 65 | 66 | 67 | } 68 | 69 | 70 | 71 | //============================================================================= 72 | // NetAddress interface 73 | //============================================================================= 74 | 75 | 76 | NetAddress :: NetAddress (){;} // sets up object 77 | 78 | void NetAddress::set(IPAddress _ip, int _port) { 79 | destinationIP = _ip; 80 | destinationPort = _port; 81 | } 82 | 83 | IPAddress NetAddress :: getIP (){ 84 | return destinationIP; 85 | } 86 | 87 | int NetAddress :: getPort(){ 88 | return destinationPort; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/OscUDP.h: -------------------------------------------------------------------------------- 1 | #ifndef OscUDP_h 2 | #define OscUDP_h 3 | 4 | #include "OSCMessage.h" 5 | 6 | 7 | // added this for out address in UDP 8 | #include 9 | #include 10 | 11 | 12 | 13 | //============================================================================= 14 | 15 | class NetAddress { 16 | 17 | 18 | protected: 19 | 20 | IPAddress destinationIP; 21 | int destinationPort; 22 | 23 | public: 24 | 25 | 26 | NetAddress(void); 27 | void set (IPAddress, int); 28 | IPAddress getIP(); 29 | int getPort(); 30 | 31 | 32 | }; 33 | 34 | 35 | 36 | //============================================================================= 37 | class OscUDP { 38 | 39 | protected: 40 | 41 | OscMessage msgIN; 42 | 43 | EthernetUDP *udp; 44 | 45 | NetAddress *destination; 46 | // add the outgoing IP and PORT 47 | IPAddress outIP; 48 | int outPort; 49 | 50 | 51 | public: 52 | 53 | OscUDP(); 54 | 55 | void begin (EthernetUDP &); 56 | 57 | void send(OscMessage &, NetAddress &); 58 | void send(OscMessage &, IPAddress, int); 59 | 60 | void listen(); 61 | 62 | }; 63 | 64 | 65 | 66 | 67 | 68 | #endif -------------------------------------------------------------------------------- /src/SLIPEncodedSerial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | CONSTRUCTOR 5 | */ 6 | //instantiate with the tranmission layer 7 | //use HardwareSerial 8 | SLIPEncodedSerial::SLIPEncodedSerial(HardwareSerial &s){ 9 | serial = &s; 10 | rstate = CHAR; 11 | } 12 | 13 | static const uint8_t eot = 0300; 14 | static const uint8_t slipesc = 0333; 15 | static const uint8_t slipescend = 0334; 16 | static const uint8_t slipescesc = 0335; 17 | /* 18 | SERIAL METHODS 19 | */ 20 | bool SLIPEncodedSerial::endofPacket() 21 | { 22 | if(rstate == SECONDEOT) 23 | { 24 | rstate = CHAR; 25 | return true; 26 | } 27 | if (rstate==FIRSTEOT) 28 | { 29 | uint8_t c =serial->peek(); 30 | if(c==eot) 31 | { 32 | serial->read(); // throw it on the floor 33 | } 34 | rstate = CHAR; 35 | return true; 36 | } 37 | return false; 38 | } 39 | 40 | int SLIPEncodedSerial::available(){ 41 | back: 42 | int cnt = serial->available(); 43 | 44 | if(cnt==0) 45 | return 0; 46 | if(rstate==CHAR) 47 | { 48 | uint8_t c =serial->peek(); 49 | if(c==slipesc) 50 | { 51 | rstate = SLIPESC; 52 | serial->read(); // throw it on the floor 53 | goto back; 54 | } 55 | else if(c==eot) 56 | { 57 | rstate = FIRSTEOT; 58 | serial->read(); // throw it on the floor 59 | goto back; 60 | } 61 | return 1; // we may have more but this is the only sure bet 62 | } 63 | else if(rstate==SLIPESC) 64 | return 1; 65 | else if(rstate==FIRSTEOT) 66 | { 67 | if(serial->peek()==eot) 68 | { 69 | rstate = SECONDEOT; 70 | serial->read(); // throw it on the floor 71 | return 0; 72 | } 73 | rstate = CHAR; 74 | }else if (rstate==SECONDEOT) { 75 | rstate = CHAR; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | //reads a byte from the buffer 82 | int SLIPEncodedSerial::read(){ 83 | back: 84 | uint8_t c = serial->read(); 85 | if(rstate==CHAR) 86 | { 87 | if(c==slipesc) 88 | { 89 | rstate=SLIPESC; 90 | goto back; 91 | } 92 | else if(c==eot){ 93 | 94 | return -1; // xxx this is an error 95 | } 96 | return c; 97 | } 98 | else 99 | if(rstate==SLIPESC) 100 | { 101 | rstate=CHAR; 102 | if(c==slipescend) 103 | return eot; 104 | else if(c==slipescesc) 105 | return slipesc; 106 | else { 107 | // insert some error code here 108 | return -1; 109 | } 110 | 111 | } 112 | else 113 | return -1; 114 | } 115 | 116 | // as close as we can get to correct behavior 117 | int SLIPEncodedSerial::peek(){ 118 | uint8_t c = serial->peek(); 119 | if(rstate==SLIPESC) 120 | { 121 | if(c==slipescend) 122 | return eot; 123 | else if(c==slipescesc) 124 | return slipesc; 125 | } 126 | return c; 127 | } 128 | 129 | //the arduino and wiring libraries have different return types for the write function 130 | #ifdef WIRING 131 | 132 | //encode SLIP 133 | void SLIPEncodedSerial::write(uint8_t b){ 134 | if(b == eot){ 135 | serial->write(slipesc); 136 | return serial->write(slipescend); 137 | } else if(b==slipesc) { 138 | serial->write(slipesc); 139 | return serial->write(slipescesc); 140 | } else { 141 | return serial->write(b); 142 | } 143 | } 144 | 145 | #else 146 | //encode SLIP 147 | size_t SLIPEncodedSerial::write(uint8_t b){ 148 | if(b == eot){ 149 | serial->write(slipesc); 150 | return serial->write(slipescend); 151 | } else if(b==slipesc) { 152 | serial->write(slipesc); 153 | return serial->write(slipescesc); 154 | } else { 155 | return serial->write(b); 156 | } 157 | } 158 | 159 | #endif 160 | 161 | void SLIPEncodedSerial::begin(unsigned long baudrate){ 162 | serial->begin(baudrate); 163 | } 164 | 165 | //signify the end of the packet with two EOT's 166 | void SLIPEncodedSerial::endPacket(){ 167 | serial->write(eot); 168 | serial->write(eot); 169 | } 170 | 171 | void SLIPEncodedSerial::flush(){ 172 | serial->flush(); 173 | } 174 | 175 | -------------------------------------------------------------------------------- /src/SLIPEncodedSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | Extends the Serial class to encode SLIP over serial 3 | */ 4 | 5 | #ifndef SLIPEncodedSerial_h 6 | #define SLIPEncodedSerial_h 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | class SLIPEncodedSerial: public Stream{ 13 | 14 | private: 15 | enum erstate {CHAR, FIRSTEOT, SECONDEOT, SLIPESC } rstate; 16 | 17 | //the serial port used 18 | HardwareSerial * serial; 19 | 20 | 21 | public: 22 | 23 | //the serial port used 24 | SLIPEncodedSerial(HardwareSerial & ); 25 | 26 | 27 | int available(); 28 | int read(); 29 | int peek(); 30 | void flush(); 31 | 32 | //same as Serial.begin 33 | void begin(unsigned long); 34 | 35 | //SLIP specific method which ends a transmittedpacket 36 | void endPacket(); 37 | // SLIP specific method which indicates that an EOT was received 38 | bool endofPacket(); 39 | 40 | 41 | //the arduino and wiring libraries have different return types for the write function 42 | #ifdef WIRING 43 | void write(uint8_t b); 44 | #else 45 | //overrides the Stream's write function to encode SLIP 46 | size_t write(uint8_t b); 47 | //using Print::write; 48 | #endif 49 | 50 | }; 51 | 52 | 53 | #endif -------------------------------------------------------------------------------- /temp/OSCBundle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Yotam Mann, The Center for New Music and Audio Technologies, 3 | University of California, Berkeley. Copyright (c) 2012, The Regents of 4 | the University of California (Regents). 5 | 6 | Permission to use, copy, modify, distribute, and distribute modified versions 7 | of this software and its documentation without fee and without a signed 8 | licensing agreement, is hereby granted, provided that the above copyright 9 | notice, this paragraph and the following two paragraphs appear in all copies, 10 | modifications, and distributions. 11 | 12 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 13 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 14 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 15 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | 17 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 20 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 21 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 22 | 23 | For bug reports and feature requests please email me at yotam@cnmat.berkeley.edu 24 | */ 25 | 26 | #include "OSCBundle.h" 27 | #include 28 | 29 | /*============================================================================= 30 | CONSTRUCTORS / DESTRUCTOR 31 | =============================================================================*/ 32 | 33 | OSCBundle::OSCBundle(uint64_t _timetag){ 34 | setTimetag(_timetag); 35 | numMessages = 0; 36 | error = OSC_OK; 37 | messages = NULL; 38 | incomingBuffer = NULL; 39 | incomingBufferSize = 0; 40 | decodeState = STANDBY; 41 | } 42 | 43 | OSCBundle::~OSCBundle(){ 44 | for (int i = 0; i < numMessages; i++){ 45 | OSCMessage * msg = getOSCMessage(i); 46 | delete msg; 47 | } 48 | free(messages); 49 | free(incomingBuffer); 50 | } 51 | 52 | //clears all of the OSCMessages inside 53 | void OSCBundle::empty(){ 54 | error = OSC_OK; 55 | for (int i = 0; i < numMessages; i++){ 56 | OSCMessage * msg = getOSCMessage(i); 57 | delete msg; 58 | } 59 | free(messages); 60 | messages = NULL; 61 | numMessages = 0; 62 | } 63 | 64 | /*============================================================================= 65 | SETTERS 66 | =============================================================================*/ 67 | 68 | OSCMessage & OSCBundle::add(char * _address){ 69 | OSCMessage * msg = new OSCMessage(_address); 70 | if (!msg->hasError()){ 71 | //realloc the array to fit the message 72 | OSCMessage ** messageMem = (OSCMessage **) realloc(messages, sizeof(OSCMessage *) * (numMessages + 1)); 73 | if (messageMem != NULL){ 74 | messages = messageMem; 75 | messages[numMessages] = msg; 76 | numMessages++; 77 | } else { 78 | error = ALLOCFAILED; 79 | } 80 | } 81 | return *msg; 82 | } 83 | 84 | OSCMessage & OSCBundle::add(){ 85 | OSCMessage * msg = new OSCMessage(); 86 | //realloc the array to fit the message 87 | OSCMessage ** messageMem = (OSCMessage **) realloc(messages, sizeof(OSCMessage *) * (numMessages + 1)); 88 | if (messageMem != NULL){ 89 | messages = messageMem; 90 | messages[numMessages] = msg; 91 | numMessages++; 92 | } else { 93 | error = ALLOCFAILED; 94 | } 95 | return *msg; 96 | } 97 | 98 | OSCMessage & OSCBundle::add(OSCMessage & _msg){ 99 | OSCMessage * msg = new OSCMessage(_msg); 100 | if (!msg->hasError()){ 101 | //realloc the array to fit the message 102 | OSCMessage ** messageMem = (OSCMessage **) realloc(messages, sizeof(OSCMessage *) * (numMessages + 1)); 103 | if (messageMem != NULL){ 104 | messages = messageMem; 105 | messages[numMessages] = msg; 106 | numMessages++; 107 | } else { 108 | error = ALLOCFAILED; 109 | } 110 | } 111 | return *msg; 112 | } 113 | 114 | /*============================================================================= 115 | GETTERS 116 | =============================================================================*/ 117 | 118 | //returns the first fullMatch. 119 | OSCMessage * OSCBundle::getOSCMessage( char * addr){ 120 | for (int i = 0; i < numMessages; i++){ 121 | OSCMessage * msg = getOSCMessage(i); 122 | if (msg->fullMatch(addr)){ 123 | return msg; 124 | } 125 | } 126 | } 127 | 128 | //the position is the same as the order they were declared in 129 | OSCMessage * OSCBundle::getOSCMessage(int pos){ 130 | if (pos < numMessages){ 131 | return messages[pos]; 132 | } 133 | } 134 | 135 | /*============================================================================= 136 | PATTERN MATCHING 137 | =============================================================================*/ 138 | 139 | 140 | bool OSCBundle::dispatch(const char * pattern, void (*callback)(OSCMessage&), int initial_offset){ 141 | bool called = false; 142 | for (int i = 0; i < numMessages; i++){ 143 | OSCMessage msg = getOSCMessage(i); 144 | called |= msg.dispatch(pattern, callback, initial_offset); 145 | } 146 | return called; 147 | } 148 | 149 | 150 | bool OSCBundle::route(const char * pattern, void (*callback)(OSCMessage&, int), int initial_offset){ 151 | bool called = false; 152 | for (int i = 0; i < numMessages; i++){ 153 | OSCMessage msg = getOSCMessage(i); 154 | called |= msg.route(pattern, callback, initial_offset); 155 | } 156 | return called; 157 | } 158 | 159 | /*============================================================================= 160 | SIZE 161 | =============================================================================*/ 162 | 163 | 164 | int OSCBundle::size(){ 165 | return numMessages; 166 | } 167 | 168 | /*============================================================================= 169 | ERROR HANDLING 170 | =============================================================================*/ 171 | 172 | bool OSCBundle::hasError(){ 173 | bool retError = error != OSC_OK; 174 | //test each of the data 175 | for (int i = 0; i < numMessages; i++){ 176 | OSCMessage * msg = getOSCMessage(i); 177 | retError |= msg->hasError(); 178 | } 179 | return retError; 180 | } 181 | 182 | OSCErrorCode OSCBundle::getError(){ 183 | return error; 184 | } 185 | 186 | 187 | /*============================================================================= 188 | SENDING 189 | =============================================================================*/ 190 | 191 | void OSCBundle::send(Print &p){ 192 | //don't send a bundle with errors 193 | if (hasError()){ 194 | return; 195 | } 196 | //write the bundle header 197 | static uint8_t header[] = {'#', 'b', 'u', 'n', 'd', 'l', 'e', 0}; 198 | p.write(header, 8); 199 | //write the timetag 200 | uint64_t t64 = BigEndian(timetag); 201 | uint8_t * tptr = (uint8_t *) &t64; 202 | p.write(tptr, 8); 203 | //send the messages 204 | for (int i = 0; i < numMessages; i++){ 205 | OSCMessage * msg = getOSCMessage(i); 206 | int msgSize = msg->bytes(); 207 | //turn the message size into a pointer 208 | uint64_t s32 = BigEndian((uint32_t) msgSize); 209 | uint8_t * sptr = (uint8_t *) &s32; 210 | //write the messsage size 211 | p.write(sptr, 4); 212 | msg->send(p); 213 | } 214 | } 215 | 216 | /*============================================================================= 217 | FILLING 218 | =============================================================================*/ 219 | 220 | void OSCBundle::fill(uint8_t incomingByte){ 221 | decode(incomingByte); 222 | } 223 | 224 | void OSCBundle::fill(uint8_t * incomingBytes, int length){ 225 | while (length--){ 226 | decode(*incomingBytes++); 227 | } 228 | } 229 | 230 | /*============================================================================= 231 | DECODING 232 | =============================================================================*/ 233 | 234 | void OSCBundle::decodeTimetag(){ 235 | //parse the incoming buffer as a uint64 236 | setTimetag(incomingBuffer); 237 | //make sure the endianness is right 238 | timetag = BigEndian(timetag); 239 | decodeState = MESSAGE_SIZE; 240 | clearIncomingBuffer(); 241 | } 242 | 243 | void OSCBundle::decodeHeader(){ 244 | const char * header = "#bundle"; 245 | if (strcmp(header, (char *) incomingBuffer)!=0){ 246 | //otherwise go back to the top and wait for a new bundle header 247 | decodeState = STANDBY; 248 | error = INVALID_OSC; 249 | } else { 250 | decodeState = TIMETAG; 251 | } 252 | clearIncomingBuffer(); 253 | } 254 | 255 | void OSCBundle::decodeMessage(uint8_t incomingByte){ 256 | //get the current message 257 | if (numMessages > 0){ 258 | OSCMessage * lastMessage = messages[numMessages - 1]; 259 | //put the bytes in there 260 | lastMessage->fill(incomingByte); 261 | //if it's all done 262 | if (incomingBufferSize == incomingMessageSize){ 263 | //move onto the next message 264 | decodeState = MESSAGE_SIZE; 265 | clearIncomingBuffer(); 266 | } else if (incomingBufferSize > incomingMessageSize){ 267 | error = INVALID_OSC; 268 | } 269 | } 270 | } 271 | 272 | //does not validate the incoming OSC for correctness 273 | void OSCBundle::decode(uint8_t incomingByte){ 274 | addToIncomingBuffer(incomingByte); 275 | switch (decodeState){ 276 | case STANDBY: 277 | if (incomingByte == '#'){ 278 | decodeState = HEADER; 279 | } else if (incomingByte == '/'){ 280 | decodeState = MESSAGE; 281 | } 282 | break; 283 | case HEADER: 284 | if (incomingBufferSize == 8){ 285 | decodeHeader(); 286 | decodeState = TIMETAG; 287 | } 288 | break; 289 | case TIMETAG: 290 | if (incomingBufferSize == 8){ 291 | decodeTimetag(); 292 | decodeState = MESSAGE_SIZE; 293 | } 294 | break; 295 | case MESSAGE_SIZE: 296 | if (incomingBufferSize == 4){ 297 | //make sure the message size is valid 298 | int32_t msgSize; 299 | memcpy(&msgSize, incomingBuffer, 4); 300 | msgSize = BigEndian(msgSize); 301 | if (msgSize % 4 != 0 || msgSize == 0){ 302 | error = INVALID_OSC; 303 | } else { 304 | //add a message to the buffer 305 | decodeState = MESSAGE; 306 | incomingMessageSize = msgSize; 307 | clearIncomingBuffer(); 308 | //add a new empty message 309 | add(); 310 | } 311 | } 312 | break; 313 | case MESSAGE: 314 | decodeMessage(incomingByte); 315 | break; 316 | } 317 | } 318 | 319 | 320 | /*============================================================================= 321 | INCOMING BUFFER MANAGEMENT 322 | =============================================================================*/ 323 | 324 | void OSCBundle::addToIncomingBuffer(uint8_t incomingByte){ 325 | //realloc some space for the new byte and stick it on the end 326 | incomingBuffer = (uint8_t *) realloc ( incomingBuffer, incomingBufferSize + 1); 327 | if (incomingBuffer != NULL){ 328 | incomingBuffer[incomingBufferSize++] = incomingByte; 329 | } else { 330 | error = ALLOCFAILED; 331 | } 332 | } 333 | 334 | void OSCBundle::clearIncomingBuffer(){ 335 | incomingBufferSize = 0; 336 | free(incomingBuffer); 337 | incomingBuffer = NULL; 338 | } 339 | 340 | 341 | 342 | -------------------------------------------------------------------------------- /temp/OSCBundle.h: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Yotam Mann, The Center for New Music and Audio Technologies, 3 | University of California, Berkeley. Copyright (c) 2012, 2013, The Regents of 4 | the University of California (Regents). 5 | 6 | Permission to use, copy, modify, distribute, and distribute modified versions 7 | of this software and its documentation without fee and without a signed 8 | licensing agreement, is hereby granted, provided that the above copyright 9 | notice, this paragraph and the following two paragraphs appear in all copies, 10 | modifications, and distributions. 11 | 12 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 13 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 14 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 15 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | 17 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 20 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 21 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 22 | 23 | 24 | */ 25 | 26 | #ifndef OSCBUNDLE_h 27 | #define OSCBUNDLE_h 28 | 29 | #include "OSCMessage.h" 30 | 31 | class OSCBundle 32 | { 33 | 34 | private: 35 | 36 | /*============================================================================= 37 | PRIVATE VARIABLES 38 | =============================================================================*/ 39 | 40 | //the array of messages contained in the bundle 41 | OSCMessage ** messages; 42 | 43 | //the number of messages in the array 44 | int numMessages; 45 | 46 | uint64_t timetag; 47 | 48 | //error codes 49 | OSCErrorCode error; 50 | 51 | /*============================================================================= 52 | DECODING INCOMING BYTES 53 | =============================================================================*/ 54 | 55 | //the decoding states for incoming bytes 56 | enum DecodeState { 57 | STANDBY, 58 | HEADER, 59 | TIMETAG, 60 | MESSAGE_SIZE, 61 | MESSAGE, 62 | } decodeState; 63 | 64 | //stores incoming bytes until they can be decoded 65 | uint8_t * incomingBuffer; 66 | int incomingBufferSize; 67 | 68 | //the size of the incoming message 69 | int incomingMessageSize; 70 | 71 | //adds a byte to the buffer 72 | void addToIncomingBuffer(uint8_t); 73 | //clears the incoming buffer 74 | void clearIncomingBuffer(); 75 | 76 | //decoding functions 77 | void decode(uint8_t); 78 | void decodeTimetag(); 79 | void decodeHeader(); 80 | void decodeMessage(uint8_t); 81 | 82 | //just a placeholder while filling 83 | OSCMessage & add(); 84 | 85 | 86 | public: 87 | 88 | /*============================================================================= 89 | CONSTRUCTORS / DESTRUCTOR 90 | =============================================================================*/ 91 | 92 | //default timetag of 1 93 | OSCBundle(uint64_t = 1); 94 | 95 | //DESTRUCTOR 96 | ~OSCBundle(); 97 | 98 | //clears all of the OSCMessages inside 99 | void empty(); 100 | 101 | /*============================================================================= 102 | SETTERS 103 | =============================================================================*/ 104 | 105 | //start a new OSC Message in the bundle 106 | OSCMessage & add( char * address); 107 | //add with nothing in it produces an invalid osc message 108 | //copies an existing message into the bundle 109 | OSCMessage & add(OSCMessage & msg); 110 | 111 | template 112 | void setTimetag(T t){ 113 | timetag = (uint64_t) t; 114 | } 115 | //sets the timetag from a buffer 116 | void setTimetag(uint8_t * buff){ 117 | memcpy(&timetag, buff, 8); 118 | } 119 | 120 | /*============================================================================= 121 | GETTERS 122 | =============================================================================*/ 123 | 124 | //gets the message the matches the address string 125 | //will do regex matching 126 | OSCMessage * getOSCMessage(char * addr); 127 | 128 | //get message by position 129 | OSCMessage * getOSCMessage(int position); 130 | 131 | /*============================================================================= 132 | MATCHING 133 | =============================================================================*/ 134 | 135 | //if the bundle contains a message that matches the pattern, 136 | //call the function callback on that message 137 | bool dispatch(const char * pattern, void (*callback)(OSCMessage&), int = 0); 138 | 139 | //like dispatch, but allows for partial matches 140 | //the address match offset is sent as an argument to the callback 141 | bool route(const char * pattern, void (*callback)(OSCMessage&, int), int = 0); 142 | 143 | /*============================================================================= 144 | SIZE 145 | =============================================================================*/ 146 | //returns the number of messages in the bundle; 147 | int size(); 148 | 149 | /*============================================================================= 150 | ERROR 151 | =============================================================================*/ 152 | 153 | bool hasError(); 154 | 155 | OSCErrorCode getError(); 156 | 157 | /*============================================================================= 158 | SENDING 159 | =============================================================================*/ 160 | 161 | void send(Print &p); 162 | 163 | /*============================================================================= 164 | FILLING 165 | =============================================================================*/ 166 | 167 | void fill(uint8_t incomingByte); 168 | 169 | void fill(uint8_t * incomingBytes, int length); 170 | }; 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /temp/README: -------------------------------------------------------------------------------- 1 | Classes from OSCuino which we're not dealing with at the moment. -------------------------------------------------------------------------------- /temp/SLIPEncodedUSBSerial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | CONSTRUCTOR 5 | */ 6 | //instantiate with the tranmission layer 7 | 8 | //different constructor for teensies 9 | #if defined(CORE_TEENSY)|| defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) 10 | 11 | 12 | //USB Serials 13 | SLIPEncodedUSBSerial::SLIPEncodedUSBSerial( 14 | #if !defined(CORE_TEENSY)|| defined(__SAM3X8E__) 15 | Serial_ 16 | #else 17 | usb_serial_class 18 | #endif 19 | &s){ 20 | serial = &s; 21 | rstate = CHAR; 22 | } 23 | 24 | static const uint8_t eot = 0300; 25 | static const uint8_t slipesc = 0333; 26 | static const uint8_t slipescend = 0334; 27 | static const uint8_t slipescesc = 0335; 28 | /* 29 | SERIAL METHODS 30 | */ 31 | //SLIP specific method which begins a transmitted packet 32 | void SLIPEncodedUSBSerial::beginPacket() {} 33 | bool SLIPEncodedUSBSerial::endofPacket() 34 | { 35 | if(rstate == SECONDEOT) 36 | { 37 | rstate = CHAR; 38 | return true; 39 | } 40 | if (rstate==FIRSTEOT) 41 | { 42 | uint8_t c =serial->peek(); 43 | if(c==eot) 44 | { 45 | serial->read(); // throw it on the floor 46 | } 47 | rstate = CHAR; 48 | return true; 49 | } 50 | return false; 51 | } 52 | int SLIPEncodedUSBSerial::available(){ 53 | back: 54 | int cnt = serial->available(); 55 | 56 | if(cnt==0) 57 | return 0; 58 | if(rstate==CHAR) 59 | { 60 | uint8_t c =serial->peek(); 61 | if(c==slipesc) 62 | { 63 | rstate = SLIPESC; 64 | serial->read(); // throw it on the floor 65 | goto back; 66 | } 67 | else if( c==eot) 68 | { 69 | rstate = FIRSTEOT; 70 | serial->read(); // throw it on the floor 71 | goto back; 72 | } 73 | return 1; // we may have more but this is the only sure bet 74 | } 75 | else if(rstate==SLIPESC) 76 | return 1; 77 | else if(rstate==FIRSTEOT) 78 | { 79 | if(serial->peek()==eot) 80 | { 81 | rstate = SECONDEOT; 82 | serial->read(); // throw it on the floor 83 | return 0; 84 | } 85 | rstate = CHAR; 86 | }else if (rstate==SECONDEOT) { 87 | rstate = CHAR; 88 | } 89 | 90 | return 0; 91 | 92 | } 93 | 94 | //reads a byte from the buffer 95 | int SLIPEncodedUSBSerial::read(){ 96 | back: 97 | uint8_t c = serial->read(); 98 | if(rstate==CHAR) 99 | { 100 | if(c==slipesc) 101 | { 102 | rstate=SLIPESC; 103 | goto back; 104 | } 105 | else if(c==eot){ 106 | 107 | return -1; // xxx this is an error 108 | } 109 | 110 | return c; 111 | } 112 | else 113 | if(rstate==SLIPESC) 114 | { 115 | rstate=CHAR; 116 | if(c==slipescend) 117 | return eot; 118 | else if(c==slipescesc) 119 | return slipesc; 120 | else { 121 | // insert some error code here 122 | return -1; 123 | } 124 | 125 | } 126 | else 127 | return -1; 128 | } 129 | 130 | // as close as we can get to correct behavior 131 | int SLIPEncodedUSBSerial::peek(){ 132 | uint8_t c = serial->peek(); 133 | if(rstate==SLIPESC) 134 | { 135 | if(c==slipescend) 136 | return eot; 137 | else if(c==slipescesc) 138 | return slipesc; 139 | } 140 | return c; 141 | } 142 | 143 | //the arduino and wiring libraries have different return types for the write function 144 | #ifdef WIRING 145 | 146 | //encode SLIP 147 | void SLIPEncodedUSBSerial::write(uint8_t b){ 148 | if(b == eot){ 149 | serial->write(slipesc); 150 | return serial->write(slipescend); 151 | } else if(b==slipesc) { 152 | serial->write(slipesc); 153 | return serial->write(slipescesc); 154 | } else { 155 | return serial->write(b); 156 | } 157 | } 158 | 159 | #else 160 | //encode SLIP 161 | size_t SLIPEncodedUSBSerial::write(uint8_t b){ 162 | if(b == eot){ 163 | serial->write(slipesc); 164 | return serial->write(slipescend); 165 | } else if(b==slipesc) { 166 | serial->write(slipesc); 167 | return serial->write(slipescesc); 168 | } else { 169 | return serial->write(b); 170 | } 171 | } 172 | 173 | #endif 174 | 175 | void SLIPEncodedUSBSerial::begin(unsigned long baudrate){ 176 | serial->begin(baudrate); 177 | } 178 | 179 | //signify the end of the packet with two EOT's 180 | void SLIPEncodedUSBSerial::endPacket(){ 181 | serial->write(eot); 182 | serial->write(eot); 183 | #if defined(CORE_TEENSY) 184 | serial->send_now(); 185 | #endif 186 | } 187 | 188 | void SLIPEncodedUSBSerial::flush(){ 189 | serial->flush(); 190 | } 191 | #endif 192 | 193 | -------------------------------------------------------------------------------- /temp/SLIPEncodedUSBSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | Extends the Serial class to encode SLIP over serial 3 | */ 4 | 5 | #ifndef SLIPEncodedUSBSerial_h 6 | #define SLIPEncodedUSBSerial_h 7 | #include 8 | #include 9 | 10 | #if defined(CORE_TEENSY)|| defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) 11 | //import the serial object 12 | #if defined (__MK20DX128__) 13 | #include 14 | #elif defined(CORE_TEENSY) 15 | #include 16 | #elif defined(__SAM3X8E__) 17 | #include 18 | #else 19 | #include "Platform.h" 20 | #include "USBAPI.h" 21 | #include 22 | // leonardo 23 | 24 | #endif 25 | 26 | 27 | 28 | class SLIPEncodedUSBSerial: public Stream{ 29 | 30 | private: 31 | enum erstate {CHAR, FIRSTEOT, SECONDEOT, SLIPESC } rstate; 32 | 33 | #if !defined(CORE_TEENSY) || defined(__SAM3X8E__) 34 | Serial_ 35 | #else 36 | usb_serial_class 37 | #endif 38 | * serial; 39 | 40 | public: 41 | 42 | //different constructor for teensies 43 | SLIPEncodedUSBSerial( 44 | #if !defined(CORE_TEENSY) 45 | Serial_ 46 | #else 47 | usb_serial_class 48 | #endif 49 | & ); 50 | 51 | int available(); 52 | int read(); 53 | int peek(); 54 | void flush(); 55 | 56 | //same as Serial.begin 57 | void begin(unsigned long); 58 | //SLIP specific method which begins a transmitted packet 59 | void beginPacket(); 60 | //SLIP specific method which ends a transmittedpacket 61 | void endPacket(); 62 | // SLIP specific method which indicates that an EOT was received 63 | bool endofPacket(); 64 | 65 | 66 | //the arduino and wiring libraries have different return types for the write function 67 | #ifdef WIRING 68 | void write(uint8_t b); 69 | #else 70 | //overrides the Stream's write function to encode SLIP 71 | size_t write(uint8_t b); 72 | //using Print::write; 73 | #endif 74 | 75 | }; 76 | #endif 77 | 78 | 79 | 80 | #endif --------------------------------------------------------------------------------