Monthly Archives: October 2017

[Tutorial] How to repair broken USB cables (Micro USB including data transfer)

In this tutorial, I want to show how to repair broken USB cables. In particular, it is shown how to repair a Micro USB cables including the data transfer wires. I made also a video tutorial about this topic. However, in the video, only a charge-only USB cable (with two wires) is repaired. You find the video here:

Just for the sake of completeness — List of Materials:
– Micro USB cable[connector must be broken ;)] [Search on Aliexpress | Amazon]
– Micro USB plug [Search on Aliexpress | Amazon]

Typically, when a USB cable stops to work, the cable and the wires are often still intact. Instead, the USB connector got probably broken. Recently, the connector of one of my USB cables became loose. Moreover, it totally stopped working after some time.

Broken Micro USB plug.

Broken Micro USB plug.

I decided to repair it and ordered a pack of USB connector replacements (also called plugs, tails, or sockets) from Aliexpress. These USB connector replacements are available in many variants (different colors, different lengths, etc.).

My connectors consist of four parts:

Micro USB connector replacement.

Micro USB connector replacement.

1) The first step is to cut off the broken connector:

2) Then, the outer jacket must be removed (e.g. with a wire stripper):

3) Next, the inner wires must be stripped:

4) The cable must be moved through the two parts of the connector replacement’s outer shell:

5) Now, the soldering (almost) starts. Luckily, the wire colors are standardized and used across many manufacturers. Typically, a micro USB cable has five wires: GND (black), 5V (red), Data+ (green), Data- (white), and the (outer) Drain Wire, which should be connected to the GND.

Unfortunately,  different types of USB connector replacements might also have a different layouts. Therefore, have a look at the datasheet or the store’s website to find out which pin has to be connected to which wire. The following picture shows the pin layout for the connectors that I bought:

If you are sure about where to connect which wire, you can start soldering:

6) After the soldering, make sure that the wires are properly isolated. I used some tape for this task as well as for strengthen my solder joint:

7) The last step is to put everything together. I used the help of some pliers to press all parts together:

If you have done all these steps correctly, you should have a working USB cable that can be used for charging as well as for data transfer again:

Augmented Arduino Car

// Remark: This post is currently under construction! Wiring layout and source code might be available at the very end of October 2017.

Arduino car before adding the augmentations

Arduino car before adding the augmentations.

Last Christmas I received the Elegoo car (Version 1) which is small car based on the Arduino platform. The car comes with an Arduino Uno, a sensor shield, and many sensors. Recently, I enhanced my car with four augmentations:

  • automatic break system (pure software update)
  • headlights
  • undercar lights
  • bluetooth remote control

I made a video about how I created these augmentations. In addition to the video, I want to give some more insights about this project on my website. You can find the video here:

The goal of this little project was to get all ideas into a prototype state. Therefore, almost all created augmentations look very prototyp’ish. If I find some time and the augmentations turn out fun, I might continue this project with 3D-printed parts.

List of materials:
– Arduino Elegoo car (Version 1) [Search on Aliexpress | Amazon]
– Relais (headlights) [Search on Aliexpress | Amazon]
– High power LEDs (headlights) [Search on Aliexpress | Amazon]
– WS2812B LEDs (undercarlights) [Search on Aliexpress | Amazon]
– Arduino Nano (bluetooth remote control) [Search on Aliexpress | Amazon]
– Bluetooth module HC-05 (bluetooth remote control) [Search on Aliexpress | Amazon]
– DIP switches (bluetooth remote control) [Search on Aliexpress | Amazon]
– Joystick KY-23 (bluetooth remote control) [Search on Aliexpress | Amazon]
– Rotary angle sensor(bluetooth remote control) [Search on Aliexpress | Amazon]

Automatic break system:

Automatic break system of the Augmented Arduino Car.

Automatic break system of the Augmented Arduino Car.

The automatic break system is basically a pure software update. If the car is driving and an obstacle comes too close, it starts to break immedately. At the same time, a yellow LED will light up to show that the car just automatically stopped. Then a phase of about 7 seconds starts. In this phase, the car can be moved to a “safe position” without being interrupted by the automatic break mechanisms. This phase is indicated by the same LED which starts blinking. If the LED goes out, the car is in “normal” mode and will stop again if an obstacles comes close.
The Elegoo car comes already with an ultrasonic sensor which was is used to implement the automatic break system. Normally, in order to read the distance values from the sensors, the pulseIn-function of the Arduino platform is used. The drawback of the pulseIn-function is that it actively waits while observing the output of the ultrasonic sensor. Therefore, I implemented the distance measurements based on interrupt routines. In particular, my routines are called when the output signal of the ultrasonic sensor switches from HIGH to LOW or from LOW to HIGH. By using this approach, the Arduino does not need to actively wait for something and the saved time can be used for other things, such as handling the bluetooth communication.

