Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Emmanuel Edwards
Published © MIT

Skiptti

A smart jump rope that counts the number of jumps (skips) and syncs the count to your phone (Android).

IntermediateFull instructions provided2 days952

Things used in this project

Hardware components

3D Magnetic Sensor 2Go
Infineon 3D Magnetic Sensor 2Go
×1
nRF51822 Bluetooth Smart Beacon Kit
Nordic Semiconductor nRF51822 Bluetooth Smart Beacon Kit
×1
Nordic Semiconductor nRF51 DK
×1
Capacitor 100 µF
Capacitor 100 µF
×2
Linear Regulator (Low Dropout)
Linear Regulator (Low Dropout)
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Plastic Jump rope
×1

Software apps and online services

Windows 10
Microsoft Windows 10
Arduino IDE
Arduino IDE
Arm Mbed OS
Infineon Evaluation Kit for 3D sensor

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Code

Arduino code

Arduino
The code for 3D magnetic 2go sensor. To detect rotational motion
#include <Tle493d_w2b6.h>
Tle493d_w2b6 magSensor = Tle493d_w2b6();


int NEGATIVE = -1, ZERO = 0, POSITIVE = 1;
int ANGLE = 0;
int UNDEFINED = -500;
int PREVIOUS_ANGLE = 0;
int CURRENT_ANGLE = 0;
int FORWARD_DIRECTION  = 10;
int BACKWARD_DIRECTION = 11;
int DIRECTION = 0;
int CURRENT_DIRECTION = 0;
int STEP = -1;
int OUTPUT2 = 4;

int ACCUMULATOR = 0;

void setup() {
 // more setup
 Serial.begin(9600);
 magSensor.begin();
 magSensor.enableTemp();
 pinMode(LED_BUILTIN, OUTPUT);
 pinMode(OUTPUT2, OUTPUT);
 delay(3000);
 digitalWrite(LED_BUILTIN, HIGH);
 digitalWrite(OUTPUT2, HIGH);
}
void loop() {

  magSensor.updateData();               // read sensor info

  int xValue = (int)magSensor.getX();
  int yValue = (int)magSensor.getY();
  int zValue = (int)magSensor.getZ();

  int normalX = normalize(xValue);
  int normalY = normalize(yValue);
  int normalZ = normalize(zValue);

  ANGLE = getAngle(normalX, normalY, normalZ);
  Serial.print("Angle: ");
  Serial.println(ANGLE);
  isCompleteRotation();
  delay(50);
}

/** normalizes values above a threshold
 value from any axis greter than 5 is taken as positive
 values from any less than -5 is taken as negative
 values from any 3 axis greater than -5 and lesser than 5 is taken as zero*/

int normalize(int value){
    if(value < 0 && value < -5){
      return NEGATIVE;
    }else if((value < 0 && value > -5)||(value > 0 && value < 5)){
      return ZERO;
    }else if(value > 0 && value > 5){
      return POSITIVE;
    }
    return ZERO;
}


/* gets a resultant value representing the current orientation of the magnet in front of the sensor*/

int getAngle(int xNormal, int yNormal, int zNormal){
  if(xNormal == NEGATIVE && yNormal == ZERO && zNormal == NEGATIVE){
    CURRENT_ANGLE = 0;
  }

  if(xNormal == ZERO && yNormal == NEGATIVE && zNormal == ZERO){
    CURRENT_ANGLE = 90;
  }

  if(xNormal == POSITIVE && yNormal == ZERO && zNormal == POSITIVE){
    CURRENT_ANGLE = 180;
  }

  if(xNormal == ZERO && yNormal == POSITIVE && zNormal == ZERO){
    CURRENT_ANGLE  = 270;
  }
  
  return CURRENT_ANGLE;
}

/* Tracks rotation states to determine backward and forward rotation. and finally
 fires a signal of a complete cycle if the direction never changed for all 4 states of rotation i.e 0, 90, 180 and 270*/

void isCompleteRotation(){    
    if(CURRENT_ANGLE != PREVIOUS_ANGLE){

      switch(CURRENT_ANGLE){
        case 0:{
            if(PREVIOUS_ANGLE == 270){
               DIRECTION = FORWARD_DIRECTION;
            }
            if(PREVIOUS_ANGLE == 90){
               DIRECTION = BACKWARD_DIRECTION;
            }
            break;
        }

        case 90:{
            if(PREVIOUS_ANGLE == 0){
               DIRECTION = FORWARD_DIRECTION;
            }
            if(PREVIOUS_ANGLE == 180){
               DIRECTION = BACKWARD_DIRECTION;
            }
            break;
        }

        case 180:{
            if(PREVIOUS_ANGLE == 90){
               DIRECTION = FORWARD_DIRECTION;
            }
            if(PREVIOUS_ANGLE == 270){
               DIRECTION = BACKWARD_DIRECTION;
            }
            break;
        }

        case 270:{
            if(PREVIOUS_ANGLE == 180){
               DIRECTION = FORWARD_DIRECTION;
            }
            if(PREVIOUS_ANGLE == 0){
               DIRECTION = BACKWARD_DIRECTION;
            }
            break;
        }
      }

      if(CURRENT_DIRECTION == DIRECTION){
          STEP++;
      }else{
          STEP = 0;
      }
      
      Serial.println(DIRECTION == FORWARD_DIRECTION? "FORWARD==" : "==BACKWARD");
      CURRENT_DIRECTION = DIRECTION;
      PREVIOUS_ANGLE = CURRENT_ANGLE;
      if(STEP >= 4){
        STEP = 0;
        digitalWrite(LED_BUILTIN, HIGH);
        digitalWrite(OUTPUT2, HIGH);
      }else{
        digitalWrite(LED_BUILTIN, LOW);
        digitalWrite(OUTPUT2, LOW);
      }
      Serial.print("STEPS: ");
      Serial.println(STEP);
    }
}

