Example Application: GY-521 module (MPU-6050 breakout board) and Arduino Uno

Example application that shows how to use the raw values of the GY-521 to blink LEDs based on the position of a breadboard.

Example application that shows how to use the raw values of the GY-521 to blink LEDs based on the position of a breadboard.

In a previous blog post, I wrote a short tutorial about the GY-521 module (MPU-6050 breakout board). In addition to the blog post, I made a video tutorial that had basically the same content. At the end of the video tutorial, I presented a small example application that was not described in the blog post. Due to a request, I decided to share the setup and code of the small example which is presented in the following:

The example is very simple. Four LEDs are put in holes at the four corners of a breadboard. In the center of the breadboard is a small breadboard. The GY-521 is used to detect which side of the small breadboard is moved to the top. In order to indicate the “highest side”, two corresponding LEDs are switched on. This example is shown at around 7:15 in the video tutorial.

Wiring:

Fritzing file of the GY-521 example application.

Fritzing file of the GY-521 example application.

Source code:

// (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


#define LED_LB 2 // LED left bottom
#define LED_RB 3 // LED right bottom
#define LED_RT 4 // LED right top
#define LED_LT 5 // LED left top

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);
  pinMode(LED_LB, OUTPUT);
  pinMode(LED_RB, OUTPUT);
  pinMode(LED_RT, OUTPUT);
  pinMode(LED_LT, OUTPUT);
  digitalWrite(LED_LB, LOW);
  digitalWrite(LED_RB, LOW);
  digitalWrite(LED_RT, LOW);
  digitalWrite(LED_LT, LOW);  
  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()<<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();

  if (accelerometer_x < 1000 && accelerometer_y < -8000) {
    digitalWrite(LED_LB, HIGH);
    digitalWrite(LED_RB, HIGH);
    digitalWrite(LED_RT, LOW);
    digitalWrite(LED_LT, LOW);      
  } else if (accelerometer_x < 1000 && accelerometer_y > 8000) {
    digitalWrite(LED_LB, LOW);
    digitalWrite(LED_RB, LOW);
    digitalWrite(LED_RT, HIGH);
    digitalWrite(LED_LT, HIGH);      
  } else if (accelerometer_x > 8000 && accelerometer_y < 1000) {
    digitalWrite(LED_LB, LOW);
    digitalWrite(LED_RB, HIGH);
    digitalWrite(LED_RT, HIGH);
    digitalWrite(LED_LT, LOW);      
  } else if (accelerometer_x < -8000 && accelerometer_y <span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span><span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span>< 1000) {
    digitalWrite(LED_LB, HIGH);
    digitalWrite(LED_RB, LOW);
    digitalWrite(LED_RT, LOW);
    digitalWrite(LED_LT, HIGH);      
  } else {
    digitalWrite(LED_LB, LOW);
    digitalWrite(LED_RB, LOW);
    digitalWrite(LED_RT, LOW);
    digitalWrite(LED_LT, LOW);          
  }

  // delay
  delay(10);
}

 

Arduino Tutorial: HX711 Load Cell Amplifier (Weight Sensor Module) + LCM1602 IIC V1 LCD

In this tutorial, it is shown how to utilize an Arduino Uno, a HX711 breakout board, and a load cell in order to measure weight. Moreover, an LCD module of type LCM1602 IIC V1 is used to display the measured weight.

List of materials:
– Arduino Uno [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– HX711 Load Cell Amplifier [Search on Aliexpress | Amazon]
– Load Cell [Search on Aliexpress | Amazon]
– LCM1602 IIC V1 (LCD) [Search on Aliexpress | Amazon]
– Small squared timber and screws

A load cell (left hand side) and an HX711 breakout board (right hand side).

A load cell (left hand side) and an HX711 breakout board (right hand side).

The HX711 is a 24-bit analog-to-digital converter which fits perfectly to weight scale applications. Fortunately, there exist many breakout boards for the HX711. Therefore, it is very easy to use it in combination with a so-called load cell. Load cells are transducers that convert pressure/force into an electrical signal. As the electrical signal has typically only a few millivolts, it has to be amplified. And that’s where the HX711 breakout board comes in:  as it amplifies the weak signal to a few volts so that we can read the signal with the help of an Arduino Uno.

Preparations for Wiring:
In order to easily connect the load cell to the pins of the HX711 module, you can add DuPont connectors to the wires. This step is totally optional. You can use whatever you want to connect the wires to module’s pins. If you use DuPont connectors, keep in mind that such load cells often have thin single-strand wires. I used the help of some solder to attach the thin wires more tightly to their connectors.

Preparation of crimping tools, raw connectors, and shells in order to add DuPont connectors to the wires of the load cell. As a result, the load cell can be easily connected to the HX711 breakout board.

Preparation of crimping tools, raw connectors, and shells in order to add DuPont connectors to the wires of the load cell. As a result, the load cell can be easily connected to the HX711 breakout board.

Preparation of crimping tools, raw connectors, and shells in order to add DuPont connectors to the wires of the load cell. As a result, the load cell can be easily connected to the HX711 breakout board.

Load cell with DuPont connectors.

You will retrieve consistent pressure values from the load cell, if you mount the load cell on something that is solid and heavy. I used a part of an old squared timber for this task:

Load cell mounted on squared timber.

Load cell mounted on squared timber.

Wiring:

Fritzing file that shows how to connect the load cell to the HX711 module. Moreover, it is shown how to connect the HX711 module and the LCM1602 IIC v1 (LCD) module to the Arduino.

Fritzing file that shows how to connect the load cell to the HX711 module. Moreover, it is shown how to connect the HX711 module and the LCM1602 IIC v1 (LCD) module to the Arduino.

First, the load cell is connected to the HX711 module. The load cell has four wires, which must be connected to the first four pins of the HX711 module: Red wire to E+, black wire to E-, white wire to A-, and green wire to A+. The remaining pins, B- and B+, can be used if a second load cell has to be connected to the HX711 module.
Next, the HX711 module is connected to the Arduino Uno. The module’s GND pin must be connected to the Arduino’s GND pin. DT and SCK must be connected to digital pins of the Arduino. In this tuorial, DT is connected to digital pin #4 and SCK is connected to digital pin #5. The remaining pin VCC must be connected to the 5V pin of the Arduino. As the LCM1602 module requires also a connection to the 5V pin, a breadboard is used in-between to split the Arduino’s 5V signal.
As a last step, the LCM1602 module’s SDA and SCL pins must be connected to the corresponding SDA and SCL pins of the Arduino Uno. Moreover, the GND pin must be connected to one of the Arduino’s GND pins and the VCC pin has to be connected to the 5V signal of the breadboard.

Example source code:

// (c) Michael Schoeffler 2017, http://www.mschoeffler.de
#include <HX711_ADC.h> // https://github.com/olkal/HX711_ADC
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // LiquidCrystal_I2C library

HX711_ADC LoadCell(4, 5); // parameters: dt pin, sck pin<span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // 0x27 is the i2c address of the LCM1602 IIC v1 module (might differ)


void setup() {
  LoadCell.begin();  // start connection to HX711
  LoadCell.start(2000); // load cells gets 2000ms of time to stabilize
  LoadCell.setCalFactor(999.0); // calibration factor for load cell => strongly dependent on your individual setup
  lcd.begin(16, 2); // begins connection to the LCD module
  lcd.backlight(); // turns on the backlight
}

void loop() {
  LoadCell.update(); // retrieves data from the load cell
  float i = LoadCell.getData(); // get output value
  lcd.setCursor(0, 0); // set cursor to first row
  lcd.print("Weight[g]:"); // print out to LCD
  lcd.setCursor(0, 1); // set cursor to secon row
  lcd.print(i); // print out the retrieved value to the second row
}

Calibration

The last step is to calibrate the weighing scale. This steps becomes very easy if you have some calibration weights. Unfortunately, I do not have such weights. Therefore, I used an alternative approach to calibrate my weighing scale. First, I grabbed another weighing scale and put something on it (dc motor):

Then I put the dc motor on my weighing scale. Next, I adapted the parameter of the setCalFactor method (see setup function) until the correct weight was shown on my weighing scale. Keep in mind, each load cell and setup needs a different calibration factor. Therefore, it makes no sense to tell you mine.

In order to check whether my calibration factor is working, I did the same thing again with something else (screwdriver):

Overall, I’m satisfied with the accuracy:

Video Tutorial:

Arduino Tutorial: IR Distance / Line Tracing / Line Tracking Sensor (MH Sensor Series/KY-033/TCRT5000)

IR distance sensor (MH Sensor Series, KY-033, TCRT5000).

IR distance sensor (MH Sensor Series, KY-033, TCRT5000).

In this tutorial, it is shown how to use an IR distance sensor with an Arduino Uno. The todays sensor comes in many names: MH Sensor Series, KY-033 (variant with 3 pins), TCRT5000, etc. Moreover, it is often advertised as IR distance sensor, line tracing sensor or line tracking sensor.
In addition to the IR distance sensor, this tutorial makes use of an LCD module called “LCM1602 IIC V1” which is utilized to show sensor values. The main advantage of the LCM1602 IIC V1 is that it is very easy-to-use. For example, it can be controlled by setting up an I2C connection.

List of materials:
– Arduino Uno [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– Mini breadboard [Search on Aliexpress | Amazon]
– MH Sensor Series [Search on Aliexpress | Amazon]
– LCM1602 IIC V1 (LCD) [Search on Aliexpress | Amazon]

Remark: Some variants of the module type, such as the KY-033, have only three pins. Typically, the A0 pin is missing. Moreover, the D0 pin is often labeled as S. If you own such a variant, this tutorial is still of use to you. Just ignore the part related to the A0 pin.

Pin layout:

The scheme shows how to wire the MH Sensor Series and the LCM1602 IIC V1 to an Arduino Uno.

The IR sensor and the LCM1602 module have only four pins. The GND pins of both modules must be connected to the Arduino’s GND pins. The same applies to the VCC pins which must be connected to the Arduino’s 5V pin. As the Arduino Uno has only a single 5V pin, a mini breadboard is used to “split” the 5V pin. Next, the A0 and D0 pin of the IR sensor must be connected to the Arduino. The A0 pin is the raw analog value (0-1023) of the measured distance between the sensor and an obstacle. In this tutorial, A0 is connected to the Arduino’s A0 pin. The D0 pin is a digital pin that goes to HIGH state if the analog value is greater than or equal to a specific threshold. The threshold can be adjusted by the blue trimpot of the IR distance sensor. Here, D0 is connected to the Arduino’s pin 8.
As a last step, the LCM1602 module’s SDA and SCL pins must be connected to the corresponding SDA and SCL pins of the Arduino Uno.

Example source code

// (c) Michael Schoeffler 2017, http://www.mschoeffler.de
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // initializes the LCM1602 IIC V1 (LCD module)
// 0x27 is the I2C address. This address might be different.

const int IN_A0 = A0; // analog input
const int IN_D0 = 8; // digital input

void setup() {
  pinMode (IN_A0, INPUT);
  pinMode (IN_D0, INPUT);
  lcd.begin(16, 2); // begins connection to the LCD module
  lcd.backlight(); // turns on the backlight
}

int value_A0;
bool value_D0;

void loop() {

  value_A0 = analogRead(IN_A0); // reads the analog input from the IR distance sensor
  value_D0 = digitalRead(IN_D0);// reads the digital input from the IR distance sensor

  lcd.setCursor(0, 0); // sets the cursor of the LCD module to the first line
  lcd.print("A0:");
  lcd.setCursor(3, 0); // sets the cursor of the LCD module to the fourth character
  lcd.print(value_A0); // prints analog value on the LCD module

  lcd.setCursor(0, 1); // sets the cursor of the LCD module to the first line
  lcd.print("D0:");
  lcd.setCursor(3, 1); // sets the cursor of the LCD module to the fourth character
  lcd.print(value_D0); // prints digital value on the LCD module

  delay(1000);
}

If the code has been compiled and transmitted to the Arduino Uno, the LCD module should show the distance between the IR distance sensor and an obstacle. Keep in mind that the distance is indicated by a analog value between 0 and 1023. Unfortunately, it is very challenging to convert the analog value to a metric unit of length, such as meter or centimeter. The reason is that the measured analog value is strongly influenced by the obstacle’s material. For example, black surface does reflect far less light than white surface. As a consequence, the measured analog value will differ. Interestingly, this characteristic can be used in order to use the IR distance sensor as a “black or white” detector. This application can often be found in “car assembly kits” where multiple IR sensors are mounted on the undercar. As a result, the car is capable of following a black line that is drawn on white ground.

The following pictures show distance measurements with black and white material. Although the distance is about the same, the measured analog value (A0) differs strongly:

The IR distance sensor measures black material. The analog sensor value is much higher than when measuring white material (A0 = 949).

The IR distance sensor measures white material. The analog sensor value is much lower than when measuring black material (A0 = 525).

Video tutorial

Seven Ways To Supply Power to a Breadboard

When doing prototype work with breadboards for a project, the question comes up “how to power the breadboard?” at the very start. Therefore, a power supply must be chosen that fulfills the requirements of the specific project. There exist a wide range of different alternatives to provide power to a breadboard. Each alternative has different characteristics. In this article, seven alternatives are presented ranging from low-cost USB-based power supplies to AC/DC power supply units.

Alternative 1 (cheap): Microcontroller [Search on Aliexpress | Amazon]

It seems natural to use a microcontroller to power the other components on the breadboard, if you utilize a microcontroller anyway. The advantage is that you require only a single USB cable (or DC plug) to provide power to the microcontroller and all the other components on the breadboard. However, this alternative has at least two drawbacks: First, a typical microcontroller features only very few voltage options. For example, an Arduino Uno has pins for 3.3V and 5V. Second, the current is often limited. For example, when utilizing an Arduino Uno, you can safely draw about 400mA from the 5V pin. In case your microcontroller does not have any output pins to provide power, this alternative is off the table.

 

Alternative 2 (cheap): Breadboard power supply [Search on Aliexpress | Amazon]

Breadboard power supplies are specifically designed for applications involving a breadboard. As a result, they fit perfectly on a breadboard in order to supply power to the “plus and minus lines”.
Moreover, most breadboard supplies have multiple convenience features: For example, the possibility to plug in different power connectors, such as USB or DC plugs. Furthermore, a switch to ‘switch on’ or ‘switch off’ the power as well as an LED to show whether the power supply is enabled or disabled. In addition, you often can choose whether to supply 3.3V or 5V to the “plus and minus” lines.
In comparison to using microcontrollers for powering,  breadboard power supplies often allow you to draw more current than microcontrollers. Unfortunately, most of them are also limited to outpout only 3.3V or 5V. If you require higher voltages, this alternative might not be an option for you.

 

Alternative 3 (cheap): Batteries [Search on Aliexpress | Amazon]
+ battery holder [Search on Aliexpress | Amazon]
+ PCB terminal [Search on Aliexpress | Amazon]

If you already own a pack of batteries, you can also use them for powering. Especially if you own multiple batteries, you can chain them in order to obtain, e.g., a voltage supply having more than 5V. This alternative becomes even better, if you also own a battery holder and a PCB terminal. Then, it becomes very easy to connect the batteries to a breadboard.
For example, if you require more than 5V, you can connect two 3.7V Li-Ion batteries in series to obtain about 7.4V. A major downside of this alternative is that the provided voltage of batteries is dependent on their charging level. As a consequence, this alternative is not suited if a constant voltage is required to power the components. Nonetheless, if you plan to use batteries as power supply for the final version of your project, it might makes sense to use this type of power supply also in the prototype phase.

 

Alternative 4 (cheap): USB DC-DC Step-up Cables [Search on Aliexpress | Amazon]

Compared to connecting batteries in series, utilizing USB DC-DC Step-Up cables is a more convenient alternative to obtain higher voltages. The principle is very simple, these cables have a USB connector on the one end, a DC connector on the other end, and in-between a Step-up module. The Step-up modules transform the 5V coming from the USB connector to a higher voltage, such as 9V or 12V. With the help of a female power jack connector, it is quite simple to bring 9V or 12V to the breadboard. Due to their compact construction, the current you can draw from these Step-up cables is often limited to 750mA or 1000mA.

 

Alternative 5 (reasonably priced): DC Transformer [Search on Aliexpress | Amazon]

In order to draw more power from a 9V or a 12V power supply, a DC transformer can be used. DC transformers are quite common for providing 12V LEDs with power. Alternatively, they can also be used to supply power to breadboards. In comparison to USB DC-DC Step-up cables, they are much more bulky. However, mainly due to their size, they can provide much more power to a breadboard. There exist many different variants, such as 10W DC transformers or 100W DC transformers. Typically when working with breadboards, very high power values are not required. Nonetheless, if, for example, an external power-hungry device has also to be provided by the same power supply as the breadboard, a dc transformer might be a good choice.

 

Alternative 6 (cheap): DC-DC Adjustable Boost Module [Search on Aliexpress | Amazon]

The previous alternatives share the same disadvantage that the provided voltage is typically not adjustable. So-called DC-DC adjustable boost modules overcome this drawback by featuring a potentiometer that can be adjusted by a screwdriver. In particular, the potentiometer controls the provided output voltage. Moreover, many of these modules feature also a USB input. The disadvantage of such module is that they are often limited to 1 or 2 Amperes that can be drawn. Therefore, there range of applications is limited.

 

Alternative 7 (reasonably priced to expensive): DC power supply unit [Search on Aliexpress | Amazon]

The highest degree of flexibility is offered by DC power supply units. In comparison to the other alternatives, this is the most bulkiest and also most expensive alternative. Though, size and prices can differ a lot between different types of DC supply units. The main advantage of DC power supplies is that the provided voltage can be adjusted very precisely. Moreover, they often provide more power than DC-DC Adjustable Boost Modules. If a DC supply unit is chosen as power supply for a breadboard, you should not forget to look out for a plug that connects the DC supply to the breadboard (e.g. “Banana-to-DuPont-connector”).

 

Related video:

Tutorial: How to crimp DuPont/Mini-PV connectors [Engineer PA-09 connector pliers]

This tutorial is about crimping Mini-PV connectors with a generic crimp tool (Engineer PA-09). Mini-PV connectors are also known as DuPont connectors, especially in the context of “breadboard prototyping” where the corresponding wires are often referred to as “DuPont jumper wires”.

You might ask yourself why do people use two different names for the same type of connectors? To put it simple: Mini-PV/DuPont connectors were originally made by a company called Berg Electronics which was part of DuPont Connector Systems. Later, Dupont Connector Systems sold Berg Electronics to Hicks, Muse, Tate, and Furst (private equity company). In 1998, Berg Electronics Corporation was acquired by FCI (Framatome Connectors International, Inc.). Then, FCI was aquired by Amphenol Corporation in 2016. Today, Amphenol Corporation lists the connectors under the name “Mini-PV Basics”.

Mini-PV can be considered a as proprietary connector type. Therefore, there exist also an official tool to crimp Mini-PV connectors. Originally, only female Mini-PV connectors were available. The original crimp tool for these female Mini-PVs is called HT-0095 (HT-95). This tool was sold since the beginning and therefore, you can also find used ones also with a DuPont branding. Today, there exists also an official crimp tool for male connectors: the HT-102. Unfortunately, both tools are very expensive and cost far more than 1000$. Depending on the country you are in, you can find used tools much cheaper on eBay. In this tutorial, I make use of the Japanese PA-09 “generic” connector pliers. Compared to the original HT-95 and HT-102, the PA-09 can be considered as cheap (30-50$). Nonetheless, the crimps made with the PA-09 turn out very suitable for most applications. One more remark: Engineer, the manufacturer of the PA-09 pliers, does not advertise the PA-09 to be compatible with Mini-PV/DuPont connectors. Still, you can find many video, articles and pictures, in which people are using them to crimp Mini-PV and seem also satisfied with the quality.
Of course, the PA-09 is not required for this tutorial, any generic crimp tool that provides sufficient quality can be used. Moreover, it is only shown how to crimp female connectors. Fortunately, you can apply the same procedure in order to crimp male connectors.

List of Materials:
– Wire (e.g., 28AWG/0.5mm²) [Search on Aliexpress | Amazon]
– DuPont connector [Search on Aliexpress | Amazon]
– DuPont connector shell [Search on Aliexpress | Amazon]

Materials required for this tutorial: some wire, DuPont/Mini-PV connectors, and connector shells.

Materials required for this tutorial: some wire, DuPont/Mini-PV connectors, and connector shells.

List of tools that I use in this tutorial:
– Engineer NS-04 Micro nippers [Search on Aliexpress | Amazon]
– Engineer PA-14 Wire Stripper [Search on Aliexpress | Amazon]
– Engineer PA-09 connector pliers [Search on Aliexpress | Amazon]

Tools that I use in this tutorial: NS-04 Micro Nippers, PA-14 Wire Stripper, and PA-09 Connector Pliers (any other set of suitable tools can be used).

Tools that I use in this tutorial: NS-04 Micro Nippers, PA-14 Wire Stripper, and PA-09 Connector Pliers (any other set of suitable tools can be used).

1) The first step is to cut off some wire. The length is totally up to you.

2) Next, the wire must be stripped.

If you are not sure how many insulation you should strip off, then have a look at the next picture. The picture shows were you have to make the crimps. If you use also the PA-09, then you have to make two crimps: the “insulation crimp” and the “wire crimp”. As you can see on the picture, only a small part of the wire has to be stripped off.

3) Then, the first crimp has to made. It is totally up to you, whether to start with the insulation or the wire crimp. I personally prefer to start with the insulation crimp:

4) If the first crimp was successful, the other crimp has to be made. As I started with the insulation crimp, I have to do the wire crimp next.

If you did both crimps, your connector should now look like this:

As you can see, the wire crimp looks fine but the insulation crimp looks a bit sloppy. In particular, the connector has been “drilled” through the insulation. If an original crimp tool was used, the insulation would be coated by the lower part of connector. The “damaged” insulation cannot be avoided if such generic crimp tools are used. The crimp mechanism of the original tools is more complex, e.g., two coils are used to perfectly coat the insulation by the lower part of the connector. Nonetheless, the achieved crimping quality should be sufficient for many applications.

5) The final step is to add a shell to the connector:

6) The same steps have to be applied to the other end of the wire. If everything is executed correctly, the final result should look like this:

Video Tutorial:

 

Another related video about red and black jumper wires:

[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:

Tutorial: How to control a servo motor (SG90) with the Arduino Uno

Typically, servo motors are a combination of four things: a conventional DC motor, a set of gearings, a potentiometer, and a control circuit. Among these four things, the potentiometer acts as a position sensor. As a result, servo motors can be controlled very precisely. In particular, a command can be sent to the servo so that the servo’s shaft rotates to a specific position. However, the disadvantage of these servos is that the rotation range is limited (e.g. between 0 and 180 degrees). Nonetheless, servo motors are very useful if a projects requires a motor with a precise control and awareness of its current position.

The SG90 micro servo motor. In the background is a rotary angle sensor module and a potentiometer. Both can be used to control the servo motor.

The SG90 micro servo motor. In the background is a rotary angle sensor module and a potentiometer. Both can be used to control the servo motor.

The SG90 is such a servo motor that can rotate approximately 180°. Moreover, is is very small and lightweight (Weight: 9g; Dimension: 22.2 x 11.8 x 31 mm). In this tutorial, it is shown how to control the SG90 servo motor. In order to control the motor, a so-called rotary angle sensor module is used. This module is nothing more than a conventional potentiometer combined with a knob. Therefore, it can be simply replaced by almost any potentiometer, since it is used here only for convenience reasons.

List of materials:
– Arduino Uno [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– Mini breadboard [Search on Aliexpress | Amazon]
– Rotary angle sensor [Search on Aliexpress | Amazon]
– Potentiometer (in alternative to the rotary angle sensor) [Search on Aliexpress | Amazon]

Pin layout:

A scheme that shows how to wire the micro servo motor SG90 to an Arduino Uno. In order to control the SG90, a rotary angle sensor is used.

A scheme that shows how to wire the micro servo motor SG90 to an Arduino Uno. In order to control the SG90, a rotary angle sensor is used.

First, we connect the SG90 servo motor to the Arduino Uno. Both, the servo and the rotary angle sensor need a voltage supply. Since the Arduino Uno has only one 5V pin, we use a breadboard to split the 5V signal. The Arduino’s 5V pin is connected to a breadboard. Then, the servo’s red wire is connected to the breadboard (same column as previous pin). Next, the brown wire of the SG90 must be connected to one of the Arduino’s GND pins. In order to control the SG90 servo, PWM signals (Pulse Width Modulation) must be sent through the yellow wire. In this tutorial, digital pin 9 of the Arduino is used for this task and, therefore, wired to the SG90’s yellow pin.
Following, we have to wire the rotary angle sensor to the Arduino. The sensor’s left pin is the ground which must be connected to one of the Arduino’s GND pins. The pin in the middle is the VCC pin, which must be connected to breadboard (same column as the other two pins). Finally, the sensor’s SIG pin must be connected to one of the Arduino’s analog input pins. Here, I make use of analog pin A0. If a potentiometer is used instead of the rotary angle sensor: Typically, the outer pins of the potentiometer must be connected to the power supply (GND and 5V pin of the Arduino). The pin in the middle is the signal pin which corresponds to the sensor’s SIG pin and must be connected to an analog input pin (e.g. A0).

Example source code:
Luckily, the Arduino IDE has already a built-in servo library. In order to use this library, we have to include its header file. Next, we define two pins: digital pin 9 for the servo motor and analog pin A0 for the rotary angle sensor (or potentiometer). Then, a servo object is created that will be used to control the servo motor. Subsequently, two variables are defined. The first one is used to store the values retrieved from the sensor/potentiometer. The second variable is the rotation value that will be sent to the servo motor.
In the setup function, we attach digital pin number 9 to the servo object. The servo object is now fully initialized and ready to control the servo.
In the loop function, we read the analog value of the rotary angle sensor’s knob (or value of the potentiometer). As the Arduino’s analog-to-digital converter will map the voltage to values between 0 and 1023, we have to remap these values to an rotary angle value that is supported by our servo motor. Since the SG90 supports approximately 180°, we map the value two values between 5° and 175°. According to my experience, the SG90 does not sound very “healthy”, if the full range of 180° is used. Therefore, we subtracted 10° in order to avoid damaging our servo motor. After calling the map-function, the resulting value is used to let the servo turn its shaft. At the end of the loop function, we add a delay of 20ms to give the servo some time to turn its shaft.


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

#include "Servo.h"

#define PIN_SERVO 9
#define PIN_KNOB A0

Servo servo; // creates servo object to control the SG90 servo

int value_knob = 0; // will be used to store the value of the potentiometer/knob
int value_servo = 0; // will be used to control the servo =&gt; should be between 5 and 175 degrees

void setup()
{
servo.attach(PIN_SERVO); // assigns pin 9 to the servo object
}

void loop()
{
value_knob = analogRead(PIN_KNOB); // reads value of the knob/potentiometer. Return value will be between 0 and 1023.
value_servo = map(value_knob, 0, 1023, 5, 175); // will map knob value range to servo value range
servo.write(value_servo); // shaft of servo will start to rotate. value_servo is interpreted as an absolute position.
delay(20); // give servo some time to rotate
}

After transferring the program to the Arduino Uno, you should be able to rotate the servo’s shaft.

A SG90 servo motor that can be controlled by the knob of a rotary angle sensor.

A SG90 servo motor that can be controlled by the knob of a rotary angle sensor.

Video tutorial:

Tutorial: How to drive the 28BYJ-48 stepper motor with a ULN2003A driver board and an Arduino Uno

A 28BYJ-48 stepper motor connected to a ULN2003A driver board.

A 28BYJ-48 stepper motor connected to a ULN2003A driver board.

A stepper motor divides a full rotation in multiple steps. As a result, a stepper motor can be driven much more precisely than a conventional dc motor. In particular, stepper motors are driven step by step. After having a quick look into the data sheet of a stepper, we know exactly how many degrees correspond to a single step of our motor. With this information, we can precisely turn the rotor of our stepper motor, since we then know how many degrees correspond to a single step.

The 28BYJ-48 is a very cheap stepper motor that often comes with a ULN2003A driver board. Luckily, the Arduino platform has already a built-in stepper library that allows us to control the 28BYJ-48 stepper motor with the ULN2003A driver board. In this tutorial, it is shown how to control the 28BYJ-48 with an Arduino Uno.

List of materials:
– Arduino Nano [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– 28BYJ-48 stepper motor [Search on Aliexpress | Amazon]
– ULN2003A driver board [Search on Aliexpress | Amazon]

Pin layout:

Pin layout that shows how to connect a 28BYJ-48 stepper motor to a ULN2003A driver board and an Arduino Uno.

Pin layout that shows how to connect a 28BYJ-48 stepper motor to a ULN2003A driver board and an Arduino Uno.

Typically, the 28BYJ-48 motor comes with a 5-pin connector that fits to driver board’s connector. The driver board has two pins for the power supply which are labeled GND and VCC. The board’s GND pin must be wired to the Arduino’s GND pin. Accordingly, the board’s VCC pin must be connected to the Arduino’s 5V pin.
Important remark: With this setup, we are powering the motor directly from the Arduino. The advantage is that this is the possible easiest solution for providing power to the motor. However, if the motor consumes too much power, the Arduino can be permanently damaged. If you use a different setup (driver, motor, source code, etc.), make sure that you do not draw more than about 300mA out of the Arduino. If you need more power, just use an external voltage supply for your driver board.
Lastly, the driver board’s IN1, IN2, IN3 and IN4 pins must be connected to the Arduino. In this tutorial, pins 8 to 11 of the Arduino are used (IN1<->8, IN2 <-> 9, IN3 <-> 10, IN4 <-> 11).

Example source code:
In the beginning, we include the header file of the Arduino plattform’s built-in stepper library. Then, we define the number of steps that the motor needs for one revolution. Stepper motors can turn out very complicated, therefore, it is not that easy to look up this number.

For example, you can typically drive stepper motors in different modes and, moreover, they have a specific gear ration. Both have an influence on the number of steps per revolution. Since we drive the motor in the so-called full step mode (control sequence with four steps), each step corresponds to a rotation of 11.25 degrees according to the data sheet. This corresponds to 32 steps per revolution (360° / 11.25° = 32). In addition, the manufacturer specified a gear ratio of 64:1 for this type of motor. Normally, this gear ratio must be multiplied by the 32 steps. If you examine the 28BYJ-48 in more detail, it turns out that the actual gear ratio is approximately 63.68395:1. Therefore we set the final number of steps to 2038 (32 * 63.68395=2037.8864).

Next, we initialize the stepper. The first parameter of the Stepper constructor is the number of steps. The other parameters correspond to the Arduino’s pins that we used to connect the ULN2003A driver board.

In the loop function, we start to drive the motor. First, we set the speed to one revolutions per minute . Next, we tell the stepper motor to do 2038 steps. As one revolution corresponds to 2038 steps, the motor’s shaft should move a full revolution in approximately one minute. Next, we set a delay of one second. Then, we do the same thing again. But this time, we set the speed to 6 rounds per minute and move the shaft in the other direction (by setting a negative number of steps). As the the shaft moves six times faster now, the motor should finish a full revolution in about 10 seconds (60s/6=10s).

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

#include <Stepper.h>

#define STEPS 2038 // the number of steps in one revolution of your motor (28BYJ-48)

Stepper stepper(STEPS, 8, 10, 9, 11);

void setup() {
  // nothing to do
}

void loop() {
  stepper.setSpeed(1); // 1 rpm
  stepper.step(2038); // do 2038 steps -- corresponds to one revolution in one minute
  delay(1000); // wait for one second
  stepper.setSpeed(6); // 6 rpm
  stepper.step(-2038); // do 2038 steps in the other direction with faster speed -- corresponds to one revolution in 10 seconds
}
The stepper motor is driven by the ULN2003A driver board. The board's LEDs show the current control sequence state.

The stepper motor is driven by the ULN2003A driver board. The board’s LEDs show the current control sequence state.

Video Tutorial: