Barqunics
Published © GPL3+

Using Ninjutsu Hand Signs (Naruto) to Unlock a Safe

This project was inspired from Naruto Manga/Anime. If the sequence of hand signs was correct, it unlocked solenoid door lock.

IntermediateShowcase (no instructions)2 days7,510

Things used in this project

Story

Read more

Schematics

transmitter_wiring_diagram_DuWMTbnRVY.jpg

Receiver Wiring diagram

Code

Glove_Transmitter

Arduino
/*
    Using Ninjutsu Hand Signs (Naruto) To Unlock A Safe
    by Ikhsan, Barqunics
*/

/* Kalman library created by  Kristian Lauszus, TKJ Electronics.
   Copyright (C) 2012 License : GNU GPL v2
   https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
   
  Contact information
  -------------------

  Kristian Lauszus, TKJ Electronics
  Web      :  http://www.tkjelectronics.com
  e-mail   :  kristianl@tkjelectronics.com
*/

/*
    Library RF24, website : https://nRF24.github.io/RF24/ 
    License : GNU GPL v2
*/

#include <Wire.h>
#include <SPI.h>      //SPI library for communication with the nRF24L01+
#include "RF24.h"    //The main library of the nRF24L01+, Library RF24 : https://github.com/nRF24/RF24
#include <Kalman.h>  //Source: https://github.com/TKJElectronics/KalmanFilter


#define RESTRICT_PITCH // Comment out to restrict roll to 90deg instead - please read: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf

const int flexPin = A2;   //pin A2 to read analog input
RF24 radio(7, 8);  // CE,CSN

Kalman kalmanX; //Create the Kalman instances
Kalman kalmanY;  //Create the Kalman instances

/* IMU Data */
double accX, accY, accZ;
double gyroX, gyroY, gyroZ;
double InputX, InputY;

double gyroXangle, gyroYangle; // Angle calculate using the gyro only
double compAngleX, compAngleY; // Calculated angle using a complementary filter
double kalAngleX, kalAngleY; // Calculated angle using a Kalman filter

uint32_t timer;
uint8_t i2cData[14]; // Buffer for I2C data

//Create a pipe addresses for  communication
const uint64_t pipe = 0xE8E8F0F0E1LL;

int flexValue;
int val = 0;  
int unlock = 0;  // variable for reading the unlock status
const long handSignsInterval = 2500; // Longest time to wait for hand signs
unsigned long now;
unsigned long handSignsStart; // Reference for when hand signs started.

void setup() {
  Serial.begin(115200);
  Wire.begin();
  radio.begin();                 //Start the nRF24 communicate
  radio.openWritingPipe(pipe);   //Sets the address of the receiver to which the program will send data.

#if ARDUINO >= 157
  Wire.setClock(400000UL); // Set I2C frequency to 400kHz
#else
  TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
#endif

  i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
  i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
  i2cData[2] = 0x00; // Set Gyro Full Scale Range to 250deg/s
  i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to 2g
  while (i2cWrite(0x19, i2cData, 4, false)); // Write to all four registers at once
  while (i2cWrite(0x6B, 0x01, true)); // PLL with X axis gyroscope reference and disable sleep mode

  while (i2cRead(0x75, i2cData, 1));
  if (i2cData[0] != 0x68) { // Read "WHO_AM_I" register
    Serial.print(F("Error reading sensor"));
    while (1);
  }

  delay(100); // Wait for sensor to stabilize

  /* Set kalman and gyro starting angle */
  while (i2cRead(0x3B, i2cData, 6));
  accX = (int16_t)((i2cData[0] << 8) | i2cData[1]);
  accY = (int16_t)((i2cData[2] << 8) | i2cData[3]);
  accZ = (int16_t)((i2cData[4] << 8) | i2cData[5]);

  // Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
  // atan2 outputs the value of - to  (radians) - see http://en.wikipedia.org/wiki/Atan2
  // It is then converted from radians to degrees
#ifdef RESTRICT_PITCH // Eq. 25 and 26
  double roll  = atan2(accY, accZ) * RAD_TO_DEG;
  double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
#else // Eq. 28 and 29
  double roll  = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;
  double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
#endif

  kalmanX.setAngle(roll); // Set starting angle
  kalmanY.setAngle(pitch);
  gyroXangle = roll;
  gyroYangle = pitch;
  compAngleX = roll;
  compAngleY = pitch;
  timer = micros();

}

