In this post we will explore mobile development for Arduino. The Internet of Things (IoT) and the Quantified Self are the two most significant trends in mobile technology.

Knowing how to develop software that interfaces with hardware via a mobile device is a skill every mobile developer should possess. For example, here at stable|kernel, we developed a mobile application for Rheem that allows users to control their home HVAC system with their mobile device.

Fortunately, many open source hardware solutions exist such as Arduino, Raspberry PI, RedBoard and a few others, which provides developers an opportunity to cultivate a thorough understanding of electronics and learn how to integrate them using software. This series of blog posts will focus on Arduino since it is the most prominent of these solutions.

So what exactly is Arduino?

mobile-development-arduino

Simply stated, it is a small computer with input ports that allow the user to gather data and output ports that allow the user to react to said data.

There are a few different types of Arduino boards, shields and modules. Arduino boards are the foundation of any Arduino project. They are comprised of a CPU and I/O ports, which vary depending on the model.

  • Arduino shields are peripheral extensions for a board. For instance, the Arduino Ethernet Shield or Arduino Wifi Shield could be used to enable network connectivity for the Arduino Uno board.
  • Arduino modules are sensors that can connect to a board and gather data.
  • The Arduino ecosystem is very modular, allowing users to easily and quickly create complex systems.

Getting Started with mobile development for Arduino

The best way to get started with Arduino is to acquire a starter kit. The official Arduino Starter Kit comes complete with all the necessary components to actualize an IoT prototype. The Arduino Starter Kit includes an Arduino Uno board, a breadboard (a solderless circuit board), numerous sensors, electrical components such as resistors and wires, and an instructional book. There are also several third-party sensor kits, which I would I recommend procuring after completing the exercises in the Starter kit.  My personal favorites are the SunFounder kits found here and here.

As an aside, I would also recommend a tackle box and magnifying glass to alleviate the hassle of storing and identifying all the small pieces in the kit.  As you work through the exercises in the instructional book, you will quickly begin to realize that hardware is the new software. Series and parallel circuits are nothing more than physical if/else and switch statements, respectively.

Arduino’s IDE, Arduino Studio, is a very simple and not intimidating programming solution. It has a viewport for sketch files, which are the programs Arduino boards execute, and it has another viewport for logging and monitoring the board’s serial data throughput. It basically feels like a refined text editor with a debugger view. Sketch files are written in the C language and are composed of two methods: setup() and loop().

The Arduino SDK also comes with many libraries that you can leverage to work with a particular module or component. For example, all that is required to work with a complex component like a Servo motor is to simply type #include <Servo.h>. Sketch files are very easy to upload to Arduino boards. The only shortcoming of Arduino Studio is that it doesn’t feature dynamic text completion, depending on what version you are using, so if you are not familiar with C’s syntax or Arduino’s library methods you will definitely spend a fair amount of time on their documentation page.

Now, for the fun part, developing an Android application that communicates directly to the Arduino board via a USB connection and plays a tone through the piezo that was selected on the mobile device.

How to develop an Android application that communicates directly to the Arduino board:

NOTE: These steps assume that you’ve already created a new Android project with a single main Activity

1. Declare the required variables in the Activity

We extensively use Android’s USB classes for this application.  A UsbDevice instance represents a physical USB device (the Arduino in this case) that is attached to the host Android device.  A UsbDevice can have multiple UsbInterfaces each possibly having multiple UsbEndpoints.  The UsbEndpoints are like URLs for the USB devices.  They allow for the transfer of data to and from the device on an open UsbDeviceConnection.

private static final String TAG = "MainActivity";

private static final String ACTION_USB_PERMISSION = "USB_PERMISSION";
private static final int RQSID_SET_LINE_CODING = 0x20; //32
private static final int RQSID_SET_CONTROL_LINE_STATE = 0x22; //34
private static final int RQSID_SET_REQUEST_TYPE = 0x21;
private byte[] encodingSetting = new byte[] {(byte) 0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08 };

