Showing posts with label tmp36. Show all posts
Showing posts with label tmp36. Show all posts

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.



Sunday, August 31, 2014

IoT: Displaying the temperature from the BeagleBone Black on my iPhone

In the first post of this series I used Javascript to read a TMP36GZ temperature sensor and write the current temperature to the console.  In the second post I used the Restify library with Javascript to create a rest based web service that would send the current temperature to a remote client upon request.  In this post I will access that web service to display the temperature on my iPhone using the RSNetworking library.

Let begin by reviewing how we wired up the TMP36GZ temperature sensor to the BeagleBone Black.  Here is the wiring diagram:



Next lets review how we developed the REST Web Service that will respond to client requests for the current temperature.  Note:  I am making one change to the code, from my last post, so the REST service returns a valid JSON object. 

The first thing we need to do is to setup a directory structure that contains the node.js modules needed to run the service.  I used the following steps to get the structure/modules set up (this is based off of the latest Debian image 2014-05-14):

1.  Start off within our home directory.  In Linux the ~ directory is the users home directory.
cd ~

2.  Create a work directory and change to that directory
mkdir temperature
cd temperature

3.  Install the restify module
npm install restify

4.  Copy the bonescript module to our working structure
mkdir node_modules/bonescript
cp /var/lib/cloud9/static/bonescript.js node_modules/bonescript/

At this point you should still be in the temperature directory and both the restify and bonescript modules should be located in the ~/temperature/node_modules directory.   This will let our application use these two modules.

From the ~/temperature directory, create a file called tempServer.js and put the following code in it.

var b = require('bonescript');
var rest = require('restify');

var ip = '0.0.0.0'
var port = '18080'
var path = 'temperature'
var tempPin = 'P9_38';
var currentTemp = 0.0;

b.analogRead(tempPin, readTemperature);
setInterval(function() {b.analogRead(tempPin, readTemperature)},30000);

var server = rest.createServer({
   name : "Temperature"
});

server.get({path : path , version : '0.1'}, getTemperature);

server.listen(port,ip, function() {
   console.log('%s listening at %s ',server.name, server.url);
});

function getTemperature(req, res, next) {
   var key = "temperature";
   var tempResponse = '{"temperature":"' + currentTemp + '"}';
   res.send(200, JSON.parse(tempResponse));
}

function readTemperature(aRead) {
   console.log("Geting Temp");
       var x = (aRead.value * 1800/1024);
       var cel = 100*x -50;
       var fah = (cel *9/5)+32;
   currentTemp = fah;
}

This code begins by loading both the bonescript and restify modules that are needed for this application.  We then set the following variables:

ip:  The IP address of the interface to bind too.  By using 0.0.0.0 the server will bind to all available interfaces (this is what we want).
port:  The port to bind too.  Typically web servers bind to port 80 but we do not want to take up that privileged port (and it also requires root access to bind to ports below 1024) so we will use 18080 for our service.
path:  The URL path for our service. 
tempPin:  The pin that will be connected to the TMP36GZ temperature sensor.
CurrentTemp:  Will contain the current temperature.  This will be updated every 30 seconds.

After we set the variables, we then read the temperature using the analogRead function from the bonescript module.  This function will read the voltage from the tempPin and then call the readTemperature function when it has the voltage.  The readTemperature function calculates the current temperature based on the voltage of the pin and stores that temperature into the currentTemp variable.

We use the Javascript setInterval function to call the readTemperature function every 30 seconds to update the currentTemp variable.

Now that we have the temperature and updating it every 30 seconds, we need to create our web service that will respond to our requests.  We start off by creating a server object using restify’s createServer function. 

Next we define what services we wish to offer though this server object.  In this case we only have one service.  This service will respond to HTTP GET requests so the get function from our server object is used to define the service.  This service will listen on the path defined in our path variable and when a request comes in it will call the getTemperature function.

Finally we till the server to listen on the port and interface that we defined in the variables earlier.

The getTemperature function simply creates a JSON object that contains the current temperature and uses the send function from the res response object to send the object back to the client that requested it.  This is where I made the code change from my previous post.  To create the JSON object, I first create a string that contains the response that I want to send back to the client and I then use JSON.parse() function to create a valid JSON object.

Now lets look at how we would write the iOS client application to retrieve the temperature from the BeagleBone Black’s web service.  We will be writing the client app in Swift (Apple’s new development language) and will use the RSNetworking library.  RSNetworking is a network library written entirely in the Swift programming language.  RSNetworking is built using Apple’s powerful URL Loading System.  The main design goal of RSNetworking is to make it easy and quick for developers to add powerful asynchronous networking requests, to their applications written in Swift.  As a disclaimer, I created and maintain the RSNetworking library. 

The first thing we will need to do is to download the RSNetworking library from Github and include the library in our project. 

In the MainStoryboard of our project we will add a UILabel (to display the temperature) and a UIButton (to get the temperature from the BeagleBone Black).  Here is a screen shot of how UI is laid out.



Now lets look at the code that will retrieve the temperature from the BeagleBone Black’s Web Service and display it on the screen when the button is pressed.

import UIKit

class ViewController: UIViewController {
   
    @IBOutlet var tempDisplay : UILabel!
                           
    override func viewDidLoad() {
        super.viewDidLoad()
       }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
       }
    
    @IBAction func getTemp(AnyObject) {
        var rsRequest : RSURLRequest = RSURLRequest();
        var url : NSURL = NSURL.URLWithString("http://10.0.1.30:18080/temperature");
        rsRequest.dictionaryFromJsonURL(url, completionHandler:{(response : NSURLResponse!, responseDictionary: NSDictionary!, error: NSError!) -> Void in
            if error == nil {
                println(responseDictionary);
                var temp: Double! = responseDictionary["temperature"]?.doubleValue!
                let displayStr : NSString = "".stringByAppendingFormat("%.2f", temp!)
                self.tempDisplay.text = displayStr
               
            } else {
                //If there was an error, log it
                println("Error : \(error)")
            }
        })
    }
}

The getTemp() function is tied to the UIButton.  When the button is pressed the applications accesses the Web Service on the BeagleBone Black and retrieves the temperature.  If there were no errors it displays the temperature in the UILabel.  So lets look at the getTemp() function.

We begin by creating an instance of the RSURLRequest class.  We then create an instance of NSURL using the URL for our Web Service.  We then use the dictionaryFromJsonURL() function from the RSURLRequest class to send our request to the BeagleBone Black. 

We pass a block (of code) to the dictionaryFromJsonURL() function.  This block of code verifies that we did not receive any errors and if no errors were received it converts the temperature that was received to a Double values and displays the temperature, with the precision that we want, in the UILabel.  If there were errors we simply log them.

That is all there is to creating our first IoT service with the BeagleBone Black.