void loop() {
 // Read all the values from the MPU6050 sensor, calculates the angles and filters them
  mpuKalman();

  InputX = kalAngleX;   // X axis Value (Roll)
  InputY = kalAngleY;   // Y axis Value (Pitch)
  flexValue = analogRead(flexPin);  //Read analog value from flex sensor
   
  //Read Hand signs
  handSigns();

  radio.write(&val, sizeof(val));
  
  Serial.print("Val : ");
  Serial.print(val);
  Serial.print("Unlock : ");
  Serial.println(unlock);
}

void handSigns() {
  // Tiger
  if ((InputY > -40 && InputY <-30) && flexValue > 245) {
    handSignsStart = millis();
    if (val == 0) {
     val = 1;
    }
  }
  // Snake 
  if (InputY < -40 && (flexValue > 150 && flexValue <210)) {
    if (val == 1) {
      val = 2;
    }
  }
  // Dog
  if (flexValue > 240 && (InputY > - 10 && InputY <10)) {
    if (val == 2) {
      val = 3;
    }
  }
  // Dragon
  if (InputX > 50 && (flexValue > 150 && flexValue <210)) {
    if (val == 3) {
      val = 4;
    }
  }
  // Hand Clap
  if (InputY < -55 && flexValue > 240) {
    if (val == 4) {
      val = 5;
    }
  }
  
  now = millis();

  if (( val == 5 ) && (now - handSignsStart < handSignsInterval)) {
    unlock = 1;
  } else if ((now - handSignsStart > handSignsInterval)) {
    val = 0;
    unlock = 0; 
  }
}

// Read all the values from the MPU6050 sensor, calculates the angles and filters them
void mpuKalman() {
  /* Update all the values */
  while (i2cRead(0x3B, i2cData, 14));
  accX = (int16_t)((i2cData[0] << 8) | i2cData[1]);
  accY = (int16_t)((i2cData[2] << 8) | i2cData[3]);
  accZ = (int16_t)((i2cData[4] << 8) | i2cData[5]);
  gyroX = (int16_t)((i2cData[8] << 8) | i2cData[9]);
  gyroY = (int16_t)((i2cData[10] << 8) | i2cData[11]);
  gyroZ = (int16_t)((i2cData[12] << 8) | i2cData[13]);;

  double dt = (double)(micros() - timer) / 1000000; // Calculate delta time
  timer = micros();

  // Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26
  // atan2 outputs the value of - to  (radians) - see http://en.wikipedia.org/wiki/Atan2
  // It is then converted from radians to degrees
#ifdef RESTRICT_PITCH // Eq. 25 and 26
  double roll  = atan2(accY, accZ) * RAD_TO_DEG;
  double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
#else // Eq. 28 and 29
  double roll  = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;
  double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
#endif

  double gyroXrate = gyroX / 131.0; // Convert to deg/s
  double gyroYrate = gyroY / 131.0; // Convert to deg/s

#ifdef RESTRICT_PITCH
  // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  if ((roll < -90 && kalAngleX > 90) || (roll > 90 && kalAngleX < -90)) {
    kalmanX.setAngle(roll);
    compAngleX = roll;
    kalAngleX = roll;
    gyroXangle = roll;
  } else
    kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter

  if (abs(kalAngleX) > 90)
    gyroYrate = -gyroYrate; // Invert rate, so it fits the restriced accelerometer reading
  kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt);
#else
  // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) {
    kalmanY.setAngle(pitch);
    compAngleY = pitch;
    kalAngleY = pitch;
    gyroYangle = pitch;
  } else
    kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt); // Calculate the angle using a Kalman filter

  if (abs(kalAngleY) > 90)
    gyroXrate = -gyroXrate; // Invert rate, so it fits the restriced accelerometer reading
  kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Calculate the angle using a Kalman filter
#endif

  gyroXangle += gyroXrate * dt; // Calculate gyro angle without any filter
  gyroYangle += gyroYrate * dt;
  //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate
  //gyroYangle += kalmanY.getRate() * dt;

  compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll; // Calculate the angle using a Complimentary filter
  compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;

  // Reset the gyro angle when it has drifted too much
  if (gyroXangle < -180 || gyroXangle > 180)
    gyroXangle = kalAngleX;
  if (gyroYangle < -180 || gyroYangle > 180)
    gyroYangle = kalAngleY;

}


const uint8_t IMUAddress = 0x68; // AD0 is logic low on the PCB
const uint16_t I2C_TIMEOUT = 1000; // Used to check for errors in I2C communication

uint8_t i2cWrite(uint8_t registerAddress, uint8_t data, bool sendStop) {
  return i2cWrite(registerAddress, &data, 1, sendStop); // Returns 0 on success
}

uint8_t i2cWrite(uint8_t registerAddress, uint8_t *data, uint8_t length, bool sendStop) {
  Wire.beginTransmission(IMUAddress);
  Wire.write(registerAddress);
  Wire.write(data, length);
  uint8_t rcode = Wire.endTransmission(sendStop); // Returns 0 on success
  if (rcode) {
    Serial.print(F("i2cWrite failed: "));
    Serial.println(rcode);
  }
  return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
}

uint8_t i2cRead(uint8_t registerAddress, uint8_t *data, uint8_t nbytes) {
  uint32_t timeOutTimer;
  Wire.beginTransmission(IMUAddress);
  Wire.write(registerAddress);
  uint8_t rcode = Wire.endTransmission(false); // Don't release the bus
  if (rcode) {
    Serial.print(F("i2cRead failed: "));
    Serial.println(rcode);
    return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
  }
  Wire.requestFrom(IMUAddress, nbytes, (uint8_t)true); // Send a repeated start and then release the bus after reading
  for (uint8_t i = 0; i < nbytes; i++) {
    if (Wire.available())
      data[i] = Wire.read();
    else {
      timeOutTimer = micros();
      while (((micros() - timeOutTimer) < I2C_TIMEOUT) && !Wire.available());
      if (Wire.available())
        data[i] = Wire.read();
      else {
        Serial.println(F("i2cRead timeout"));
        return 5; // This error value is not already taken by endTransmission
      }
    }
  }
  return 0; // Success
}

The Safe_Receiver

Arduino
/*
    Library RF24, website : https://nRF24.github.io/RF24/ 
    License : GNU GPL v2
*/


#include <SPI.h>      //SPI library for communication with the nRF24L01+
#include "RF24.h"  //The main library of the nRF24L01+

RF24 radio (7, 8); // CE,CSN

//Create a pipe addresses for  communication
const uint64_t pipe = 0xE8E8F0F0E1LL;

int relayPin = 3; //Relay Pin
int led = 5;
int val = 0;

void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);
  radio.begin();                    //Start the nRF24 communicate
  radio.openReadingPipe(1, pipe);   //Sets the address of the transmitter to which the program will receive data.
  radio.startListening();
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, HIGH);
  delay(100);
}

void loop() {
  if (radio.available()) {
    radio.read(&val, sizeof(val));
    Serial.print("Val:  ");
    Serial.println(val);
  }


  switch (val) {
    case 1 :
      analogWrite(led, 20);
      break;
    case 2 :
      analogWrite(led, 50);
      break;
    case 3 :
      analogWrite(led, 100);
      break;
    case 4 :
      analogWrite(led, 150);
      break;
    case 5 :
      analogWrite(led, 250);
      digitalWrite(relayPin, LOW);
      Serial.println("Safe unlocked");
      delay(500);
      digitalWrite(relayPin, HIGH);
      break;
    default :
      analogWrite(led, 0);
      digitalWrite(relayPin, HIGH);
      break;
  }
}

Credits

Barqunics

Barqunics

7 projects • 71 followers
Hello, My name is Ikhsan

Comments