Internet of (sex) things – part 2: MQTT messages

In the first IOT tutorial we have shown how to build a sex toy based on a ESP8266 MCU.

There are two more parts of this tutorial series:

part 3: Node-RED

part 4: Building a sex toy dashboard with Node-RED

The ESP8266 is a microcontroller which can connect to the internet. You can write sketches with the Arduino IDE and connect a lot of actuator modules (like vibration motors, displays) and sensors (motion detection).

nodemcu prototype breadboard

In the first approach a web server was installed on the ESP8266. Control was possible by browsing to the web server, which could be accessed by a computer or smart phone using the same WiFi Access Point (AP).

browser vibrator control

In this second tutorial we will connect the ESP8266 to the internet and publish and receive data as well as commands by internet.

Protocols

There are some protocols for publishing (sending) data. We use the MQTT protocol which is very fast and suited for short messages. In addition we need a MQTT server. We will send the ESP8266 data to the server. And the ESP8266 can receive data (or commands) from the server. Other services can connect to the MQTT server, too. In this tutorial we will use a MQTT client which can display the sex toy data and send commands to the ESP8266.

mqtt-diagramm

Sex toy data exchange format

A big disadvantage of commercial sex toys is their proprietary data exchange formats. Kyle Machutis developed buttplug.io a Cross Platform Framework for Sex Toy Control to overcome this issue.

For this IOT project we will use the JSON format which is a very simple way to exchange data:

This is an example for the message type “sensor”. It says that the fusioned sensor data of the “mps9250” MPU is 5657, the LED is off (0), the motor is in mode sinus curve (2) and the speed of the vibration motor is 766.

{"messageType":"sensor",
"sensor":"mps9250",
"fusionedData":5675,
"messageNumber":8,
"LEDstatus":0,
"motor1mode":2,
"motor1speed":766}

There are three message types: sensor, execute and message.

  • Sensor is for publishing sensor data to everyone who is interested in that.
  • Execute is for controlling a specific device, eg for turning the motor on.
  • Message sends a text message to specific device. If the deviceID is not given it will be sent to all receivers,

For this project we need the

  • hardware from part 1 of the IOT project
  • the Arduino library JSON for parsing and encoding exchange data
  • a library for sending and receiving MQTT data
  • the mqtt-spy software to send and receive MQTT messages
  • optional: a MQTTserver like mosquitto

Arduino JSON library

There is a great library which can parse and build JSON files. Here is the documentation: https://github.com/bblanchon/ArduinoJson

Please go into the Arduino library manager and install the JSON package.

Arduino PubSubClient library

In addition we need a package for sending and receiving MQTT messages. You will find the PubSubClient library in the library manager, too.

Documentation: https://github.com/bblanchon/ArduinoJson

You have to change the maximum length of a message. By default it is only 128. Search for the file PubSubClient.h and change MQTT_MAX_PACKET_SIZE to 1024. On my Win10 system the file is located at

C:\Users\...\Documents\Arduino\libraries\PubSubClient\src\PubSubClient.h

Change the following line:

// MQTT_MAX_PACKET_SIZE : Maximum packet size
 #define MQTT_MAX_PACKET_SIZE 1024

MQTT-SPY software

This is an excellent piece of software which can connect to a MQTT server and send and receive messages: mqtt.spy. We use it to test the connection from the ESP8266 to the mosquitto server and for debugging our vibrator toy control software on the ESP8266.

Download from https://github.com/kamilfb/mqtt-spy/wiki/Downloads

If you haven’t your own MQTT server, you can use the Mosquito test server (only for testing, don’t spam them). Start mqtt-spy and connect to “test.mosquito.org”.

mqtt-start-screen

Subscription of messages: To receive the messages from the ESP8266  you have to subscribe to a topic. Use the same topic as in the source code, eg. “BIoutTopic”. You have to select the “New”-tab in the subscription, a new window pops up. Enter “BIoutTopic”. Now you receive all messages from the ESP8266.mqtt-client

Publishing messages: In the upper part of the window enter “BIinTopic”. In the “data” field you enter commands which will be sent to the ESP8266. Enter the commands and press “Publish”.

subscription-windowTry it out. Here are some examples:

JSON data exchange & command examples

Send message:

{
 messageType : "message",
 message : " Hello world!"
 }

Send Command – LED on:

{
 messageType : "execute",
 actuator : "LED",
 actuatorValue : 1
 }

Send command – motor1 modus is set to sinus curve vibration pattern

{
 messageType : "execute",
 actuator : "motor1",
 actuatorMode : "sinus"
 }

Send Command – set motor speed to 1000.

{
 messageType : "execute",
 actuator : "motor1",
 actuatorValue: 1000
 }

Send command – set motor1 off.

{
 messageType : "execute",
 actuator : "motor1",
 actuatorMode: "off"
 }

