Europe Magic Wand Randomizer

Selfbondage software and other kinky developments

Moderators: Riddle, Shannon SteelSlave

Post Reply
User avatar
PutStringsOnMe
*
Posts: 18
Joined: 19 Nov 2016, 09:37

Europe Magic Wand Randomizer

Post by PutStringsOnMe »

Hello everyone,
I wanted to put a Europe Magic Wand to better use, but the momentary push buttons to control the speed restricted me (in the wrong way :mrgreen: ).
Once power was removed, the device would stay off regardless of the previous selected setting.

So I set out to either break it and buy another, possibly dangerous one with "only" two speeds and control it using a switching outlet or fix the Europe Magic Wand (EMW for short) with some solder, duct tape and an Arduino Pro Mini.
The primary goal was to control the speed using as little modifications to the device as possible.

Looking back now, I think that I could have gone the direct motor control route, but that would have involved tinkering with the scary end of the power cord and creating my own PCB to fit into the case.

Well, all plans hold up until contact with the enemy.

I regret not taking any pictures, but rest assured I have some punishment for that coming my way.

To gain access to the innards of the EMW I removed three screws (Philips head).
On the backside, down by the power cable is a recessed plastic plug that hides one of the screws.
One is found under the button strip which is just glued with easy enough removed double-sided tape.
The last one is keeping the metal ring in place. You can see it when looking down the shaft beneath the head.

I was careful enough removing the cover, and so I was able to check out the button assembly.
The buttons that control 4 pins of a micro controller are of the SMD type, so very, very *loving* tiny.
So there go my chances of tapping into these switches, but I was stubborn enough to try and pull away the rubber covering the switches to peek if I could sneak in very thin wire. DON'T do this. The buttons were soldered onto the flat-flex and the connections are very brittle. I broke them lifting the glued on rubber from the flat-flex, you will too.

So there I sat, my mouse hovering above the 'Buy' button for another wand.
Screw that. I went looking for a replacement for that stupid flat-flex cable.

After checking with my local electronic supplier, I ordered a couple of flat-flex cables. (Cheap parts, costly shipping.)
The one that fit the connector on the PCB is marked "WUERTH ELEKTRONIK E328849 // AWM 20706 105C 60V VW-1", it has 4 contacts on both ends, and is reinforced on the opposite side of the connector to provide a bit of stiffness.

Before switching on my soldering iron, I checked the continuity between the pins when pressing the switches, which was hard because of the brittle / broken soldering joints my previous fuckup left me with.

The switch for reducing the speed closes the circuit between the left most and right most pin on the flat-flex cable.
The switch for increasing the speed closes the circuit between the both inner pins on the cable.

I soldered some wires to one end of my new flat-flex. (Buy spares!).
To accomplish a good solder joint with only one pin /lane on the flat-flex you will have to scrape of a bit of insulation for the pin/lane you wish to solder to. Use some soldering flux to clean up the now shiny area and position a pre-tinned wire on the scraped of area, then solder it to the newly created contact.
Be careful, the flat-flex do not withstand much heat.
Repeat four times. This gets frustrating, let me tell you. The best way to avoid shorts between two soldering spots is to stagger the connections on the flat-flex by a couple of millimeters.

Check the continuity and resistance on the connections and you are done with that cable.

Now all we need to to is connect the flat-flex, route it so that it will not shake loose and provide a means of connecting the Arduino to the EMW.
I used a 4-Pin Molex connector that can be found on computer fans. There is a small slot where the flat-flex used to pass through which I widened a bit and passed the wires through . I soldered the female end of the Molex connector to the cables and superglued it to the case. Apply duct-tape, now you have a rugged connection.

On to hooking this thing up to the Arduino.
Maybe this is a bit over-engineered, but I'm german so I'm prone to this sort of thing :D .
I chose to use two optocouplers (TIL 117). This is a six-pin dip package chip that will let our Arduino interface safely with the EMW.
I soldered a 1K ohm resistor to pin 1 of the optocoupler and a wire to pin 2.
Then I connected the leads belonging to speed-down to the pins 5+6. (Polarity matters here, please check before soldering.) Repeat for the "speed-up" pair.

Now we are ready to connect the wires to the Arduino Pro Mini, feel free to use any compatible knockoff, but this is what worked for me.
Remember: An optocoupler is just an IR LED and an IR Sensor in one package. Polarity matters.

Get a little momentary switch to toggle the "Hold-Mode" and wire it up too.

This is the code I wrote, I make use of the elapsedMillis library to keep track of time.

Code: Select all

#include <elapsedMillis.h>
//#define DEBUG //Uncomment to enable debug output
elapsedMillis timeElapsed; //Create a timer
#ifdef DEBUG
long totalCycles; //The amount of completed cycles
long totalZeroCycles; //The amount of zero-speed cycles
#endif
boolean hold; //Hold mode state
int newSpeed; //The speed to be applied next
int currentSpeed; //The currently active speed
int timeoutMultiplier; //The multiplier for zero-speed cycles
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers
unsigned long duration = 10000; //The duration of the current cycle
long activeTime; //The vibrator has been active this long since the last forced break

const long activeTimeThresh = 900000; //The threshold at which a forced break is triggered to protect the vibrator
const long forcedBreakTime = 300000; //The duration of a forced break
const int minDuration = 18000; //The minimum cycle duration
const long maxDuration = 42000; //The maximum cycle duration
const int switchDelay = 143; //The delay between switch state changes

const int buttonPin = 2; //Pin provides 5V to the Button Circuit
const int holdLED = 13; //Hold mode indicator LED
const int speedDownPin = 10; //Pin that decreases the speed
const int speedUpPin = 11; //Pin that increases the speed


void setup() {
#ifdef DEBUG
  Serial.begin(9600);
  Serial.println("Startup...");
#endif
  pinMode(buttonPin, INPUT_PULLUP); //Sets the digital pin as input
  pinMode(holdLED, OUTPUT); //Sets the digital pin as output
  hold = false; //Start in hold mode (Hold mode will flip after startup)
  digitalWrite(holdLED, HIGH);
  pinMode(speedDownPin, OUTPUT); //Sets the digital pin as output
  digitalWrite(speedDownPin, LOW);
  pinMode(speedUpPin, OUTPUT); //Sets the digital pin as output
  digitalWrite(speedUpPin, LOW);

  //You're so random...
  seed();
}

void loop() {
  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;

      if (buttonState == HIGH) {
        hold = !hold;
#ifdef DEBUG
        Serial.print("Hold state: " );
        Serial.println(hold);
#endif
      }
    }
  }

  lastButtonState = reading;

  //only start when hold is off
  if (hold == false) {
    digitalWrite(13, LOW);
    //Check if the threshold for a forced break has been reached
    if (activeTime > activeTimeThresh) {
#ifdef DEBUG
      Serial.println("Active time threshold reached!");
#endif
      //Time for a break, shut it down
      newSpeed = 0;
      duration = forcedBreakTime;
      adjustSpeed (newSpeed);
      //Clear the active time
      activeTime = 0;
      //reset timer
      timeElapsed = 0;
    }
    else {
      if (timeElapsed > duration) {
        //seed RNG
        seed();
        //Choose a new speed
        newSpeed = random(0, 7);
        //The speed has to be different
        while (newSpeed == currentSpeed) {
          newSpeed = random(0, 7);
        }
        if (newSpeed >= 3) {
          newSpeed -= random(0 - 4);
        }
#ifdef DEBUG
        Serial.print("New speed: ");
        Serial.println(newSpeed);
#endif
        timeoutMultiplier = random(2, 5); //Choose the timeout multiplier that is applied to zero-speed cycles
#ifdef DEBUG
        Serial.print("Timeout Multiplier: ");
        Serial.println(timeoutMultiplier);
#endif
        duration = random(minDuration, maxDuration); //Choose how long to turn the vibrator on in ms
        //We don’t want to spoil ourselves, so the duration decreases with higher speeds
        if (newSpeed > 0) {
          duration /= newSpeed;
          activeTime += duration;
#ifdef DEBUG
          Serial.print("Next forced break: ");
          Serial.println(activeTimeThresh - activeTime);
#endif
        }
        else {
          duration *= timeoutMultiplier;
#ifdef DEBUG
          totalZeroCycles++;
#endif
        }
        //set the new speed
        adjustSpeed (newSpeed);
        //reset timer
        timeElapsed = 0;
#ifdef DEBUG
        totalCycles++;
        Serial.print("Duration: ");
        Serial.println(duration);
        Serial.print("Total cycles: ");
        Serial.println(totalCycles);
        Serial.print("Zero-speed cycles: ");
        Serial.println(totalZeroCycles);
#endif
      }
    }
  }
  else {
    //Engage hold mode, shut it down!
    digitalWrite(13, !digitalRead(13));
    while (currentSpeed != 0) {
      currentSpeed--;
      digitalWrite(speedDownPin, HIGH);
      delay(switchDelay);
      digitalWrite(speedDownPin, LOW);
#ifdef DEBUG
      Serial.print("Current speed: ");
      Serial.println(currentSpeed);
#endif
    }
  }
}

