Thursday, April 28, 2016

Beaglebone Black Analog input with Swift and the TMP36 sensor

In my earlier posts that should how to use Swift with the Beaglebone Black I used the SwiftyGPIO library to interact with digital GPIO ports.  While using digital GPIO ports can be very powerful the Beaglebone Black also has analog ports.  In this post I will show how we can use the TMP36 temperature sensor with an analog port to determine the temperature.

With the digital ports we measure one of two values (high or low).  With the analog ports we measure a range a values.  The Beaglebone Black provides seven analog ports labeled AIN0 through AIN6.  These ports are located on the P9 header and the following list shows what pin corresponds to which analog inputs:

AIN0  -  39
AIN1  -  40
AIN2  -  37
AIN3  -  38
AIN4  -  33
AIN5  -  36
AIN6  -  35

The following image shows the full headers:




Lets get started, if you have not already installed Swift on your Beaglebone Black you can check out my earlier post that talks about how to install it and also how to use the SwiftyGPIO library.  In this post we cannot use the SwiftyGPIO library because it does not support analog ports instead we will interact directly with GPIO ports through the file system.  This is how the SwiftGPIO actually works behind the scenes when you use it to access the digital GPIO.

Like always the first thing we need to do is to connect our sensor to the Beaglebone Black.  The following diagram shows how I connected the TMP36 temperature sensor to my Beaglebone Black.  Note that I have the TMP36 sensor connected to the AIN1 port.





Now that we have the TMP36 Temperature sensor wired to our Beaglebone Black lets power it up and see how we would read the Analog port.  Before we write our Swift code lets see how we would read the port manually from the shell.  The first thing we would need to do is to enable the analog ports.  To enable the ports we would echo “BB-ADC” to the  /sys/devices/platform/bone_capemgr/slots file.  Before we do that, lets look at the file.  If we run the following command we can see the contents of the file:

cat /sys/devices/platform/bone_capemgr/slots

The contents of the file should look something like this:

 0: PF----  -1
 1: PF----  -1
 2: PF----  -1
 3: PF----  -1
 4: P-O-L-   0 Override Board Name,00A0,Override Manuf,cape-universaln

Now lets enable the analog ports by running the following command:

echo BB-ADC > /sys/devices/platform/bone_capemgr/slots

If we cat out the contents of the slots file again, it should now look something like this:

 0: PF----  -1
 1: PF----  -1
 2: PF----  -1
 3: PF----  -1
 4: P-O-L-   0 Override Board Name,00A0,Override Manuf,cape-universaln
 5: P-O-L-   1 Override Board Name,00A0,Override Manuf,BB-ADC

The last line shows that the analog ports are now enabled.  Now that the ports are enabled, lets see how we can read the AIN1 port.  We can do that by reading the contents of the  /sys/bus/iio/devices/iio:device0/in_voltage1_raw file like this:

cat /sys/bus/iio/devices/iio:device0/in_voltage1_raw

The output of this command should be a number around 1700 or so.  If you look in the /sys/bus/iio/devices/iio:device0/ directory, you should see 7 in_voltage files numbered 0 through 7.  These files correspond to the seven AIN ports therefore if we connected our TMP36 temperature sensor to AIN2 instead of AIN1 we would read the in_voltage2_raw file.

Now that we know how to get the value from the analog ports, lets see how we would do this with Swift and convert that value to the current temperature.  The first thing we need to do is to create a function that will read the value from a file.  The following will do this and return an optional that would be either a String value or nil.

func readStringFromFile(path: String) -> String? {
      let fp = fopen(path, "r")
      guard fp != nil else {
            return nil
      }
      var oString = ""
      let bufSize = 8
      let buffer: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer.alloc(bufSize)
       defer {
            fclose(fp)
            buffer.dealloc(bufSize)
      }

      repeat {
            let count: Int = fread(buffer, 1, bufSize, fp)
            guard ferror(fp) == 0 else {
                  break
            }
            if count > 0 {
                  oString += stringFromBytes(buffer, count: count)
            }
      } while feof(fp) == 0
      return oString
}    
In this function we use the fopen() function to open the file.   We use a guard statement to verify that the file opened properly.  If the file did not open properly we return nil.  To ensure that the file is properly closed and the buffer that we used to read the data is properly released we use a defer block to close the file and release the buffer.

We then continuously read from the file with a repeat block until we reach the end of the file.  When we read from the analog files, we will only go though this loop once and read four or less bytes of data but it is good practice to keep the repeat block to ensure we are reading all data.

We convert the bytes that we read from the file to a string using the stringFromBytes() function.   The following code shows this function:

func stringFromBytes(bytes: UnsafeMutablePointer<UInt8>, count: Int) -> String {
      var retString = ""
      for index in 0..<count {
            if bytes[index] > 47 && bytes[index] < 58 {
                  retString += String(Character(UnicodeScalar(bytes[index])))
            }
      }
      return retString
}

In this function we loop through the byte array and if the value of the individual element is greater than 47 or less than 58 (ASCII representations of 0-9 because we only want numbers) then we convert the byte to a character and append it to the return string.

Using these two functions we would read the TMP36 temperature sensor and calculate the temperature like this:

var file = "/sys/bus/iio/devices/iio:device0/in_voltage1_raw"
if let input = readStringFromFile(file) {
      if let rawValue = Double(input) {  
            print("RawValue:  \(rawValue)")
            let milliVolts = (rawValue / 4096.0) * 1800.0
            print("milliVolts:  \(milliVolts)")
            let celsius = (milliVolts - 500.0) / 10.0
            print("Celsius:  \(celsius)")
            let fahrenheit = (celsius * 9.0 / 5.0) + 32.0
            print("Fahrenheit:  \(fahrenheit)")
      }
}
In this code we begin by reading the value from the /sys/bus/iio/devices/iio:device0/in_voltage1_raw file.  We then convert that string value to a Double value.  Once we have the Double value we need to convert it to millivolts using the (value/4096) * 1800 equations.  We then convert the millivolts to the temperature in Celsius and then convert the Celsius temperature to Fahrenheit.  This code will print out all of the values so you can see how everything is calculated.

To put all of this together, we would create a file named main.swift at put the following code into it:

import Glibc           
           
func stringFromBytes(bytes: UnsafeMutablePointer<UInt8>, count: Int) -> String {
      var retString = ""
      for index in 0..<count {
            if bytes[index] > 47 && bytes[index] < 58 {
                  retString += String(Character(UnicodeScalar(bytes[index])))
            }
      }
      return retString
}
           
func readStringFromFile(path: String) -> String? {
      let fp = fopen(path, "r")
      guard fp != nil else {
            return nil
      }
      var oString = ""
      let bufSize = 8
      let buffer: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer.alloc(bufSize)
       defer {
            fclose(fp)
            buffer.dealloc(bufSize)
      }

      repeat {
            let count: Int = fread(buffer, 1, bufSize, fp)
            guard ferror(fp) == 0 else {
                  break
            }
            if count > 0 {
                  oString += stringFromBytes(buffer, count: count)
            }
      } while feof(fp) == 0
      return oString
}    
           
var file = "/sys/bus/iio/devices/iio:device0/in_voltage1_raw"    
while(true) {
      if let input = readStringFromFile(file){
            if let rawValue = Double(input) {  
                  print("RawValue:  \(rawValue)")
                  let milliVolts = (rawValue / 4096.0) * 1800.0
                  print("milliVolts:  \(milliVolts)")
                  let celsius = (milliVolts - 500.0) / 10.0
                  print("Celsius:  \(celsius)")
                  let fahrenheit = (celsius * 9.0 / 5.0) + 32.0
                  print("Fahrenheit:  \(fahrenheit)")
                  usleep(1000000)
            }
      } else {
            break
      }
}
We would then compile this application like this: 

swiftc –o temperature main.swift

To run the application we need to ensure that we initiate the analog ports first with the echo BB-ADC > /sys/devices/platform/bone_capemgr/slots command.  You will only need to run this command once after booting your Beaglebone.  Once you initiate the analog ports you can read the temperature like this.

./temperature

If everything is properly connected you should see the temperature printed to the screen.



Tuesday, April 26, 2016

Beaglebone Black motion Detector with Swift and the HC-SR501 Sensor

In an earlier post I showed how we could use the Swift on the Beaglebone Black to read the state of a button to turn a LED on or off.  While this is a good start for using Swift with the Beaglebone Black, it really is not that exciting therefore in the next couple of posts I would like to show how we can interact with various sensors.  In this post I will show how we could use the HC-SR501 sensor to create a motion detector.

Before we start here are a couple of links that you may want to look at:
    -   You can look at this post to see how to install Swift on your Beaglebone Balck and also how to use the SwiftyGPIO library
    -  The datasheet for the HC-SR501 sensor can be found here.
    -   You can get the SwiftyGPIO library here.

 The first thing we need to do is to wire the HC-SR501 sensor to our Beaglebone Black.  The following diagram shows how we would connect the sensor to our Beaglebone Black.





One thing with the HC-SR501 sensor, is there really isn't a good way to have it stand up by itself therefore I made a stand that I printed using my Monoprice Maker Select 3D printer.  You can find the STL file for the sensor on thingiverse here.  The following image is a picture of my setup:




Now that we have everything wired up we can write our application that will interact with the HC-SR501 sensor through a GPIO port on the Beaglebone Black.  We need to start by creating a directory for our code.  Lets name this directory motion_detector.  We would create this directory with the following command  mkdir motion_detector

In the motion_detector directory copy the SwiftyGPIO.swift file from SwiftyGPIO library.  Now create a file named main.swift and put the following code in it:

     import Glibc

     let gpios = SwiftyGPIO.getGPIOsForBoard(.BeagleBoneBlack)
     var sensor = GPIO(name:"GPIO_60", id: 60)
    
     sensor.direction = .IN
    
     while(true){
          if sensor.value == 0 {
     print("No Motion")
          } else {
             print("INTRUDER")
          }
          usleep(100000)
     }

In this file we start off by importing the GLibc module.  In the next line we retrieve the list of GPIOs available for the BeagleBone Black.  We then get a reference to GPIO_60 (pin 12 of the P9 expansion header).  

The next line configures the port direction for the GPIO port.  We can use GPIODirection.IN or GPIODirection.OUT here.  In this example we used GPIODirection.IN because we want to read the state of the port.  Next we create a while loop.  Within the while loop we check the state of the port and if it is low (value of 0) we print the message “No Motion” to the console letting us know there was no motion detected otherwise we print the message “INTRUDER” to the console letting us know that the sensor detected motion.  We then use the usleep function to pause before we loop back.

To compile this application we use the following command:

    swiftc –o sensor SwiftyGPIO.swift main.swift 

This command uses the swift compiler to compile SwiftyGPIO.swift and main.swift and writes the output to the file named sensor.  We are now able to run our application.  The following command will run our application.

    sudo ./sensor

If everything is connected correctly the application will print either “No Motion” or “INTRUDER” to the screen depending on if it detects motion or not.


Sunday, April 10, 2016

IPv6 over Bluetooth on the BeagleBone Black

When we remotely connect to a BeagleBone Black we generally SSH into them over a WiFi or wired network.  There are several drawbacks to using a WiFi or wired network connection with a BeagleBone Black especially with robotic projects.  In this post I will demonstrate how we can use 6LoWPAN over Bluetooth Smart to connect two BeagleBone Blacks together.  This will allow us to use standard Internet technologies like SSH or HTTP over a Bluetooth connection. 

6LoWPAN (IPv6 over Low Power Wireless Networks) for Bluetooth was introduced with the Bluetooth Smart 4.2 specifications.  It is defined by RFC7668. 6LoWPAN is a software specification that will work with most Bluetooth 4.0 adapters therefore we do not need a special Bluetooth 4.2 adapter to use it.  We also do not need any additional network infrastructure, beside the Bluetooth adapters, like we do with wired and WiFi networks.  Bluetooth smart technologies also use significantly less power as compared to USB WiFi adapters.

One thing to note is I am using two BeagleBone Blacks for this post however these instructions will also work with Ubuntu 15 computers (or most other Linux based computers) if you are using a 4.4.X or newer kernel.  You can check your kernel version by issuing the following command:

uname -r

In this post I will walk though installing a new kernel on the BeagleBone Black.  If you are using Ubuntu 15 and have an older kernel, you will need to upgrade it.  This Google search should return a number of sites that will walk you though the upgrade process.

For this post I am starting with the standard Debian 8.3 image on a 16 gig SD card.  When I attempted the steps described in this post with a 4 gig SD card I ran out of room so you will need to use at least an 8 gig SD card.  The first step will be installing a new kernel.


Installing a new kernel on your BeagleBone Black (pre-built 4.4.6-bone6 kernel)

If you attempt to use 6LoWPAN with the standard kernel that comes with Debian 8.3 the two devices appear to connect however the connection will drop after a few seconds.  The problem is fixed in the 4.4.X or newer kernels.  The kernel that I am using is the pre-built 4.4.6-bone6 kernel.  Any of the 4.4.X or newer kernels should work but I can confirm that the 4.4.6-bone6 kernel does work. 

When I first followed the steps define in this post I already had Swift installed on both of my BeagleBone Blacks as described in this post and the kernel installed correctly with the Bluetooth drivers.  After working with this a little bit, I had to reset one of my BeagleBone Blacks and I ended up installing the new kernel without installing Swift first.  When the installation of the new kernel was complete and my BeagleBone Black came back up, after the reboot, my Bluetooth adapters no longer worked.  In the logs I saw that there was an issue loading the Broadcom drivers for the adapter.  It took me two days of experimenting but I finally figured out that if I installed Swift first then after the new kernel was installed my Bluetooth adapters worked properly.  I believe it has to do with installing libicu-dev and clang-3.6 with Swift. 

If you have a problem with your adapter after upgrading the kernel you may want to start over and use the following steps to install libicu-dev and clang-3.6 prior to upgrading your kernel.

sudo apt-get install libicu-dev
sudo apt-get install clang-3.6
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.6 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 100

When we install the 4.4.6-bone6 kernel we also need to install the headers.  The following command will install both the kernel and the headers.

apt-get install linux-headers-4.4.6-bone6 linux-image-4.4.6-bone6

Once the new kernel is installed we will need to restart the BeagleBone Black.  Once it has restarted we can begin configuring the 6LoWPAN connection on both the master and slave devices.

Configure the 6LoWPAN Master

The 6LoWPAN Master is the device that listens for incoming connections.  Create a new file named bluetoothMaster.sh on the device you wish to use as the master and put the following code in it.

modprobe bluetooth_6lowpan
echo 1 > /sys/kernel/debug/bluetooth/6lowpan_enable
hciconfig hci0 leadv

In this script we begin by loading the bluetooth_6lowpan module.  We then echo a 1 into the 6lowpan_enable file.  This will enable 6LoWPAN on the device.  Finally we use the hciconfig hci0 leadv command to begin advertising. 

We will also need another script that will setup the network once the client connects.  Let's call this script setNet.sh but we will want to wait to add the code to this script until the first client connects.  This will make it easer to get the IP Address that we will define for our interface.

Configure the 6LoWPAN Slave

The 6LoWPAN Slave is the device that connects to the master.  Create a new file named bluetoothSlave.sh and put the following code in it.

modprobe bluetooth_6lowpan
echo 1 > /sys/kernel/debug/bluetooth/6lowpan_enable
hcitool lecc 98:58:8a:06:db:9b
echo "connect 98:58:8a:06:db:9b 1" > /sys/kernel/debug/bluetooth/6lowpan_control

Note:  Change the 98:58:8a:06:db:9b in this script to the Bluetooth MAC address of the Bluetooth adapter on the master device.  To get the MAC address for a Bluetooth adapter you can use the hcitool dev command on the device that the Bluetooth adapter is connected to. 

In this script we begin by loading the bluetooth_6lowpan module and enabling 6lowpan as we did in the bluetoothMaster.sh script.  We then use the hcitool command with the lecc option to connect to the master.  The address in the hcitool command is the MAC address for the Bluetooth adapter on the master.   Finally we echo “connect {MAC address} 1” to the 6lowpan_control file which will enable the 6LoWPAN connection between the master and slave devices.

Testing the 6LoWPAN connection

Now that we have a 6LoWPAN connection between our two devices we can test the connection by attempting to ping one device from the other.  The first thing we need to do is to get the IPv6 addresses assigned to each device. We can do this by running the following command on each device.
ifconfig bt0

You should see something like this:

bt0       Link encap:UNSPEC  HWaddr 98-58-8A-FF-FE-06-DB-9B-00-00-00-00-00-00-00-00 
          inet6 addr: fe80::9a58:8aff:fe06:db9b/64 Scope:Link
          UP POINTOPOINT RUNNING MULTICAST  MTU:1280  Metric:1
          RX packets:13 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:527 (527.0 B)  TX bytes:321 (321.0 B)

If this were the bt0 address on my master device then on the slave device I would run the following command:

ping6 –I bt0 fe80::9a58:8aff:fe06:db9b

If the ping works then our connection is correctly setup however addresses with the fe80 prefix are link-local address that really isn’t that useful except for testing the connection.  Lets see how we could generate a non link-local address and assign it to the bt0 interface.

Generating and setting an IPv6 address

Before we generate our own IPv6 address, lets look at how the OS generated the link-local address.  In the example above the address generate when the interface came up is: fe80::9a58:8aff:fe06:db9b .  The Bluetooth MAC address for the adapter is: 98:58:8a:06:db:9b.  Do you notice anything similar between the IPv6 address assigned to the bt0 adapter and the MAC address of the Bluetooth adapter?

As you probably saw, the OS uses the Bluetooth MAC address to generate the IPv6 address.  Lets look at how the IPv6 address was generated.  We start off with the MAC address and add an 0xFF and 0xFE between the third and forth octet to give us 98:58:8a:ff:fe:06:db:9b.  We then OR 0x02 to the first octet to give us 9a:58:8a:ff:fe:06:db:9b.  We can now covert this to standard IPv6 notation where we group four hexadecimal digits together and separate the groups by a colon.  This will give us  9a58:8aff:fe06:db9b.   Finally we need to add a prefix.  The OS used the fe80 prefix that is reserved for link-local connections which gave us the fe80::9a58:8aff:fe06:db9b .   

For the IPv6 address that we will assign to the bt0 interface we will use the 2001:db8 prefix that is reserved for documentation.  This will give us the following address: 2001:db8::9a58:8aff:fe06:db9b.  Since the OS generates the link-local address, rather than going through these calculation steps we could simply take the link-local address and remove the fe80 prefix and add the prefix we wish to use.

Now that we have an address we will want to assign it to the bt0 interface.  To do this we will add the following line to the setNet.sh script on the master:

ifconfig bt0 inet6 add 2001:db8::9a58:8aff:fe06:db9b/64

Remember to replace 2001:db8::9a58:8aff:fe06:db9b with the address for your device. 

We will also want to add the following two lines to the end of the bluetoothSlave.sh file on the slave device:

sleep 2 //pause to make sure the bt0 interface is up
ifconfig bt0 inet6 add 2001:db8::5ef3:70ff:fe75:b1d6/64

Remember to replace the 2001:db8::5ef3:70ff:fe75:b1d6 address with the one for your slave.

The final scripts

Before we test everything lets look at the final versions of our scripts.  On the master we have two scripts.  These are named bluetoothMaster.sh and setNet.sh.  The bluetoothMaster.sh should contain the following code:
 
modprobe bluetooth_6lowpan
echo 1 > /sys/kernel/debug/bluetooth/6lowpan_enable
hciconfig hci0 leadv

The setNet.sh should contain the following code:

ifconfig bt0 inet6 add 2001:db8::9a58:8aff:fe06:db9b/64

Remember to replace the IPv6 address in this script with the correct address for your device.

The slave should contain one script named bluetoothSlave.sh which should contain the following code.

modprobe bluetooth_6lowpan
echo 1 > /sys/kernel/debug/bluetooth/6lowpan_enable
hcitool lecc 98:58:8a:06:db:9b
echo "connect 98:58:8a:06:db:9b 1" > /sys/kernel/debug/bluetooth/6lowpan_control

sleep 2 //pause to make sure the bt0 interface is up
ifconfig bt0 inet6 add 2001:db8::5ef3:70ff:fe75:b1d6/64

In this script your will want to replace the  98:58:8a:06:db:9b with the Bluetooth MAC address of your Master device and replace the 2001:db8::5ef3:70ff:fe75:b1d6  with the IPv6 address for your slave device.

Putting it all together

Now that we have our scripts lets test everything and see how it works.  Go ahead and reboot both devices to clear everything.  When the master device comes up run the bluetoothMaster.sh script.  After the bluetoothMaster.sh finishes, run the bluetoothSlave.sh script on the slave device.  After the bluetoothSlave.sh finishes run the setNet.sh script on the master.  

After all three scripts complete the 6LoWPAN connection should be up.  We can test this with the ifconfig bt0 command.  If all is well we should see that the bt0 interface is up on both devices with both the link-local IPv6 address and the IPv6 address that you assigned it.  We can now test the connection by using ssh to remotely connect to one device from the other.