Message type “sensor”: this is for publishing sensor data of a sex toy (eg. position in 3d space, movement data, vibration pattern in use). These data can be received by other sex toys for vibration adjustment and synchronization of the vibration speed .

{
 messageType : "sensor",
 sensor: "mps9250",
 fusionedData: 10000,
 }

Remark: If it doesn’t work as expected, check the data field of the MQTT software. Paste & Copy doesn’t work as it should be and you may have to remove some redundant “{“-signs.

Code

The code is based on part 1. In the new code functions for publishing and receiving JSON data over MQTT are added.

Receiving and parsing incoming JSON files is done in “callback”. “callback” will be executed whenever a MQTT message comes in.

void callback(char* topic, byte* payload, unsigned int length) {

At first the incoming message is parsed and the result is stored in “root”. Now each entry in the JSON file can be accessed by assigning a new variable to root[entry]:

StaticJsonBuffer<500> jsonBuffer;
 JsonObject& root = jsonBuffer.parseObject(s);
 if (!root.success()) {
 Serial.println("parseObject() failed");
 } 
 String messageType = root["messageType"];

Build and publishing JSON files is done in the loop. A JsonObject “root” is created and then each entry in the JSON file is added by an assignment to root eg:  root[“messageType”] = “sensor”:

StaticJsonBuffer<500> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["messageType"] = "sensor"; // execute, message, sensor

For producing the JSON file there is “printTo” function. The JSON array of char must be formatted with the snprintf() function and then it can be published.

 root.printTo(outMsg, sizeof(outMsg));
 snprintf (msg,1000, "%s",outMsg);
 mqttclient.publish("BIoutTopic", msg);

Download the zipped and formatted code here: nodemcu-server-mqtt-iot.

In the next part of the tutorial we use Node-RED for visual programming our IOT sex toys.

Here is the complete source code for part 2 of the tutorial.

// bodyinteraction IOT project
// IOT sex toy prototype - control via browser and MQTT
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

#define MPU9250_ADDRESS 0x68
#define MAG_ADDRESS 0x0C

#define GYRO_FULL_SCALE_250_DPS 0x00
#define GYRO_FULL_SCALE_500_DPS 0x08
#define GYRO_FULL_SCALE_1000_DPS 0x10
#define GYRO_FULL_SCALE_2000_DPS 0x18

#define ACC_FULL_SCALE_2_G 0x00
#define ACC_FULL_SCALE_4_G 0x08
#define ACC_FULL_SCALE_8_G 0x10
#define ACC_FULL_SCALE_16_G 0x18

const char* ssid = "ALICE-WLAN";
const char* password = "36s69c3756a65";
const char* mqtt_server = "test.mosquitto.org";

int ledPin = 0; // NodeMCU pad D3 = GPIO 0
int motorPin= 13; // NodeMCU pad D7 = GPIO 13
double sinusValue=0;

// define constants for four different vibration modes
const int offMode=0;
const int maxMode=1;
const int sinusMode=2;
const int motionMode=3;

int motorMode =offMode; //current mode
int motionVector = 0; //current fusioned motion
// Acceleration in x,y and z direction at t(ime)=1 and time=0
// Geroscop data
int16_t ax,ay,az,ax1,ay1,az1,gx,gy,gz,gx1,gy1,gz1;
int valueMotor; //vibrator motor speed 0-1023

WiFiServer server(80);

WiFiClient espclient;
PubSubClient mqttclient(espclient);
char msg[512];
char outMsg[512];

bool requesting=false; // is there a request eg button pressed on webpage

//timing of mqtt messages
long now=0;
long lastMsg = 0;


int value = 0;
int valueLED = LOW;

// This function read Nbytes bytes from I2C device at address Address.
// Put read bytes starting at register Register in the Data array.
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
// Set register address
Wire.beginTransmission(Address);
Wire.write(Register);
Wire.endTransmission();

// Read Nbytes
Wire.requestFrom(Address, Nbytes);
uint8_t index=0;
while (Wire.available())
Data[index++]=Wire.read();
}

// Write a byte (Data) in device (Address) at register (Register)
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
// Set register address
Wire.beginTransmission(Address);
Wire.write(Register);
Wire.write(Data);
Wire.endTransmission();
}



// Return the response /generate webpage
void generateWebpage(WiFiClient espclient) {
espclient.println("HTTP/1.1 200 OK");
espclient.println("Content-Type: text/html");
espclient.println(""); // do not forget this one
espclient.println("<!DOCTYPE HTML>");
espclient.println("<html>");

espclient.print("Led pin is now: ");

if(valueLED == HIGH) {
espclient.print("On");
} else {
espclient.print("Off");
}

espclient.print("
Motor pin is now: ");
espclient.print(valueMotor);

espclient.println("

");
espclient.println("<a href=\"/LED=ON\"\"><button>Turn On </button></a>");
espclient.println("<a href=\"/LED=OFF\"\"><button>Turn Off </button></a>

");
espclient.println("<a href=\"/MOTOR=MAX\"\"><button>Motor Max </button></a>");
espclient.println("<a href=\"/MOTOR=OFF\"\"><button>Motor Off </button></a>");
espclient.println("<a href=\"/MOTOR=SINUS\"\"><button>Motor sinus curve </button></a>");
espclient.println("<a href=\"/MOTOR=MOTION\"\"><button>Motor motion controlled </button></a>
");
espclient.println("</html>");
}

// function callback is executed when a Mqtt message comes in
// - prints mqtt message
// - parse JSON file
// - execute commands
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
char s[length];
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
s[i]=payload[i];
}
StaticJsonBuffer<500> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(s);
if (!root.success()) {
Serial.println("parseObject() failed");
}
String messageType = root["messageType"]; //sensor, execute , message
String targetDeviceID = root["targetDeviceID"];
String actuator = root["actuator"];
int actuatorValue = root["actuatorValue"];
String actuatorMode = root["actuatorMode"];
String message = root["message"];
Serial.print("messageType: ");
Serial.println(messageType);
Serial.print("actuator: ");
Serial.println(actuator);
Serial.print("actuatorValue: ");
Serial.println(actuatorValue);

// print message
if (messageType=="message") {
Serial.print("Incoming message: ");
Serial.println(message);
}
// LED commands
if (messageType=="execute"&&actuator=="LED"&&actuatorValue==1) {
Serial.println("LED on received");
digitalWrite(ledPin, HIGH);
valueLED = HIGH;
}
if (messageType=="execute"&&actuator=="LED"&&actuatorValue==0) {
Serial.println("LED off received");
digitalWrite(ledPin, LOW);
valueLED = LOW;
}
// set modes commands
if (messageType=="execute"&&actuator=="motor1"&&actuatorMode=="off") {
analogWrite(motorPin, 0);
valueMotor = 0;
motorMode=offMode;
}
if (messageType=="execute"&&actuator=="motor1"&&actuatorMode=="sinus") {
motorMode=sinusMode;
}
if (messageType=="execute"&&actuator=="motor1"&&actuatorMode=="motion") {
motorMode=motionMode;
valueMotor=600;
if (valueMotor<500) {valueMotor=500;} if (valueMotor>1023) {valueMotor=1023;}
}
// set motor speed command
if (messageType=="execute"&&actuator=="motor1"&&actuatorValue!=0) {
Serial.println("set motor speed to fixed value");
valueMotor=actuatorValue;
analogWrite(motorPin,valueMotor);
}
// incoming sensor data, adjust motor speed when in motion mode
int fusionedData = root["fusionedData"];
String sensor = root["sensor"];
if (messageType=="sensor"&&sensor=="mps9250"&&motorMode==motionMode) {
if (fusionedData > 5000) {valueMotor=valueMotor+25;} else {valueMotor=valueMotor-10;}
if (valueMotor<500) {valueMotor=500;} //values must be above 500 otherwise the motor is off if (valueMotor>1023) {valueMotor=1023;} // values higher than 1023 are not supported
analogWrite(motorPin, valueMotor); // set motor speed
}
generateWebpage(espclient);
}

// connect to mqtt server
void reconnect() {
while (!mqttclient.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (mqttclient.connect(clientId.c_str())) {
Serial.println("connected");
mqttclient.subscribe("BIinTopic");
} else {
Serial.print("failed, rc=");
Serial.print(mqttclient.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}

void setup() {
Wire.begin();
// connect MPU9265 via i²c bus
// NodeMCU D1 = GPIO5 connected to MCU9265 SCL
// NodeMCU D2 = GPIO4 connected to MCU9265 SDA
Wire.pins(5,4);
Serial.begin(115200);

// Configure gyroscope range
I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_2000_DPS);
// Configure accelerometers range
I2CwriteByte(MPU9250_ADDRESS,28,ACC_FULL_SCALE_16_G);
// Set by pass mode for the magnetometers
I2CwriteByte(MPU9250_ADDRESS,0x37,0x02);

// Request first magnetometer single measurement
I2CwriteByte(MAG_ADDRESS,0x0A,0x01);

Serial.begin(115200);
delay(10);

// init LED pin
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);

// init motor pin
pinMode(motorPin, OUTPUT);
analogWrite(motorPin, 0);

// Connect to WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");

// Start the server
server.begin();
Serial.println("Server started");

// Print the IP address
Serial.print("Use this URL to connect: ");
Serial.print("http://");
Serial.print(WiFi.localIP());
Serial.println("/");

// init mqtt client
mqttclient.setServer(mqtt_server, 1883);
mqttclient.setCallback(callback);
}

void loop() {
if (!mqttclient.connected()) {
reconnect();
}
mqttclient.loop();

// Read accelerometer and gyroscope
uint8_t Buf[14];
I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);

// Create 16 bits values from 8 bits data

// Accelerometer
ax=-(Buf[0]<<8 | Buf[1]);
ay=-(Buf[2]<<8 | Buf[3]);
az=Buf[4]<<8 | Buf[5];

// Gyroscope
gx=-(Buf[8]<<8 | Buf[9]);
gy=-(Buf[10]<<8 | Buf[11]);
gz=Buf[12]<<8 | Buf[13]; // when in "motionMode" the vibration motor is controlled by motion if (motorMode==motionMode) { motionVector=sqrt(pow(ax-ax1,2)+pow(ay-ay1,2)+pow(az-az1,2)); //calculate motion vector // adjust vibration motor speed // if motion vector > 5000 raise speed by 25
// otherwise lover speed by 10
// adjust these constants to your needs
if (motionVector > 5000) {valueMotor=valueMotor+25;} else {valueMotor=valueMotor-10;}
if (valueMotor<500) {valueMotor=500;} //values must be above 500 otherwise the motor is off if (valueMotor>1023) {valueMotor=1023;} // values higher than 1023 are not supported
analogWrite(motorPin, valueMotor); // set motor speed

Serial.print("motionVector: ");
Serial.print(motionVector);
Serial.print(", valueMotor: ");
Serial.println(valueMotor);
delay(200);

// save values
ax1=ax;
ay1=ay;
az1=az;
gx1=gx;
gy1=gy;
gz1=gz;
}

// change vibration motor speed according to a sinus curve
if (motorMode==sinusMode) {
sinusValue=sinusValue+.01;
delay(20);
int sinTmp = ((sin(sinusValue)+1)*.5*(1023-500))+500;
analogWrite(motorPin, sinTmp);
valueMotor=sinTmp;
}

StaticJsonBuffer<500> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["messageType"] = "sensor"; // execute, message, sensor
// execute - send command to other device
// root["targetDeviceID"] = "xxxxx"; //for execute and message message types
// root["actuator"] = "motor1";
// root["actuatorValue"]="222";
// root["actuatorMode"] = "sinus";
// root["command"] = none;
// root["commandParameter1"] ="";

// message - send message to targetDeviceID
// root["message"] = "hello world";

//sensor - for publishing sensor data
root["sensor"] = "mps9250";
// root["time"] = none;
root["fusionedData"] = motionVector;
root["messageNumber"] = 0;
// example for raw data
// JsonArray& rawdata = root.createNestedArray("rawdata"); // x,y,z,roll, pitch better??
// rawdata.add(0, 0); // ax
// rawdata.add(0, 0); // ay
// rawdata.add(0, 0); // az
// rawdata.add(0, 0); // gx
// rawdata.add(0, 0); // gx
// rawdata.add(0, 0); // gx
// rawdata.add(0, 0); // mx
// rawdata.add(0, 0); // mx
// rawdata.add(0, 0); //mx
root["LEDstatus"] = valueLED;
root["motor1mode"] = motorMode;
root["motor1speed"] = valueMotor;

// publish motor speed as mqtt message every 10 seconds
// only when motor in not off
if (motorMode==maxMode||motorMode==sinusMode||motorMode==motionMode) {
now = millis();
if (now - lastMsg > 1000) {
if (!mqttclient.connected()) {
reconnect();
}
lastMsg = now;
++value;
root["messageNumber"] = value;
// publish data as MQTT message in JSON format
root.printTo(outMsg, sizeof(outMsg));
snprintf (msg, 1000, "%s",outMsg);
mqttclient.publish("BIoutTopic", msg);
Serial.print("Publish message every 10 sec: ");
Serial.println(msg);
}
}

// Check if a client has connected to the wifi server
WiFiClient espclient = server.available();
if (!espclient) {
return;
}

while(!espclient.available()){
delay(1);
}

// read the first line of the request
String request = espclient.readStringUntil('\r');
espclient.flush();

// LED on button pressed
Serial.println("hi execute rqeuest");
if (request.indexOf("/LED=ON") != -1) {
requesting=true;
digitalWrite(ledPin, HIGH);
valueLED = HIGH;
root["LEDstatus"] = valueLED;
}
// LED off button pressed
if (request.indexOf("/LED=OFF") != -1) {
requesting=true;
digitalWrite(ledPin, LOW);
valueLED = LOW;
root["LEDstatus"] = valueLED;
}
// set motor to maximum speed button pressed
if (request.indexOf("/MOTOR=MAX") != -1) {
requesting=true;
analogWrite(motorPin, 1023);
valueMotor = 1023;
motorMode=maxMode;
root["motor1mode"] = motorMode;
root["motor1speed"] = valueMotor;
}
// set motor off button pressed
if (request.indexOf("/MOTOR=OFF") != -1) {
requesting=true;
analogWrite(motorPin, 0);
valueMotor = 0;
motorMode=offMode;
root["motor1mode"] = motorMode;
root["motor1speed"] = valueMotor;
}
// motor amplitude controlled like a sinus curve
if (request.indexOf("/MOTOR=SINUS") != -1) {
requesting=true;
motorMode=sinusMode;
root["motor1mode"] = motorMode;
root["motor1speed"] = valueMotor;
}
// motor speed is adjusted by movements (classical "body interaction" interaction pattern)
if (request.indexOf("/MOTOR=MOTION") != -1) {
requesting=true;
motorMode=motionMode;
valueMotor=600;
root["motor1mode"] = motorMode;
root["motor1speed"] = valueMotor;
}


generateWebpage(espclient);

// send outMessage to Mqtt server
if (requesting) {
requesting=false;
if (!mqttclient.connected()) {
reconnect();
}
++value;

root["messageNumber"] = value;
root["motor1mode"] = motorMode;
root["motor1speed"] = valueMotor;

// publish data as MQTT message in JSON format
root.printTo(outMsg, sizeof(outMsg));
snprintf (msg,1000, "%s",outMsg);
mqttclient.publish("BIoutTopic", msg);

Serial.print("Publish message: ");
Serial.println(msg);
}
}

Moving dildo with motor driven skeleton

skeleton-without-hull-servo-bi1So far we have used vibration motors for our sex toys. Vibration motors are cheap, powerful, easy to control and robust actuators. That’s why they are part of most sex toys. But what about moving or touching  objects. Obviously we need some mechanics, maybe joints and gears? Or is there a simple option? A skeleton?

I realized the idea for using some type of skeleton for moving a dildo when I saw the video of a naked Pleo – one of the best artificial life forms ever.

skeleton-hull-servo-body-interaction-board

On www.thingiverse.com you will find more inspiration for using a skeleton to move something. The design is very simple.

The skeleton is composed of a number of vortexes. The holes are for connecting all vortexes and a servo with a nylon wire or similar.

backbone_skeleton_24

In addition we need a handle where the vortexes are fastened to. There is also space for a servo. Then use a nylon wire to connect the vortexes with the servo. You can drive the servo with a Arduino development board or you use the body interaction development board as described here.

servo_handle_b

The servo should turn only 15-30 degree or so. If you use the body interaction development board please copy the following code and upload the code to the board.

#include &lt;TinyServo.h&gt;

// servo control with the body interaction development board using the TinyServo library
// -- adaption of the demo script by
// tylernt@gmail.com's ATTiny Hardware Timer Assisted Servo Library v1.0 20-Nov-13
// http://forum.arduino.cc/index.php?action=dlattach;topic=198337.0;attach=71790

const byte SERVOS = 1; // number of servos is 1
const byte servoPin[SERVOS] = { 7 }; // servo is connected to PA1 which is pin 7
#define SERVO 0 // our servo is given the name &amp;quot;SERVO&amp;quot;

void setup() {
 setupServos();
}

void loop() {
 moveServo(SERVO, 0); // move servo to 0°
 delay(1000);
 moveServo(SERVO, 30); // move servo to 30°
 delay(2000);
}

In addition we need a wrapping for the skeleton. This can be made using these two forms (download STL files).

nonmech

Use flexible silicone and poor it in the form. The thickness of the wrapping is a bit too large – it rather hinders the skeleton in its movements. But it works!

sceleton-hull

Now we can put everything together.

 

Download on Thingiverse: http://www.thingiverse.com/thing:1736282

 

Tinker with Tinkercad!
Form: https://tinkercad.com/things/e8yscABu9Al
Skeleton: https://tinkercad.com/things/dfbMQsE4Mtl
Servo handle: https://tinkercad.com/things/5EHHrqM5sqC

YouTube: https://youtu.be/F1b8bGbuSHw

Exploring the internet of (sex) things 1

The internet of things – or short IOT – is getting popular. IOT is a network of physical things like vehicles, buildings, but also everyday objects like lamps, refrigerators. IOT allows objects to be sensed and controlled remotely across the Internet. As a result the physical world will be integrated into the internet and computer systems.

Popular examples are home axiomatization or collection of environmental data.  Even sex toy industry use the internet to connect sex toy users which are far away of each other (like the OhMiBod blueMotion). The vision of remote sex driven by technology is also known as Teledildonics. Unfortunately I didn’t pay much attention to this movement which goes back to 1975. body interaction focused more on wireless connected sex toys for users having sex together and want to integrate his and her’s sex toy. You will find a lot of information at Kyle Machulis site Metafetish. Also have a look at the annual conference Arse Elektronika which focus on the intersection of technology and sex.

In this blog I have already shown how to connect the body interaction development board to the internet. Now I will present some first steps into IOT. I will use the NodeMCU development board which is based on the popular ESP8266 System on a chip. The ESP is a wi-fi enabled microcontroller where you can connect sensors and actuators. It can connect to your wi-fi access point and home and it can be an access point itself and host eg. an internet server.

In my explorations I will try to find out if a IOT sex toy is useful for DIY sex toy community.

In this blog post we will use the ESP8266 as a wi-fi server. The server will connect to your wi-fi access point at home.

The series has 4 parts:

part 1: Exploring the internet of (sex) things

part 2: MQTT messages

part 3: Node-RED

part 4: Building a sex toy dashboard with Node-RED

Building a bread board prototype

nodemcu prototype breadboard

Material needed

  • bread board, wires
  • Node MCU or similar
  • small vibration motor (or LED), eg the Lilipad vibration motor
  • optional: accelerometer  MPU9265
  • optional: another LED and a resistor

WIre MPU9265

mpu-92-65Connect

  • SCL (on MPU9265) and D1 (on NodeMCU),
  • SDA and D2,
  • VCC and 3V3
  • GND and GND

Wire vibration motor

Connect

  • D7 (node MCU) with vibration motor (+) and
  • GND (NodeMCU) and (-)

Wire LED

Connect:

  • D3 (NodeMCU) and LED (long end)
  • LED (short end) and resistor
  • resistor and GND (NodeMCU)

Using the Arduino IDE

NodeMCU and all other ESP8266 boards are not supported by Arduino. But you can use the Arduino board manager to add other development boards. This short Tutorial explains the necessary steps: http://www.instructables.com/id/Quick-Start-to-Nodemcu-ESP8266-on-Arduino-IDE/

Connect the NodeMCU to your access point (WLAN router)

Upload script to NodeMCU

Copy the code to Arduino IDE.

Within the sketch you have to change the constants SSID and password. Use the same SSID as you would do to connect your smart phone or computer to the internet.

Select your NodeMCU and the port. Connect your computer and the NodeMCU with USB wire. Upload the script to NodeMCU

#include <ESP8266WiFi.h>
#include <Wire.h>

#define MPU9250_ADDRESS 0x68
#define MAG_ADDRESS 0x0C

#define GYRO_FULL_SCALE_250_DPS 0x00
#define GYRO_FULL_SCALE_500_DPS 0x08
#define GYRO_FULL_SCALE_1000_DPS 0x10
#define GYRO_FULL_SCALE_2000_DPS 0x18

#define ACC_FULL_SCALE_2_G 0x00
#define ACC_FULL_SCALE_4_G 0x08
#define ACC_FULL_SCALE_8_G 0x10
#define ACC_FULL_SCALE_16_G 0x18

const char* ssid = "????"; // Enter the name of your Access point
const char* password = "????"; //Enter the password SSID
int ledPin = 0; // NodeMCU pad D3 = GPI0
int motorPin= 13; // NodeMCU pad D7 = GPIO 13
double sinusValue=0;

// define constants for four different vibration modes
const int off_mode=0;
const int max_mode =1;
const int sinus_mode =2;
const int motion_mode =3;

int motor_mode =off_mode;

WiFiServer server(80);

 // This function read Nbytes bytes from I2C device at address Address.
// Put read bytes starting at register Register in the Data array.
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
 // Set register address
 Wire.beginTransmission(Address);
 Wire.write(Register);
 Wire.endTransmission();

 // Read Nbytes
 Wire.requestFrom(Address, Nbytes);
 uint8_t index=0;
 while (Wire.available())
 Data[index++]=Wire.read();
}

