Introduction

This is the chapter web page to support the content in Chapter 9 of the book: Exploring BeagleBone – Tools and Techniques for Building with Embedded Linux. The summary introduction to the chapter is as follows:

In this chapter you can learn how to build on your knowledge of GPIO and bus interfacing. In particular, you can combine hardware and software in order to provide the Beagle boards with the ability to interact with their physical environments in the following three ways: First, by controlling actuators such as motors, the board can affect its environment, which is very important for applications such as robotics and home automation. Second, the board can gather information about its physical environment by communicating with sensors. Third, by interfacing to display modules, the board can present information. This chapter explains how each of these interactions can be performed. Physical interaction hardware and software provides you with the capability to build advanced projects; for example, to build a robotic platform that can sense and interact with its environment. The chapter finishes with a discussion on how a reader can build their own C/C++ code libraries and how they can interact with them in order to build highly-scalable projects

Learning Outcomes

After completing this chapter, you should be able to:

  • Interface to actuators, such as DC motors, stepper motors, and relays.
  • Protect the AM335x ADC from damage using op-amp clamping.
  • Condition a sensor signal so that it can be interfaced to the Beagle board ADCs, regardless of the output voltage levels.
  • Interface analog sensors such as distance sensors and accelerometers to the Beagle boards.
  • Interface to low-cost display modules such as seven-segment displays and character LCD displays.
  • Build C/C++ code as a dynamic library to be used on a Linux SBC.

Chapter 9 on a Breadboard

Figure 9-A1: Chapter 9 all on one breadboard! (First Edition)

Additional Content (for First Edition)

The exploringBB Library

There are several examples in this chapter that leverage the code that is available in Chapter 6 and Chapter 8 of the book. One way that I could have written these examples is to copy the library code into each project directory; however, it would be incredibly difficult to make future corrections and keep the source-code examples up to date. Instead, the code from Chapters 6 and 8 is packaged as a library in the exploringBB/library/ directory.

For an example of how this is utilized, the LCDcharacter example in Chapter 9 consists of a single C++ program, LCDApp.cpp, which begins by including the LCDCharacterDisplay.h header and the exploringBB namespace:

#include < iostream >
#include < sstream >
#include "display/LCDCharacterDisplay.h"
using namespace std;
using namespace exploringBB;

