From 336b2b45ab016defe9e36139139544db7bf5ba2f Mon Sep 17 00:00:00 2001 From: Bill Verplank Date: Tue, 5 Feb 2013 19:01:35 +0100 Subject: [PATCH] Bill Examples M&MStudioBV --- .../CirclesClick/CirclesClick.pde | 22 +++ .../DragBeads/DragBeads.pde | 34 +++++ .../--ProcessingProfile/Dragging/Dragging.pde | 59 ++++++++ .../DraggingFixedWidths.pde | 83 ++++++++++++ .../DraggingWidthsKbdToo.pde | 96 +++++++++++++ .../DraggingWidthsWILD/DraggingWidthsWILD.pde | 83 ++++++++++++ .../DraggingWidthsWorks.pde | 91 +++++++++++++ .../InterpolationReady/InterpolationReady.pde | 50 +++++++ .../LonzengesThree/LonzengesThree.pde | 49 +++++++ .../Profile/Profile/Profile.ino | 10 ++ .../Profile/ProfileFx/ProfileFx.pde | 91 +++++++++++++ .../ProfileFixedWidths/ProfileFixedWidths.pde | 82 ++++++++++++ .../ProfileNeedScaling/ProfileNeedScaling.pde | 82 ++++++++++++ .../StrangeDragging/StrangeDragging.pde | 50 +++++++ .../M&MStudioBV/ABSlaveMusic/ABSlaveMusic.ino | 25 ++++ .../ABSlaveMusicWarble/ABSlaveMusicWarble.ino | 56 ++++++++ software/apps/M&MStudioBV/ABslave/ABslave.ino | 43 ++++++ software/apps/M&MStudioBV/Bumps4/Bumps4.ino | 74 ++++++++++ .../apps/M&MStudioBV/CenterABM/CenterABM.ino | 42 ++++++ .../CenterABtoggle/CenterABtoggle.ino | 43 ++++++ .../FM/CenterFSR_A/CenterFSR_A.ino | 34 +++++ software/apps/M&MStudioBV/FM/FM.ino | 31 +++++ .../apps/M&MStudioBV/FM/FMGraph/FMGraph.pde | 63 +++++++++ .../M&MStudioBV/FSR/Bumps4FSR/Bumps4FSR.ino | 97 ++++++++++++++ .../FSR/FSR_AB_center/FSR_AB_center.ino | 37 +++++ .../FSR/FSR_AB_test/FSR_AB_test.ino | 20 +++ .../FSR/FSR_D4_OUTPUT/FSR_D4_OUTPUT.ino | 17 +++ .../M&MStudioBV/FSR/HumpFSR_A/HumpFSR_A.ino | 36 +++++ .../M&MStudioBV/FSR/SandFSR_A/SandFSR_A.ino | 33 +++++ .../M&MStudioBV/Gran/GongRoarCockShort.wav | Bin 0 -> 31798 bytes software/apps/M&MStudioBV/Gran/GranFlash.rtf | 13 ++ .../M&MStudioBV/PluckOverlap/PluckOverlap.ino | 55 ++++++++ .../PluckOverlapHalf/PluckOverlapHalf.ino | 51 +++++++ .../ProcessingProfile/ProfileFx/ProfileFx.pde | 126 ++++++++++++++++++ software/apps/M&MStudioBV/Pulse/Pulse.ino | 20 +++ .../M&MStudioBV/Pulse_Beat/Pulse_Beat.ino | 34 +++++ .../apps/M&MStudioBV/ReadA0A1/ReadA0A1.ino | 17 +++ .../M&MStudioBV/WallPluck_in/WallPluck_in.ino | 54 ++++++++ .../WallPluck_out/WallPluck_out.ino | 54 ++++++++ .../M&MStudioBV/motortestM/motortestM.ino | 22 +++ software/apps/M&MStudioBV/plotOsc/plotOsc.pde | 27 ++++ .../Motor/pos_FSR_AB_test/pos_FSR_AB_test.ino | 30 +++++ 42 files changed, 2036 insertions(+) create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/CirclesClick/CirclesClick.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/DragBeads/DragBeads.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/Dragging/Dragging.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/DraggingFixedWidths/DraggingFixedWidths.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/DraggingWidthsKbdToo/DraggingWidthsKbdToo.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/DraggingWidthsWILD/DraggingWidthsWILD.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/DraggingWidthsWorks/DraggingWidthsWorks.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/InterpolationReady/InterpolationReady.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/LonzengesThree/LonzengesThree.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/Profile/Profile/Profile.ino create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/Profile/ProfileFx/ProfileFx.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/ProfileFixedWidths/ProfileFixedWidths.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/ProfileNeedScaling/ProfileNeedScaling.pde create mode 100644 software/apps/M&MStudioBV/--ProcessingProfile/StrangeDragging/StrangeDragging.pde create mode 100644 software/apps/M&MStudioBV/ABSlaveMusic/ABSlaveMusic.ino create mode 100644 software/apps/M&MStudioBV/ABSlaveMusicWarble/ABSlaveMusicWarble.ino create mode 100644 software/apps/M&MStudioBV/ABslave/ABslave.ino create mode 100644 software/apps/M&MStudioBV/Bumps4/Bumps4.ino create mode 100644 software/apps/M&MStudioBV/CenterABM/CenterABM.ino create mode 100644 software/apps/M&MStudioBV/CenterABtoggle/CenterABtoggle.ino create mode 100644 software/apps/M&MStudioBV/FM/CenterFSR_A/CenterFSR_A.ino create mode 100644 software/apps/M&MStudioBV/FM/FM.ino create mode 100644 software/apps/M&MStudioBV/FM/FMGraph/FMGraph.pde create mode 100644 software/apps/M&MStudioBV/FSR/Bumps4FSR/Bumps4FSR.ino create mode 100644 software/apps/M&MStudioBV/FSR/FSR_AB_center/FSR_AB_center.ino create mode 100644 software/apps/M&MStudioBV/FSR/FSR_AB_test/FSR_AB_test.ino create mode 100644 software/apps/M&MStudioBV/FSR/FSR_D4_OUTPUT/FSR_D4_OUTPUT.ino create mode 100644 software/apps/M&MStudioBV/FSR/HumpFSR_A/HumpFSR_A.ino create mode 100644 software/apps/M&MStudioBV/FSR/SandFSR_A/SandFSR_A.ino create mode 100644 software/apps/M&MStudioBV/Gran/GongRoarCockShort.wav create mode 100644 software/apps/M&MStudioBV/Gran/GranFlash.rtf create mode 100644 software/apps/M&MStudioBV/PluckOverlap/PluckOverlap.ino create mode 100644 software/apps/M&MStudioBV/PluckOverlapHalf/PluckOverlapHalf.ino create mode 100644 software/apps/M&MStudioBV/ProcessingProfile/ProfileFx/ProfileFx.pde create mode 100644 software/apps/M&MStudioBV/Pulse/Pulse.ino create mode 100644 software/apps/M&MStudioBV/Pulse_Beat/Pulse_Beat.ino create mode 100644 software/apps/M&MStudioBV/ReadA0A1/ReadA0A1.ino create mode 100644 software/apps/M&MStudioBV/WallPluck_in/WallPluck_in.ino create mode 100644 software/apps/M&MStudioBV/WallPluck_out/WallPluck_out.ino create mode 100644 software/apps/M&MStudioBV/motortestM/motortestM.ino create mode 100644 software/apps/M&MStudioBV/plotOsc/plotOsc.pde create mode 100644 software/apps/Motor/pos_FSR_AB_test/pos_FSR_AB_test.ino diff --git a/software/apps/M&MStudioBV/--ProcessingProfile/CirclesClick/CirclesClick.pde b/software/apps/M&MStudioBV/--ProcessingProfile/CirclesClick/CirclesClick.pde new file mode 100644 index 0000000..a844331 --- /dev/null +++ b/software/apps/M&MStudioBV/--ProcessingProfile/CirclesClick/CirclesClick.pde @@ -0,0 +1,22 @@ + +// Click within the image to change +// the value of the rectangle + +int value = 20; + +void setup(){ + size(300,400); + stroke(value); + background(250); +} + +void draw(){ + value = mouseX; + fill(value); + stroke(value-100); +} + +void mouseReleased() { + ellipse(mouseX,mouseY,20,20); +} + diff --git a/software/apps/M&MStudioBV/--ProcessingProfile/DragBeads/DragBeads.pde b/software/apps/M&MStudioBV/--ProcessingProfile/DragBeads/DragBeads.pde new file mode 100644 index 0000000..e446bf8 --- /dev/null +++ b/software/apps/M&MStudioBV/--ProcessingProfile/DragBeads/DragBeads.pde @@ -0,0 +1,34 @@ + +int[] altitude = { 10, 20, 30, 40, 50, 40, 30, 20, 10, 0 }; +int i; +int s = 80; + +void setup() { + size(800,100); +} +void draw(){ + background(200); + stroke(150); + line(0,50,799,50); + stroke(0); + + for (i = 0; i < 9; i = i+1) { + line(s*i,altitude[i],s*(i+1),altitude[i+1]); + } + + line(s*9, altitude[9], s*10-1, 50); + + for (i = 0; i < 9; i = i+1) { + ellipse(s*(i+1),altitude[i+1], 10, 10); + } + + if (mousePressed == true) { + int pos = (mouseX+40)/s; + altitude[pos]=mouseY; + } +} + +void mouseReleased() { + int pos = mouseX/s; + altitude[pos]=mouseY; +} diff --git a/software/apps/M&MStudioBV/--ProcessingProfile/Dragging/Dragging.pde b/software/apps/M&MStudioBV/--ProcessingProfile/Dragging/Dragging.pde new file mode 100644 index 0000000..99fcc54 --- /dev/null +++ b/software/apps/M&MStudioBV/--ProcessingProfile/Dragging/Dragging.pde @@ -0,0 +1,59 @@ +// two-dimensional array? +// i = 1, 8; +// int F[], // shown an height +// x[i], // + +// constrain x[i] sq(F[i]+100-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]+100-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest]+100,20,20); + +text (Integer.toString(ibest), mouseX+10, mouseY-12); +text (Integer.toString(F[ibest]), mouseX+10, mouseY); +text (Integer.toString(x[ibest]), mouseX+10, mouseY+12); + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + x[ibest] = min(x[ibest+1],max(x[ibest-1],mouseX)); + F[ibest] = mouseY-100; + + } +} + + + diff --git a/software/apps/M&MStudioBV/--ProcessingProfile/DraggingFixedWidths/DraggingFixedWidths.pde b/software/apps/M&MStudioBV/--ProcessingProfile/DraggingFixedWidths/DraggingFixedWidths.pde new file mode 100644 index 0000000..79fe817 --- /dev/null +++ b/software/apps/M&MStudioBV/--ProcessingProfile/DraggingFixedWidths/DraggingFixedWidths.pde @@ -0,0 +1,83 @@ +// two-dimensional array? +// i = 1, 8; +// int F[], // shown an height +// x[i], // + +// constrain x[i] sq(F[i]+100-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]+100-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest]+100,20,20); + +text (Integer.toString(ibest), mouseX+10, mouseY-12); +text (Integer.toString(F[ibest]), mouseX+10, mouseY); +text (Integer.toString(x[ibest]), mouseX+10, mouseY+12); + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + x[ibest] = min(x[ibest+1],max(x[ibest-1],mouseX)); + F[ibest] = mouseY-100; + + } + +//plot "terrain" assuming 80 point separation of F[]'s + pushMatrix(); + translate(0,height/2); + /*for (i = 0; i sq(F[i]+100-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]+100-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest]+100,20,20); + +text (Integer.toString(ibest), mouseX+10, mouseY-12); +text (Integer.toString(F[ibest]), mouseX+10, mouseY); +text (Integer.toString(x[ibest]), mouseX+10, mouseY+12); + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + x[ibest] = min(x[ibest+1],max(x[ibest-1],mouseX)); + F[ibest] = mouseY-100; + + } + +//plot "terrain" assuming 80 point separation of F[]'s + pushMatrix(); + translate(0,height/2); + /*for (i = 0; i sq(F[i]+100-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]+100-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest]+100,20,20); + +text (Integer.toString(ibest), mouseX+10, mouseY-12); +text (Integer.toString(F[ibest]), mouseX+10, mouseY); +text (Integer.toString(x[ibest]), mouseX+10, mouseY+12); + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + x[ibest] = min(x[ibest+1],max(x[ibest-1],mouseX)); + F[ibest] = mouseY - 100; + if (mouseY < 0) F[ibest] = -100; + if (mouseY > 200) F[ibest] = 100; + } + +//plot "terrain" assuming 80 point separation of F[]'s + pushMatrix(); + translate(0,height/2); + + top = 0; + FF = F[0]; + for (i = 0; i < n; i = i+1) { + s = x[i+1]-x[i]+1; // increment used for plotting + inc = 0.01*(F[i+1]-F[i])/s; + for (j = 0; j < s; j = j+1) { + FF = FF + inc; + top = top + FF; // j increments of inc = F diff. + line(x[i]+j,height,x[i]+j,top); + } + } + popMatrix(); +} + + + diff --git a/software/apps/M&MStudioBV/--ProcessingProfile/DraggingWidthsWorks/DraggingWidthsWorks.pde b/software/apps/M&MStudioBV/--ProcessingProfile/DraggingWidthsWorks/DraggingWidthsWorks.pde new file mode 100644 index 0000000..9d5efcb --- /dev/null +++ b/software/apps/M&MStudioBV/--ProcessingProfile/DraggingWidthsWorks/DraggingWidthsWorks.pde @@ -0,0 +1,91 @@ +// two-dimensional array? +// i = 1, 8; +// int F[], // shown an height +// x[i], // + +// constrain x[i] sq(F[i]+100-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]+100-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest]+100,20,20); + +text (Integer.toString(ibest), mouseX+10, mouseY-12); +text (Integer.toString(F[ibest]), mouseX+10, mouseY); +text (Integer.toString(x[ibest]), mouseX+10, mouseY+12); + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + x[ibest] = min(x[ibest+1],max(x[ibest-1],mouseX)); + F[ibest] = mouseY - 100; + if (mouseY < 0) F[ibest] = -100; + if (mouseY > 200) F[ibest] = 100; + } + +//plot "terrain" assuming 80 point separation of F[]'s + pushMatrix(); + translate(0,height/2); + + top = 0; + FF = F[0]; + for (i = 0; i < n; i = i+1) { + s = x[i+1]-x[i]+1; // increment used for plotting + inc = 0.01*(F[i+1]-F[i])/s; + for (j = 0; j < s; j = j+1) { + FF = FF + inc; + top = top + FF; // j increments of inc = F diff. + line(x[i]+j,height,x[i]+j,top); + } + } + popMatrix(); +} + +void keyPressed() { + if (key == CODED) { + if (keyCode == UP) --F[ibest]; + if (keyCode == DOWN) ++F[ibest]; + if (keyCode == RIGHT) ++x[ibest]; + if (keyCode == LEFT) --x[ibest]; + } +} + + diff --git a/software/apps/M&MStudioBV/--ProcessingProfile/InterpolationReady/InterpolationReady.pde b/software/apps/M&MStudioBV/--ProcessingProfile/InterpolationReady/InterpolationReady.pde new file mode 100644 index 0000000..e89d1e1 --- /dev/null +++ b/software/apps/M&MStudioBV/--ProcessingProfile/InterpolationReady/InterpolationReady.pde @@ -0,0 +1,50 @@ +// Profile of forces and terrain + +int n = 10; //number of values of F calculated for each s +int s = 80; //number of samples for each n +int[] F = new int[11]; //course Force values +float[] FF = new float[800]; //fine Forces interpolated +int i,j; +//int s = 80; +//int n = 10; +int pos; + +void setup() { + size(800,200); // double height to see what interpolation looks like +} + + +void draw() { + background(200); + stroke(150); + line(0,50,n*s-1,50); + stroke(0); + + for (i = 0; i sq(F[i]+100-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]+100-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest]+100,20,20); + +text (Integer.toString(ibest), mouseX+10, mouseY-12); +text (Integer.toString(F[ibest]), mouseX+10, mouseY); +text (Integer.toString(x[ibest]), mouseX+10, mouseY+12); + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + x[ibest] = min(x[ibest+1],max(x[ibest-1],mouseX)); + F[ibest] = mouseY - 100; + if (mouseY < 0) F[ibest] = -100; + if (mouseY > 200) F[ibest] = 100; + } + +//plot "terrain" assuming 80 point separation of F[]'s + pushMatrix(); + translate(0,height/2); + + top = 0; + FF = F[0]; + for (i = 0; i < n; i = i+1) { + s = x[i+1]-x[i]+1; // increment used for plotting + inc = 0.01*(F[i+1]-F[i])/s; + for (j = 0; j < s; j = j+1) { + FF = FF + inc; + top = top + FF; // j increments of inc = F diff. + line(x[i]+j,height,x[i]+j,top); + } + } + popMatrix(); +} + +void keyPressed() { + if (key == CODED) { + if (keyCode == UP) --F[ibest]; + if (keyCode == DOWN) ++F[ibest]; + if (keyCode == RIGHT) ++x[ibest]; + if (keyCode == LEFT) --x[ibest]; + } +} + + diff --git a/software/apps/M&MStudioBV/--ProcessingProfile/ProfileFixedWidths/ProfileFixedWidths.pde b/software/apps/M&MStudioBV/--ProcessingProfile/ProfileFixedWidths/ProfileFixedWidths.pde new file mode 100644 index 0000000..16fca5b --- /dev/null +++ b/software/apps/M&MStudioBV/--ProcessingProfile/ProfileFixedWidths/ProfileFixedWidths.pde @@ -0,0 +1,82 @@ +// Profile of forces and terrain + +int n = 10; //number of values of F calculated for each s +int s = 80; //number of samples for each n +int[] F = new int[11]; // Force values [-512 to +511] scaled for plot? +float FF; // forces interpolated +float top; // terrain value (integral of forces) +float inc; // interpolated slope ( F[i+1] - F[i] ) / s +int i,j; +int pos; +int vt = 50; //vertical translation for force curve + + + +void setup() { + size(800,800); // double height to see what interpolation looks like + for(i=0;i sq(F[i]-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest],20,20); + // } + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + F[ibest] = mouseY; + x[ibest] = min(x[ibest+1],mouseX); + x[ibest] = max(x[ibest-1],mouseX); + } + +} + + + diff --git a/software/apps/M&MStudioBV/ABSlaveMusic/ABSlaveMusic.ino b/software/apps/M&MStudioBV/ABSlaveMusic/ABSlaveMusic.ino new file mode 100644 index 0000000..63d0af8 --- /dev/null +++ b/software/apps/M&MStudioBV/ABSlaveMusic/ABSlaveMusic.ino @@ -0,0 +1,25 @@ +// CenterAB - both motors +// CenterA at xB, CenterB at xA? +// feels like "Slave" + +#include +#include + +int duty, count, fout; +int xA, xB, foutA, foutB; + +void setup(){ + MotorA.init(); + MotorB.init(); +} + +void loop(){ + xA = analogRead(A0); + xB = analogRead(A3); + foutA = 6*(xB-xA); // this will peak at x=1024/6 + MotorA.torque(foutA); // 1/4 or 1/2 ? + + foutB = 6*(xA-xB); // this will peak at x=1024/6 + MotorB.torque(foutB); // 1/4 or 1/2 ? + +} diff --git a/software/apps/M&MStudioBV/ABSlaveMusicWarble/ABSlaveMusicWarble.ino b/software/apps/M&MStudioBV/ABSlaveMusicWarble/ABSlaveMusicWarble.ino new file mode 100644 index 0000000..e21c843 --- /dev/null +++ b/software/apps/M&MStudioBV/ABSlaveMusicWarble/ABSlaveMusicWarble.ino @@ -0,0 +1,56 @@ +// CenterAB - both motors +// Freqequency1 B->Frequency2 +//CenterA at xB, CenterB at xA? +//feels like "Slave" +//position on A0, pwm:D9, dir:D8,D7 +//CenterB +//position on A3, pwm:D10, dir:D11,D12 + + +#include +#include +#define BIT_DEPTH 8 +#define NUM_OSCILLATORS 2 + +int duty, count, fout; +int xA, xB, foutA, foutB; + +void setup(){ + Serial.begin(9600); + MotorA.init(); + MotorB.init(); + Music.init(); + Music.setWaveform1(0);//8bit + Music.setWaveform2(0); + Music.setGain1(1.0f); + Music.setGain2(1.0f); +} + +void loop(){ + + xA = analogRead(A0); + Music.setFrequency1(map (xA, 0, 1023, 40, 2000)); + + xB = analogRead(A3); + Music.setFrequency2(map (xB, 0, 1023, 40, 2000)); + + foutA = -6*(xA-xB); // this will peak at x=1024/6 + MotorA.torque(foutA); // 1/4 or 1/2 ? + + foutB = -6*(xB-xA); // this will peak at x=1024/6 + MotorB.torque(foutB); // 1/4 or 1/2 ? + + Music.setGain(float(abs(xA-xB))/1024.0f); + // print every 1000 cycles + if(count++>=0){ + count=-500; + Serial.print(xA,DEC); + Serial.print(" "); + Serial.print(foutA,DEC); + Serial.print(" "); + Serial.print(xB,DEC); + Serial.print(" "); + Serial.println(foutB,DEC); + } + +} diff --git a/software/apps/M&MStudioBV/ABslave/ABslave.ino b/software/apps/M&MStudioBV/ABslave/ABslave.ino new file mode 100644 index 0000000..ea396cb --- /dev/null +++ b/software/apps/M&MStudioBV/ABslave/ABslave.ino @@ -0,0 +1,43 @@ +// CenterAB - both motors + +//CenterA at xB, CenterB at xA? +//feels like "Double Toggle" +//position on A0, pwm:D9, dir:D8,D7 +//CenterB +//position on A3, pwm:D10, dir:D11,D12 + + +#include + +int duty, count, fout; +int xA, xB, foutA, foutB; + +void setup(){ + Serial.begin(9600); + MotorA.init(); + MotorB.init(); +} + +void loop(){ + + xA = analogRead(A0); + xB = analogRead(A3); + foutA = 6*(xA-xB); // this will peak at x=1024/6 + MotorA.torque(foutA); // 1/4 or 1/2 ? + + foutB = 6*(xB-xA); // this will peak at x=1024/6 + MotorB.torque(foutB); // 1/4 or 1/2 ? + + // print every 1000 cycles + if(count++>=0){ + count=-500; + Serial.print(xA,DEC); + Serial.print(" "); + Serial.print(foutA,DEC); + Serial.print(" "); + Serial.print(xB,DEC); + Serial.print(" "); + Serial.println(foutB,DEC); + } + +} diff --git a/software/apps/M&MStudioBV/Bumps4/Bumps4.ino b/software/apps/M&MStudioBV/Bumps4/Bumps4.ino new file mode 100644 index 0000000..447850b --- /dev/null +++ b/software/apps/M&MStudioBV/Bumps4/Bumps4.ino @@ -0,0 +1,74 @@ +//plucks - four bumps +//three notes (400,500,600hz) +//can't get Music.setGain1, etc to work only Music.setGain() starts all of them. + +#include +#include + +int x, xold, xt, F; // input position x, output force F +int K = 10; // slope constant +byte count; //for print count-down + +void setup(){ + Music.init(); + Music.setFrequency1(200); + Music.setFrequency2(250); + Music.setFrequency3(300); + + + MotorA.init(); + + Serial.begin(9600); + x = analogRead(A0); // initialize x +} + +void loop(){ + xold = x; + x = analogRead(A0); + +// did xold - x include 125, 375, 625, 875? or x%250 = 125 + +if (((xold <= 125) && (x > 125)) || ((xold >= 125) && (x < 125))){ + Music.setGain1(1.0f); + //Music.setFrequency(200); +} +if (((xold <= 375) && (x > 375)) || ((xold >= 375) && (x < 375))){ + Music.setGain2(1.0f); + //Music.setFrequency(250); +} +if (((xold <= 625) && (x > 625)) || ((xold >= 625) && (x < 625))){ + Music.setGain3(1.0f); + //Music.setFrequency(300); +} +if (((xold <= 875) && (x > 875)) || ((xold >= 875) && (x < 875))){ + Music.setGain1(1.0f); + //Music.setFrequency(400); + } + else{ + Music.setGain1(0.995f*Music.getGain1Float()); + Music.setGain2(0.995f*Music.getGain2Float()); + Music.setGain3(0.995f*Music.getGain3Float()); + } + + + + xt = x % 250; //same force for each 250 ranage + F = 0; + if (xt > 60) F = - K * (xt - 60); + if (xt > 80) F = - K * (100 - xt); + if (xt > 120) F = K * (140 - xt); + if (xt > 140) F = 0; + MotorA.torque(F); + + + // print every 256 cycles + if(count++==0){ + Serial.print(x); + Serial.print(" "); + Serial.print(xt); + Serial.print(" "); + Serial.println(F); + } + +} + diff --git a/software/apps/M&MStudioBV/CenterABM/CenterABM.ino b/software/apps/M&MStudioBV/CenterABM/CenterABM.ino new file mode 100644 index 0000000..08112bc --- /dev/null +++ b/software/apps/M&MStudioBV/CenterABM/CenterABM.ino @@ -0,0 +1,42 @@ +// CenterAB - both motors + +//CenterA +//position on A0, pwm:D9, dir:D8,D7 +//CenterB +//position on A3, pwm:D10, dir:D11,D12 + + +#include + +int duty, count, fout; +int xA, xB, foutA, foutB; + +void setup(){ + Serial.begin(9600); + MotorA.init(); + MotorB.init(); +} + +void loop(){ + + xA = analogRead(A0); + foutA = 2*(512 - xA); // this will peak at x=1024/2 + MotorA.torque(foutA); // 1/4 or 1/2 ? + + xB = analogRead(A3); + foutB = (512 - xB); // this will peak at x=1024/6 + MotorB.torque(foutB); // 1/4 or 1/2 ? + + // print every 1000 cycles + if(count++>=0){ + count=-1000; + Serial.print(xA,DEC); + Serial.print(" "); + Serial.print(foutA,DEC); + Serial.print(" "); + Serial.print(xB,DEC); + Serial.print(" "); + Serial.println(foutB,DEC); + } + +} diff --git a/software/apps/M&MStudioBV/CenterABtoggle/CenterABtoggle.ino b/software/apps/M&MStudioBV/CenterABtoggle/CenterABtoggle.ino new file mode 100644 index 0000000..463e7f3 --- /dev/null +++ b/software/apps/M&MStudioBV/CenterABtoggle/CenterABtoggle.ino @@ -0,0 +1,43 @@ +// CenterAB - both motors + +//CenterA at xB, CenterB at xA? +//feels like "Double Toggle" +//position on A0, pwm:D9, dir:D8,D7 +//CenterB +//position on A3, pwm:D10, dir:D11,D12 + + +#include + +int duty, count, fout; +int xA, xB, foutA, foutB; + +void setup(){ + Serial.begin(9600); + MotorA.init(); + MotorB.init(); +} + +void loop(){ + + xA = analogRead(A0); + xB = analogRead(A3); + foutA = 6*(xB-512); // this will peak at x=1024/6 + MotorA.torque(foutA); // 1/4 or 1/2 ? + + foutB = 6*(xA-512); // this will peak at x=1024/6 + MotorB.torque(foutB); // 1/4 or 1/2 ? + + // print every 1000 cycles + if(count++>=0){ + count=-500; + Serial.print(xA,DEC); + Serial.print(" "); + Serial.print(foutA,DEC); + Serial.print(" "); + Serial.print(xB,DEC); + Serial.print(" "); + Serial.println(foutB,DEC); + } + +} diff --git a/software/apps/M&MStudioBV/FM/CenterFSR_A/CenterFSR_A.ino b/software/apps/M&MStudioBV/FM/CenterFSR_A/CenterFSR_A.ino new file mode 100644 index 0000000..d394241 --- /dev/null +++ b/software/apps/M&MStudioBV/FM/CenterFSR_A/CenterFSR_A.ino @@ -0,0 +1,34 @@ +//CenterFSR-A +//position on A0, pwm:D9, dir:D8,D7 +//FSR on A1 and D4 + +// BUZZ! (float vs int?) +#include "Motor.h" + +int x, duty, fin; +float fout; +byte count; // used as counter + +void setup(){ + Serial.begin(9600); + MotorA.init(); + //for FSR + pinMode(A1,INPUT); + digitalWrite(A1,HIGH); + pinMode(4,OUTPUT); + digitalWrite(4,LOW); +} + +void loop(){ + fin = 1024 - analogRead(A1); + x = analogRead(A0)-512; + fout = - .004*(float(fin)*float(x)); + MotorA.torque(fout); + if(count++ == 0){ + Serial.print(x,DEC); + Serial.print(" "); + Serial.print(fin,DEC); + Serial.print(" "); + Serial.println(fout,DEC); + } +} diff --git a/software/apps/M&MStudioBV/FM/FM.ino b/software/apps/M&MStudioBV/FM/FM.ino new file mode 100644 index 0000000..b7eb8c0 --- /dev/null +++ b/software/apps/M&MStudioBV/FM/FM.ino @@ -0,0 +1,31 @@ +//FM synthes of a sort +//hang on! it's unstable and wants to limit cycle + + +#include +#define BIT_DEPTH 8 // gives us 16 Waveforms + +#include + +byte cnt; +float xf, vf; // +float k = 6.0; // increase FM frequency +float b = 0.40; // increase +float Tf = .030 ; //integration time + +void setup(){ + Serial.begin(9600); + Music.init(); + Music.setWaveform(0); + Music.setGain(1.0f); + MotorA.init(); +} + +void loop(){ + xf += vf * Tf; + vf += (k * (analogRead(A0) - xf) - b*vf) * Tf; + Music.setFrequency(100+vf); + Music.setGain(.001*abs(vf)); + MotorA.torque(500-xf); + //if(cnt++==0)Serial.println(.001*abs(vf)); +} diff --git a/software/apps/M&MStudioBV/FM/FMGraph/FMGraph.pde b/software/apps/M&MStudioBV/FM/FMGraph/FMGraph.pde new file mode 100644 index 0000000..326cc39 --- /dev/null +++ b/software/apps/M&MStudioBV/FM/FMGraph/FMGraph.pde @@ -0,0 +1,63 @@ +// Graphing sketch + + + // This program takes ASCII-encoded strings + // from the serial port at 9600 baud and graphs them. It expects values in the + // range 0 to 1023, followed by a newline, or newline and carriage return + + // Created 20 Apr 2005 + // Updated 18 Jan 2008 + // by Tom Igoe + // This example code is in the public domain. + + import processing.serial.*; + + Serial myPort; // The serial port + int xPos = 1; // horizontal position of the graph + + void setup () { + // set the window size: + size(400, 300); + + // List all the available serial ports + println(Serial.list()); + // I know that the first port in the serial list on my mac + // is always my Arduino, so I open Serial.list()[0]. + // Open whatever port is the one you're using. + myPort = new Serial(this, Serial.list()[0], 9600); + // don't generate a serialEvent() unless you get a newline character: + myPort.bufferUntil('\n'); + // set inital background: + background(0); + } + void draw () { + // everything happens in the serialEvent() + } + + void serialEvent (Serial myPort) { + // get the ASCII string: + String inString = myPort.readStringUntil('\n'); + + if (inString != null) { + // trim off any whitespace: + inString = trim(inString); + // convert to an int and map to the screen height: + float inByte = float(inString); + inByte = map(inByte, 0, 1023, 0, height); + + // draw the line: + stroke(127,34,255); + line(xPos, height, xPos, height - inByte); + + // at the edge of the screen, go back to the beginning: + if (xPos >= width) { + xPos = 0; + background(0); + } + else { + // increment the horizontal position: + xPos++; + } + } + } + diff --git a/software/apps/M&MStudioBV/FSR/Bumps4FSR/Bumps4FSR.ino b/software/apps/M&MStudioBV/FSR/Bumps4FSR/Bumps4FSR.ino new file mode 100644 index 0000000..7d3b9d3 --- /dev/null +++ b/software/apps/M&MStudioBV/FSR/Bumps4FSR/Bumps4FSR.ino @@ -0,0 +1,97 @@ +//plucks - four bumps +//three notes (400,500,600hz) +//can't get Music.setGain1, etc to work only Music.setGain() starts all of them. +//requires FSR attached to A + +#define NUM_OSCILLATORS 3 +#define BIT_DEPTH 8 + +#include +#include + +int x, xold, xt; // input position x, output force F +float K = 20; // slope constant +byte count; //for print count-down +float fin; // scaled to be 0.0 - 1.0 +float fout; // from FSR + +void setup(){ + Music.init(); + Music.setFrequency1(200); + Music.setFrequency2(250); + Music.setFrequency3(300); + + //FSR-A + pinMode(A1,INPUT); + digitalWrite(A1,HIGH); //set pull-up + pinMode(4,OUTPUT); + digitalWrite(4,LOW); + //FSR-B + pinMode(A4,INPUT); + digitalWrite(A4,HIGH); //set pull-up + pinMode(5,OUTPUT); + digitalWrite(5,LOW); + + Serial.begin(9600); + + MotorA.init(); + + Serial.begin(9600); + x = analogRead(A0); // initialize x +} + +void loop(){ + xold = x; + x = analogRead(A0); + +// did xold - x include 125, 375, 625, 875? or x%250 = 125 + +if (((xold <= 125) && (x > 125)) || ((xold >= 125) && (x < 125))){ + Music.setGain1(fin*fin); + Music.setFrequency1(200); +} +if (((xold <= 375) && (x > 375)) || ((xold >= 375) && (x < 375))){ + Music.setGain2(fin*fin); + //Music.setFrequency(250); +} +if (((xold <= 625) && (x > 625)) || ((xold >= 625) && (x < 625))){ + Music.setGain3(fin*fin); + //Music.setFrequency(300); +} +if (((xold <= 875) && (x > 875)) || ((xold >= 875) && (x < 875))){ + Music.setGain1(fin*fin); + Music.setFrequency1(400); + } + else{ + Music.setGain1(0.995f*Music.getGain1()); + Music.setGain2(0.995f*Music.getGain2()); + Music.setGain3(0.995f*Music.getGain3()); + } + fin = 1000 - analogRead(A1); // invert and shift + fin = max (0, fin); // make sure it's > 0 + fin = min (1023, fin); // and < 1023 + fin = fin/1000; // scale 0-1.0 + fin = fin*fin; //square it for more effect near 1.0. + xt = x % 250; //same force for each 250 ranage + fout = 0; + if (xt > 60) fout = - K * (xt - 60); + if (xt > 80) fout = - K * (100 - xt); + if (xt > 120) fout = K * (140 - xt); + if (xt > 140) fout = 0; + fout = fout * fin; + MotorA.torque(fout); + + + // print every 256 cycles + if(count++==0){ + Serial.print(x); + Serial.print(" "); + Serial.print(xt); + Serial.print(" "); + Serial.print(fin); + Serial.print(" "); + Serial.println(fout); + } + +} + diff --git a/software/apps/M&MStudioBV/FSR/FSR_AB_center/FSR_AB_center.ino b/software/apps/M&MStudioBV/FSR/FSR_AB_center/FSR_AB_center.ino new file mode 100644 index 0000000..ad1e1b1 --- /dev/null +++ b/software/apps/M&MStudioBV/FSR/FSR_AB_center/FSR_AB_center.ino @@ -0,0 +1,37 @@ +// FSR A and B +// stick-slip + +#include +int xA, finA, foutA; //fout gets too big if fout=fin*x; +float x, fin, fout; +byte c; + +void setup(){ + //FSR-A + pinMode(A1,INPUT); + digitalWrite(A1,HIGH); //set pull-up + pinMode(4,OUTPUT); + digitalWrite(4,LOW); + //FSR-B + pinMode(A4,INPUT); + digitalWrite(A4,HIGH); //set pull-up + pinMode(5,OUTPUT); + digitalWrite(5,LOW); + + //Motors + MotorA.init(); + MotorB.init(); + + Serial.begin(9600); +} +void loop(){ + x = 512 - analogRead(A0); //in:[0,1023] out: [-512,511] + fin = 1024 - analogRead(A1); //in:[1023,0] out [0,1023] + fout = x*fin/500; + MotorA.torque(fout); + + x = 512 - analogRead(A3); //in:[0,1023] out: [-512,511] + fin = 1024 - analogRead(A4); //in:[1023,0] out [0,1023] + fout = x*fin/500; + MotorB.torque(fout); +} diff --git a/software/apps/M&MStudioBV/FSR/FSR_AB_test/FSR_AB_test.ino b/software/apps/M&MStudioBV/FSR/FSR_AB_test/FSR_AB_test.ino new file mode 100644 index 0000000..ededaf5 --- /dev/null +++ b/software/apps/M&MStudioBV/FSR/FSR_AB_test/FSR_AB_test.ino @@ -0,0 +1,20 @@ +// FSR A and B test +void setup(){ + //FSR-A + pinMode(A1,INPUT); + digitalWrite(A1,HIGH); //set pull-up + pinMode(4,OUTPUT); + digitalWrite(4,LOW); + //FSR-B + pinMode(A4,INPUT); + digitalWrite(A4,HIGH); //set pull-up + pinMode(5,OUTPUT); + digitalWrite(5,LOW); + + Serial.begin(9600); +} +void loop(){ + Serial.print(analogRead(A1)); + Serial.print(" "); + Serial.println(analogRead(A4)); +} diff --git a/software/apps/M&MStudioBV/FSR/FSR_D4_OUTPUT/FSR_D4_OUTPUT.ino b/software/apps/M&MStudioBV/FSR/FSR_D4_OUTPUT/FSR_D4_OUTPUT.ino new file mode 100644 index 0000000..52ba259 --- /dev/null +++ b/software/apps/M&MStudioBV/FSR/FSR_D4_OUTPUT/FSR_D4_OUTPUT.ino @@ -0,0 +1,17 @@ +void setup(){ + pinMode(A1,INPUT); + pinMode(4,OUTPUT); + digitalWrite(4,LOW); //sets ground + digitalWrite(A1,HIGH); //sets pull-up +} +void loop(){ + digitalWrite(A1,LOW); + digitalWrite(4,HIGH); + delay(1000); + digitalWrite(A1,HIGH); + digitalWrite(4,LOW); + delay(1000); +} + +//does D4 actually go 5v to GND? +//yes, if D4 is OUTPUT. diff --git a/software/apps/M&MStudioBV/FSR/HumpFSR_A/HumpFSR_A.ino b/software/apps/M&MStudioBV/FSR/HumpFSR_A/HumpFSR_A.ino new file mode 100644 index 0000000..9e71054 --- /dev/null +++ b/software/apps/M&MStudioBV/FSR/HumpFSR_A/HumpFSR_A.ino @@ -0,0 +1,36 @@ +//HumpFSR-A +//same as CenterFSR-A with sign reversal +//position on A0, pwm:D9, dir:D8,D7 +//FSR on A1 and D4 + +// BUZZ! (float vs int?) +#include "Motor.h" + +int x, duty, fin; +float fout; +byte count; // used as counter + +void setup(){ + Serial.begin(9600); + MotorA.init(); + //for FSR + pinMode(A1,INPUT); + digitalWrite(A1,HIGH); + pinMode(4,OUTPUT); + digitalWrite(4,LOW); +} + +void loop(){ + fin = 1024 - analogRead(A1); + x = analogRead(A0)-512; + fout = .004*(float(fin)*float(x)); + duty = min(1023,fout); + MotorA.torque(fout); + if(count++ == 0){ + Serial.print(x,DEC); + Serial.print(" "); + Serial.print(fin,DEC); + Serial.print(" "); + Serial.println(fout,DEC); + } +} diff --git a/software/apps/M&MStudioBV/FSR/SandFSR_A/SandFSR_A.ino b/software/apps/M&MStudioBV/FSR/SandFSR_A/SandFSR_A.ino new file mode 100644 index 0000000..15ade1c --- /dev/null +++ b/software/apps/M&MStudioBV/FSR/SandFSR_A/SandFSR_A.ino @@ -0,0 +1,33 @@ +#include "Motor.h" + +int x; //position +float fin; //from hall-sensor [ wired GND to A1 ] +float fout; //force +int duty; // max 1024 +byte c; // counter to send data every 256 cycles + +void setup(){ + MotorA.init(); + pinMode(A1,INPUT); //used for FSR [A1 to D4] + pinMode(4,OUTPUT); //used for FSR as GND + digitalWrite(A1,HIGH); //enable pull-up resistor + digitalWrite(4,LOW); //GND for FSR + //TimerOne + Serial.begin(9600); +} + +void loop(){ + fin = analogRead(A1) - 1000; + //fout = random(fin); + fout = 2*fin + random(fin); // range [0,4096]; + fout = fout/3; + MotorA.torque(fout); + // Timer1.pwm(9,duty); //output force + + if(c++==0){ + Serial.print(fin); + Serial.print(" "); + Serial.println(fout); + } +} + diff --git a/software/apps/M&MStudioBV/Gran/GongRoarCockShort.wav b/software/apps/M&MStudioBV/Gran/GongRoarCockShort.wav new file mode 100644 index 0000000000000000000000000000000000000000..a8b495438a0c6284401d4a65b5833e0655cd90a9 GIT binary patch literal 31798 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 z

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 dvD*!C_ + +void setup(){ + MotorA.init(); + MotorB.init(); + Serial.begin(9600); +} + +void loop(){ + //x = analogRead(A0) - 512; + x = analogRead(A3) - 512; + if (forward){ + if (x<=0) f = 0; + if (x>0 && xw){ + f = 0; + forward = false; + Serial.println("pluck forward"); + } + } + else + { + if (x>0) f = 0; + if (x<0 && x>-w) f = - slope*x; + if (x < -w){ + f = 0; + forward = true; + Serial.println("pluck back"); + } + } + fout = int(f); + //MotorA.torque(fout); + MotorB.torque(fout); + if(count++>=0){ + count=-1000; // wait 1000 loops before print + Serial.print(x,DEC); + Serial.print(" "); + Serial.println(fout,DEC); + } +} + + + + diff --git a/software/apps/M&MStudioBV/PluckOverlapHalf/PluckOverlapHalf.ino b/software/apps/M&MStudioBV/PluckOverlapHalf/PluckOverlapHalf.ino new file mode 100644 index 0000000..1167de7 --- /dev/null +++ b/software/apps/M&MStudioBV/PluckOverlapHalf/PluckOverlapHalf.ino @@ -0,0 +1,51 @@ +//PluckOverlapHalf +// BV 3 Feb 13 +// one pluck at x=512 + +int x, fout, count; +float f; +float w = 50; //width of pluck +float h = 500; //height of pluck +float slope = h/w; +boolean forward = true; + +#include + +void setup(){ + MotorA.init(); + MotorB.init(); + Serial.begin(9600); +} + +void loop(){ + //x = analogRead(A0) - 512; + x = analogRead(A3) - 512; + if (forward){ + if (x <= - w/2) f = 0; + if (x > -w/2 && x< w/2) f = - slope*(x + w/2); + if (x > w/2){ + f = 0; + forward = false; + Serial.println("pluck forward"); + } + } + else + { + if (x > w/2) f = 0; + if (x < w/2 && x > -w/2) f = - slope*(x - w/2); + if (x < -w/2){ + f = 0; + forward = true; + Serial.println("pluck back"); + } + } + fout = int(f); + //MotorA.torque(fout); + MotorB.torque(fout); + if(count++>=0){ + count=-1000; // wait 1000 loops before print + Serial.print(x,DEC); + Serial.print(" "); + Serial.println(fout,DEC); + } +} diff --git a/software/apps/M&MStudioBV/ProcessingProfile/ProfileFx/ProfileFx.pde b/software/apps/M&MStudioBV/ProcessingProfile/ProfileFx/ProfileFx.pde new file mode 100644 index 0000000..332bfff --- /dev/null +++ b/software/apps/M&MStudioBV/ProcessingProfile/ProfileFx/ProfileFx.pde @@ -0,0 +1,126 @@ +// ProfileFx +// receives pos from Arduino +// +// +// int F[],x[i], shown as height F[x] "profile" +// then as slope of "terrain" dT = F[x] + + +// constrains x[i] sq(F[i]+100-mouseY)+sq(x[i]-mouseX)){ + ibest = i; + dist = sq(F[i]+100-mouseY)+sq(x[i]-mouseX); + } + } + ellipse(x[ibest],F[ibest]+100,20,20); + +text (Integer.toString(ibest), mouseX+10, mouseY-12); +text (Integer.toString(F[ibest]), mouseX+10, mouseY); +text (Integer.toString(x[ibest]), mouseX+10, mouseY+12); + + if (mousePressed == true){ + //now use ibest to move to mouseX, mouseY + x[ibest] = min(x[ibest+1],max(x[ibest-1],mouseX)); + F[ibest] = mouseY - 100; + if (mouseY < 0) F[ibest] = -100; + if (mouseY > 200) F[ibest] = 100; + } + +//plot "terrain" assuming 80 point separation of F[]'s + pushMatrix(); + translate(0,height/2); + + top = 0; + FF = F[0]; + for (i = 0; i < n; i = i+1) { + s = x[i+1]-x[i]+1; // increment used for plotting + inc = 0.01*(F[i+1]-F[i])/s; + for (j = 0; j < s; j = j+1) { + FF = FF + inc; + top = top + FF; // j increments of inc = F diff. + line(x[i]+j,height,x[i]+j,top); + } + } + popMatrix(); + + //receive pos from Arduino +while (myPort.available() > 0) { + myString = myPort.readStringUntil(lf); + if (myString != null) { + myString = trim(myString); + pos = int(myString); + } + } + // draw pos cursor + stroke(150); + line(pos,200,pos,800); + +} + +void keyPressed() { + if (key == CODED) { + if (keyCode == UP) --F[ibest]; + if (keyCode == DOWN) ++F[ibest]; + if (keyCode == RIGHT) ++x[ibest]; + if (keyCode == LEFT) --x[ibest]; + } +} + + diff --git a/software/apps/M&MStudioBV/Pulse/Pulse.ino b/software/apps/M&MStudioBV/Pulse/Pulse.ino new file mode 100644 index 0000000..1df2328 --- /dev/null +++ b/software/apps/M&MStudioBV/Pulse/Pulse.ino @@ -0,0 +1,20 @@ +//"Pulse" - small duration positive then negative force +// parameters: F1, T1, D1, F2, T2, D2 +#include + +void setup(){ + MotorB.init(); +} + +void loop(){ + //for (int i; i < 512; i + 100){ + MotorB.torque(10); + delay (20); + MotorB.torque(0); + delay (150); + MotorB.torque(-50); + delay (40); + MotorB.torque(0); + delay (750); + //} +} diff --git a/software/apps/M&MStudioBV/Pulse_Beat/Pulse_Beat.ino b/software/apps/M&MStudioBV/Pulse_Beat/Pulse_Beat.ino new file mode 100644 index 0000000..6d5a4d5 --- /dev/null +++ b/software/apps/M&MStudioBV/Pulse_Beat/Pulse_Beat.ino @@ -0,0 +1,34 @@ +//"Pulse" - small duration positive then negative force +// parameters: F1, T1, D1, F2, T2, D2 + +#define BIT_DEPTH 12 + +#include +#include + +void setup(){ + MotorB.init(); + Music.init(); + + Music.setWaveform(SINE); + Music.enableEnvelope(); + Music.setAttack(10); + Music.setDecay(10); + Music.setRelease(10); +} + +void loop(){ + //for (int i; i < 512; i + 100){ + MotorB.torque(40); + Music.noteOn(map(analogRead(A3),0,1023,30,60)); + delay (20); + Music.noteOff();MotorB.torque(0); + delay (150); + MotorB.torque(-70); + Music.noteOn(map(analogRead(A3),0,1023,35,65)); + delay (40); + Music.noteOff();MotorB.torque(0); + MotorB.torque(0); + delay (750); + //} +} diff --git a/software/apps/M&MStudioBV/ReadA0A1/ReadA0A1.ino b/software/apps/M&MStudioBV/ReadA0A1/ReadA0A1.ino new file mode 100644 index 0000000..6b9ccaa --- /dev/null +++ b/software/apps/M&MStudioBV/ReadA0A1/ReadA0A1.ino @@ -0,0 +1,17 @@ +//read position A0 FSR A1 + +void setup(){ + Serial.begin(9600); + pinMode(A0,INPUT); + //for FSR + pinMode(A1,INPUT); + digitalWrite(A1,HIGH); + pinMode(4,OUTPUT); + digitalWrite(4,LOW); +} + +void loop(){ + Serial.print(analogRead(A0)); + Serial.print(" "); + Serial.println(analogRead(A1)); +} diff --git a/software/apps/M&MStudioBV/WallPluck_in/WallPluck_in.ino b/software/apps/M&MStudioBV/WallPluck_in/WallPluck_in.ino new file mode 100644 index 0000000..b763017 --- /dev/null +++ b/software/apps/M&MStudioBV/WallPluck_in/WallPluck_in.ino @@ -0,0 +1,54 @@ +//Wall +//need some mass so it "bounces"? + +#include +#include + +int x; +boolean inside; +int Fout; +int wave; +int Fmax = 4023; + +void setup(){ + Serial.begin(9600); + MotorA.init(); + + Music.init(); + Music.setWaveform(0);//8bit 0 = sine. + Music.setGain(0.0f); + Music.setFrequency(200); +} + +void loop() { +// waiting for "return" or "line-feed" +while (Serial.available()) { + wave = Serial.parseInt(); + if (Serial.read() == '\n') { + Serial.print("I received: "); + Serial.println(wave, DEC); + if (wave > 16) wave = 16; + if (wave < 0) wave - 0; + Music.setWaveform(wave); + } + } x = analogRead(A0)-512; + if(x < 0){ + Fout = -20*x; + MotorA.torque(Fout); + Music.setGain(1.0f); //contact silences music? + //Music.setGain(float(x/200)); + inside = true; + } + else{ + if (inside){ //first time outside + inside = false; //start note + Music.setGain(1.0f); + } + if(x>10){ + Music.setGain(0.998f*Music.getGainFloat()); + } + } +} + + + diff --git a/software/apps/M&MStudioBV/WallPluck_out/WallPluck_out.ino b/software/apps/M&MStudioBV/WallPluck_out/WallPluck_out.ino new file mode 100644 index 0000000..ce52cb7 --- /dev/null +++ b/software/apps/M&MStudioBV/WallPluck_out/WallPluck_out.ino @@ -0,0 +1,54 @@ +//Wall +//need some mass so it "bounces"? + +#include +#define BIT_DEPTH 8 // gives us 16 Waveforms + +#include + +int x; +boolean inside; +int Fout; +int wave; + +void setup(){ + Serial.begin(9600); + MotorA.init(); + + Music.init(); + Music.setWaveform(1);//8bit + Music.setGain(0.0f); + Music.setFrequency(200); +} + +void loop() { +// waiting for "return" or "line-feed" +while (Serial.available()) { + wave = Serial.parseInt(); + if (Serial.read() == '\n') { + Serial.print("I received: "); + Serial.println(wave, DEC); + if (wave > 16) wave = 16; + if (wave < 0) wave - 0; + Music.setWaveform(wave); + } + } x = analogRead(A0)-512; + if(x < 0){ + Fout = -20*x; + MotorA.torque(Fout); + Music.setGain(0.0f); //contact silences music + inside = true; + } + else{ + if (inside){ //first time outside + inside = false; //start note + Music.setGain(1.0f); + } + if(x>10){ + Music.setGain(0.998f*Music.getGain()); + } + } +} + + + diff --git a/software/apps/M&MStudioBV/motortestM/motortestM.ino b/software/apps/M&MStudioBV/motortestM/motortestM.ino new file mode 100644 index 0000000..9c97c80 --- /dev/null +++ b/software/apps/M&MStudioBV/motortestM/motortestM.ino @@ -0,0 +1,22 @@ +//motortest +//.5 sec forward, .5 sec back + +#include + +void setup() +{ + MotorA.init(); + MotorB.init(); + MotorA.torque(40); // small force for Plank + MotorB.torque(200); // need 200 for Fader +} + +void loop() +{ + MotorA.direction(FORWARD); + MotorB.direction(FORWARD); + delay(500); + MotorA.direction(BACKWARD); + MotorB.direction(BACKWARD); + delay(500); +} diff --git a/software/apps/M&MStudioBV/plotOsc/plotOsc.pde b/software/apps/M&MStudioBV/plotOsc/plotOsc.pde new file mode 100644 index 0000000..fd67978 --- /dev/null +++ b/software/apps/M&MStudioBV/plotOsc/plotOsc.pde @@ -0,0 +1,27 @@ +// step response of second order system +// m mass, k spring, b damping + +float xPos = 0.0; +float yPos = 0.0; +float xVel = 0.0; +float T = 0.1; +float koverm = 0.1; +float boverm = 0.01; + +int x, y; // variables at mouse- and screen-resolution + +void setup() { // setup() runs once + size(800, 500); + frameRate(30); +} + +void draw() { // draw() loops forever, until stopped + xPos = width/2; + xVel = 0.0; +background(204); + for(int y=0; y < height; y = y +1) { + xVel += koverm * (mouseX - xPos) * T - boverm * xVel; //a=F/m + xPos += xVel * T; + point(xPos, y); + } +} diff --git a/software/apps/Motor/pos_FSR_AB_test/pos_FSR_AB_test.ino b/software/apps/Motor/pos_FSR_AB_test/pos_FSR_AB_test.ino new file mode 100644 index 0000000..22a3294 --- /dev/null +++ b/software/apps/Motor/pos_FSR_AB_test/pos_FSR_AB_test.ino @@ -0,0 +1,30 @@ +//position and force reporting +//sends A0 (MotorA), A1 (ForceA) +//sends A3 (MotorB), A4 (ForceB) +// to Serial Monitor + +void setup() +{ + Serial.begin(9600); + //set up for FSR A1 to D4 + pinMode(A1,INPUT); + pinMode(4,OUTPUT); + digitalWrite(4,LOW); + digitalWrite(A1,HIGH); //internal pull-up + //set up for FSR A4 to D5 + pinMode(A4,INPUT); + pinMode(5,OUTPUT); + digitalWrite(5,LOW); + digitalWrite(A4,HIGH); //internal pull-up +} + +void loop() +{ + Serial.print(analogRead(A0)); //positionA + Serial.print(" "); + Serial.print(analogRead(A1)); //positionA + Serial.print(" "); + Serial.print(analogRead(A3)); //positionA + Serial.print(" "); + Serial.println(analogRead(A4)); //forceA +}