void seed () {
  unsigned long seed = 0, count = 32;
  while (--count)
    seed = (seed << 1) | (analogRead(0) & 1);
  randomSeed(seed);

}

void adjustSpeed ( int newSpeed ) {
  //Adjust to the new speed
  while (currentSpeed != newSpeed) {
    delay(switchDelay);
    if (currentSpeed < newSpeed ) {
      currentSpeed++;
      digitalWrite(speedUpPin, HIGH);
      delay(switchDelay);
      digitalWrite(speedUpPin, LOW);
    }
    else  {
      currentSpeed--;
      digitalWrite(speedDownPin, HIGH);
      delay(switchDelay);
      digitalWrite(speedDownPin, LOW);
    }
#ifdef DEBUG
    Serial.print("Current speed: ");
    Serial.println(currentSpeed);
#endif
  }
}
So what happens next?
Well, assume the position of your choice and press the button you wired in. (The "Hold-Button")
The Arduino will (pseudo-)randomly choose a speed and a duration to apply that speed.
The duration of the cycles shorten with increasing speeds.
There is a chance of a zero-speed cycle.
There is a chance that your zero-speed cycle is prolonged.
There are regular breaks to protect the device from overheating.
There should be no two identical speed settings following each other.
The program will run until you press the "Hold-Button".

Feel free to adjust the variables at the top of the code to your liking or modify the code to add features.

I hope that my experiences can inspire some of you to build something exciting, but please be careful when handling electricity!

Cheers,
PutStringsOnMe
-PutStringsOnMe
User avatar
Gregovic
****
Posts: 1119
Joined: 26 Mar 2016, 21:31
Location: Netherlands

Re: Europe Magic Wand Randomizer

Post by Gregovic »

Nice work. Very "German engineering" indeed (not necessarily a bad thing).

Personally I have one of those el-cheapo chinese "10 speed" (More like 10 pattern) magic wands. Just for the heck of it I have opened it up (I have a picture if anyone is interested) and the inside of that one consist of a small PCB containing a 240v AC to 3.5v DC power regulator and a second PCB with 2 pushbuttons and some unmarked ICs for generating the patterns and driving the motor. The motor itself looks like it would work just fine on 5v for low duty cycles. I fully intend to replace the guts with a 5v regulator, a Digispark and a small motor driver circuit (planned is the TINY Pololu DRV8838 board) and the original 2 buttons for interface. That way I can make the thing do whatever I want, like turning on when the external power turns on. Currently it has the same behaviour you describe for the EMW. Upping the max voltage to 5 volts should also give a bit more power (If needed) and the new brains would allow lots of new and interesting/torturous patterns to be programmed in.
How may I serve you? *Curtsey*
Post Reply