Headlights:

The headlights augmentation consists of a relay breakout board and a pair of power LEDs.

The headlights augmentation consists of a relay breakout board and a pair of power LEDs.

For the headlights, so-called “power LEDs” are used. In contrast to conventional LEDs, they require a forward voltage between 6 and 7 Volt. As a result, they shine very bright. Luckily, the Elegoo car comes with a 9V battery unit. Therefore, using such “power LEDs” is not a problem at all. In order to separate the Arduino’s 5V circuit from the 9V circuit which powers the headlights, a relay breakout board is used. Such relay boards can be used with any of the Arduino’s digital or analog output pins. Moreover, the relay board has an LED which indicates whether the relay is in close or open state. This is especially useful for debugging a program or wiring layout.

Undercar lights:
Depending on the viewer, the undercar lights improve the look of the Elegoo car. For this task, WS2812B LED modules are used. Multiple WS2812B can be cascaded and then individually controlled. This makes them perfect for showing individually programmed lighting routines. In total, six WS2812B LEDs are added to the Elegoo car. The lighting routines were programmed with the FastLED library.

Undercar lights of the augmented car.

Undercar lights of the augmented car.

Bluetooth remote control:

Bluetooth remote control for the Arduino Elegoo car.

Bluetooth remote control for the Arduino Elegoo car.

The Elegoo car comes already with a Bluetooth HC-06 module. In order to communicate with this Bluetooth module, a Bluetooth HC-05 module was used. The difference between the HC-05 and HC-06 is that the HC-05 can be set to be either Master or Slave. On the contrary, the HC-06 can only be set to be Slave. Therefore, one requires a HC-05 module if a Bluetooth communication should be established with the existing HC-06 module.
The heart of the remote control is an Arduino Nano, since it is very small and fits perfectly on a breadboard. The Arduino Nano handles the Bluetooth communication and takes care of all the connected knobs, switches, and joysticks. I thought a lot about how to implement steering and acceleration. After some tests, I decided to use two joysticks placed on the left and right: The left joystick controls the steering and the right joystick controls the acceleration (speed and direction). This means, only the x-axis of the left joystick and only the y-axis of the right joystick is used. All in all I’m very happy with this decision. Controlling the car works like a charm.

Final augmented Arduino car prototype:

Final Augmented Arduino Car.

Final Augmented Arduino Car.

Wiring of Bluetooth Remote Control: 

Wiring Layout of the bluetooth remote control for controlling the augmented Arduino car.

Wiring Layout of the bluetooth remote control for controlling the augmented Arduino car.

Wiring of Augmented Arduino Car:

Fritzing file of the Augmented Arduino Car.

Fritzing file of the Augmented Arduino Car.

Source code Augmented Arduino Car:


// (c) Michael Schoeffler 2017, http://www.mschoeffler.de
// Remark: This code was written for a prototype project. Therefore, the code style is somewhat "prototyp'ish" 😉

#include <IRremote.h>
#include <Servo.h> //servo library
#include <SoftwareSerial.h>
#include "FastLED.h"


// pins
const int ultrasonic_echo = 2;
const int ultrasonic_trig = A5;
const int ultrasonic_led = A1;

const int motorcontrol_IN1 = 9;
const int servo_ctrl = 3; 
const int motorcontrol_IN2 = 4;
const int motorcontrol_ENA = 5;
const int motorcontrol_ENB = 6;
const int motorcontrol_IN3 = 7;
const int motorcontrol_IN4 = 8;
const int motorcontrol_led = A0;

const int headlights_relais = A2;
const int undercarlights_data = 10;
const int irremotecontrol_receiverpin = 11;
const int bt_rx = 12;
const int bt_tx = 13;

#define undercarlights_LED_TYPE    WS2812B
#define undercarlights_COLOR_ORDER GRB
#define undercarlights_NUM_LEDS    6
#define undercarlights_BRIGHTNESS  96