Core51822

C/C++
The Code for The Bluetooth connection and counting logic with Arm Mbed OS
/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed.h"
#include "ble/BLE.h"
#include "ble/services/UARTService.h"
#include "DeviceInformationService.h"


const static char     DEVICE_NAME[]        = "Skiptti3";
uint8_t            READING []          = "Skipptii counts";
uint8_t * dataPointer;
char buffer [50];

//static const uint16_t uuid16_list[]        = {GattService::UUID_HEART_RATE_SERVICE,GattService::UUID_DEVICE_INFORMATION_SERVICE};

BLEDevice  ble;
DigitalOut led1(LED1);
//DigitalOut led1(P0_14);

InterruptIn counterSource(P0_17);

UARTService *uartServicePtr;
bool isConnected = false;
uint16_t period = 0, clockCounter = 0, skipCounts = 0;
uint8_t rpm = 0;
uint16_t size;

enum {
    RELEASED = 0,
    PRESSED,
    IDLE
};
static uint8_t buttonState = IDLE;

void buttonPressedCallback(void)
{
    /* Note that the buttonPressedCallback() executes in interrupt context, so it is safer to access
     * BLE device API from the main thread. */
    buttonState = PRESSED;
}

void buttonReleasedCallback(void)
{
    /* Note that the buttonReleasedCallback() executes in interrupt context, so it is safer to access
     * BLE device API from the main thread. */
    if(buttonState == PRESSED){
        period = clockCounter;
        clockCounter = 0;
        buttonState = RELEASED;
        if(period > 50){
            skipCounts = 0;    //please start again you rested and that shows the end of skip session
        }
        skipCounts++;
        if(ble.gap().getState().connected){
            printf("data received %u bytes\n\r", skipCounts);
            int n = sprintf(buffer, "period %u skip %u\n\r", period, skipCounts);
            ble.updateCharacteristicValue(uartServicePtr->getRXCharacteristicHandle(), (uint8_t*)buffer, n);
        }
    }
    
}

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    printf("Disconnected!\r\n");
    isConnected = false;
    ble.startAdvertising();
}

void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
{
    printf("Connected!\r\n");
    isConnected = true;
}

void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
{
    printf("Input passKey: ");
    for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
        printf("%c ", passkey[i]);
    }
    printf("\r\n");
}

void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
{
    if (status == SecurityManager::SEC_STATUS_SUCCESS) {
        printf("Security success\r\n");
    } else {
        printf("Security failed\r\n");
    }
}

void onDataWritten(const GattWriteCallbackParams *params)
{
    if ((uartServicePtr != NULL) && (params->handle == uartServicePtr->getTXCharacteristicHandle())) {
        uint16_t bytesRead = params->len;
        printf("received %u bytes\n\r", bytesRead);
        printf("data received %s bytes\n\r", params->data);
        ble.updateCharacteristicValue(uartServicePtr->getRXCharacteristicHandle(), params->data, bytesRead);
    }
}

void periodicCallback(void)
{
    led1 = !led1;
    clockCounter++;
}

int main(void)
{
    led1 = 1;
    Ticker ticker;
    ticker.attach(periodicCallback, 0.1);

    printf("Initialising the nRF51822\n\r");
    ble.init();
    
    /* Initialize BLE security */
    bool enableBonding = true;
    bool requireMITM   = true;
    ble.securityManager().init(enableBonding, requireMITM, SecurityManager::IO_CAPS_DISPLAY_ONLY);
    
    /* Set callback functions */
    ble.gap().onConnection(connectionCallback);
    ble.onDisconnection(disconnectionCallback);
    ble.onDataWritten(onDataWritten);
    ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
    ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);

    /* setup advertising */
    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,(const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME) - 1);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,(const uint8_t *)UARTServiceUUID_reversed, sizeof(UARTServiceUUID_reversed));

    ble.setAdvertisingInterval(1000); /* 1000ms; in multiples of 0.625ms. */
    ble.startAdvertising();

    UARTService uartService(ble);
    uartServicePtr = &uartService;
    
    counterSource.fall(buttonPressedCallback);
    counterSource.rise(buttonReleasedCallback);

    while (true) {
        ble.waitForEvent();
    }
}

Credits

Emmanuel Edwards

Emmanuel Edwards

4 projects • 10 followers
A Java based application developer. I do more of android application development and embedded system designs with C/C++,

Comments