carr3r

Arduino ( BT ) Android

Fundefinedollowing up on my last post, it was time to see some data exchange between my smartphone and Arduino through the Bluetooth module I had just set up. I connected the JY-MCU HC06 V1.06 Bluetooth board to my Arduino respecting the wiring diagram aside.

At first, I thought of creating a simple ping-pong application (where Arduino would reply a "pong" message whenever it receives a "ping" message), but it would be nice to have a way of visually debugging Arduino, since it won't be connected to a terminal (although it could be done connecting the Bluetooth module to Arduino's non-regular TX/RX pins, as Matt Bell did in his blog). So I decided to invert the 13th pin output value (turning on and off its LED) whenever I receive a "ping" message.

#define LED 13
String str;
byte state = 0;

void setup()
{
   pinMode(LED, OUTPUT);
   digitalWrite(LED, LOW);
   Serial.begin(9600);
}

void loop() 
{
   if(Serial.available() > 0)
   {
      str = Serial.readStringUntil('\n');
      if (str.equals("ping"))
      {
         if (state==0)
         {
            digitalWrite(LED, HIGH);
            state = 1;
         }
         else
         {
            digitalWrite(LED, LOW);
            state = 0;
         }
         Serial.write("pong\n");
      }
      else
      {
         Serial.write("ok\n");
      }
   }
}

I used Android Studio for developing the mobile App. It comes with everything you need to code and build apps for Android devices.

I'll just point out the important parts and concepts of my code, instead of explaining it line-by-line. First of all, as our app will use the Bluetooth resource, we must declare it in the App's manifest (so the user can consent and allow the app to do so, at installation time). To simplify things, our application will only list previously paired Bluetooth devices (so we don't need to handle device discovery and pairing processes). Whenever we select a Bluetooth device in the list, the app will try to establish a socket-like connection with it. This connection will be always re-established at the Activity's onResume event and should be always closed at the onPause event ("Release system resources, such as broadcast receivers, handles to sensors (like GPS), or any resources that may affect battery life while your activity is paused and the user does not need them.", from Pausing and Resuming an Activity). Once our socket connection is set, we can read (receive) and write (send) data through it's InputStream and OutputStream. As data reception is "asynchronous" in terms that we do not trigger it, it just happens, we should create a background thread for continuously checking if anything hits the InputStream. Having said that, all code not related to these features is mere decoration.

I'll put the whole Main class code here, but everything is packed up and available to download at the end of this post.

package com.carr3r.arduinobluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBarActivity;
import android.text.Editable;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;


public class MainActivity extends ActionBarActivity implements AdapterView.OnItemSelectedListener {

    private static final String TAG = "carr3r.arduinobluetooth";

    private BluetoothAdapter btAdapter = null;
    private BluetoothSocket btSocket = null;
    private OutputStream outStream = null;
    private InputStream inStream = null;

    private String MACaddress = null;
    private boolean hasConnected = false;

    private Thread workerThread;
    byte[] readBuffer;
    int readBufferPosition;
    volatile boolean stopWorker;

    // SPP UUID service
    private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btAdapter = BluetoothAdapter.getDefaultAdapter();

        if (btAdapter == null) {
            Log.e(TAG, "Bluetooth not supported");
            add2terminal("Bluetooth not supported");
        } else {
            if (btAdapter.isEnabled()) {

                Log.i(TAG, "Bluetooth is ON");
                add2terminal("Bluetooth is ON");

            } else {
                //Prompt user to turn on Bluetooth
                Log.i(TAG, "Bluetooth is OFF. Switch it on?");
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, 1);
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();

        if (btAdapter != null) {
            btAdapter.cancelDiscovery();

            Set<BluetoothDevice> pairedDevices = btAdapter.getBondedDevices();
            // If there are paired devices
            if (pairedDevices != null && pairedDevices.size() > 0) {


                List<String> list = new ArrayList<String>();
                list.add("");

                // Loop through paired devices
                for (BluetoothDevice device : pairedDevices) {
                    list.add(device.getName() + " (" + device.getAddress() + ")");
                    Log.i(TAG, "Device found: " + device.getName() + " (" + device.getAddress() + ")");
                }

                ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list);
                dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                final Spinner deviceList = (Spinner) findViewById(R.id.lstBluetoothDevice);
                deviceList.setAdapter(dataAdapter);
                deviceList.setClickable(true);
                deviceList.setOnItemSelectedListener(this);
            }

            if (hasConnected)
                connect2device(MACaddress);
        }

    }

    private void connect2device(String mac) {
        BluetoothDevice device = btAdapter.getRemoteDevice(mac);

        try {

            if (Build.VERSION.SDK_INT >= 10) {
                try {
                    Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[]{UUID.class});
                    btSocket = (BluetoothSocket) m.invoke(device, MY_UUID);
                } catch (Exception e) {
                    Log.e(TAG, e.getMessage());
                }
            } else
                btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }

        if (btSocket != null)
            try {
                btSocket.connect();
            } catch (IOException e1) {
                Log.e(TAG, e1.getMessage());
                try {
                    btSocket.close();
                } catch (IOException e2) {
                    Log.e(TAG, e2.getMessage());
                }
                btSocket = null;
            }

        if (btSocket != null) {
            try {
                outStream = btSocket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            }

            try {
                inStream = btSocket.getInputStream();
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            }
        }

        if (inStream != null) {
            startListeningThread();
            hasConnected = true;
            MACaddress = mac;

            add2terminal("Connected to " + MACaddress + "!");
        } else {
            hasConnected = false;
            btSocket = null;
            outStream = null;
            inStream = null;

            add2terminal("It was not possible to establish a connection to " + mac);
        }

    }

    void startListeningThread() {
        final Handler handler = new Handler();
        final byte delimiter = 10; //This is the ASCII code for a newline character

        stopWorker = false;
        readBufferPosition = 0;
        readBuffer = new byte[1024];
        workerThread = new Thread(new Runnable() {
            public void run() {
                while (!Thread.currentThread().isInterrupted() && !stopWorker) {
                    try {
                        int bytesAvailable = inStream.available();
                        if (bytesAvailable > 0) {
                            byte[] packetBytes = new byte[bytesAvailable];
                            inStream.read(packetBytes);
                            for (int i = 0; i < bytesAvailable; i++) {
                                byte b = packetBytes[i];
                                if (b == delimiter) {
                                    byte[] encodedBytes = new byte[readBufferPosition];
                                    System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
                                    final String data = new String(encodedBytes, "US-ASCII");
                                    readBufferPosition = 0;

                                    handler.post(new Runnable() {
                                        public void run() {
                                            add2terminal("< " + data);
                                        }
                                    });
                                } else {
                                    readBuffer[readBufferPosition++] = b;
                                }
                            }
                        }
                    } catch (IOException ex) {
                        stopWorker = true;
                    }
                }
            }
        });

        workerThread.start();
    }

    @Override
    public void onPause() {
        super.onPause();

        if (btAdapter != null) {

            if (outStream != null) {
                try {
                    outStream.close();
                } catch (IOException e) {
                    Log.e(TAG, e.getMessage());
                }
            }

            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException e) {
                    Log.e(TAG, e.getMessage());
                }
            }

            if (btSocket != null)
                try {
                    btSocket.close();
                } catch (IOException e) {
                    Log.e(TAG, e.getMessage());
                }

            stopWorker = true;
        }
    }

    private void sendData(String message) {

        if (outStream != null) {
            byte[] msgBuffer = message.getBytes();
            try {
                outStream.write(msgBuffer);
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        return id == R.id.action_settings ? true : super.onOptionsItemSelected(item);
    }

    public void add2terminal(String message) {
        TextView term = (TextView) findViewById(R.id.edtTerminal);
        term.append(message + "\n");
    }

    @Override
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
        String device = ((Spinner) findViewById(R.id.lstBluetoothDevice)).getSelectedItem().toString();
        if (device.length() > 0) {
            String selectedMac = device.substring(device.indexOf(" (") + 2).replace(")", "");
            add2terminal("Trying to connect to " + selectedMac);
            connect2device(selectedMac);
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> adapterView) {

    }

    public void onBtnSendClick(View v) {
        EditText edit = (EditText) findViewById(R.id.edtSend);
        Editable text = edit.getText();
        if (text != null && text.length() > 0) {
            sendData(text.toString() + "\n");
            add2terminal("> " + text.toString());
            text.clear();
        }
    }
} 

A screenshot of the app running is shown below. At least in my MotoG, it works like a charm.

 

undefined

 

Source code:

Touching Bluetooth with JY-MCU HC-06 V1.06 Wireless Serial Port board

undefinedMany weeks ago I ordered a Bluetooth Module for Arduino, planning to use it for controlling my 16x16 red dot LED display (instead of using IR) and also for collecting data from my Swimming-pool-NFC-stopwatch-counter device, both using my smartphone.

As I'm not good at soldering, I bought an assembled one, that comes attached to an interface board with 4 pins, making it very easy to use. Data transfer is done by Serial communication (UART) using TTL level (0 and 5V), which is fine to work with Arduino (Read "UART – Universal Asynchronous Receiver and Transmitter" and "Microcontroller UART Tutorial" to get familiar with terms like UART, RS232, TTL, etc - before writing this post, I confess I didn't know much about them). As I had an USB to "RS232" TTL converter, I wanted to test the board and set it up using my own computer.

The HC06 is a slave Bluetooth module (read this). That means it can be only connected to a single master and, therefore, can not communicate with other slave devices or anybody else. Its instruction set, for configuration purposes, is very short. We can only setup its baudrate, Bluetooth device name and PIN code, apart from checking its firmware version and connection state.

The table below describes all the AT commands available at the HC06 module ("The AT is an ATTENTION command and is used as a prefix to other parameters in a string. The AT command combined with other parameters can be set up in the communications package or typed in manually as a command line instruction" taken from AT Commands Reference Guide )

AT Commands Set 
AT CommandResponseDescription
AT OK Check connection
AT+VERSION OKlinvorVxxx Get the firmware's version
AT+BAUDx OKyyyy Set baudrate. Replace x with:
1 for 1200 bps
2 for 2400 bps
3 for 4800 bps
4 for 9600 bps
5 for 19200 bps
6 for 38400 bps
7 for 57600 bps
8 for 115200 bps
9 for 230400 bps
A for 460800 bps
B for 921600 bps
C for 1382400 bps
AT+NAMEString OKsetname Change the Bluetooth device name, maximum of 20 characters
AT+PINxxxx OKsetpin Set the Bluetooth PIN code.

The table and figure below illustrate the modules wiring diagram. Wire both devices using the following instructions.

Pinout diagram
USB to RS232 TTLJY-MCU V1.06
3V3 VCC 
GND GND 
TX RX 
RX  TX

undefined

Now, connect the "USB to RS232 TTL" into one of your computer's USB ports, open up a terminal (on Linux) and type:

dmesg

Here's what my Fedora outputs:

[ 4275.425111] usb 3-1: new full-speed USB device number 4 using uhci_hcd
[ 4275.570131] usb 3-1: New USB device found, idVendor=067b, idProduct=2303
[ 4275.570139] usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 4275.570144] usb 3-1: Product: USB-Serial Controller
[ 4275.570148] usb 3-1: Manufacturer: Prolific Technology Inc.
[ 4275.575231] pl2303 3-1:1.0: pl2303 converter detected
[ 4275.587677] usb 3-1: pl2303 converter now attached to ttyUSB1

The important part here is to check which interface your system gave/linked to your device. In my case, the interface is /dev/ttyUSB1

We can use minicom as a Serial console to visually interact (send/receive data) with our Serial devices.

minicom -b 9600 -8 -D /dev/ttyUSB1

As Byron states at his blog, "For this firmware you have to not send LF or CR character at the end of your AT command. AT programming mode is up when your board is not paired by Bluetooth devices. For this firmware, you have to copy/paste AT command since timings is important. You can send about one command per second."

You'll realize you need to copy/paste the AT commands when you try to write any AT command at the console by yourself. It's almost impossible to get it done.

Copy and paste, one line at a time, the following AT commands in order to set up the device to be announced as "ArduinoBluetooth" and to use the PIN code "1234".

AT+NAMEArduinoBluetooth
AT+PIN1234

Done! Your device should have been properly set up if you get something similar to the image below.

undefinedundefined

You can use your smart phone or other master Bluetooth device to check the connection parameters.

My next step is to develop a native Android application and try some data exchange over the air through simple Serial consoles (using minicom on my Laptop and building a similar app on Android).

Arduino Laser Spirograph

I believe I always wanted to build something with lasers, mainly because lasers are cool. It's like playing with fire: it's dangerous, but fun. As a kid, I had a stamp collection and won a magnifying glass to see and analyze my stamps. I think I only used it for putting things on fire.

Fundefinedew days ago I saw some cool projects (this one uses laser to put things on fire as well) using lasers and I got tempted to do something as well, since I already had a red laser diode in hands. When I realized how the laser Spirograph worked, I knew it would require very few components to build it and I also found the whole idea behind it very interesting (this illustration made by the Interactive Electronics Design is very explanatory). I'm not talking about the Spirograph itself, but the persistence of vision phenomenon, which allows us to draw things/lines/curves using a single dot of light and see it in a continuous form.

"The essence of the mini-laser Spirograph is two mirrors attached to small dc motors. A laser beam striking the rotating mirrors will produce a wide array of patterns. With one mirror rotating, the reflected beam “paints” an ellipse on a screen. When both mirrors are spinning, two elliptical patterns superimpose to produce more complex shapes. The pattern observed depends on the ratio of the speeds and direction of rotation of the mirrors." - as well stated by Chris Chiaverina here.

A friend of mine gave me a broken remote-controlled helicopter, from which I removed all (5!) DC motors (they are just like this). They don't have much torque, but they're very, very fast and very, very small.

People seem to always use potentiometers to control motor speed, but I wanted my Arduino to do all the job. So I started worrying about controlling a motor speed through the Arduino. I could had used a motor driver to do so, but I found an interesting article at Adafruit that described an inexpensive circuit to control it. So, following the tutorial, I built the circuit, tested it and approved it. Then I transferred the circuit into a PCB protoboard, duplicating it for controlling two motors instead.

I think the most difficult part of assembling the components was to adjust the mirrors in a way they would reflect the laser's projections properly. This was done using a trial and error approach. I used hot glue to fix cables and holders.


undefined

The schematic/wiring diagram is as shown below. Some people will notice upfront that the diagram is a little bit messed up. I was reluctant to publish a post with a "problematic" diagram, but since it is how it was truly done and I intent to improve it, I think it's okay. The problem is that, as I'm using the speed-controlling circuits in parallel under the same power supply (Arduino's 3.3V power pin), whenever I let too much current passes through one circuit, it drains out the current available to the other one, eventually collapsing Arduino, making it reboot. I know, terrible, but it can be easily fixed by adding a separated power supplier (probably I'll make another post whenever I get it done). After few tests I found out that, as far as I use speed values below 90 for both motors, it works fine the way it is now. 

undefined

 

Demo video:

Be aware of the potential hazards when working with lasers. Read this.

Arduino Smart Window Sticker for vehicles - Part 3

Well, this is very embarrassing to assume but, after doing some math, I realized my first approach of storing all symbols and transitions into a big array it's just a huge, massive absurd.

Each symbol, like "A" or "a" uses 32 bytes (16x16 LEDs) of storage memory. Therefore, just to store the whole alphabet and all digits, it would take (26 letters lowercase + 26 letters uppercase + 10 digits) 62 * 32 bytes, that equals 1984 bytes. So far, so good, since Arduino has ( 32kb - 5kb that is used by the bootloader) approximately 27Kb of storage memory available to our code. The problem is that each symbol transition (lets say, to scroll a character to one side) takes 16 frames to happen. In other words, to slide a symbol "A" to the left, bringing a "B" symbol from the right, takes (16x32) 480 bytes of storage. Now the absurd: if we have 62 symbols and considering that all transitions happen towards one single side (which isn't our case since it would be nice to have the text scrolling up/down/left/right), they can transit on 62² different possible combinations. That would take, at least, (3844*480bytes) 1801Kb of storage, more than sixty! times the available storage space. So, in short, storing transitions was never a feasible option, I just hadn't realized it yet.

To fix that, I embedded all the necessary functions to make symbol transitions into the Arduino. As I said before, it compromises the speed that the Arduino can paint/refresh each frame (since it requires quite a lot of bit "polishing") but the outcome was still acceptable.

I also included a buzzer to the circuit, so I can feedback the commands sent through the remote control, specially when I'm not looking at the board. I dit not use Arduino's Tone library since it collides with the IR receiver's library (both use the same timer interrupt). Instead, I used PWM over the analogWrite function to control the buzzer.

I also used a very handy library for storing/retrieving information on the Flash memory available at http://arduiniana.org/libraries/flash/ .

Now I'm working on building a plastic case for holding the whole circuit. Planning on doing it with a 3D printer.

New wiring diagram:

 undefined

Necessary library for handling stuff on the Flash memory: Flash.zip

Source code

Arduino Smart Window Sticker for vehicles - Part 2

After spending more time with my adapted 16x16 LED board (made with four 8x8 red dot LED matrices - as shown in Part 1), I added an IR receiver module to the circuit and used an "old" 3.7v lithium battery (taken from a broken Motorola Defy smartphone) to power it.

A long time ago I bought an IR Kit for Arduino from DFRobot and it came with an useful IR remote control. Using the sample source code and wiring diagram available at the DFRobot's wiki, I managed to identify the frequencies related to all buttons from the remote control.

To be able to use the lithium battery to power Arduino, I took an USB female plug from an old USB hub and, following the USB Pinout description available here, I simply connected the VCC and GROUND pins to the + and - battery pins, respectively, as illustrated at the picture below.

undefined

The IR receiver module specifies 5V for power supply. As I was planning on using 3.7V for powering Arduino, I didn't bother to connect the IR receiver's VCC pin at Arduino's 3V3 pin, and give it less power than I was "required" to. And guess what, it worked like a charm!

New wiring diagram:0

undefined

Necessary library for the IR module: IRremote.zip

Source code

 

Arduino Smart Window Sticker for vehicles - Part 1

You're driving around aimlessly when you suddenly notice that, in the car just behind you, there is a beautiful and cheerful girl, singing by herself. Wouldn't it be nice to communicate with her, to throw away an innocent compliment and "attract" her attention? Or maybe you really need to notify something on the road, call names, make fun or even advertising something: your company, a trademark, your candidate, whatever. This project is about creating a smart sticker that would enable all these sort of things to happen.

With four 8x8 red LED dot matrix boards, like the ones available in DX, I was able to buld a 16x16 matrix board, sizing approximately 6.5x6.5 centimetres. It's little, but yet very visible. The downside of my final board is that each one of the tiny 8x8 matrix boards ended up in one different position, implying on several calculations to translate a 16x16 matrix into 4 8x8 rotated matrix at once.

After spending some time toying around with the source code, I realized that it would be very difficult to rely on the Arduino to make all calculations/translations in runtime and yet display the information within an acceptable time. So I decided to create a drawing board, with HTML& Javascript, to help me out with the drawing issue, and coded all the necessary math in PHP for translating the 16x16 matrix into 4 8x8 matrices.

undefined

This board can be seen in http://www.carr3r.com.br/matrix . I also coded a PHP script that calculates all the frames necessary to fade in and fade out an image, so all the Arduino was to do is to read an array of matrices and display each element of it (actually it slides a frame towards left, right, up and down - http://www.carr3r.com.br/matrix/transition.html).

The video below shows the current state-of-art of this project.

Next steps:

  • Create several animations;
  • Install an infra red sensor, so I can control what the board displays with an ordinary infra red remote control.

 

 

Updated on March 24, 2014

Material list:

Circuit Diagram (made with Circuit Diagram and GIMP):

undefined

 

Necessary library: LedControl.zip (documentation)

Source code: _16x16RedDotMatrix.ino

Newer posts → Home ← Older posts