CRGB undercarlights_leds[undercarlights_NUM_LEDS];
unsigned long undercarlights_specialprog_delay = 750;
unsigned long undercarlights_specialprog_lastcall = 0;
unsigned long undercarlights_specialprog_docall = false;

int motorcontrol_on = 0;
int motorcontrol_x = 511;
int motorcontrol_y = 511;

// automatic break system led
byte ultrasonic_state = 0;
unsigned long ultrasonic_time = 0;
// automatic break system measurement
unsigned long ultrasonic_distance = 4096;
volatile long ultrasonic_echo_start = 0;                         
volatile long ultrasonic_echo_end = 0;                           




// Codes for IR keypad
const unsigned long CODE_IR_KEYPAD_UP = 0xFF629D;
const unsigned long CODE_IR_KEYPAD_LEFT = 0xFF22DD;
const unsigned long CODE_IR_KEYPAD_OK = 0xFF02FD;
const unsigned long CODE_IR_KEYPAD_RIGHT = 0xFFC23D;
const unsigned long CODE_IR_KEYPAD_DOWN = 0xFFA857;
const unsigned long CODE_IR_KEYPAD_1 = 0xFF6897;
const unsigned long CODE_IR_KEYPAD_2 = 0xFF9867;
const unsigned long CODE_IR_KEYPAD_3 = 0xFFB04F;
const unsigned long CODE_IR_KEYPAD_4 = 0xFF30CF;
const unsigned long CODE_IR_KEYPAD_5 = 0xFF18E7;
const unsigned long CODE_IR_KEYPAD_6 = 0xFF7A85;
const unsigned long CODE_IR_KEYPAD_7 = 0xFF10EF;
const unsigned long CODE_IR_KEYPAD_8 = 0xFF38C7;
const unsigned long CODE_IR_KEYPAD_9 = 0xFF5AA5;
const unsigned long CODE_IR_KEYPAD_0 = 0xFF4AB5;
const unsigned long CODE_IR_KEYPAD_MUL = 0xFF42BD;
const unsigned long CODE_IR_KEYPAD_SHARP = 0xFF52AD;

Servo servo; // create servo object to control motor for ultrasonic sensor
IRrecv irrecv(irremotecontrol_receiverpin); // initializes ir remote control
SoftwareSerial BTserial(bt_rx, bt_tx); 


void movement_move(int speedLeft, int speedRight) {
  if (speedLeft > 255) { speedLeft = 255; }
  if (speedRight > 255) { speedRight = 255; }
  if (speedLeft < -255) { speedLeft = -255; }
  if (speedRight < -255) { speedRight = -255; } if (speedRight == 0) { analogWrite(motorcontrol_ENA, LOW); } else { analogWrite(motorcontrol_ENA, abs(speedRight)); } if (speedLeft == 0) { analogWrite(motorcontrol_ENB, LOW); } else { analogWrite(motorcontrol_ENB, abs(speedLeft)); } if (speedRight > 0) {
    digitalWrite(motorcontrol_IN1, LOW);
    digitalWrite(motorcontrol_IN2, HIGH);
  } else {
    digitalWrite(motorcontrol_IN1, HIGH);
    digitalWrite(motorcontrol_IN2, LOW);    
  }

  if (speedLeft > 0) {
    digitalWrite(motorcontrol_IN3, LOW);
    digitalWrite(motorcontrol_IN4, HIGH);
  } else {
    digitalWrite(motorcontrol_IN3, HIGH);
    digitalWrite(motorcontrol_IN4, LOW);
  }
}