// Write a byte (Data) in device (Address) at register (Register)
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
 // Set register address
 Wire.beginTransmission(Address);
 Wire.write(Register);
 Wire.write(Data);
 Wire.endTransmission();
}

void setup() {

 Wire.begin();
 // NodeMCU D1 = GPIO5 connected to MCU9265 SCL
 // NodeMCU D2 = GPIO4 connected to MCU9265 SDA
 Wire.pins(5,4);
 Serial.begin(115200);

 // Configure gyroscope range
 I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_2000_DPS);
 // Configure accelerometers range
 I2CwriteByte(MPU9250_ADDRESS,28,ACC_FULL_SCALE_16_G);
 // Set by pass mode for the magnetometers
 I2CwriteByte(MPU9250_ADDRESS,0x37,0x02);

 // Request first magnetometer single measurement
 I2CwriteByte(MAG_ADDRESS,0x0A,0x01);

 Serial.begin(115200);
 delay(10);

 pinMode(ledPin, OUTPUT);
 digitalWrite(ledPin, LOW);

 pinMode(motorPin, OUTPUT);
 analogWrite(motorPin, 0);

 // Connect to WiFi network
 Serial.println();
 Serial.println();
 Serial.print("Connecting to ");
 Serial.println(ssid);

 WiFi.begin(ssid, password);

 while (WiFi.status() != WL_CONNECTED) {
 delay(500);
 Serial.print(".");
 }
 Serial.println("");
 Serial.println("WiFi connected");

 // Start the server
 server.begin();
 Serial.println("Server started");

 // Print the IP address
 Serial.print("Use this URL to connect: ");
 Serial.print("http://");
 Serial.print(WiFi.localIP());
 Serial.println("/");

}

int16_t ax,ay,az,ax1,ay1,az1,gx,gy,gz,gx1,gy1,gz1;
int valueMotor; //vibrator motor speed 0-1023

void loop() {

 // Read accelerometer and gyroscope
 uint8_t Buf[14];
 I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);

 // Create 16 bits values from 8 bits data

 // Accelerometer
 ax=-(Buf[0]<<8 | Buf[1]);
 ay=-(Buf[2]<<8 | Buf[3]);
 az=Buf[4]<<8 | Buf[5];

 // Gyroscope
 gx=-(Buf[8]<<8 | Buf[9]);
 gy=-(Buf[10]<<8 | Buf[11]);
 gz=Buf[12]<<8 | Buf[13];  // when in "motion_mode" the vibration motor is controlled by motion  if (motor_mode==motion_mode) {  int v = 0;  v=sqrt(pow(ax-ax1,2)+pow(ay-ay1,2)+pow(az-az1,2)); //calculate motion vector  // adjust vibration motor speed  // if motion vector > 5000 raise speed by 25
 // otherwise lover speed by 10
 // adjust these constants to your needs
 if (v > 5000) {valueMotor=valueMotor+25;} else {valueMotor=valueMotor-10;}
 if (valueMotor<500) {valueMotor=500;} //values must be above 500 otherwise the motor is off  if (valueMotor>1023) {valueMotor=1023;} // values higher than 1023 are not supported
 analogWrite(motorPin, valueMotor); // set motor speed

 Serial.print("v: ");
 Serial.print(v);
 Serial.print(", valueMotor: ");
 Serial.println(valueMotor);
 delay(200);

 // save values
 ax1=ax;
 ay1=ay;
 az1=az;
 gx1=gx;
 gy1=gy;
 gz1=gz;
 }

 // change vibration motor speed according to a sinus curve
 if (motor_mode==sinus_mode) {
 sinusValue=sinusValue+.01;
 delay(20);
 int sin_tmp = ((sin(sinusValue)+1)*.5*(1023-500))+500;
 Serial.println(sin_tmp);
 analogWrite(motorPin, sin_tmp);
 valueMotor=sin_tmp;
 }

 // Check if a client has connected
 WiFiClient client = server.available();
 if (!client) {
 return;
 }

 // Wait until the client sends some data
 Serial.println("new client");
 while(!client.available()){
 delay(1);
 }

 // Read the first line of the request
 String request = client.readStringUntil('\r');
 Serial.println(request);
 client.flush();

 // Match the request

 int valueLED = LOW;
 if (request.indexOf("/LED=ON") != -1) {
 digitalWrite(ledPin, HIGH);
 valueLED = HIGH;
 }
 if (request.indexOf("/LED=OFF") != -1) {
 digitalWrite(ledPin, LOW);
 valueLED = LOW;
 }

 if (request.indexOf("/MOTOR=MAX") != -1) {
 analogWrite(motorPin, 1023);
 valueMotor = 1023;
 motor_mode=max_mode;
 }
 if (request.indexOf("/MOTOR=OFF") != -1) {
 analogWrite(motorPin, 0);
 valueMotor = 0;
 motor_mode=off_mode;
 }

 if (request.indexOf("/MOTOR=SINUS") != -1) {
 motor_mode=sinus_mode;
 }

 if (request.indexOf("/MOTOR=MOTION") != -1) {
 motor_mode=motion_mode;
 valueMotor=600;
 }

 // Return the response
 client.println("HTTP/1.1 200 OK");
 client.println("Content-Type: text/html");
 client.println(""); // do not forget this one
 client.println("<!DOCTYPE HTML>");
 client.println("<html>");

 client.print("Led pin is now: ");

 if(valueLED == HIGH) {
 client.print("On");
 } else {
 client.print("Off");
 }

 client.print("
Motor pin is now: ");
 client.print(valueMotor);

 client.println("

");
 client.println("<a href=\"/LED=ON\"\"><button>Turn On </button></a>");
 client.println("<a href=\"/LED=OFF\"\"><button>Turn Off </button></a>

");
 client.println("<a href=\"/MOTOR=MAX\"\"><button>Motor Max </button></a>");
 client.println("<a href=\"/MOTOR=OFF\"\"><button>Motor Off </button></a>");
 client.println("<a href=\"/MOTOR=SINUS\"\"><button>Motor sinus curve </button></a>");
 client.println("<a href=\"/MOTOR=MOTION\"\"><button>Motor motion controlled </button></a>
");
 client.println("</html>");

 delay(1);
 Serial.println("Client disonnected");
 Serial.println("");

}

Controlling the vibrator prototype

After uploading the script above, open the serial monitor. After some time the NodeMCU will report “wifi connected”.

IOT wifi connectdNow start a browser. Use the URL which the NodeMCU reported eg. http://192.168.1.12/

If your smart phone is connected to the same Access point as your computer is, you can use your smart phone to control the prototype, too.

You can turn the LED on or off by pressing the “Turn On” and “Turn Off” buttons.

For controlling the motor you have 4 options

  • motor on
  • motor off
  • sinus curve (the motor will speed up and slow down according to a sinus curve)
  • motion controlled (more motion -> motor speeds up)

Summary

To build a vibrator prototype based on the ESP8266 MCU is very easy. You can use your Arduino IDE to upload scripts to the prototype. Then you can control the vibration motor through a browser. If you don’t want to use these external control options the vibrator prototype can be controlled by motion similar to the body interaction vibrator development board.

In this blog post series we will  explore other interesting features of the ESP8266:

  • the ESP8266 as an access point (no need for private or public access points)
  • over the air (OTA) uploading of sketches  (wireless – no USB connector needed)
  • MQTT protocol & server
  • Node-RED (visual IOT programming)
  • review of development boards for building IOT vibrators

Go to the second part of the tutorial.

WeVibe sends usage pattern over the internet

On the Def Con 24 two hackers that the WeVibe4 plus send a lot of usage data to the company. This is reported by EAN online. According to EAN online the following data are recorded and maybe stored in a database: how often you play with the vibe what setting you use how long you play for the temperature of the…

“An exploration of the sex toy DIY movement” – interview with EAN Online Erotic Industry

EAN_LogoRandolph Heil from EAN online interviewed Jacob from bodyinteraction.com. In the interview the DIX sex toy movements – its motivation and aims are discussed.  3d printing – and its future – as a method for making your own personal sex toy is described. Jacob gave some details about his story. The interview mention the Arduino software and hardware as a platform for sex tech products. Finally the future of (open source) sex toys is discussed. Read the interview here.

Sex toy patents arrived in the EU

Recently the EAN magazine interviewed Frank Ferrari from we-vibe. The US company was granted the patent EP1824440. They applied in 2005. It took so long “at least in part to the filing of several third-party observations”. The patent is for a couple vibrator with a specific form. A couple vibrator is defined as a sex toy which permits sexual intercourse while inserted into the vagina.

Is this a patent which will destroy open-source sex toys in generel as it happened to Comingle? I don’t think so. You can still make a vibrator and sell it as long as it doesn’t have the form and functionality.

But: If you are interested in building couple vibrators (like we-vibe define them) then you have a problem. I don’t think the patent forbids every sex toy which can be inserted into the vagina while having sexual intercourse  (sorry, but what is new about this??).

But if you design a similar form, equip it with vibrators then you definitely should consult your patent lawyer before selling it.

What is the unique about the form? Is it an innovation by we-vibe? Or is it rather a negative copy of a part of what we call genital area?

Or it is like giving a patent for T-shirts? A negative copy of the human upper body…

we-vibe-claim-1-700x350

Maybe this is not a good example to blame patents. Maybe the EU patent agency has done a good job to grant a rather specific than general patent. Maybe we-vibe had enormous R&D efforts. Or they need enourmous effort to bring it into the market. And then a company makes  a copy and sells it for the half of the price? Ok, then the we-vibe patent is ok.

My opinion:  Patents are not good for innovation. They prevent innovations to become reality. They hinder competition. They support rich companies with large law departments. They promote lawyers and the economy of patents.

New small fusion vibrator

fusion-small-side-viewThis is a small version of the fusion vibrator. The small fusion form for molding can be made with 3d printers with smaller volume. In addition the inlay for the body interaction development board is reduced in size, but there is still enough place for board and battery. Only about 120ml silicone are needed. The form was constructed in Tinkercad.

Modify the form in Tinkercad: all forms, inlay only, form only

Download the STL files for printing: round_something_06_final_inlay_improved. There are some artifacts but printing is fine on the daVinci 1.0.

Download and discuss in Thingiverse: http://www.thingiverse.com/thing:1589075

round_something_06_final

Please follow the instruction from the large fusion vibrator. When you poor the silicone into the form it is very important to keep the USB connector free from silicone. Even very small amounts can cause the USB connector to break apart from the board.  When the USB connector is broken then you cannot recharge the vibrator. Also be always very cautious when you plug-in or plug-out the USB connector.

Finally you can remove the overhanging parts from the inlay.

fusion-small-top-view

 

%d bloggers like this: