Author Archives: Andreas Rudolf

Embedded Android Tutorial: Part II, Setting up the Arduino to measure light intensity and reconfigure the Pandaboard’s kernel for communication

In this post we are going to set up the Arduino Uno to measure light intensity. Furthermore, we configure and recompile the Pandaboard’s kernel to communicate with the Arduino Uno through USB-serial.

For measuring light, we will require some kind of photoresistor. If you do not have any spare ones around, you can easily get them on ebay. A photoresistor is a resistor that changes its resistance depending on the current light intensity. In the end, we will simply measure an analogue voltage that changes depending on the attached photoresistor.

For this tutorial you will need:

  • Pandaboard with Linaro’s Android build from Part I
  • Arduino Uno
  • 1 Photoresistor
  • 1 Resistor with a value around 3 kilo-ohm (we are using 3.9kOhm)
  • Breadboard
  • 3 jumper wire cables (male-male)

Lets get started with setting up the Arduino Uno
The following diagram shows the components and connections for interacting with the Arduino Uno. As can be seen, we will measure the voltage in between the photoresistor and the common resistor, as this terminal is connected to the Arduino’s analog in pin A0. At normal daylight, our photoresistor has a resistance of around 3kOhm (measured with a multimeter). So we choose the other resistor at around the same value of 3.9kOhm (this was just the closest resistor value we had lying around). This way, when there is normal light, the voltage at the A0 pin will be about half of 5V, so 2.5V, and yielding an ADC value of 128. If there is more light, the photoresistor’s resistance drops and the voltage at the terminal rises. And vice versa if there is less light.

So take out your breadboard and said components and try to rebuild the above diagram. If you do not have a photoresistor, you can also just attach a jumper cable to the A0 pin and measure different voltages such as the on-board 5V, 3.3V and GND pins.

Depending on your setup, this should look something like that …

… and like this when connected to the Arduino Uno
Arduino connected to breadboard

We will of course also need some firmware running on the Arduino. The following code will measure the voltage at the A0 pin 10 times per second and send back the values over the serial communication interface. So build and upload the following source file:
serverbox_arduino_lighlogger.c

Communication between the Arduino and your host computer

Once this is done, we can already try if everything works so far by connecting the Arduino to our host computer via USB. Right after connecting, run dmesg in a terminal. You should see something like new USB device, and the corresponding name, ttyACM0 in our case:

user@host:~$ dmesg
...
[185930.828750] usb 1-2.4.2: new full-speed USB device number 21 using ehci_hcd
[185930.924549] cdc_acm 1-2.4.2:1.0: ttyACM0: USB ACM device

Let’s have a look at the newly created device node on the host computer:

user@host:~$ ls -l /dev/ttyACM*
crw-rw---- 1 root dialout 166, 0 Nov 28 17:38 /dev/ttyACM0

The Arduino shows itself as a device node with major number 166, which is used for USB ACM devices. You can double check that in the kernel documentation under /opt/android/source/kernel/Documentation/devices.txt. The actual ACM device is the small ATmega16u2 controller right next to the USB connector. We will use this device node for serial communication, so we need to adjust the permissions:

user@host:~$ sudo chmod 666 /dev/ttyACM0

Now there are many different ways to read from the serial console. I personally like to use hterm (you will need the ia32-libs package if your host is 64 Bit.. which should already be installed if you followed the guide in part 1). You can also use gtkterm, or even minicom. Our firmware on the Arduino simply writes the current ADC value (0…255) to the serial console. So if this output is interpreted as ASCII symbols, this will pretty much look like bogus. Some terminal programs such as hterm allow you to change the output type to hex or dec. Minicom does not have this functionality however. If you want to use a terminal program, just remember to use the following settings:

Port: /dev/ttyACM0
Speed: 9600baud
Data-Parity-Stopbits: 8N1

In this tutorial we will use a small program which reads directly from the created serial device node.
termios.c
Download the above source file, compile it with gcc and run it.

user@host:~$ gcc termios.c -o termios
user@host:~$ ./termios
read 136: 166
read 1: 157
read 1: 159
read 1: 160
read 1: 160
read 1: 158
...

If you do not see any output, make sure the device node still has the appropriate permissions as set above (sudo chmod 666 /dev/ttyACM0), also the device node name /dev/ttyACM0 is hard-coded in this example. So if your device node is called ttyACM1 for example, make sure to adjust it in termios.c as well. If everything works, try to shade your photoresistor or brighten it up with a flash light. The values should update accordingly.

Configuring the Pandaboard to communicate with the Arduino
One can communicate between the Arduino and the Pandaboard pretty much in the same way. However, the current Pandaboard kernel does not have USB ACM support yet. So we need to reconfigure and recompile the kernel with this configuration enabled. With the new kernel running, a device node /dev/ttyACM0 will be created on the Pandaboard as soon as the Arduino is connected, just like on our host computer.
The current kernel configuration is called android_omap4_defconfig. We will use this config and add USB ACM support.

user@host:/opt/android/source/kernel$ cp arch/arm/configs/android_omap4_defconfig .config
user@host:/opt/android/source/kernel$ make menuconfig ARCH=arm

Now navigate to Device drivers, USB support, and enable USB Modem (CDC ACM) support by pressing “Y”. Navigate back with Escape and save your changes. We will also save the new config for later:

user@host:/opt/android/source/kernel$ mv .config arch/arm/configs/android_omap4_cdc_defconfig
user@host:/opt/android/source/kernel$ make clean
user@host:/opt/android/source/kernel$ make mrproper

Our kernel was automatically built through the Android build system and we need to make adjustments so that from now on our modified kernel configuration is used.

user@host:/opt/android/source/kernel$ cd ..
user@host:/opt/android/source$ gedit device/linaro/pandaboard/BoardConfig.mk

In the above BoardConfig.mk file, we replace all occurences of android_omap4_defconfig with our new config android_omap4_cdc_defconfig. Now we are ready to rebuild the kernel:

user@host:/opt/android/source$ . build/envsetup.sh
user@host:/opt/android/source$ choosecombo 1 pandaboard 3
user@host:/opt/android/source$ make boottarball TARGET_TOOLS_PREFIX=../android-toolchain-eabi/bin/arm-linux-androideabi-

The new kernel will be found in the out folder and needs to be copied onto the SD card’s boot partition:

user@host:/opt/android/source$ cp out/target/product/pandaboard/boot/uImage /media/boot
user@host:/opt/android/source$ sync
user@host:/opt/android/source$ umount /media/*

Congratulations! You have just recompiled and deployed the kernel to support USB ACM devices. On the Pandaboard’s Android terminal, run ‘dmesg’ right after connecting the Arduino to your Pandaboard. You should see that a new device node /dev/ttyACM0 was created.

So what now? We have ACM support enabled on our Pandaboard’s kernel, now we need a user space program to actually read from the created device node. The cool thing is that we already have an existing command line program, the exact same as above – termios.c – which we used on the host computer. Earlier we used gcc to compile this program for the host computer, this time however we need to cross-compile the exact same program for our target, the Pandaboard, and thus compile it for an ARM architecture.

Again, there are multiple ways to do this. Here we will present a method that is very similar to the way that Android’s external projects are built. First of all download the Android Native Development Kit (Android NDK). Make sure that you choose the right version (Linux 64-bit). Extract the Android NDK anywhere you like, it’s also a good idea to add the installation directory to your PATH variable. Inside the extracted Android NDK folder, there are many good sample applications for using JNI and also a well-written documentation in html form. Be sure to check that out some time. For now, we are only going to use the ‘ndk-build’ executable inside the Android NDK top folder. When calling the ‘ndk-build’ executable from the command line, it will look for a sub-folder called ‘jni’ (relative to the current working directory), and within this folder it will parse a file named Android.mk. The reason for that is that ndk-build is normally used in conjunction with a Java/Android project (just like the ones created with Eclipse ADT Bundle), and all native C code inside an Android project source folder is placed in a sub-folder called jni. Inside this jni folder there should be at least said Android.mk file and also of course the native source file, termios.c in our case.

user@host:~$ mkdir termios
user@host:~$ cd termios
user@host:~/termios$ mkdir jni
user@host:~/termios$ cd jni
user@host:~/termios/jni$ gedit termios.c
user@host:~/termios/jni$ gedit Android.mk

The contents for termios.c can again be downloaded here. The contents for the Android.mk file should be :

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= termios.c
LOCAL_MODULE := termios
include $(BUILD_EXECUTABLE)

(compare this to /opt/android/source/external/ping/Android.mk)
Within the created termios folder call ‘ndk-build’

user@host:~/termios$ ndk-build

This will cross-compile the program termios.c as an executable for our ARM target architecture and save it under termios/libs/armeabi/termios. Use the Android Debug Bridge (adb) to push this file to the target, and then execute it on the Pandaboard. You should see a similar output as on the host computer.

user@host:~/termios$ adb push libs/armeabi/termios /termios
user@host:~/termios$ adb shell
root@android:/# chmod 777 termios
root@android:/# ./termios

In this tutorial’s next part we will show you how to create a JNI wrapper function and thus how to use JNI to communicate with external hardware from within an Android Java application.

Arduino USB transfers

In this post we will show you how to communicate between the Arduino Uno and your computer using plain USB bulk and control transfers, not relying on a serial communication interface. Our development computer runs Ubuntu 11.10 (Oneiric Ocelot).

This is a follow up to Programming the Arduino Uno with Eclipse on Linux. The next post in this series is Android USB Host + Arduino: How to communicate without rooting your Android Tablet or Phone.

The Arduino Uno has an on-board USB-to-serial converter (ATMega8u2 or ATMega16u2 since rev. 3). The preloaded firmware on this converter presents itself as an USB Communication Device Class (CDC). Usually, no further software driver is needed on the host computer. You can simply plug in the Arduino Uno to your computer, and you will be able to communicate with the Arduino’s main microcontroller, the ATMega328, over an emulated serial communication interface.

For many applications, a serial communication interface is just fine. You can use minicom, gtkterm or a similar terminal application to send and receive data from your microcontroller. Or even use a python serial library and start off from there. In some circumstances however, you might not have the possibility to open a serial communication. Android for instance does not have a native serial communication interface, instead it features an USB host API. In this case, you must rely solely on USB control and bulk transfers.

When you connect an Arduino Uno to your linux computer with an USB cable, you will notice that a new device node /dev/ttyACM0 or similar is created. For this purpose, check out the last few lines from dmesg right after connecting the Arduino Uno.
$ dmesg | tail
...
[] usb 1-5.2: new full speed USB device number 43 using ehci_hcd
[] cdc_acm 1-5.2:1.0: ttyACM0: USB ACM device

In the above example, the Arduino Uno is assigned to USB device number 43, and a new device node /dev/ttyACM0 is created. This device node is automatically created by the module cdc_acm and represents a serial communication interface which you can use with minicom, gtkterm, etc. However, we are not going to do that. We want to find out how to talk to this device using USB transfers, right? ;-)

Further investigations with the vendor (0×2341) and product (0×0001) id …
lsusb -v -d 2341:0001
… yield more interesting information about this device. For instance, bNumInterfaces = 2 shows that there are two available interfaces. This stems from the mentioned pre-loaded Arduino firmware. There you will see two projects:

  • arduino-usbdfu is the Arduino USB DFU bootloader firmware, which is used for flashing the ATmega8u2 or ATmega16u2 respectively.
  • arduino-usbserial is the real firmware of the USB-to-serial converter. This is where the magic happens :-)

Again, looking at the output of …
lsusb -v -d 2341:0001
… you will see two interface descriptors. The first one with bInterfaceNumber=0 is the DFU bootloader, and the second one with bInterfaceNumber=1 is our usb-serial firmware interface descriptor. You can double-check that in arduino-usbdfu/Descriptors.c and arduino-usbserial/Descriptors.c respectively.

The starting point of the usb-serial project is in firmwares/arduino-usbserial/Arduino-usbserial.c. At the top, a variable VirtualSerial_CDC_Interface is initialized, and it is then passed in the main’s for-loop:
...
CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
USB_USBTask();

The function CDC_Device_USBTask() is part of the LUFA-lib (Lighweight USB Framework Library), and is defined in CDCClassDevice.c. Likewise, this source file can be considered as the heart-piece of the usb-serial firmware. There we find all important functions such as CDC_Device_SendByte and CDC_Device_ReceiveByte. You will notice that almost every function returns immediately if no LineEncoding/Baudrate has been set. Luckily, the function CDC_Device_ProcessControlRequest (also in CDCClassDevice.c) unravels the mysteries of setting the LineEncoding, Baudrate, LineState, etc. Here is a shortened excerpt:

void CDC_Device_ProcessControlRequest() {
 switch (USB_ControlRequest.bRequest){
  case CDC_REQ_SetLineEncoding:
  if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)){
   CDCInterfaceInfo->State.LineEncoding.BaudRateBPS = Endpoint_Read_32_LE();
   CDCInterfaceInfo->State.LineEncoding.CharFormat  = Endpoint_Read_8();
   CDCInterfaceInfo->State.LineEncoding.ParityType  = Endpoint_Read_8();
   CDCInterfaceInfo->State.LineEncoding.DataBits    = Endpoint_Read_8();
  }
...

Now we’re cooking :-) . To set the LineEncoding we therefore need to send an USB control request with bRequest = CDC_REQ_SetLineEncoding = 0×20 (as defined in CDCClassCommon.h) and with bmRequestType = 0×21. Also, the data must hold 7 bytes with corresponding values.

Once you know which requests to look for, it is pretty straightforward. Use an USB sniffer like wireshark and observe what happens when you plug in your Arduino board. With wireshark, you can also search for a specific bmRequestType by applying a filter like:
usb.bmRequestType == 0x21

As it turns out, there are two important control transfers when connecting the Arduino board to the computer. The first one has bRequest = CDC_REQ_SetControlLineState = 0×22 = 34 and the second one bRequest = CDC_REQ_SetLineEncoding = 0×20 = 32.

If you want to try it for yourself, download and run this python script (with sudo). This script will initialize the usb-serial-converter with USB control transfers and then send single bytes from your computer to the Arduino Uno using bulk transfers. Just make sure to remove the cdc_acm module beforehand by issuing:
$ sudo rmmod cdc_acm
As this module will otherwise lock access to the USB device. Also, you will need appropriate software running on your Arduino board. You may download this source code and flash it onto the device. This will set the duty cycle of a PWM signal on PIN3 according to the byte received from the computer. Also it will light up the on-board LED if the received byte is an odd value and shut it off when an even value is received.

Programming the Arduino with Eclipse on Linux

This is the first of a series of three posts. The ultimate goal is to setup a communication interface between an Arduino Uno/Mega board and an Android tablet over USB. Everything will be as user-friendly as possible, i.e. no root will be required on your Android tablet. The following posts Arduino USB transfers and Android USB Host + Arduino: How to communicate without rooting your Android Tablet or Phone conclude the series.

The Arduino Uno is a popular and affordable hardware platform which comes with its own IDE and core libraries for programming. If you like the Arduino IDE you may skip this post, however, if you feel more comfortable developing in Eclipse this might help you out.
We are using Ubuntu 11.10 (oneiric ocelot) and Eclipse 3.6.1 (helios), although this should not play an important role (as it turns out, Eclipse Indigo might need a few tweaks, check the end of this post). Also, setting up Eclipse for programming the Arduino Uno Atmega328P is pretty straightforward. Nevertheless, here is a quick how-to for the Arduino Uno (Arduino Mega 2560 below):

  1. Install avrdude and AVR libraries:
    sudo apt-get install gcc-avr avr-libc avrdude
  2. Start Eclipse and install CDT Plugin (C/C++ Development Tools):
    • Help -> Install New Software…
    • Work with: (your current Eclipse version)
      i.e. “Helios – http://download.eclipse.org/releases/helios”
    • Download the “pending” software package (don’t worry, download starts automatically ;-) )
    • Choose “Programming Languages” and select “C/C++ Development Tools”
    • Accept and continue by restarting Eclipse
  3. Install AVR Eclipse Plugin:
    • Help -> Install New Software…
    • Add new repository: http://avr-eclipse.sourceforge.net/updatesite/
    • Re-download the “pending” software package, download will be faster since it is probably cached :-D
    • Download AVR Eclipse Plugin and restart Eclipse
  4. Create new C project named “BlinkBlink”:
    • Project Type AVR Cross Target Application (Empty Project, AVR-GCC Toolchain)
    • Click next…
    • Untick “Debug” (in Debug mode, no hex-files are generated and avrdude can’t flash the device)
    • Click Advanced settings…
    • AVR -> AVRDude -> Programmer configuration…
    • Create a new programmer and name it “Arduino Uno”. Make sure this newly created programmer configuration is selected for the current project.
      • Programmer Hardware: Arduino
      • Override default port: /dev/ttyACM0 or similar
      • Override default baudrate: 115200
    • AVR -> Target Hardware:
      • MCU Type: ATmega328P (or load from MCU)
      • MCU Clock Frequency: 16000000 (default external clock source of Arduino Uno)
  5. Click Apply and OK to leave the properties window and click Finish to create the new project in the workspace.
  6. Create a new source file main.c and copy the contents from this link. Make sure to save main.c before proceeding (File -> Save).
  7. Project -> Build Project
  8. Click on the AVR Button within Eclipse to upload the generated hex file from BlinkBlink/Release/BlinkBlink.hex.
    Your Arduino Uno’s LED should be blinking on and off repeatedly. If somehow it doesn’t work, right-click your Project and select Properties. Make sure all AVR and Programmer settings are active as mentioned above.

For the Arduino Mega 2560 you should choose Atmel STK500 Version 2.x firmware as Programmer Hardware, and ATMega2560 as target hardware, the rest is the same as with the Arduino Uno. Also, if you are using the above source file for testing, you should change the definition of LED from PB5 to PB7, since the LED on the Arduino Mega is on Pin7 of Port B.

Update: if you are using Eclipse Indigo, some specific AVR symbols such as DDRB (data direction register of port b) may not be recognized.
To solve this problem go to preferences…

  • C/C++
  • Language Mappings
  • Add the following mappings:
    • Content Type: C Header File / Language: GNU C
    • Content Type: C Source File / Language: GNU C

Your Language Mapping preference window should look like the following screenshot:

If it still does not work, also try adding this line at the beginning of your main.c source file:
#include <avr/iom128.h>

Oscione – an open source Android Oscilloscope

Hello everybody, it has been a while since our last post, nevertheless we are proud to introduce our new project:

Oscione – an open source Android Oscilloscope.

The project consists of both a custom hardware front-end for data acquisition and an Android application for visualizing the collected data samples to the user. Data is transferred over USB to the Beagleboard running Android, and then plotted on an external screen. As mentioned, the whole design is entirely open source, therefore everybody is highly encouraged to rebuild and further develop both the hardware and software parts of the application. Source code and layout files are all included in the above link. Furthermore, the Android application is considered to be a stand-alone Oscilloscope UI, with the ability to visualize other data sources such as the microphone input of an Android mobile phone.

In the above link, you will also find a technical report with detailed explanation of both hardware and software development.

Oscione User Interface

Oscione User Interface

Oscione custom hardware front-end

Oscione custom hardware front-end

Technical Report: Using Android in Industrial Automation

The title says it all, finally it is complete:
the long-awaited Technical Report of our project :-D
… which you can download from here

Using Android in Industrial Automation – Technical Report

This report aims to cover all important aspects when setting up an appropriate hardware and software platform for Android, and also leads the reader through our spectrum analyzer application development. On top of that, you will find a setup guide in the appendix, containing detailed instructions on how to build your Kernel and the Android root file system, and further information on how to make use of the DSP, etc. Although some chapters are not as detailed as others, we always try to provide you with a good starting point by leading you into the right direction. Also, dont be afraid of the length of this report since you should be able to pick only these chapters that are of most interest to you ;-)