void movement_forward(byte speed)
{ 
  analogWrite(motorcontrol_ENA, speed);
  analogWrite(motorcontrol_ENB, speed);
  digitalWrite(motorcontrol_IN1, LOW);
  digitalWrite(motorcontrol_IN2, HIGH);
  digitalWrite(motorcontrol_IN3, LOW);
  digitalWrite(motorcontrol_IN4, HIGH);
  Serial.println("movement_forward");
}
void movement_back(byte speed)
{
  analogWrite(motorcontrol_ENA, speed);
  analogWrite(motorcontrol_ENB, speed);
  digitalWrite(motorcontrol_IN1, HIGH);
  digitalWrite(motorcontrol_IN2, LOW);
  digitalWrite(motorcontrol_IN3, HIGH);
  digitalWrite(motorcontrol_IN4, LOW);
  Serial.println("movement_back");
}
void movement_left(byte speed)
{
  analogWrite(motorcontrol_ENA, speed);
  analogWrite(motorcontrol_ENB, speed);
  digitalWrite(motorcontrol_IN1, LOW);
  digitalWrite(motorcontrol_IN2, HIGH);
  digitalWrite(motorcontrol_IN3, HIGH);
  digitalWrite(motorcontrol_IN4, LOW);
  Serial.println("movement_left");
  
}
void movement_right(byte speed)
{
  analogWrite(motorcontrol_ENA, speed);
  analogWrite(motorcontrol_ENB, speed);
  digitalWrite(motorcontrol_IN1, HIGH);
  digitalWrite(motorcontrol_IN2, LOW);
  digitalWrite(motorcontrol_IN3, LOW);
  digitalWrite(motorcontrol_IN4, HIGH);
  Serial.println("movement_right");
}
void movement_stop()
{
  digitalWrite(motorcontrol_ENA, LOW);
  digitalWrite(motorcontrol_ENB, LOW);
}


void headlights_switchOff() {
  digitalWrite(headlights_relais, LOW);
}

void headlights_switchOn() {
  digitalWrite(headlights_relais, HIGH);
}

void echo_interrupt()
{
  switch (digitalRead(ultrasonic_echo))                     
  {
    case HIGH:                   
      ultrasonic_echo_end = 0;   
      ultrasonic_echo_start = micros();                        
      break;      
    case LOW:  
      if (ultrasonic_echo_end == 0) {
        ultrasonic_echo_end = micros();  
        long duration = ultrasonic_echo_end - ultrasonic_echo_start;
        long durationOneWay = duration / 2; // divided by two, since duration is a roundtrip signal
        // acoustic velocity of air at a temperature of 20°C => ~343.5 m/s
        // => 0.03435 cm/us
        ultrasonic_distance = durationOneWay * 0.03435; // distance in cm
      }
      break;
  }
}



void setup() {
  pinMode(motorcontrol_IN1, OUTPUT);
  pinMode(motorcontrol_IN2, OUTPUT);
  pinMode(motorcontrol_IN3, OUTPUT);
  pinMode(motorcontrol_IN4, OUTPUT);
  pinMode(motorcontrol_ENA, OUTPUT);
  pinMode(motorcontrol_ENB, OUTPUT);

  pinMode(irremotecontrol_receiverpin, INPUT);

  pinMode(headlights_relais, OUTPUT);
  
  pinMode(ultrasonic_trig, OUTPUT);
  pinMode(ultrasonic_echo, INPUT);
  pinMode(ultrasonic_led, OUTPUT);
  digitalWrite(ultrasonic_led, LOW);

  servo.attach(servo_ctrl);// attach servo on pin 3 to servo object  
  irrecv.enableIRIn();
  

  FastLED.addLeds<undercarlights_LED_TYPE,undercarlights_data,undercarlights_COLOR_ORDER>(undercarlights_leds, undercarlights_NUM_LEDS).setCorrection(TypicalLEDStrip); // initializes LED strip
  FastLED.setBrightness(undercarlights_BRIGHTNESS);// global brightness
  Serial.begin(9600);
  movement_stop();
  headlights_switchOff();
  showLightprogram(0);
  BTserial.begin(9600);

  attachInterrupt(digitalPinToInterrupt(ultrasonic_echo), echo_interrupt, CHANGE);  


}


void showProgramShiftMultiPixel() {
  for (int i = undercarlights_NUM_LEDS-1; i > 0; --i) { 
    undercarlights_leds[i] = undercarlights_leds[i-1];
  }
  CRGB newPixel = CHSV(random8(),255,255);
  undercarlights_leds[0] = newPixel;
  FastLED.show();
}

void showLightprogram(int undercarlights_program) {
  if (undercarlights_program == 0) {
    undercarlights_specialprog_docall = false;
    for (int i = 0; i < undercarlights_NUM_LEDS; ++i) { 
      undercarlights_leds[i] = CRGB::Black;
    }
    FastLED.show();  
    return;
  } else if (undercarlights_program == 7) {
    undercarlights_specialprog_docall = true;
    return;
  } else {
    undercarlights_specialprog_docall = false;
    CRGB crgb;
    switch (undercarlights_program) {
      case 1:
        crgb = CRGB::Maroon;
      break;
      case 2:
        crgb = CRGB::Amethyst;
      break;
      case 3:
        crgb = CRGB::Azure;
      break;
      case 4:
        crgb = CRGB::Chartreuse;
      break;
      case 5:
        crgb = CRGB::CornflowerBlue;
      break;
      case 6:
        crgb = CRGB::Fuchsia;
      break;
    }
    for (int i = 0; i < undercarlights_NUM_LEDS; ++i) { undercarlights_leds[i] = crgb; } FastLED.show(); return; } } char buf[8]; int index_buf= 0; unsigned long ultrasonic_time_last_trigger = 0; void loop() { byte default_speed = 128; unsigned long time = millis(); if (ultrasonic_time_last_trigger + 50 > time) {
    digitalWrite(ultrasonic_trig, LOW); // turn off the trigger
    delayMicroseconds(3);
    digitalWrite(ultrasonic_trig, HIGH);// prepare to send "trigger" command to module
    delayMicroseconds(10); // wait for 10us (module sends signal only, if trigger had a HIGH signal for at least 10 us)
    digitalWrite(ultrasonic_trig, LOW); // module sends signal now
    ultrasonic_time_last_trigger = time;    
  }
  // receive IR commands
  decode_results results;
  if (irrecv.decode(&results)) { 
    unsigned long irCommand;
    irCommand = results.value;
    irrecv.resume();

    switch(irCommand) {
      case CODE_IR_KEYPAD_UP:
        movement_forward(default_speed);
      break;
      case CODE_IR_KEYPAD_LEFT:
        movement_left(default_speed + 64);
      break;
      case CODE_IR_KEYPAD_RIGHT:
        movement_right(default_speed + 64);
      break;
      case CODE_IR_KEYPAD_DOWN:
        movement_back(default_speed);
      break;
      case CODE_IR_KEYPAD_OK:
        movement_stop();
      break;
      case CODE_IR_KEYPAD_2:
        servo.write(90);
      break;
      case CODE_IR_KEYPAD_3:
        servo.write(10);
      break;
      case CODE_IR_KEYPAD_1:
        servo.write(170);
      break;
      case CODE_IR_KEYPAD_4:
        headlights_switchOff();
      break;
      case CODE_IR_KEYPAD_5:
        headlights_switchOn();  
      break;
    } 
    
  }

  // receive bluetooth commands
  while (BTserial.available()) {  
    char c = BTserial.read();
    if (index_buf == 0 && c != '<') { // wait for start sign
      continue;
    } else if (index_buf == 1 && c < 'a' || c > 'f') {
      index_buf = 0;
      continue;
    }
    buf[index_buf++] = c;
    if (c == '>') { // full frame received START_BYTE, SENSOR_ID_BYTE, VALUE[length:1-4], STOP_BYTE
      char sensorId = buf[1];
      int value = atoi(buf+2);

      switch (sensorId) {
        case 'a': // servo
        {         
          int value_servo = map(value, 1023, 0, 5, 175);
          servo.write(value_servo);          
          break;
        }
        case 'b': // headlights
        {         
          if (value == 0) {
            headlights_switchOff();
          } else if (value == 1) {
            headlights_switchOn();
          }
          break;
        }
        case 'c': // undercar lights
        {
          //Serial.println(value);
          showLightprogram(value);          
          break;
        }
        case 'd': // undercar lights
        {
          motorcontrol_on = value;          
          break;
        }
        case 'e': // undercar lights
        {
          motorcontrol_x = value;          
          break;
        }
        case 'f': // undercar lights
        {
          motorcontrol_y = value;          
          break;
        }
        
      }
      /*
      Serial.print("Bluetooth [RX]: ");
      Serial.print(sensorId);
      Serial.print(" - ");
      Serial.println(value);      
      */
      index_buf = 0;
      buf[0] = ' ';      
    }
  }

  if (undercarlights_specialprog_docall) {
    unsigned long current_time = millis();
    if (current_time > (undercarlights_specialprog_lastcall + undercarlights_specialprog_delay)) {
      showProgramShiftMultiPixel();
      undercarlights_specialprog_lastcall = current_time;
    }
  }


  // ultrasonic
  unsigned long curtime = millis();
  if (ultrasonic_state == 0) {
    if (ultrasonic_distance < 15) { digitalWrite(ultrasonic_led, HIGH); ultrasonic_state = 1; ultrasonic_time = curtime; movement_stop(); } } else if (ultrasonic_state > 0 && (ultrasonic_time + 250) < curtime) { ultrasonic_time = curtime; if (ultrasonic_state > 8) {
      digitalWrite(ultrasonic_led, ultrasonic_state%2);
    }
    ++ultrasonic_state;
    if (ultrasonic_state > 44) {
      ultrasonic_state = 0;
    }
    
  }
  // motorcontrol
  byte max_speed = 255;
  if (motorcontrol_on == 1) {
    digitalWrite(motorcontrol_led, HIGH);
    int speed = map(motorcontrol_y, 1023, 0, -max_speed, max_speed);
    if (speed < 25 && speed > -25 ) { speed = 0; };
    int speed_left = speed;
    int speed_right = speed;
    if (motorcontrol_x >= 512) { // go right
      int value = motorcontrol_x - 512; // should be between 0 and 511
      float modifier = map(value, 0, 511, 511, 0) / 511.0;       
      speed_right = speed_right * modifier;
    } else { // go left
      int value = motorcontrol_x; // should be between 0 and 511
      float modifier = value / 511.0; 
      speed_left = speed_left * modifier;      
    }    
    if (ultrasonic_state == 0 || ultrasonic_state > 8) {
      movement_move(speed_left, speed_right);
    }
  } else {
    digitalWrite(motorcontrol_led, LOW);
    movement_stop();
  }
}

 