UsbInterface activeUsbInterface;
UsbDeviceConnection activeUsbDeviceConnection;
UsbDevice activeUsbDevice;
UsbEndpoint activeEndpointIn;
UsbEndpoint activeEndpointOut;
String[] musicalNotes = new String[] { "A","B","C","D","E","F","G"};

 

2. Create Broadcast Receivers for USB communication

This application needs two broadcast receivers in the Activity to kick off communication. The first receiver will receive a callback whenever the USB has been either attached or detached.  The second broadcast receiver will receive a callback when the USB connection is either granted or denied permission to communicate with the Arduino.  This will allow the application to determine whether the user has granted permission to interact with the Arduino. From there we will call connect and disconnect methods for the USB.

private final BroadcastReceiver usbConnectionAttachedReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(@NonNull Context context, @NonNull Intent intent) {
      activeUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
      Log.d(TAG, "ACTION_USB_DEVICE_ATTACHED: n" + activeUsbDevice.toString());
      connectUsb();
  }
};

private final BroadcastReceiver usbConnectionDetachedReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(@NonNull Context context, @NonNull Intent intent) {
      UsbDevice detachedDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
      if (detachedDevice != null) {
          Log.d(TAG, "ACTION_USB_DEVICE_DETACHED: " + detachedDevice.toString());
          if (detachedDevice == activeUsbDevice) {
              disconnectUsb();
          } else {
              Log.d(TAG, "device == arduino, cannot disconnect n" + detachedDevice.toString() + "n" + activeUsbDevice.toString());
          }
      } else {
          Log.d(TAG, "device == null, cannot disconnect ");
      }
  }
};

private final BroadcastReceiver usbPermissionReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(@NonNull Context context, @NonNull Intent intent) {
      Log.d(TAG, ACTION_USB_PERMISSION);
      UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
      if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) && device != null) {
          connectUsb();
      } else {
          Log.d(TAG, "permission denied for device ");
      }
  }
};

private void connectUsb() {
  Log.d(TAG, "connecting USB");

  findEndPoints();
  if (activeUsbInterface != null) {
      setupUsbComm();
  }
}

private void disconnectUsb() {
  Log.d(TAG, "disconnecting USB");
  if (activeUsbDeviceConnection != null) {
      if (activeUsbInterface != null) {
          activeUsbDeviceConnection.releaseInterface(activeUsbInterface);
          activeUsbInterface = null;
      }
      activeUsbDeviceConnection.close();
      activeUsbDeviceConnection = null;
  }
  activeUsbDevice = null;
  activeUsbInterface = null;
  activeEndpointIn = null;
  activeEndpointOut = null;
}

In the onCreate method, be sure to register the broadcast receivers and set up the listView.

ListView notesListView = (ListView) findViewById(android.R.id.list);

ArrayAdapter<String> musicalNotesAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, musicalNotes);
notesListView.setOnItemClickListener(this);
notesListView.setAdapter(musicalNotesAdapter);

registerReceiver(usbPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
registerReceiver(usbConnectionAttachedReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED));
registerReceiver(usbConnectionDetachedReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));

 

3. Determine if attached device is an Arduino device.

Since a USB interface can have multiple endpoints. We need to identify the particular endpoint that allows for bulk transfer to the Arduino.

private void findEndPoints() {
  activeUsbInterface = null;
  activeEndpointOut = null;
  activeEndpointIn = null;

  if (activeUsbDevice == null) {
      UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
      HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
      activeUsbDevice = deviceList.values().iterator().next();
  }

  if (activeUsbDevice != null) {
      for (int i = 0; i < activeUsbDevice.getInterfaceCount(); i++) {
          UsbInterface usbInterface = activeUsbDevice.getInterface(i);
          UsbEndpoint endpointOut = null;
          UsbEndpoint endpointIn = null;

          int endpointCount = usbInterface.getEndpointCount();
          Log.d(TAG, "Endpoint count = " + endpointCount);
          if (endpointCount == 2) {
              for (int j = 0; j < endpointCount; j++) {
                  UsbEndpoint endpoint = usbInterface.getEndpoint(j);
                  if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                      if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
                          endpointOut = usbInterface.getEndpoint(j);
                      } else if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
                          endpointIn = usbInterface.getEndpoint(j);
                      }
                  }
              }

              if (endpointOut != null && endpointIn != null) {
                  activeUsbInterface = usbInterface;
                  activeEndpointOut = endpointOut;
                  activeEndpointIn = endpointIn;
              } else {
                  Log.w(TAG, "One or more endpoints not found");
              }
          }
      }
  }
}

 

4. Establishing communication with the Arduino

The Arduino Uno comes with a USB-to-serial converter. The firmware on this converter is recognized as a USB Communication Device Class. This is what allows a user to simply plug in the Arduino Uno to their mobile device, and communicate with the Arduino over an emulated serial communication interface.

Here, implement two controlTransfers that initialize the Arduino USB serial converter.  The first call sets the control line state, the second call sets the line encoding (9600, 8N1). This baseline transfer is what enables constant communications with the Arduino once a connection is established.

private void setupUSBComm() {
  UsbManager manager = (UsbManager) getActivity().getSystemService(Context.USB_SERVICE);
  Boolean permitToRead = manager.hasPermission(arduinoDevice);

  if (permitToRead) {
      usbArduinoConnection = manager.openDevice(arduinoDevice);
      if (usbArduinoConnection != null) {
          usbArduinoConnection.claimInterface(activeUSBInterface, true);

          //requestType, SET_CONTROL_LINE_STATE, value, index, buffer, length, timeout
          usbArduinoConnection.controlTransfer(RQSID_SET_REQUEST_TYPE, RQSID_SET_CONTROL_LINE_STATE, 0, 0, null, 0, 0);
         
          usbArduinoConnection.controlTransfer(RQSID_SET_REQUEST_TYPE, RQSID_SET_LINE_CODING, 0, 0, encodingSetting, 7, 0);
      }

  } else {
      manager.requestPermission(arduinoDevice, permissionIntent);
      Log.d(TAG, "App doesn't have permission to read.");

  }
}

 

5. Last thing on the Android side is to turn the ListView selection into bytes and send it through the Arduino connection

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  playNote(position);
}

public void playNote(int note_value) {
  byte[] bytesOut = new byte[]{ (byte) note_value};

  if(arduinoDevice != null){
      usbArduinoConnection.bulkTransfer(endpointOut, bytesOut, 1, 0);
  }
}

 

6. Now, on the Arduino side, read the bytes and send them to the Piezo

Declare an int variable called speakerPin to represent digital pin 9 . In the setup method, set the pinMode for speakerPin to OUTPUT. Also open the serial port so that the Arduino can receive the data coming from the phone. In the loop method, first check to determine whether or not the serial is available.  Next, we check to see if any data is coming through the serial port.  If so, the method playNote is called, which plays the selected tone.  Make sure you have board and port set correctly in Arduino Studio.  To do this, go to the tools menu in the toolbar and check the settings.

int speakerPin = 9;
int tones[] = { 440, 494, 523, 587, 659, 698, 784};

void setup() {
 pinMode(speakerPin, OUTPUT);
 Serial.begin(9600);
}

void loop() {
   if (Serial.available()) {
       int incomingByte = Serial.read();

       if (incomingByte > -1) {
         playNote(incomingByte);
       }     
   }
}

void playNote(int note) {
 tone(speakerPin, tones[note], 200);
}

 

7. Connect your physical Arduino components in the manner shown below

Mobile Development for Arduino

  • Connect your power (red jumper cable) and ground (black jumper cable) outer bus lines from the Arduino to the breadboard.  Be sure to connect to the 5-volt connection on the Arduino for power.
  • Place the piezo on the breadboard and connect one end to the ground and the other to the 9 digital pin on the Arduino.

Mobile Development for Arduino

  • Now, all you have to do is upload your sketch, launch the app to start the broadcast receivers, connect your phone to the Arduino via a USB OTG cable and run application.

Congratulations! You’ve created your first Internet of Things application.  In Part 2 of Mobile Development for Arduino, we’ll cut the cord on our mobile device-driven speaker.

Leave a Reply

Your email address will not be published. Required fields are marked *