RPI Haptics:Pulsing from Max
From BluWiki
Switching between 0V and PWM allows you to simulate a low frequency square wave of variable frequency and intensity. It should look like this:
+-+ +-+ +-+
| | | | | |
| +-+ +-+ +-----------
|a|
|----b----|
|----------c----------|
|-----d-----|
Where a is standard PWM (determining the amplitude of the square wave), b describes the length of the pulses, and c the overall frequency. b and c can also be expressed as b and d.
Contents |
Parameter Control
Early Version
These three parameters can be controlled from Max using a custom Wiring sketch. Wiring code for receiving parameters over serial that modify characteristics of the output signal:
#define params 3
#define pin 3
boolean state, lastState;
int value = 0;
int mode = 0;
int param[params]; // intensity, b, d
void setup() {
Serial.begin(115200);
}
void loop() {
while (Serial.available() > 0) {
value = Serial.read();
if(value == 255) {
mode = 0;
} else if(mode < params) {
param[mode] = value;
mode++;
}
}
analogWrite(pin, param[0]);
delay(param[1]);
analogWrite(pin, 0);
delay(param[2]);
}
An earlier version used millis() to alternate between b and d, delay() works better.
Max/MSP patch for sending parameters to the Arduino.
max v2; #N vpatcher 639 371 885 611; #P window setfont "Sans Serif" 9.; #P window linecount 1; #P comment 128 94 70 9109513 d (ms); #P comment 110 76 70 9109513 b (ms); #P number 91 94 35 9 0 254 3 139 0 0 0 221 221 221 222 222 222 0 0 0; #P number 73 76 35 9 0 254 3 139 0 0 0 221 221 221 222 222 222 0 0 0; #P number 55 58 35 9 0 254 3 139 0 0 0 221 221 221 222 222 222 0 0 0; #P newex 37 135 65 9109513 pak 255 0 0 0; #P message 50 161 26 9109513 print; #P newex 37 186 68 9109513 serial j 115200; #P comment 92 58 70 9109513 intensity (0-5v); #P connect 2 0 1 0; #P connect 3 0 1 0; #P connect 4 0 3 1; #P connect 5 0 3 2; #P connect 6 0 3 3; #P pop;
Revised Version
Demonstrates writing to three outputs without "jumps". Updates a single output pin (11) with PWM of serial input value.
float counter[3] = {0., 0., 0.};
float frequency[3] = {1, 2, 3};
float sustain[3] = {.8, .1, .5};
float amplitude[3] = {1, .5, .2};
int outputPin[3] = {9, 10, 11};
void setup() {
Serial.begin(19200);
}
void loop() {
if(Serial.available())
frequency[2] = Serial.read();
updateOutputs();
}
float mod(float x, float y) {
return x - ((int) (x / y) * y);
}
float lastTime = 0;
void updateOutputs() {
float time = (float) millis() / 1000.;
float timeDiff = time - lastTime;
for(int i = 0; i < 3; i++) {
counter[i] = mod(counter[i] + (timeDiff * frequency[i]), 1.);
analogWrite(outputPin[i], 255 * amplitude[i] * squareWave(counter[i], sustain[i]));
}
lastTime = time;
}
float squareWave(float c, float s) {
return c < s ? 1. : 0.;
}
Direct Control
An alternative approach is to use Max to determine the timing of the signal, and only send the PWM values.
void setup() {
Serial.begin(115200);
}
void loop() {
if (Serial.available() > 0)
analogWrite(3, Serial.read());
}
max v2; #N vpatcher 257 237 431 510; #P window setfont "Sans Serif" 9.; #P flonum 99 101 35 9 0. 100. 3 139 0 0 0 221 221 221 222 222 222 0 0 0; #P newex 78 166 31 9109513 delay; #P message 51 190 23 9109513 255; #P message 78 190 14 9109513 0; #P flonum 21 101 35 9 0. 500. 3 139 0 0 0 221 221 221 222 222 222 0 0 0; #P newex 21 135 40 9109513 train~; #P message 22 190 26 9109513 print; #P user ezdac~ 54 43 98 76 0; #P newex 51 224 68 9109513 serial j 115200; #P connect 4 0 3 0; #P connect 3 1 6 0; #P connect 2 0 0 0; #P connect 5 0 0 0; #P connect 6 0 0 0; #P connect 3 1 7 0; #P connect 7 0 5 0; #P connect 8 0 7 1; #P pop;
The downside of this approach is that communicating quickly over serial produces discrepancies in timing.