int main(){
   cout << "Starting EBB LCD Character Display Example" << endl;
...

There is no display sub-directory in this project, rather display is a sub-directory of the directory exploringBB/library/. Therefore, to build this code the following compilation instruction is used:

g++ LCDApp.cpp ../../library/libEBBLibrary.so -o LCDApp -I "../../library"

This instruction explicitly includes the libEBBLibrary.so shared library and also states that the exploringBB/library/ directory should be added to the include path (using -I), thus bringing the library include directory into the project. The advantages of this approach are:

  • The library code only exists in a single location, and any corrections are therefore applied to all of the examples in the repository — that is a very significant advantage!
  • The library executable code only exists in a single location on the BeagleBone, reducing its storage overhead.
  • The library executable code is compiled code that need not be compiled again. You have access to the text-format header files, but these are not “compiled” into your project. Therefore, compilation times are much faster.

The downside is complexity, but once you have the project structure in place then changes are reasonably straightforward. I would suggest that if you are planning to write a significant amount of code that you use this library directory as a template for your project. To ensure that the process is as seamless as possible, I modified the library in April 2015 to provide support for CMake.

CMake and the exploringBB Library

The make utility and Makefiles provide a build system that can be used to manage the compilation and re-compilation of programs that are written in any programming language. I use Makefiles quite often in my projects to automate the build process; however, there are times when Makefiles become overly complex for the task — particularly when building projects that have multiple sub directories, or projects that are to be deployed to multiple platforms.

The use of Makefiles is described in Chapter 11 (Pg. 439) in order to build Gtk applications. In that context, this discussion is somewhat premature but Chapter 9 is the natural home for this material, so please treat this as a placeholder to which you can revisit after you have completed the later chapters.

The article on my blog site: Introduction to CMake by Example describes how to use CMake in your projects, describing how you can build a simple project, build shared/static libraries, and to use a shared/static library in your application code.

Introduction to CMake by Example

An Introduction to CMake by Example (click to view the article)

The current directory structure of the exploringBB library is visible below:

molloyd@beaglebone:~/exploringBB/library$ tree
.
|-- CMakeLists.txt
|-- ExploringBB.Doxyfile
|-- README
|-- build
|-- bus
|   |-- BusDevice.cpp
|   |-- BusDevice.h
|   |-- I2CDevice.cpp
|   |-- I2CDevice.h
|   |-- SPIDevice.cpp
|   `-- SPIDevice.h
|-- display
|   |-- LCDCharacterDisplay.cpp
|   |-- LCDCharacterDisplay.h
|   |-- SevenSegmentDisplay.cpp
|   `-- SevenSegmentDisplay.h
|-- example
|   `-- TestCode.cxx
|-- gpio
|   |-- GPIO.cpp
|   |-- GPIO.h
|   |-- PWM.cpp
|   |-- PWM.h
|   |-- util.cpp
|   `-- util.h
|-- libEBBLibrary.a
|-- libEBBLibrary.so
|-- motor
|   |-- DCMotor.cpp
|   |-- DCMotor.h
|   |-- Servo.cpp
|   |-- Servo.h
|   |-- StepperMotor.cpp
|   `-- StepperMotor.h
|-- network
|   |-- SocketClient.cpp
|   |-- SocketClient.h
|   |-- SocketServer.cpp
|   `-- SocketServer.h
`-- sensor
    |-- ADXL345.cpp
    |-- ADXL345.h
    |-- BMA180.cxx
    |-- BMA180.hxx
    |-- ITG3200.cpp
    `-- ITG3200.h
8 directories, 38 files

You can modify the library code to suit your application, for example by adding additional C++ classes. Then, when you are ready you can rebuild the library using the following steps:

molloyd@beaglebone:~/exploringBB/library$ sudo apt-get install cmake
...
molloyd@beaglebone:~/exploringBB/library$ cd build
molloyd@beaglebone:~/exploringBB/library/build$ cmake ..
-- The C compiler identification is GNU 4.6.3
-- The CXX compiler identification is GNU 4.6.3
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Looking for include file pthread.h
-- Looking for include file pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found.
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE  
-- Configuring done
-- Generating done
-- Build files have been written to: /home/molloyd/exploringBB/library/build
molloyd@beaglebone:~/exploringBB/library/build$ make
Scanning dependencies of target EBBLibrary
[  6%] Building CXX object CMakeFiles/EBBLibrary.dir/display/SevenSegmentDisplay.cpp.o
...
[100%] Building CXX object CMakeFiles/EBBLibrary.dir/motor/Servo.cpp.o
Linking CXX shared library libEBBLibrary.so
[100%] Built target EBBLibrary
molloyd@beaglebone:~/exploringBB/library/build$ ls -l libEBBLibrary.so 
-rwxr-xr-x 1 molloyd molloyd 93856 Apr  2 23:39 libEBBLibrary.so
molloyd@beaglebone:~/exploringBB/library/build$ cp libEBBLibrary.so ..

This process builds the shared library libEBBLibrary.so in the build directory. You can then place it in the parent directory for archival and then install it on your BeagleBone using a call to make install — this essentially places the library in the /usr/lib directory and therefore must be executed with superuser privileges:

molloyd@beaglebone:~/exploringBB/library/build$ sudo make install
[100%] Built target EBBLibrary
Install the project...
-- Install configuration: "Release"
-- Installing: /usr/lib/libEBBLibrary.so

molloyd@beaglebone:~/exploringBB/library/build$ ls -l /usr/lib/libEBBLibrary.so 
-rw-r--r-- 1 root root 93856 Apr  3 14:15 /usr/lib/libEBBLibrary.so

If you so wish, you can delete the contents in the build directory after you have built the library.

If you add a new C++ class/file to the project, it is very important that you perform the cmake step again, as the generated Makefiles will not include the new class/file.

Digital Media Resources

Here the digital resources referred to in the chapter web page are provided. There are high-resolution versions of some of the important figures and links to videos, resources and websites that are described in the chapter.
Driving Stepper Motors with the EasyDriver Board

This video examines how we can drive stepper motors using C++ within Embedded Linux using the open source hardware EasyDriver board. The video begins by describing stepper motors and the effects of micro-stepping. It then discusses the EasyDriver Board (V4.4) and all of the available inputs and outputs. The board uses the Allegro A3967 which allows for full, half-, quarter and one eight micro-stepping. The video then explains C++ code that uses the GPIOs on the BeagleBoard to wrap the EasyDriver with a C++ class that is easy to use by creating an object of the class for each stepper motor that is connected.

Source Code

DCMotor.h

Listing 9-1: DCMotor.h
/*
 * DCMotor.h  Created on: 12 Jun 2014
 * Copyright (c) 2014 Derek Molloy (www.derekmolloy.ie)
 * Made available for the book "Exploring BeagleBone" 
 * See: www.exploringbeaglebone.com
 * Licensed under the EUPL V.1.1
 *
 * This Software is provided to You under the terms of the European 
 * Union Public License (the "EUPL") version 1.1 as published by the 
 * European Union. Any use of this Software, other than as authorized 
 * under this License is strictly prohibited (to the extent such use 
 * is covered by a right of the copyright holder of this Software).
 * 
 * This Software is provided under the License on an "AS IS" basis and 
 * without warranties of any kind concerning the Software, including 
 * without limitation merchantability, fitness for a particular purpose, 
 * absence of defects or errors, accuracy, and non-infringement of 
 * intellectual property rights other than copyright. This disclaimer 
 * of warranty is an essential part of the License and a condition for 
 * the grant of any rights to this Software.
 * 
 * For more details, see http://www.derekmolloy.ie/
 */

#ifndef DCMOTOR_H_
#define DCMOTOR_H_
#include "../gpio/GPIO.h"
#include "../gpio/PWM.h"

#define DEFAULT_DCMOTOR_PWM_PERIOD 4000
#define DEFAULT_DCMOTOR_SPEED      50.0f

namespace exploringBB {

/**
 * @class DCMotor
 * @brief A generic DC motor class that controls a motor driver board using a
 * PWM signal,and a GPIO state to control the motor direction.
 */
class DCMotor {
public:
	enum DIRECTION{ CLOCKWISE, ANTICLOCKWISE };
private:
	GPIO *gpio;
	PWM *pwm;
	float speedPercent;
	DIRECTION direction;
	void init(PWM *pwm, GPIO *gpio, DCMotor::DIRECTION direction, float speedPercent);
public:
	DCMotor(PWM *pwm, GPIO *gpio);
	DCMotor(PWM *pwm, int gpioNumber);
	DCMotor(PWM *pwm, GPIO *gpio, DCMotor::DIRECTION direction);
	DCMotor(PWM *pwm, int gpioNumber, DCMotor::DIRECTION direction);
	DCMotor(PWM *pwm, GPIO *gpio, DCMotor::DIRECTION direction, float speedPercent);
	DCMotor(PWM *pwm, int gpioNumber, DCMotor::DIRECTION direction, float speedPercent);
	virtual void go();
	virtual void setSpeedPercent(float speedPercent);
	virtual float getSpeedPercent() { return this->speedPercent; }
	virtual void setDirection(DIRECTION direction);
	virtual DIRECTION getDirection() { return this->direction; }
	virtual void reverseDirection();
	virtual void stop();
	virtual void setDutyCyclePeriod(unsigned int period_ns);
	virtual ~DCMotor();
};

} /* namespace exploringBB */

#endif /* DCMOTOR_H_ */

StepperMotor.h

Listing 9-3: StepperMotor.h
/*
 * StepperMotor.h  Created on: 13 Jun 2014
 * Copyright (c) 2014 Derek Molloy (www.derekmolloy.ie)
 * Made available for the book "Exploring BeagleBone" 
 * See: www.exploringbeaglebone.com
 * Licensed under the EUPL V.1.1
 *
 * This Software is provided to You under the terms of the European 
 * Union Public License (the "EUPL") version 1.1 as published by the 
 * European Union. Any use of this Software, other than as authorized 
 * under this License is strictly prohibited (to the extent such use 
 * is covered by a right of the copyright holder of this Software).
 *
 * This Software is provided under the License on an "AS IS" basis and 
 * without warranties of any kind concerning the Software, including 
 * without limitation merchantability, fitness for a particular purpose, 
 * absence of defects or errors, accuracy, and non-infringement of 
 * intellectual property rights other than copyright. This disclaimer 
 * of warranty is an essential part of the License and a condition for 
 * the grant of any rights to this Software.
 *
 * For more details, see http://www.derekmolloy.ie/
 */

#ifndef STEPPERMOTOR_H_
#define STEPPERMOTOR_H_
#include "../gpio/GPIO.h"

namespace exploringBB {

/**
 * @class StepperMotor
 * @brief A class to control a stepper motor using a motor driver board, such as the
 * Easy Driver board, or compatible. The class uses five GPIOs to control each motor.
 */
class StepperMotor {

public:
	enum STEP_MODE { STEP_FULL, STEP_HALF, STEP_QUARTER, STEP_EIGHT };
	enum DIRECTION { CLOCKWISE, ANTICLOCKWISE };

private:
	// The GPIO pins MS1, MS2 (Microstepping options), STEP (The low->high step)
	// SLP (Sleep - active low) and DIR (Direction)
   GPIO *gpio_MS1, *gpio_MS2, *gpio_STEP, *gpio_SLP, *gpio_DIR;
   unsigned int uSecDelay;
   DIRECTION direction;
   int delayFactor;      // keep constant rpm even with microstepping
   STEP_MODE stepMode;
   float speed;
   int stepsPerRevolution;
   bool asleep;
   void init(int speedRPM, int stepsPerRevolution);

public:
   StepperMotor(GPIO *gpio_MS1, GPIO *gpio_MS2, GPIO *gpio_STEP, GPIO *gpio_SLP,
    			GPIO *gpio_DIR, int speedRPM = 60, int stepsPerRevolution = 200);
   StepperMotor(int gpio_MS1, int gpio_MS2, int gpio_STEP, int gpio_SLP,
    			int gpio_DIR, int speedRPM = 60, int stepsPerRevolution = 200);

   virtual void  step();
   virtual void  step(int numberOfSteps);
   virtual int   threadedStepForDuration(int numberOfSteps, int duration_ms);
   virtual void  threadedStepCancel() { this->threadRunning = false; }
   virtual void  rotate(float degrees);
   virtual void  setDirection(DIRECTION direction);
   virtual DIRECTION getDirection() { return this->direction; }
   virtual void  reverseDirection();
   virtual void  setStepMode(STEP_MODE mode);
   virtual STEP_MODE getStepMode() { return stepMode; }
   virtual void  setSpeed(float rpm);
   virtual float getSpeed() { return speed; }
   virtual void  setStepsPerRevolution(int steps) { stepsPerRevolution = steps; }
   virtual int   getStepsPerRevolution() { return stepsPerRevolution; }
   virtual void  sleep();
   virtual void  wake();
   virtual bool  isAsleep() { return asleep; }
   virtual ~StepperMotor();

private:
   bool threadRunning;
   pthread_t thread;
   CallbackType callbackFunction;
   int threadedStepPeriod, threadedStepNumber;
   friend void* threadedStep(void *value);
};

void* threadedStep(void *value);

} /* namespace exploringBB */

#endif /* STEPPERMOTOR_H_ */

testADC.cpp

Listing 9-5: testADC.cpp
/* A Test ADC Application
* Written by Derek Molloy for the book "Exploring BeagleBone: Tools and 
* Techniques for Building with Embedded Linux" by John Wiley & Sons, 2014
* ISBN 9781118935125. Please see the file README.md in the repository root 
* directory for copyright and GNU GPLv3 license information.            */

#include<iostream>
#include<fstream>
#include<string>
#include<sstream>
#include<cmath>
using namespace std;

#define LDR_PATH "/sys/bus/iio/devices/iio:device0/in_voltage"

int readAnalog(int number){
   stringstream ss;
   ss << LDR_PATH << number << "_raw";
   fstream fs;
   fs.open(ss.str().c_str(), fstream::in);
   fs >> number;
   fs.close();
   return number;
}

int main(int argc, char* argv[]){
   cout << "The value on the ADC is:" << endl;
   for(int i=0; i<1000; i++){
      int value = readAnalog(0);
      cout << "  = " << readAnalog(0) << "/4095    " << '\r' << flush;
      usleep(50000);
   }
   return 0;
}

SevenSegmentDisplay.h

Listing 9-7: SevenSegmentDisplay.h
/*
 * SevenSegmentDisplay.h  Created on: 6 Jun 2014
 * Copyright (c) 2014 Derek Molloy (www.derekmolloy.ie)
 * Made available for the book "Exploring BeagleBone" 
 * See: www.exploringbeaglebone.com
 * Licensed under the EUPL V.1.1
 *
 * This Software is provided to You under the terms of the European 
 * Union Public License (the "EUPL") version 1.1 as published by the 
 * European Union. Any use of this Software, other than as authorized 
 * under this License is strictly prohibited (to the extent such use 
 * is covered by a right of the copyright holder of this Software).
 * 
 * This Software is provided under the License on an "AS IS" basis and 
 * without warranties of any kind concerning the Software, including 
 * without limitation merchantability, fitness for a particular purpose, 
 * absence of defects or errors, accuracy, and non-infringement of 
 * intellectual property rights other than copyright. This disclaimer 
 * of warranty is an essential part of the License and a condition for 
 * the grant of any rights to this Software.
 * 
 * For more details, see http://www.derekmolloy.ie/
 */

#ifndef SEVENSEGMENTDISPLAY_H_
#define SEVENSEGMENTDISPLAY_H_
#include "../bus/SPIDevice.h"

namespace exploringBB {

/**
 * @class SevenSegmentDisplay
 * @brief A class that allows you to drive an array of 7 segment displays using an array of 74XX595 ICs
 */
class SevenSegmentDisplay {
private:
	SPIDevice *spidevice; //!< The SPI bus device
	int numberSegments;   //!< The number of segments connected
	int numberBase;       //!< Base can be between 2 (binary) and 16 (hexadecimal). Default is decimal (base 10)
	bool isCommonAnode;   //!< Is a common anode display -- false by default

public:
	SevenSegmentDisplay(SPIDevice *device, int numberSegments);
	virtual int write(int number);                // writes an integer
	virtual int write(float number, int places);  // uses the "decimal" point
	virtual int setNumberBase(int base);
	virtual int getNumberBase() { return this->numberBase; }
	virtual int getNumberSegments() { return this->numberSegments; }
	virtual void setCommonAnode(bool isCommonAnode) { this->isCommonAnode = isCommonAnode; }
	virtual ~SevenSegmentDisplay();       // closes the SPI device

private:
	const static unsigned char symbols[]; //!< The array of symbols (i.e., characters to LED segments)
};

} /* namespace exploringBB */

#endif /* SEVENSEGMENTDISPLAY_H_ */

LCDCharacterDisplay.h

Listing 9-9: LCDCharacterDisplay.h
/*
 * LCDCharacterDisplay.h  Created on: 24 May 2014
 * Copyright (c) 2014 Derek Molloy (www.derekmolloy.ie)
 * Made available for the book "Exploring BeagleBone"
 * See: www.exploringbeaglebone.com
 * Licensed under the EUPL V.1.1
 *
 * This Software is provided to You under the terms of the European
 * Union Public License (the "EUPL") version 1.1 as published by the
 * European Union. Any use of this Software, other than as authorized
 * under this License is strictly prohibited (to the extent such use
 * is covered by a right of the copyright holder of this Software).
 *
 * This Software is provided under the License on an "AS IS" basis and
 * without warranties of any kind concerning the Software, including
 * without limitation merchantability, fitness for a particular purpose,
 * absence of defects or errors, accuracy, and non-infringement of
 * intellectual property rights other than copyright. This disclaimer
 * of warranty is an essential part of the License and a condition for
 * the grant of any rights to this Software.
 *
 * For more details, see http://www.derekmolloy.ie/
 */

#ifndef LCDCHARACTERDISPLAY_H_
#define LCDCHARACTERDISPLAY_H_
#include "../bus/SPIDevice.h"
#include <string>

namespace exploringBB {

/**
 * @class LCDCharacterDisplay
 * @brief A class that provides an interface to an LCD character module. It provices support
 * for multiple rows and columns and provides methods for formatting and printing text. You
 * should use a 4 wire interface and a 74XX595 to communicate with the display module.
 */

class LCDCharacterDisplay {

private:
	SPIDevice *device;          //!< a pointer to the SPI device
	int width, height;          //!< the width and height of the module in characters
	void command(char i);
	void setup4bit();
	unsigned char cursorState;
	unsigned char displayState;
	unsigned char entryState;
	void writeCursorState();
	void writeDisplayState();
	void writeEntryState();

public:
	LCDCharacterDisplay(SPIDevice *device, int width, int height);

	virtual void write(char c);
	virtual void print(std::string message);

	virtual void clear();
	virtual void home();
	virtual int  setCursorPosition(int row, int column);
	virtual void setDisplayOff(bool displayOff);
	virtual void setCursorOff(bool cursorOff);
	virtual void setCursorBlink(bool isBlink);
	virtual void setCursorMoveOff(bool cursorMoveOff);
	virtual void setCursorMoveLeft(bool cursorMoveLeft);
	virtual void setAutoscroll(bool isAutoscroll);
	virtual void setScrollDisplayLeft(bool scrollLeft);

	virtual ~LCDCharacterDisplay();
};

} /* namespace exploringBB */

#endif /* LCDCHARACTERDISPLAY_H_ */

Some High-Resolution Figures from this Chapter

Here are high-resolution images of some of the more complex figures in this chapter, which may help you in wiring the circuits. Please note that you can close this pop-up window by pressing the Escape key.

External Resources

Important Documents

External Web Sites

AM335x ARM A8 Technical Reference Manual

The AM335x Technical Reference Manual (TRM)

BeagleBone Black System Reference Manual

The BeagleBone Black System Reference Manual (SRM)

Errata

Second Edition

  • None so far

First Edition

  • Page 335, typo in figure 9-5 — the PDF input should be PFD input.
  • Page 336, Listing 9-3 refers to StepperMotor.h, not StepperMotor.cpp.
  • Page 346. There is a mistake in the calculation of the cutoff frequency (fc) that is carried forward to Figure 9-15. The equation should be R12C1 = 1/(2π x fc), where R12 = R1||R2 (i.e., R1 in parallel with R2). The top-left of Figure 9-11 should also read: fc = 1/(2π x R12C1). The calculation in Figure 9-15 should therefore read: R12C1 = 1/(2π x fc) = 1/(2π x 52Hz) = 0.00306. Since R12 = R1||R2 = 3080||6920 = 2131 Ω, C1 = 0.00306 / 2131 = 1.44 µF instead of 0.994 µF. Here is an update of Figure 9-15 that includes corrected calculations.
  • Page 353, Figure 9-16 contains an error. The two lines on the right-hand side of the figure have accidentally been reversed. The x-axis output from the ADXL335 should be connected to the 2IN+ input of the MCP6002 and the 2Out/2IN- output from the MCP6002 should be connected to the 3.3KΩ resistor. An updated figure is available in the image carousel above and can be downloaded and printed using this link: Figure 9-16
Click edit button to change this text.