Since I had scripted the different gestures I wanted to have for Tap in Arduino, I could now combine all the gestures into one script. I then connected the Wemos board that controls Tap to the wifi so that I could trigger each of these gestures wirelessly from my laptop via a simple interface.
Interface
To create the interface, I used Max 8 to build a simple patch to send OSC messages to my Wemos board. The patch consists of a few buttons that trigger messages with a value between 0 & 3, which are then sent as an OSC message via UDP to trigger different parts of the Arduino script.
Code
I combined all the different gesture scripts into one Arduino script that can receive OSC messages via UDP. These messages set the value for a variable which is then queried in the loop of the script to start the desired gestures. After the gestures have finished, the variable is reset to put Tap back into a suspended state.
#include <AccelStepper.h>
#include <ESP8266WiFi.h> // The Library for WIFI
#include <WiFiUdp.h> // Library for UDP
#include <OSCMessage.h> //Library for OSC
#define motorInterfaceType 1
// Define the stepper motor and the pins that is connected to // (STEP, DIR)
AccelStepper stepper1(motorInterfaceType, D5, D6);
AccelStepper stepper2(motorInterfaceType, D7, D8);
// Set the target positions for both steppers
int PositionDown = 0;
int PositionUp = 0;
// State variable to keep track of movement sequence
int state1 = 0;
int state2 = 0;
// For triggering movements
int mode = 0;
WiFiUDP Udp;
const char* ssid = "************";
const char* password = "************";
const IPAddress dstIp(192,168,1,129);
const unsigned int dstPort = 7250; // Destination OSC
const unsigned int localPort = 7300; // Reciving OSC
////////////////////////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
// Settings for Motor 1
stepper1.setMaxSpeed(1000);
stepper1.setAcceleration(500);
stepper1.setCurrentPosition(0);
// Settings for Motor 2
stepper2.setMaxSpeed(1000);
stepper2.setAcceleration(500);
stepper2.setCurrentPosition(0);
// Connecting to WIFI
Serial.print("Connecting WiFi ");
// Prevent need for powercyle after upload.
WiFi.disconnect();
// Use DHCP to connect and obtain IP Address.
WiFi.mode(WIFI_STA);
WiFi.begin(ssid,password);
// Wait until we have connected to the WiFi AP.
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Done!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Udp.begin(localPort);
}
////////////////////////////////////////////////////////////////////////////////////
void loop() {
handleOSC();
if (mode == 0) {
state1 = 0;
state2 = 0;
}
if (mode == 1) {
Wave1();
Wave2();
}
if (mode == 2) {
Knock1();
}
if (mode == 3) {
Tap1();
Tap2();
}
}
////////////////////////////////////////////////////////////////////////////////////
void Wave1() {
switch (state1) {
case 0:
stepper1.setAcceleration(200);
PositionDown = 40;
stepper1.moveTo(PositionDown);
state1 = 1;
break;
case 1:
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
PositionDown = -40;
stepper1.moveTo(PositionDown);
state1 = 2;
}
break;
case 2:
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
PositionDown = 20;
stepper1.moveTo(PositionDown);
state1 = 3;
}
break;
case 3:
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
PositionDown = -20;
stepper1.moveTo(PositionDown);
state1 = 4;
}
break;
case 4:
stepper1.setAcceleration(100);
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
PositionDown = 0;
stepper1.moveTo(PositionDown);
state1 = 5;
}
break;
case 5:
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
}
break;
}
}
void Wave2() {
if (state1 >= 4){
switch (state2) {
case 0:
stepper2.setAcceleration(275);
PositionUp = 25;
stepper2.moveTo(PositionUp);
state2 = 1;
break;
case 1:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = -25;
stepper2.moveTo(PositionUp);
state2 = 2;
}
break;
case 2:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 15;
stepper2.moveTo(PositionUp);
state2 = 3;
}
break;
case 3:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = -15;
stepper2.moveTo(PositionUp);
state2 = 4;
}
break;
case 4:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 15;
stepper2.moveTo(PositionUp);
state2 = 5;
}
break;
case 5:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = -15;
stepper2.moveTo(PositionUp);
state2 = 6;
}
break;
case 6:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 0;
stepper2.moveTo(PositionUp);
state2 = 7;
}
break;
case 7:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
mode = 0;
}
break;
}
}
}
void Knock1() {
switch (state1) {
case 0:
stepper2.setAcceleration(400);
PositionUp = -20;
stepper2.moveTo(PositionUp);
state1 = 1;
break;
case 1:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 80;
stepper2.moveTo(PositionUp);
state1 = 2;
}
break;
case 2:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 60;
stepper2.moveTo(PositionUp);
state1 = 3;
}
break;
case 3:
stepper2.setAcceleration(800);
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 80;
stepper2.moveTo(PositionUp);
state1 = 4;
}
break;
case 4:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 0;
stepper2.moveTo(PositionUp);
state1 = 5;
}
break;
case 5:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
mode = 0;
}
break;
}
}
void Tap1() {
if (state2 >= 2){
switch (state1) {
case 0:
stepper1.setAcceleration(300);
PositionDown = -20;
stepper1.moveTo(PositionDown);
state1 = 1;
break;
case 1:
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
PositionDown = 5;
stepper1.moveTo(PositionDown);
state1 = 2;
}
break;
case 2:
stepper1.setAcceleration(600);
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
PositionDown = -20;
stepper1.moveTo(PositionDown);
state1 = 3;
}
break;
case 3:
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
PositionDown = 0;
stepper1.moveTo(PositionDown);
state1 = 4;
}
break;
case 4:
stepper1.setAcceleration(100);
if (stepper1.distanceToGo() != 0) {
stepper1.run();
} else {
stepper1.stop();
}
break;
}
}
}
void Tap2() {
switch (state2) {
case 0:
stepper2.setAcceleration(400);
PositionUp = 50;
stepper2.moveTo(PositionUp);
state2 = 1;
break;
case 1:
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
PositionUp = 0;
stepper2.moveTo(PositionUp);
state2 = 2;
}
break;
case 2:
if (state1 >= 4) {
stepper2.setAcceleration(200);
if (stepper2.distanceToGo() != 0) {
stepper2.run();
} else {
stepper2.stop();
mode = 0;
}
}
break;
}
}
////////////////////////////////////////////////////////////////////////////////////
void handleOSC() {
OSCMessage msg("/Mode");
int size = Udp.parsePacket();
if (size > 0) {
while (size--) {
msg.fill(Udp.read());
}
if (!msg.hasError()) {
msg.dispatch("/Mode", Activating);
}
}
}
void Activating(OSCMessage &msg) {
if (msg.isInt(0)) {
int receivedMode = msg.getInt(0);
mode = receivedMode;
Serial.println("Received Mode Command");
Serial.println(mode);
}
}
outcome
After finishing the interface and the now complete Arduino script, I tested them together. With my laptop set up as a touch interface running Max 8 and the Wemos board running Tap as usual. All went quite smoothly and after some tweaking everything is now running stable. You can see the result below.
With this, my prototype for this semester’s Design & Research project is almost finished. The last step will be to make the interface a bit cleaner and more intuitive, and then I will make a video explaining my prototype Tap and how it works.
This is so sick, can’t wait to see more!