supercolliderArduino_sendAndReceive

clean-up: #33

below is some supercollider code for sending and receiving data from an arduino. it is a good example i think and worth sharing. it can be used as a template for when you want to read and write at the same time.

as the example is written supercollider writes data to six pwm channels (0-255), and reads data from six analog inputs (also 0-255). the sliders are the pwm output controllers, and the sensor input data is just posted as an array.

//supercolliderArduino_sendAndReceive /f0
//  controlling 6 pwm channels and reading 6 analog inputs at the same time
//  use with sc code supercolliderArduino_sendAndReceive.scd

//--serial protocol
// pwm sc->arduino: 10 nn nn nn nn nn nn 11 (nn bytes 1-6= pwm channels 0-5)
// adc arduino->sc: 20 nn nn nn nn nn nn 21 (nn bytes 1-6= analog channels 0-5)

//--settings
byte pwmPins[]= {5, 3, 9, 6, 11, 10};
byte adcPins[]= {0, 1, 2, 3, 4, 5};  //A0-A5

//--variables
boolean changed;
byte val, cnt= 0;
byte pwmData[]= {0, 0, 0, 0, 0, 0, 0, 0};
byte adcData[]= {0, 0, 0, 0, 0, 0, 0, 0};  //for detecting change and filter out repetitions

void setup() {
  Serial.begin(38400);    //baudrate must match in sc
  for(byte i= 0; i<6; i++) {
    pinMode(pwmPins[i], OUTPUT);
    pinMode(adcPins[i], INPUT);
  }
}
void loop() {
 
  //--from sc
  while(Serial.available()) {
    val= Serial.read();
    if(cnt==0) {
      if(val==10) {  //beginning of message found
        cnt= 1;  //start counter
      }
    } else if(cnt<7) {  //between 1 and 6 means message started and that bytes should be saved
      pwmData[cnt-1]= val;  //saving incoming bytes in temporary data array
      cnt++;
    } else {
      if(val==11) {
        for(byte i= 0; i<6; i++) {
          analogWrite(pwmPins[i], pwmData[i]);  //output read message to pwm pins
        }
      } else {
        //serial read error
      }
      cnt= 0;  //reset byte counter
    }
  }
 
  //--to sc
  changed= false;
  for(byte i= 0; i<6; i++) {
    val= analogRead(adcPins[i])>>4;  //scale from 10 to 8 bits
    if(val!=adcData[i]) {
      adcData[i]= val;
      changed= true;
    }
  }
  if(changed) {  //check if any sensor changed
    Serial.write(20);
    Serial.write(adcData[0]);
    Serial.write(adcData[1]);
    Serial.write(adcData[2]);
    Serial.write(adcData[3]);
    Serial.write(adcData[4]);
    Serial.write(adcData[5]);
    Serial.write(21);
  }
  delay(10);  //wait 10 milliseconds
}

//--use with supercolliderArduino_sendAndReceive.ino
//supercollider code for reading 6 analog sensors and sending out to 6 pwm channels

SerialPort.listDevices; //run this and see post window for name of serial device

//--gui code for sending 6 pwm
(
var name= "/dev/tty.usbserial-A101NB76";        //edit to match your serial device
var port= SerialPort(name, 38400, crtscts: true);
var pwm= [10, 0, 0, 0, 0, 0, 0, 11];
var win= Window("pwm", Rect(99, 99, 260, 200), false);
Slider(win, Rect(10, 10, 30, 170)).action_{|view| pwm.put(1, (view.value*255).asInteger); port.putAll(pwm.postln)};
Slider(win, Rect(50, 10, 30, 170)).action_{|view| pwm.put(2, (view.value*255).asInteger); port.putAll(pwm.postln)};
Slider(win, Rect(90, 10, 30, 170)).action_{|view| pwm.put(3, (view.value*255).asInteger); port.putAll(pwm.postln)};
Slider(win, Rect(130, 10, 30, 170)).action_{|view| pwm.put(4, (view.value*255).asInteger); port.putAll(pwm.postln)};
Slider(win, Rect(170, 10, 30, 170)).action_{|view| pwm.put(5, (view.value*255).asInteger); port.putAll(pwm.postln)};
Slider(win, Rect(210, 10, 30, 170)).action_{|view| pwm.put(6, (view.value*255).asInteger); port.putAll(pwm.postln)};
win.front;
CmdPeriod.doOnce({port.putAll([10, 0, 0, 0, 0, 0, 0, 11]); port.close; win.close});
//press cmd+. to stop and close window and serial port

s.waitForBoot{
        var byte, num= 6, index= 0, data= Array.newClear(num);
        Ndef(\arduino, {|data= #[0, 0, 0, 0, 0, 0]| Splay.ar(SinOsc.ar(data.lag(0.02)+500, 0, 0.4))}).play; //temp sound
        Routine.run({
                inf.do{
                        while({byte= port.read; byte.notNil}, {
                                //byte.postln;  //debug
                                if(index==0 and:{byte==20}, {//check if first byte is 20
                                        index= 1;
                                        }, {
                                                if(index>=1 and:{index<(num+1)}, {//ok, now start collecting bytes
                                                        data[index-1]= byte;
                                                        index= index+1;
                                                        }, {
                                                                if(index==(num+1) and:{byte==21}, {//until last data byte
                                                                        data.postln;    //debug
                                                                        Ndef(\arduino).setn(\data, data);
                                                                        index= 0;//done. reset index to prepare for new message
                                                                        }, {
                                                                                //something broke or beginning - restart
                                                                                "restart".postln;       //debug
                                                                                index= 0;
                                                                });
                                                });
                                });
                        });
                };
        });
};

)

i also attached the complete example as a zip file.