Source code Bluetooth remote control:


// (c) Michael Schoeffler 2017, http://www.mschoeffler.de
// Remark: This code was written for a prototype project. Therefore, the code style is somewhat "prototyp'ish" 😉

#include <SoftwareSerial.h>

const int control_servo = A0;
const int control_headlights = 2;
const int control_undercarlights_a = 3; // DIP switch #a
const int control_undercarlights_b = 4; // DIP switch #b
const int control_undercarlights_c = 5; // DIP switch #c

const int bt_rx = 12;
const int bt_tx = 13;

const int motorcontrol_x = A1;
const int motorcontrol_y = A2;
const int motorcontrol_on = 8;


SoftwareSerial BTserial(bt_rx, bt_tx); // RX | TX
 
char c = ' ';
 
void setup() 
{
    pinMode(control_servo, INPUT);
    pinMode(control_headlights, INPUT_PULLUP);
    pinMode(control_undercarlights_a, INPUT_PULLUP);
    pinMode(control_undercarlights_b, INPUT_PULLUP);
    pinMode(control_undercarlights_c, INPUT_PULLUP);
    pinMode(motorcontrol_on, INPUT_PULLUP);
    pinMode(motorcontrol_x, INPUT);
    pinMode(motorcontrol_y, INPUT);
    
    Serial.begin(9600);
    BTserial.begin(9600);  
    Serial.println("Remote control is ready");
}

void writeFrame(char controlId, int value) {
  char buf[5];
  BTserial.write("<"); BTserial.write(controlId); String str = String(value, DEC); str.toCharArray(buf, 5); BTserial.write(buf); BTserial.write(">");  
  
}

int control_servo_last_value = -32.768;
int control_servo_last_epsilon = 16;
int control_headlights_last_value = -32.768;
int control_undercarlights_last_value = -32.768;

int control_motorcontrol_x_last_value = -32.768;
int control_motorcontrol_y_last_value = -32.768;
int control_motorcontrol_on_last_value = -32.768;

void loop()
{

  int control_servo_value = analogRead(control_servo);
  if (abs(control_servo_value - control_servo_last_value) > control_servo_last_epsilon) {
    writeFrame('a', control_servo_value);
  }
  control_servo_last_value = control_servo_value;


  int control_headlights_value = !digitalRead(control_headlights);
  if (control_headlights_value != control_headlights_last_value) {
    writeFrame('b', control_headlights_value);
  }
  control_headlights_last_value = control_headlights_value;
  
  int control_undercarlights_value = !digitalRead(control_undercarlights_a) <<2 | !digitalRead(control_undercarlights_b) <<1 | !digitalRead(control_undercarlights_c);
  if (control_undercarlights_value != control_undercarlights_last_value) {
    writeFrame('c', control_undercarlights_value);
  }
  control_undercarlights_last_value = control_undercarlights_value;


  int control_motorcontrol_on_value = !digitalRead(motorcontrol_on);
  if (control_motorcontrol_on_value != control_motorcontrol_on_last_value) {
    writeFrame('d', control_motorcontrol_on_value);
  }
  control_motorcontrol_on_last_value = control_motorcontrol_on_value;


  int control_motorcontrol_x_value = analogRead(motorcontrol_x);
  if (control_motorcontrol_x_value != control_motorcontrol_x_last_value) {
    writeFrame('e', control_motorcontrol_x_value);
  }
  control_motorcontrol_x_last_value = control_motorcontrol_x_value;

  int control_motorcontrol_y_value = analogRead(motorcontrol_y);
  if (control_motorcontrol_y_value != control_motorcontrol_y_last_value) {
    writeFrame('f', control_motorcontrol_y_value);
  }
  control_motorcontrol_y_last_value = control_motorcontrol_y_value;
  
  delay(20); 
}

Tutorial: How to use the GY-521 module (MPU-6050 breakout board) with the Arduino Uno

GY-521 breakout board with an MPU-6050 MEMS.

GY-521 breakout board with an MPU-6050 MEMS.

The GY-521 module is a breakout board for the MPU-6050 MEMS (Microelectromechanical systems) that features a 3-axis gyroscope, a 3-axis accelerometer, a digital motion processor (DMP), and a temperature sensor. The digital motion processor can be used to process complex algorithms directly on the board. Usually, the DMP processes algorithms that turn the raw values from the sensors into stable position data. This tutorial gives only a brief introduction to the GY-521/MPU-6050. In particular, it is shown how to retrieve the raw sensor values. The sensor values are retrieved by using the I2C serial data bus, which requires only two wires (SCL and SDA). If you plan to use the full range of features or require reliable and stable position data, then I recommend to have also a look at ready-to-use libraries. Please follow this link to find an excellent library with many examples: https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050.

List of materials:
– Arduino Uno [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– Breadboard [Search on Aliexpress | Amazon]
– GY-521 module [Search on Aliexpress | Amazon]

Wiring scheme:
The GY-521 breakout has eight pins:

  • VCC (The breakout board has a voltage regulator. Therefore, you can connect the board to 3.3V and 5V sources.)
  • GND
  • SCL (Serial Clock Line of the I2C protocol.)
  • SDA (Serial Data Line of the I2C protocol.)
  • XDA (Auxiliary data => I2C master serial data for connecting the module to external sensors.)
  • XCL (Auxiliary clock => I2C master serial clock for connecting the module to external sensors.)
  • AD0 (If this pin is LOW, the I2C address of the board will be 0x68. Otherwise, if the pin is HIGH, the address will be 0x69.)
  • INT (Interrupt digital output)

Wiring layout:

Fritzing file that shows how to wire the GY-521 breakout board to an Arduino Uno.

Fritzing file that shows how to wire the GY-521 breakout board to an Arduino Uno.

In this tutorial we will make use only of the first four pins: VCC, GND, SDA, and SCL. First, we connect the module’s VCC to the Arduino’s 5V pin. Then, the module’s GND is connected to one of the Arduino’s GND pins.
Next, we have to set up the I2C connection between the module and the Arduino. Most Arduino Uno variants have an SCL and SDA pin. If you have such an Arduino Uno, just connect SCL to SCL and SDA to SDA.
If you can’t find an SCL and SDA pin on your Arduino, you have to use other pins. Unfortunately, you cannot use just any pin. For each type of Arduino, SCL and SDA are tied to different pins:

  • Arduino Uno, Arduino Ethernet, Arduino Nano: A4 (SDA), A5 (SCL)
  • Arduino Mega2560: 20 (SDA), 21 (SCL)
  • Arduino Leonardo: 2 (SDA), 3 (SCL)
  • Arduino Due: 20 (SDA), 21 (SCL)

So, if you have an Arduino Uno without SCL and SDL pins, then connect the Arduino’s A4 pin to the module’s SDA pin. Next, connect the Arduino’s A5 pin to the module’s SCL pin.

Example source code:

We make use of the Arduino platform’s in-built library (Wire) to establish an I2C connection between the Arduino Uno and the GY-521 sensor. At the beginning of our source code, the Wire library’s header file is included. Next, we define and declare some variables.
Then, a convert-function is defined. The convert-function makes sure that all sensor values have the same width when they are printed out to the serial monitor later.
In the setup function, a serial connection is established. Moreover, we start our first I2C transmission to the GY-521 board to wake it up from sleep mode.
In the loop function, seven sensor values (3x accelerometer, 1x temperature, and 3x gyro) are requested from the GY-521 module. The MPU-6050 has many registers which can be read. Fourteen of these registers contain the sensor values that we need. As a first step, we tell the GY-521 module where we are going to start reading (“Wire.write(0x3B);”). Then, we request to read 14 registers (“Wire.requestFrom(MPU_ADDR, 7*2, true);”). If you are wondering, why 14 registers are read instead of 7 registers, the reason is quite simple: Each sensor value has a size of 2 byte. As each register has a size of one byte, a single sensor value must be retrieved by accessing two registers. The first register contains the so-called “high byte” and the second register contains the “low byte”. Next, all values are retrieved and printed out to the serial connection. At the end of the loop function, a delay of one second is added in order to avoid flooding the serial monitor with messages.

// (c) Michael Schoeffler 2017, http://www.mschoeffler.de

#include "Wire.h" // This library allows you to communicate with I2C devices.

const int MPU_ADDR = 0x68; // I2C address of the MPU-6050. If AD0 pin is set to HIGH, the I2C address will be 0x69.

int16_t accelerometer_x, accelerometer_y, accelerometer_z; // variables for accelerometer raw data
int16_t gyro_x, gyro_y, gyro_z; // variables for gyro raw data
int16_t temperature; // variables for temperature data

char tmp_str[7]; // temporary variable used in convert function

char* convert_int16_to_str(int16_t i) { // converts int16 to string. Moreover, resulting strings will have the same length in the debug monitor.
  sprintf(tmp_str, "%6d", i);
  return tmp_str;
}

void setup() {
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(MPU_ADDR); // Begins a transmission to the I2C slave (GY-521 board)
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
}
void loop() {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) [MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2, p.40]
  Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. As a result, the connection is kept active.
  Wire.requestFrom(MPU_ADDR, 7*2, true); // request a total of 7*2=14 registers

  // "Wire.read()&lt;&lt;8 | Wire.read();" means two registers are read and stored in the same variable
  accelerometer_x = Wire.read()<<8 | Wire.read(); // reading registers: 0x3B (ACCEL_XOUT_H) and 0x3C (ACCEL_XOUT_L)
  accelerometer_y = Wire.read()<<8 | Wire.read(); // reading registers: 0x3D (ACCEL_YOUT_H) and 0x3E (ACCEL_YOUT_L)
  accelerometer_z = Wire.read()<<8 | Wire.read(); // reading registers: 0x3F (ACCEL_ZOUT_H) and 0x40 (ACCEL_ZOUT_L)
  temperature = Wire.read()<<8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L)
  gyro_x = Wire.read()<<8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
  gyro_y = Wire.read()<<8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
  gyro_z = Wire.read()<<8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)

  // print out data
  Serial.print("aX = "); Serial.print(convert_int16_to_str(accelerometer_x));
  Serial.print(" | aY = "); Serial.print(convert_int16_to_str(accelerometer_y));
  Serial.print(" | aZ = "); Serial.print(convert_int16_to_str(accelerometer_z));
  // the following equation was taken from the documentation [MPU-6000/MPU-6050 Register Map and Description, p.30]
  Serial.print(" | tmp = "); Serial.print(temperature/340.00+36.53);
  Serial.print(" | gX = "); Serial.print(convert_int16_to_str(gyro_x));
  Serial.print(" | gY = "); Serial.print(convert_int16_to_str(gyro_y));
  Serial.print(" | gZ = "); Serial.print(convert_int16_to_str(gyro_z));
  Serial.println();

  // delay
  delay(1000);
}

If the code is compiled and transferred to the Arduino Uno, you should see the sensor values in the serial monitor of the Arduino IDE. Moreover, when the GY-521 board is rotated or moved, the sensor values should change according to the movement.
As mentioned before, this is basically a “hello world program” for the GY-521. if you plan to use the board more seriously, I highly recommend to dig deeper into the possibilities of the MPU-6050 MEMS.

If the GY-521 is moved (left hand side), the sensor values should change accordingly (right hand side).

If the GY-521 is moved (left hand side), the sensor values should change accordingly (right hand side).

Video Tutorial: