Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

Motor Shield

4.78/5 (10 votes)
3 Oct 2017CPOL14 min read 22.7K   402  
Brief example on how to use Android and Arduino to control a DC motor and LEDs over Bluetooth

Introduction

I purchased my first of many Arduino boards years ago after seeing an advertisement on some web site (I don't recall the specifics). With all of the extra time I had on my hands Image 1, I decided I needed another interest! To see how it all works and what I could make with it, I created dozens of sketches to do various things, and coupled several of those to an Android device.

One of those was a project that involved connecting an Android device, over Bluetooth, to a OneTouch® Ultra® 2 glucometer and extracting the BG records from it. I chose this particular meter because its RS-232 communication specification was available online. It supported nine commands to do things like download BG records from the meter's memory, set/get meter's clock, erease the meter's memory, etc. The meter's serial port was connected to the Arduino's Rx/Tx pins using a cable I made from an old computer audio cable (you could purchase a pre-made one but they were too pricey). A Bluetooth shield was connected to the Arduino board. Once paired and connected, the Android device would send the appropriate command to the meter asking for its BG records. After downloading, the Android code would display all of the readings and an estimated HbA1c. This was a fun exercise.

One of the latest shields I purchased was a motor shield. I got this wild hair to make some sort of 3- or 4-wheel vehicle that could be controlled from an Android device. From a previous project, I already had two DC motors that I could use for the rear wheels. For the front, I could just use regular wheels not connected to a motor, or maybe even a caster. I would also need some sort of "body" to hold the components. It was easy enough to get both the Arduino sketch and the Android code communicating, but then came the realization that I was going to need more parts if I wanted this 'vehicle' to actually work (not polished, but at least functional). At the time, I wasn't willing to spend any more money on it until I had a better idea of what exactly it is that I wanted. So I changed direction.

Putting the vehicle part of the project aside, I then decided to just present what I had in an article format. So what I'll be showing is how I connected my Android device, over Bluetooth, to the Arduino motor shield. The UI will be controlling the speed and direction of the motor, and the on/off of two LEDs. Let's get started...

Arduino

The Arduino components consist of:
 

  • Arduino Uno Rev3
  • Arduino Motor Shield Rev3
  • Seeed Studio Bluetooth Shield
  • DC motor
  • Red LED
  • White LED
  • Two resistors
  • Small breadboard

    Because the header pins are longer on the motor shield than on the Bluetooth shield, I chose to stack them in the following order:
     
    Image 2The Arduino board is on the bottom.
    The motor shield is in the middle.
    The Bluetooth shield is on the top.

    Now we need to get the DC motor set up. The motor shield I am using can control two DC motors. For channel A (motor 1), direction is set using digital pin 12, pulse width modulation (PWM) is set using digital pin 3, braking is set using digital pin 9, and the motor's current can be read from analog pin 0. For channel B (motor 2, if we were using one), direction is set using digital pin 13, PWM is set using digital pin 11, braking is set using digital pin 8, and the motor's current can be read from analog pin 1. All of this is handled in the motor's init() function.

    The red and white LEDs are connected to pins 4 and 5 respectively. With all of the wires connected to their respective pins, our hardware now looks the following. The straw in the picture was put on the motor's axle so that I could see which way it was spinning. It also helped to slow the motor down as speeds in excess of 200 sounded as though it was about to explode!

    Image 3

    As far as the coding goes, the setup() function is called once when the sketch starts. In it, we'll initialize some variables, pin modes, and libraries. For this project, I've opted for the Bluetooth shield to communicate over pins 6 and 7. After the bluetooth object has been constructed, we'll need to initialize it. That happens in setupBlueToothConnection(). Codes in that function are described here. This all looks like:
    #include <SoftwareSerial.h>
    #include <ArduinoMotorShieldR3.h>
    
    #define btTx 6
    #define btRx 7
    
    SoftwareSerial bluetoothSerial(btRx, btTx);
    
    ArduinoMotorShieldR3 motor;
    
    //======================================================================
    
    void setup() 
    {
        Serial.begin(19200);
    
        setupBlueToothConnection();
        
        motor.init();
        motor.setM1Speed(0); // initially off
    
        pinMode(4, OUTPUT); // red
        pinMode(5, OUTPUT); // white
    }
    The loop() function runs consecutively, allowing the program to change, respond, and actively control the Arduino board. With each iteration, it first checks to make sure the Bluetooth shield is listening (for data coming from the Android device). If it is, we call readBytesUntil(), which blocks until the terminator character is detected, the determined length has been read, or it times out (1 second default). When that function returns and it has data to process, we look it over to make sure it is what we are expecting. The only characters we are interested in are the digits 0-9, a dash, an asterisk, or a pound sign.

    If an asterisk is found, the digit immediately following tells whether to turn the white LED on or off. If a pound sign is found, the digit immediately following tells whether to turn the red LED on or off. Otherwise, we treat what's found as a [negative] number representing a speed and write that to channel A (motor 1). This process continues indefinitely. The code for this looks like:
    void loop() 
    {
        if (bluetoothSerial.isListening())
        {
            char bufferIn[10] = {0};
            int count = bluetoothSerial.readBytesUntil('\n', bufferIn, 10);
            if (count > 0)
            {
                bufferIn[count] = '\0';
    
                // check the characters that were received
                bool bProceed = true;
                for (int x = 0; x < strlen(bufferIn) && bProceed; x++)
                {
                    if (! isDesiredCharacter(bufferIn[x]))
                        bProceed = false;
                }
    
                // only continue if accepted characters were received
                if (bProceed)
                {
                    if (bufferIn[0] == '*') // white LED
                    {
                        if (bufferIn[1] == '0')
                            digitalWrite(5, 0);
                        else if (bufferIn[1] == '1')
                            digitalWrite(5, 1);
                    }
                    else if (bufferIn[0] == '#') // red LED
                    {
                        if (bufferIn[1] == '0')
                            digitalWrite(4, 0);
                        else if (bufferIn[1] == '1')
                            digitalWrite(4, 1);
                    }
                    else // DC motor
                    {
                        String str = bufferIn;                                 
                        int speed = str.toInt();
                        motor.setM1Speed(speed);
                    }
                }
            }
        }    
    }
    
    //======================================================================
    
    bool isDesiredCharacter( char c )
    {
        return (isDigit(c) || c == '-' || c == '*' || c == '#');    
    }
    
    //======================================================================
    
    /*
    BTSTATE:0 = Initializing
    BTSTATE:1 = Ready
    BTSTATE:2 = Inquiring
    BTSTATE:3 = Connecting
    BTSTATE:4 = Connected
    */
    
    void setupBlueToothConnection()
    {
        bluetoothSerial.begin(38400);                        // set baud rate to default baud rate 38400
        bluetoothSerial.print("\r\n+STBD=38400\r\n");    
        bluetoothSerial.print("\r\n+STWMOD=0\r\n");          // set the bluetooth to work in slave mode
        bluetoothSerial.print("\r\n+STNA=SeeedBTSlave\r\n"); // set the bluetooth name as "SeeedBTSlave"
        bluetoothSerial.print("\r\n+STOAUT=1\r\n");          // permit paired device to connect 
        bluetoothSerial.print("\r\n+STAUTO=0\r\n");          // auto-connection off
        delay(2000);                                         // this delay is required
        bluetoothSerial.print("\r\n+INQ=1\r\n");             // make the slave bluetooth inquirable 
        delay(2000);                                         // this delay is required
        bluetoothSerial.flush();
    }
    After this sketch is sent to the Arduino board, it will initialize and start listening for data over Bluetooth. Now let's get things going on the Android side.

    Android

    The Java code running on the Android device is only slightly more complicated. There are the UI components to contend with, the device's Bluetooth adapter, and the actual connection between the Arduino board and the Android device. All of those must play nicely together.

    In the activity's onCreate() method, one of the first things it does is enable the Bluetooth adapter if it's not already enabled. Before allowing this, the app will ask for permission like:

    Image 4

    The code, specifically the Intent, to accomplish this looks like:
    Java
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter != null)
    {
        if (! mBluetoothAdapter.isEnabled())
        {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivity(intent);
        }
        else
        {
            tvBluetoothStatus.setText("Enabled");
    
            if (! MainApplication.isSocketConnected())
            {
                btnConnect.setEnabled(true);
                Toast.makeText(this, "Click the Connect button to connect to a device.", Toast.LENGTH_LONG).show();
            }
        }
    }
    else
    {
        tvBluetoothStatus.setText("Not supported");
        showDialog(this, "Unsupported", "Bluetooth is not supported on this device.");
    }
    Once enabled, our UI now looks the following. Note that the only UI control that is enabled is the Connect button.

    Image 5

    Unlike startActivityForResult(), you will not receive any information when startActivity() exits. However, the BluetoothAdapter.ACTION_REQUEST_ENABLE intent makes an exception for this. Notification is posted to the onActivityResult() callback. The resultCode will be RESULT_OK if Bluetooth has been turned on or RESULT_CANCELED if the user has rejected the request or an error has occurred. Instead, this app will be listening for the ACTION_STATE_CHANGED notification whenever Bluetooth is turned on or off. We'll set this up, along with other notifications, with a BroadcastReceiver.

    In this project, we are interested in the following notifications: when the Bluetooth adapter is 1) enabled or 2) disabled, and 3) when a Bluetooth device is connected or disconnected. These notifications will be handled by the BTReceiver class, which will be discussed later. Setting up a receiver for these looks like:
    Java
    try
    {
        // unregister any previously registered receivers
        unregisterReceiver(receiver);
    }
    catch(IllegalArgumentException iae) // fail gracefully if null
    {
    }
    catch(Exception e)
    {
        Log.e(TAG, "Unregistering BT receiver: " + e.getMessage());
    }
    
    receiver = new BTReceiver(this);
    
    IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); // turning on, on, turning off, off
    registerReceiver(receiver, filter);
    
    filter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
    registerReceiver(receiver, filter);
    
    filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    registerReceiver(receiver, filter);
    You may be wondering about the call to unregisterReceiver(). When I first started working on this project, I coded it up and tested it on my Android device in portrait mode. I guess I tilted it a little too far one day and it switched to landscape mode. Hmmmm. The layout adjusted nicely, and things seemed to look in order, but when I went to interact with the UI, there was no Bluetooth communication with the Arduino. Ugh! One of the issues I found (yes, there were several) was the receiver was no longer receiving. No exceptions were being thrown, but still no notifications. Since I was reregistering the Intents in the onCreate() method, I guessed that the initial Intents were being masked by subsequent creations. Calling unregisterReceiver() not only unregisters a previously registered receiver, it also removes all filters (i.e., Intents) that have been registered for the receiver. Now I could change the device's orientation and still receive Bluetooth notifications.

    The next issue I discovered had to do with the Bluetooth connection itself. Initially I had the BluetoothSocket object as part of the activity. As long as there was no connection, restarting the activity was fine. After a connection, however, when the Android device's orientation changed and the onCreate() method was called again, the socket would get re-created which resulted in no Bluetooth communication with the Arduino. I first thought about trying to save/load the socket object in the onSaveInstanceState() and onRestoreInstanceState() methods. Sounded good in theory, but it did not produce the desired result. I then decided to put the BluetoothSocket object in the application class itself. Now when the Android device's orientation changes and the activity gets re-created, the one-and-only application instance keeps the BluetoothSocket object in place.

    The last issue I observed had to do with the DC motor or the LEDs being on while the Android device's orientation changed. During activity re-creation, the UI controls would be set to their default "off" state, yet the Arduino would still be powering the DC motor or LEDs. To solve this (i.e., make the UI match), I was able to use the onSaveInstanceState() and onRestoreInstanceState() methods. This looks like:
    Java
    @Override
    protected void onSaveInstanceState( Bundle outState )
    {
        Log.d(TAG, "onSaveInstanceState()");
    
        super.onSaveInstanceState(outState);
    
        int speed = seekbar.getProgress();
        outState.putInt("SPEED", speed);
    
        boolean white = ledWhite.isChecked();
        outState.putBoolean("WHITE", white);
    
        boolean red = ledRed.isChecked();
        outState.putBoolean("RED", red);
    }
    
    @Override
    protected void onRestoreInstanceState( Bundle savedInstanceState )
    {
        Log.d(TAG, "onRestoreInstanceState()");
    
        super.onRestoreInstanceState(savedInstanceState);
    
        // did the orientation change (or some other event) while the motor was running or the LEDs were on?
        if (mBluetoothAdapter.isEnabled() && MainApplication.isSocketConnected())
        {
            Log.d(TAG, "The device was rotated.  Setting the motor and LEDs to what they were before rotation.");
    
            tvDeviceStatus.setText("Connected to " + MainApplication.strDeviceName);
    
            int speed = savedInstanceState.getInt("SPEED");
            seekbar.setProgress(speed);
    
            boolean white = savedInstanceState.getBoolean("WHITE");
            ledWhite.setChecked(white);
    
            boolean red = savedInstanceState.getBoolean("RED");
            ledRed.setChecked(red);
        }
        else
        {
            Log.d(TAG, "The device was rotated but is neither enabled nor connected.  Resetting the motor and LEDs");
    
            // these should probably already be off, but just in case...
            seekbar.setProgress(50);
    
            ledWhite.setChecked(false);
            ledRed.setChecked(false);
        }
    }
    Quite a bit of the above code and text dealt with a connection, which I haven't shown yet. I sort of put the cart before the horse, so to speak. As was mentioned earlier, once the Bluetooth adapter is enabled, the Connect button will be available. When clicked, it will start a secondary activity showing the list of previously paired devices:

    Image 6

    This list is obtained using code like:
    Java
    ArrayList<String> arrItems = new ArrayList<String>();
    BluetoothAdapter btnAdapter = BluetoothAdapter.getDefaultAdapter();
    Set<BluetoothDevice> pairedDevices = btnAdapter.getBondedDevices();
    if (pairedDevices.size() > 0)
    {
    	for (BluetoothDevice device : pairedDevices)
    		arrItems.add(device.getName() + "\n" + device.getAddress());
    	
    	adapter.notifyDataSetChanged();
    }
    As was shown in the Arduino code, the name of our Bluetooth device is SeeedBTSlave. When that device is clicked, in the onItemClicked() handler we package its MAC address in an Intent to pass back to the main activity. The main activity receives this response in onActivityResult(), like:
    Java
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);
    
        Log.d(TAG, getClass().getName() + "::onActivityResult(" + resultCode + ")");
    
        if (requestCode == 1 && resultCode == RESULT_OK)
        {
            String strAddress = data.getExtras().getString("device_address");
            Connect(strAddress);
        }
    }
    With the MAC address of the device to connect to, we can now do the actual connecting. When I initially put the connecting code together, it was natural to just use the BluetoothDevice.createRfcommSocketToServiceRecord() method to create a socket and the BluetoothSocket.connect() method to connect to it. This worked, but only part of the time. I kept receiving sporadic java.io.IOException: read failed, socket might closed or timeout, read ret: -1 errors. After a bit of research, I found others that were experiencing the same problem. While not 100% reliable and no guarantee of it remaining available, several of those found success by using Java reflection to get access to a createRfcommSocket() function. Using that code as a fallback method, I changed my connect() function to look like:
    Java
    private void Connect( String address )
    {
        Log.d(TAG, getClass().getName() + "::Connect()");
    
        if (mBluetoothAdapter == null || ! mBluetoothAdapter.isEnabled())
        {
            Log.e(TAG, "  Bluetooth adapter is not enabled.");
            return;
        }
    
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        Toast.makeText(this, "Connecting to device " + device.getName() + "...", Toast.LENGTH_LONG).show();
        mBluetoothAdapter.cancelDiscovery();
    
        try
        {
            MainApplication.btSocket = device.createRfcommSocketToServiceRecord(BT_UUID);
            MainApplication.btSocket.connect();
        }
        catch(IOException ioe)
        {
            Log.e(TAG, "  Connection failed.  Trying fallback method...");
    
            try
            {
                MainApplication.btSocket = (BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device, 1);
                MainApplication.btSocket.connect();
            }
            catch(Exception e)
            {
                Log.e(TAG, "  Connection failed.");
    
                showDialog(this, "Socket creation failed", e.getMessage() + "\n\nThe remote device may need to be restarted.");
            }
        }
    }
    This way, if the primary connection method fails, we try the fallback method. If that fails, too, we just pack up and go home!

    Now that we have requested a connection, it's time to start listening for notifications. Earlier we registered a receiver with three filters (i.e., Intents). This is handled via a BroadcastReceiver extension. I chose to put this extension in its own file rather than make it an internal class to the activity. One of the caveats of this is that no UI interaction could take place. In other words, when the Bluetooth adapter was enabled or disabled, or a device became disconnected, I could not update any of the status controls on the UI. One solution to this was to use an interface. This class looks like:
    Java
    public class BTReceiver extends BroadcastReceiver
    {
        private static String TAG = "Test2";
    
        private Callback mCallback = null;
    
        public interface Callback
        {
            public void updateStatus( String strAction, int nState, String strDevice );
        }
    
        //================================================================
    
        public BTReceiver( Activity activity )
        {
            if (! (activity instanceof Callback))
                throw new IllegalStateException("Activity must implement 'Callback' interface.");
    
            Log.d(TAG, "BTReceive()");
    
            mCallback = (Callback) activity;
        }
    
        //================================================================
    
        @Override
        public void onReceive( Context context, Intent intent)
        {
            Log.d(TAG, getClass().getName() + "::onReceive()");
    
            try
            {
                String strAction = intent.getAction();
                if (strAction.equals(BluetoothAdapter.ACTION_STATE_CHANGED))
                {
                    int nState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                    mCallback.updateStatus(strAction, nState, "");
                }
                else if (strAction.equals(BluetoothDevice.ACTION_ACL_CONNECTED))
                {
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    MainApplication.strDeviceName = device.getName();
                    mCallback.updateStatus(strAction, -1, MainApplication.strDeviceName);
                }
                else if (strAction.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))
                    mCallback.updateStatus(strAction, -1, "");
            }
            catch(Exception e)
            {
                Log.e(TAG, e.getMessage());
            }
        }
    }
    Once a connection has been established, the UI looks like the following with all available controls enabled. You can click the Disconnect button to disconnect from the Arduino. You can slide the SeekBar left and right to control the motor's speed. You can turn the red and white LEDs on and off with the corresponding Switch.

    Image 7

    When Bluetooth notifications are received, we can simply call an interface method that is being implemented by the activity. In the updateStatus() implementation, we do different things depending on which of the notifications was received. If the Bluetooth device was either connected or disconnected, we just update a status control on the UI (like above). The state of the adapter can change in one of four ways: STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, and STATE_OFF. If the adapter is turning on, we simply display a brief Toast message. Most of the time you may not even see this message because the activity that is enabling the adapter still has its own messages on the screen. Once the adapter is actually on, we update a status control on the UI and display a brief Toast message as a reminder to click the Connect button now that a connection can be made. If the adapter is turning off, we send 'stop' messages to the DC motor and the two LEDs. These messages are sent before the STATE_OFF notification so they have time to be processed before the Bluetooth socket is closed. And lastly, if the adapter is actually off, we update a status control on the UI and close the Bluetooth socket. Code for all of this looks like:
    Java
    @Override
    public void updateStatus( String strAction, int nState, String strDevice )
    {
        Log.d(TAG, getClass().getName() + "::updateStatus()");
    
        try
        {
            if (strAction.equals(BluetoothAdapter.ACTION_STATE_CHANGED))
            {
                if (nState == BluetoothAdapter.STATE_TURNING_ON)       // 11
                    Toast.makeText(MainActivity.this, "Bluetooth is turning on...", Toast.LENGTH_LONG).show();
                else if (nState == BluetoothAdapter.STATE_ON)          // 12
                {
                    tvBluetoothStatus.setText("Enabled");
                    Toast.makeText(MainActivity.this, "Click the Connect button to connect to a device.", Toast.LENGTH_LONG).show();
                }
                else if (nState == BluetoothAdapter.STATE_TURNING_OFF) // 13
                {
                    Toast.makeText(MainActivity.this, "Bluetooth is turning off...", Toast.LENGTH_LONG).show();
    
                    // send 'stop' requests to the motor and LEDs before the BT pipe is disconnected in BluetoothAdapter.STATE_OFF
    
                    seekbar.setProgress(50); // 50% sets motor to 0
    
                    ledWhite.setChecked(false);
                    ledRed.setChecked(false);
                }
                else if (nState == BluetoothAdapter.STATE_OFF)         // 10
                {
                    tvBluetoothStatus.setText("Disabled");
    
                    // for some reason, when the adapter is disabled, the socket remains 'open' so we need to explicitly close it
                    if (MainApplication.btSocket != null)
                    {
                        MainApplication.btSocket.close();
                        MainApplication.btSocket = null;
                    }
                }
            }
            else if (strAction.equals(BluetoothDevice.ACTION_ACL_CONNECTED))
            {
                tvDeviceStatus.setText("Connected to " + strDevice);
                Toast.makeText(MainActivity.this, "Slide the bar left or right to control the speed of the motor.", Toast.LENGTH_LONG).show();
            }
            else if (strAction.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))
                tvDeviceStatus.setText("Disconnected");
    
            updateControls();
        }
        catch(Exception e)
        {
            Log.e(TAG, e.getMessage());
        }
    }
    There are three other handlers (or two since two of them are nearly identical) we need to discuss: the DC motor and the two LEDs. The DC motor's speed is controlled by a SeekBar, and the two LEDs are each controlled by a Switch. For the latter, it's a simple matter of sending the appropriate bytes to the Arduino. The first of the two bytes is either an asterisk for the white LED or a pound sign for the red LED. The second of the two bytes is either 0 for off or 1 for on. Code for these two handlers looks like:
    Java
    ledWhite = (Switch) findViewById(R.id.ledWhite);
    ledWhite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
    {
        @Override
        public void onCheckedChanged( CompoundButton buttonView, boolean isChecked )
        {
            Log.d(TAG, "White LED checked = " + isChecked);
    
            if (isChecked)
                writeData("*1");
            else
                writeData("*0");
        }
    });
    
    ledRed = (Switch) findViewById(R.id.ledRed);
    ledRed.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
    {
        @Override
        public void onCheckedChanged( CompoundButton buttonView, boolean isChecked )
        {
            Log.d(TAG, "Red LED checked = " + isChecked);
    
            if (isChecked)
                writeData("#1");
            else
                writeData("#0");
        }
    });
    Those two are pretty straightforward. For the DC motor. however, it's not any more complicated per se, but there is the issue of mapping the motors' -400 to +400 range to the SeekBar's 0-100 range. When the SeekBar is in the 50% position, that maps to 0 (off) for the DC motor. When the SeekBar is in the 0% or 100% position, that maps to -400 or +400 for the DC motor. Consider this illustration:
    SeekBar position     0%       25%    50%    75%    100%
    DC motor value      -400     -200     0     200     400
    The way I chose to go about translating one value to the other is first getting the range of the DC motor values, or 800 in this case. When the onProgressChanged() method is called, one of the parameters passed is the percent (i.e., progress). It's a whole number so we'll need to divide it by 100 to get it down to a decimal. Now we can multiply this decimal percent by the above range to get how far we are into that range. For example, if the SeekBar is at 25 percent, we should be 25 percent of the way into 800, which is 200. The value is right but not the sign. Since 25 percent is to the left of 0, our answer should be negative. The way to fix that is to simply add the motor's minimum value of -400 to our answer. So, -400 + 200 equals -200, which is the 25 percent value. Similarly, if the SeekBar is at 75 percent, we should be 75 percent of the way into 800, which is 600. The sign is right but not the value. Again, by adding the motor's minimum value of -400 to our answer, we get -400 + 600 equals 200, which is the 75 percent value. With that, the code to set the motor's speed looks like:
    Java
    int MIN  = -400;
    int MAX  = 400;
    int SPAN = MAX - MIN;
    
    @Override
    public void onProgressChanged( SeekBar seekBar, int progress, boolean fromUser )
    {
        Log.d(TAG, "Progress = " + progress + "%");
    
        double dProgress = progress / 100.0;
        String strSpeed = String.format(Locale.getDefault(), "%d", (int) Math.floor(MIN + (SPAN * dProgress)));
        tvSpeed.setText(strSpeed);
    
        writeData(strSpeed);
    }
    Something that I noticed part way through this exercise is that the listeners for the SeekBar and both of the Switch controls are notified whether a change is made from the UI or from code. For example, I initially had code in place that would send the 'off' command to the white LED, and then turn the white LED switch off. I later discovered that I could remove the code that was sending the 'off' command to the white LED and just use the code that was turning the 'white' switch off. The handler for the 'white' switch would take care of sending the 'off' command to the white LED. Doing this for both LEDs and for the DC motor allowed me to have one single location (rather than multiple) that sent data over Bluetooth to the Arduino. Sort of reminds me of a quote I saw on a poster over 20 years ago by French writer Antoine de Saint-Exupery, "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

    With both LEDs on and the motor spinning along rather briskly, our screen finally looks like:

    Image 8

    Conclusion

    There is undoubtedly lots of room for improvement with this project, but hopefully I was able to successfully show how to control an Arduino motor shield with an Android device over Bluetooth.

    Enjoy!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)